From 07b2f55022f98c10a879aed997b095aad2f8df21 Mon Sep 17 00:00:00 2001 From: Davit Matchakhelidze Date: Sun, 18 Oct 2026 16:14:56 +0400 Subject: [PATCH 1/5] fonts: Georgian UI text from craft-fonts' Noto Sans Georgian on the web Native already appends every non-CJK craft face via ui_fallback; the wasm build only embedded BIZ UDPGothic and Noto Sans Thai, so add Noto Sans Georgian to that filter. Check its glyphs next to Thai and Arabic, and bump craft-fonts-ref to a71c20e (storytold/craft-fonts#27). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF --- .github/workflows/freebsd.yml | 2 +- .github/workflows/release.yml | 2 +- crates/engine/build.rs | 4 ++-- crates/engine/src/fonts.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 5fc2092a9..d85332b23 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -33,4 +33,4 @@ jobs: uses: storytold/craft-common-actions/.github/workflows/freebsd.yml@main with: require-craft-fonts: true - craft-fonts-ref: 3d9ab3a132ed0eff457e424226a435a901da30d3 + craft-fonts-ref: a71c20e30af613287c504f7e685ca8fad77752bc diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e24229356..7bc123bea 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,7 +26,7 @@ jobs: with: app-name: lightcraft app-title: LightCraft - craft-fonts-ref: 3d9ab3a132ed0eff457e424226a435a901da30d3 + craft-fonts-ref: a71c20e30af613287c504f7e685ca8fad77752bc version: ${{ inputs.version }} secrets: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} diff --git a/crates/engine/build.rs b/crates/engine/build.rs index 056c7adc8..7f5aa7777 100644 --- a/crates/engine/build.rs +++ b/crates/engine/build.rs @@ -3,7 +3,7 @@ //! Recipe from craft-fonts' docs/integration.md; craft-fonts is never a Cargo dependency, and this //! script only reads a local directory (no network). //! -//! wasm32 embeds BIZ UDPGothic Regular (~4.5 MB) and Noto Sans Thai (~214 KiB), to keep the +//! wasm32 embeds BIZ UDPGothic Regular (~4.5 MB) and Noto Sans Thai (~214 KiB) and Noto Sans Georgian, to keep the //! `.wasm` within the web size budget (Cloudflare serves at most 25 MiB per file); the export //! watermark falls back to it there. Native builds embed every font. use std::fmt::Write as _; @@ -78,7 +78,7 @@ fn craft_fonts(dir: &Path, required: bool) -> Result { has_japanese_ui |= (*family, *style) == ("BIZ UDPGothic", "Regular"); has_chinese_ui |= (*family, *style) == ("Noto Sans CJK SC", "Regular") && scripts.split(',').any(|script| script.trim() == "Hans"); has_thai_ui |= (*family, *style) == ("Noto Sans Thai", "Regular") && scripts.split(',').any(|script| script.trim() == "Thai"); - if wasm && !matches!((*family, *style), ("BIZ UDPGothic" | "Noto Sans Thai", "Regular")) { + if wasm && !matches!((*family, *style), ("BIZ UDPGothic" | "Noto Sans Thai" | "Noto Sans Georgian", "Regular")) { continue; } let path = dir.join(file).canonicalize().map_err(|e| format!("{file}: {e}"))?; diff --git a/crates/engine/src/fonts.rs b/crates/engine/src/fonts.rs index a246c4dca..db9e658d0 100644 --- a/crates/engine/src/fonts.rs +++ b/crates/engine/src/fonts.rs @@ -105,7 +105,7 @@ mod tests { assert!(japanese(CRAFT_FONTS).next().is_some(), "craft-fonts carries Japanese faces"); // Every script face that ships is a UI fallback, and has the glyphs of its script (#736). use ab_glyph::Font; - for (script, text) in [("Thai", "ภาพถ่ายสวัสดี๑๒๓"), ("Arab", "صورة")] { + for (script, text) in [("Thai", "ภาพถ่ายสวัสดี๑๒๓"), ("Arab", "صورة"), ("Geor", "ფოტო")] { let Some(face) = ui_fallback(CRAFT_FONTS, "Latn", "Regular").into_iter().find(|f| f.covers(script)) else { continue }; let font = ab_glyph::FontRef::try_from_slice(face.bytes).expect("parses"); assert!(text.chars().all(|c| font.glyph_id(c).0 != 0), "{} draws {script}", face.family); From e6e5d2d8a6101e43263a3ea5eb43f51ba1f2802c Mon Sep 17 00:00:00 2001 From: Davit Matchakhelidze Date: Sun, 18 Oct 2026 18:27:44 +0400 Subject: [PATCH 2/5] =?UTF-8?q?i18n(ka):=20=C3=97=20for=20the=20stop=20but?= =?UTF-8?q?ton=20in=20two=20What's=20new=20lines=20(Noto=20Sans=20Georgian?= =?UTF-8?q?=20has=20no=20=E2=9C=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit catalog_characters_are_paintable checks ka against the craft-fonts face since #27; U+2715 is in none of the UI faces, U+00D7 is. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF --- crates/ui-egui/locales/ka.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/locales/ka.json b/crates/ui-egui/locales/ka.json index bafd7827c..f6530929d 100644 --- a/crates/ui-egui/locales/ka.json +++ b/crates/ui-egui/locales/ka.json @@ -2575,7 +2575,7 @@ "on the CPU, are not waited for).": "CPU-ზე, არ ელოდება).", "on the DLL search path without its `dxil.dll` (issue #471): DirectX 12 shaders now always compile with the": "DLL-ების საძიებო გზაზეა მისი `dxil.dll`-ის გარეშე (ხარვეზი #471): DirectX 12 შეიდერები ახლა ყოველთვის კომპილირდება", "on the card after Review Import is reported instead of being imported with stale details. Checking an export path": "ბარათზე იმპორტის გადახედვის შემდეგ, აღირიცხება იმის ნაცვლად, რომ მოძველებული მონაცემებით იმპორტირდეს. ექსპორტის მისამართის შემოწმება", - "on; ✕ stops the tasks that can stop. Three rows show, then \"+N more\". The separate export panel and the progress": "მუშაობს; ✕ აჩერებს შეჩერებად ამოცანებს. ჩანს სამი სტრიქონი, შემდეგ „+N კიდევ“. ცალკე ექსპორტის პანელი, პროგრესის", + "on; ✕ stops the tasks that can stop. Three rows show, then \"+N more\". The separate export panel and the progress": "მუშაობს; × აჩერებს შეჩერებად ამოცანებს. ჩანს სამი სტრიქონი, შემდეგ „+N კიდევ“. ცალკე ექსპორტის პანელი, პროგრესის", "once you zoom out or move on.": "როცა დააპატარავებთ ან სხვაზე გადახვალთ.", "one by one — they can always be exported again, and on a USB drive or a NAS that per-file sync dominated a large": "სათითაოდ — მათი ხელახლა ექსპორტი ყოველთვის შეიძლება, ხოლო USB დისკზე ან NAS-ზე თითოეული ფაილის სინქრონიზაცია დიდ", "only convert between the camera's multipliers and Kelvin; the colour look is what it was.": "მხოლოდ კამერის მამრავლებსა და კელვინებს შორის გარდაქმნის; ფერის იერი ისეთივეა, როგორიც იყო.", @@ -2615,7 +2615,7 @@ "renders and screenshots are still written to a temp file and renamed into place, but no longer forced to disk": "რენდერები და ეკრანის ანაბეჭდები კვლავ დროებით ფაილში იწერება და ადგილზე გადაირქმევა, მაგრამ დისკზე იძულებით აღარ ჩაიწერება", "reports each row's `label`.": "თითოეული სტრიქონის `label`-ს აბრუნებს.", "restore the photo too and say \"Restored 1 photo from Recently Deleted\". A deleted photo whose file was moved is": "ფოტოს ასევე აღადგენს და ამბობს „ახლახან წაშლილიდან აღდგა 1 ფოტო“. წაშლილი ფოტო, რომლის ფაილიც გადაიტანეს,", - "runs in the background with a row in the activity stack (✕ stops it); everything it does is one undo step, and no file on disk": "ფონურად მიმდინარეობს აქტივობების დასტაში საკუთარი სტრიქონით (✕ აჩერებს); ყველაფერი, რასაც აკეთებს, ერთი დაბრუნების ნაბიჯია, და დისკზე არცერთ ფაილს", + "runs in the background with a row in the activity stack (✕ stops it); everything it does is one undo step, and no file on disk": "ფონურად მიმდინარეობს აქტივობების დასტაში საკუთარი სტრიქონით (× აჩერებს); ყველაფერი, რასაც აკეთებს, ერთი დაბრუნების ნაბიჯია, და დისკზე არცერთ ფაილს", "save goes through.": "შენახვა შესრულდება.", "saved in": "შენახულია:", "saved in an earlier version now shifts the other way; set it again (or re-run Auto / the white-balance picker).": "შენახული მორგებული ტონირება ახლა საპირისპიროდ იხრება; კვლავ დააყენეთ (ან ხელახლა გაუშვით ავტომატური / თეთრის ბალანსის პიპეტი).", From 18cfda0ee97e8c9455181cc6221a40ab86f48977 Mon Sep 17 00:00:00 2001 From: Davit Matchakhelidze Date: Sun, 18 Oct 2026 20:25:24 +0400 Subject: [PATCH 3/5] Release: build the web version with craft-fonts too (web-craft-fonts) The shared release workflow builds the web release without CRAFT_FONTS_DIR unless web-craft-fonts is set, so the web version would still have no Georgian face (CodeRabbit on vectorcraft#2934). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7bc123bea..b8fb01326 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,6 +27,7 @@ jobs: app-name: lightcraft app-title: LightCraft craft-fonts-ref: a71c20e30af613287c504f7e685ca8fad77752bc + web-craft-fonts: true version: ${{ inputs.version }} secrets: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} From 04e11e701a74f4754ed8dfa156a9300f527435f6 Mon Sep 17 00:00:00 2001 From: Davit Matchakhelidze Date: Mon, 19 Oct 2026 18:13:10 +0400 Subject: [PATCH 4/5] docs: Georgian draws with craft-fonts' Noto Sans Georgian; the web WASM keeps the Japanese, Thai and Georgian faces Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF --- docs/localization-ka.md | 5 +++-- docs/parity-checklist.md | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/localization-ka.md b/docs/localization-ka.md index 234ab5ee9..2addeaa2c 100644 --- a/docs/localization-ka.md +++ b/docs/localization-ka.md @@ -23,8 +23,9 @@ interface never uses Mtavruli (a test checks the whole catalog). Commands are ve are count-neutral, noun first (`იმპორტირებულია ფოტო: 3`); English plural endings are dropped with `{:.0}`. Dates read `კვირა, 20.09.2026` (`{weekday}, {day:02}.{month:02}.{year}`), months `09.2026`. -Inter has no Georgian letters and craft-fonts has no Georgian face yet, so Georgian text shows boxes -until a Georgian face is available to the UI. +Inter has no Georgian letters, so the UI draws Georgian with craft-fonts' Noto Sans Georgian (`Geor`), +like the Thai and Arabic faces: release builds embed it, and the web build keeps it in the WASM +(`crates/engine/build.rs`). A build without `CRAFT_FONTS_DIR` has no Georgian face and shows boxes. ## Terminology diff --git a/docs/parity-checklist.md b/docs/parity-checklist.md index 4e0531793..72035b54a 100644 --- a/docs/parity-checklist.md +++ b/docs/parity-checklist.md @@ -481,7 +481,7 @@ differences and missing bindings are in [ui-parity.md](ui-parity.md#shortcuts). | LR-BEHAV-HEADLESS | Headless UI snapshots | P2 | ✅ | `apps/lightcraft-cli/src/main.rs`, `crates/ui-egui/src/headless.rs`, `docs/control-protocol.md` | `snapshot` rasterizes the UI and defaults photo development to the CPU before the first frame, without adapter discovery; scaled dimensions are validated before allocation, and UI zoom preserves the requested physical viewport; JSON-lines control scripts are supported | | LR-BEHAV-ACCESS | Accessibility | P2 | 🟡 | `crates/ui-egui/src/widgets.rs`, `crates/ui-egui/src/text_field.rs`, `crates/ui-egui/src/panels/grid.rs`, `crates/ui-egui/tests/accessibility.rs` | AccessKit (VoiceOver / Narrator / AT-SPI): sliders announce control, value, range and step and take Increment / Decrement, buttons / icon buttons / dropdowns / section headers / sources their labels and state, text fields their caption, grid thumbnails file name, rating, flag and label. `tests/accessibility.rs` fails on an unnamed control, a slider without range or an untitled dialog in every view, Develop tool and 13 dialogs, and checks that Crop and Remove sliders are reached with Tab and that Tab walks the right panels top to bottom. Canvas tools by keyboard (`crates/ui-egui/src/canvas_keys.rs`, `crates/ui-egui/src/tests_canvas_keys.rs`): crop corners, edges and box, spot targets and sources, a New spot point (Return adds), radial / linear gradient handles and mask pins take the focus, are announced with their position and move with the arrows (Shift: further). Brush strokes and red-eye ellipses are pointer-only; not tried with a real screen reader ([audit](ui-parity.md#accessibility-audit)) | | LR-BEHAV-LOCALE | Language options | P2 | ✅ | `cmd:app.language.english`, `cmd:app.language.simplifiedChinese`, `cmd:app.language.traditionalChinese`, `cmd:app.language.japanese`, `cmd:app.language.portuguese`, `cmd:app.language.french`, `cmd:app.language.spanish`, `cmd:app.language.german`, `cmd:app.language.russian`, `cmd:app.language.ukrainian`, `cmd:app.language.georgian` | Edit > Language lists every language in the table and the active one is checked; shared with Settings > General (docs/localization.md) | -| LR-BEHAV-LOCALIZE | Localisation | P2 | 🟡 | `crates/ui-egui/src/i18n.rs`, `crates/ui-egui/locales/`, `docs/localization.md`, `docs/localization-zh-hans.md`, `docs/localization-zh-hant.md`, `docs/localization-ja.md`, `docs/localization-pt-br.md`, `docs/localization-es.md`, `docs/localization-de.md`, `docs/localization-ru.md`, `docs/localization-ka.md` | One table entry per language (code, endonym, ISO 15924 script, catalog): English, Simplified Chinese, Traditional Chinese (Taiwan), Japanese, Brazilian Portuguese, Spanish, German, Russian, Ukrainian and Georgian UI with a persisted language preference and craft-fonts regular/bold faces per script (a variable face, as for Arabic, is drawn heavier for bold from its one file); menus, edit, crop/masks, settings, import/export and primary progress messages. Adding a language registers the locale and its language command, plus two catalogs. Stock presets/profiles, history steps, library headings, date headings and capture times are localised (user-named items stay verbatim); the Ukrainian catalog covers every existing catalog key, command/control label and rule-editor label; the German catalog reports newly added gaps; the other catalogs fall back to English for messages they lack. Technical errors remain English; release notes are translated in German and Ukrainian; craft-fonts faces for other scripts (Thai, Arabic, …) are UI fallbacks in every language, for file, folder and album names (#736); Traditional Chinese borrows the Simplified Chinese face (no TC face in craft-fonts yet), and the web build embeds the Japanese face in WASM while loading the Simplified Chinese face from a separate font file. | +| LR-BEHAV-LOCALIZE | Localisation | P2 | 🟡 | `crates/ui-egui/src/i18n.rs`, `crates/ui-egui/locales/`, `docs/localization.md`, `docs/localization-zh-hans.md`, `docs/localization-zh-hant.md`, `docs/localization-ja.md`, `docs/localization-pt-br.md`, `docs/localization-es.md`, `docs/localization-de.md`, `docs/localization-ru.md`, `docs/localization-ka.md` | One table entry per language (code, endonym, ISO 15924 script, catalog): English, Simplified Chinese, Traditional Chinese (Taiwan), Japanese, Brazilian Portuguese, Spanish, German, Russian, Ukrainian and Georgian UI with a persisted language preference and craft-fonts regular/bold faces per script (a variable face, as for Arabic, is drawn heavier for bold from its one file); menus, edit, crop/masks, settings, import/export and primary progress messages. Adding a language registers the locale and its language command, plus two catalogs. Stock presets/profiles, history steps, library headings, date headings and capture times are localised (user-named items stay verbatim); the Ukrainian catalog covers every existing catalog key, command/control label and rule-editor label; the German catalog reports newly added gaps; the other catalogs fall back to English for messages they lack. Technical errors remain English; release notes are translated in German and Ukrainian; craft-fonts faces for other scripts (Thai, Arabic, …) are UI fallbacks in every language, for file, folder and album names (#736); Traditional Chinese borrows the Simplified Chinese face (no TC face in craft-fonts yet), and the web build embeds the Japanese, Thai and Georgian faces in WASM while loading the Simplified Chinese face from a separate font file. | | LR-BEHAV-LEARN | Tutorials | OOS | 🚫 | | | | LR-BEHAV-WHATSNEW | What's new | P2 | ✅ | `cmd:app.whatsNew`, `docs/whats-new.md` | Help ▸ What's New: release highlights | | LR-BEHAV-AI-EA | Early-access badges | P2 | ⬜ | | | From 2015f44c06dce1f7bbcaf0972f7c6dc24be08b8c Mon Sep 17 00:00:00 2001 From: Davit Matchakhelidze Date: Tue, 20 Oct 2026 12:56:17 +0400 Subject: [PATCH 5/5] Release: keep the web build without craft-fonts for now (its WASM would pass Cloudflare's 25 MiB limit); docs say so Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF --- .github/workflows/release.yml | 1 - docs/localization-ka.md | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b8fb01326..7bc123bea 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,6 @@ jobs: app-name: lightcraft app-title: LightCraft craft-fonts-ref: a71c20e30af613287c504f7e685ca8fad77752bc - web-craft-fonts: true version: ${{ inputs.version }} secrets: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} diff --git a/docs/localization-ka.md b/docs/localization-ka.md index 2addeaa2c..4d517c6ca 100644 --- a/docs/localization-ka.md +++ b/docs/localization-ka.md @@ -24,8 +24,9 @@ are count-neutral, noun first (`იმპორტირებულია ფ `{:.0}`. Dates read `კვირა, 20.09.2026` (`{weekday}, {day:02}.{month:02}.{year}`), months `09.2026`. Inter has no Georgian letters, so the UI draws Georgian with craft-fonts' Noto Sans Georgian (`Geor`), -like the Thai and Arabic faces: release builds embed it, and the web build keeps it in the WASM -(`crates/engine/build.rs`). A build without `CRAFT_FONTS_DIR` has no Georgian face and shows boxes. +like the Thai and Arabic faces: desktop release builds embed it, and a web build made with +`CRAFT_FONTS_DIR` keeps it in the WASM (`crates/engine/build.rs`). The release web build doesn't use +craft-fonts yet, and a build without `CRAFT_FONTS_DIR` has no Georgian face and shows boxes. ## Terminology