* fix(gpu): rebuild the GPU canvas after an out-of-memory error
An uncaptured wgpu error of any kind dropped the GPU canvas for the rest
of the session, so one recoverable Out of Memory on a working device
left every redraw on the CPU until Retry GPU. Dropping the canvas frees
its memory, so after an out-of-memory error a new GPU canvas is now
built at once on the same device, with a fresh health flag and no
dialog, up to three times a session; past that the CPU renderer and the
Retry GPU dialog stay as before. Validation errors and device losses
are unchanged.
Fixes#6564
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(gpu): only wgpu's out-of-memory error is retried
The fault is classified by wgpu's Error::OutOfMemory variant and recorded
as exactly "Out of Memory", so a validation error that mentions memory
keeps its fallback.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(gpu): a later out-of-memory error replaces an earlier error
So the canvas still rebuilds itself when memory ran out after a
validation error in the same frame; a device loss still wins.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Delete the coverage tests, the per-language parity checks and the tests
of one language's exact wording. Catalog integrity, placeholder and
plural-form checks stay, as do the engine tests (locale resolution,
plural rules, fallback, language scoping, drawing in a language); those
compare against tr() or the plural rule rather than catalog wording.
Docs describe the report instead of a failing test.
crates/ui-egui/src/lib.rs: union in the PhotocraftApp fields, the initialiser and set_wgpu (main's window_device/window_lost/window_recovery_retry and set_available, the PR's gpu_render_state/gpu_fault).
docs/control-protocol.md: ui.gpu.simulateLoss line takes the PR's text and keeps main's destroy: true sentences.
After an error on a device that still works (out of memory, validation), Retry GPU builds a new GPU canvas on the same render state with a fresh health flag, and the views go back to the GPU. After a loss or a failed GPU start it still schedules the retry for the next launch. The dialog says which. Two new strings, translated in all 20 catalogs.
When the window's own wgpu device was lost (a driver reset, e.g. after sleep), egui-wgpu could
not draw another frame: a frame with shapes panicked inside it ("Failed to create staging buffer
for index data", #1215, exit 101, unsaved work lost), and a frozen window's close button opened
an unsaved-changes prompt nobody could see (#3438). The #243 fallback only covered the canvas.
- DeviceHealth::device_lost (wgpu reported the loss, not a recorded fault) and ::probe (a
non-blocking poll that keeps running after a recorded fault).
- RecoveryStore::flush: finish queued autosaves and wait for them.
- gpu_status::check_window, at the end of every frame: once the window's device is lost the
frame draws nothing; the first time every unsaved document is written to crash recovery
(Services::flush_autosaves) and the app closes without asking. The next launch reopens them.
- The app watches the window device on the CPU canvas too; ui.gpu.simulateLoss {destroy: true}
destroys the real device to drive the path.
Verified on an RX 6900 XT, Vulkan and DX12: destroying the device in the running app exited 101
with the egui-wgpu panic before, and now exits 0 with the document recovered on the next launch.
* i18n(cs): translate dynamic labels the literal scanner can't see
The Czech catalog covered every enforced key (menus, tl! literals, blend
modes, preferences) but not the labels built at run time: filter and
Filter Gallery parameter names and choices humanized from their keys,
Camera Raw controls, the Flame / Picture Frame / Tree render filters,
measurement columns, file info fields and the macOS application menu.
Those showed in English under Czech.
Adds 853 entries (2,441 -> 3,294) for keys that the other catalogs
already translate and that still exist in the code. Existing entries are
unchanged. Translated clean-room from the plain meaning of the English
labels, with the terminology already used in cs.tsv; no proprietary
localisation was consulted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n(cs): credit the Czech catalog in ATTRIBUTION.md and LICENSE-translations.txt
cs.tsv is a bundled translation asset but had no row in ATTRIBUTION.md
and was not named in LICENSE-translations.txt. Add both, worded like the
other catalogs (PhotoCraft contributors, original translations, MIT OR
Apache-2.0).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n(cs): terminology review fixes for the new dynamic labels
Changes from an independent review pass over the added block:
- "Create By" (De-Interlace: how the missing lines are made) reads
"Vytvořit pomocí", not "Vytvořil" (author)
- luminance is "jas" in Camera Raw, as in the existing "Luminance" label
("Detail jasu", "HSL – jas", "Jas")
- slider labels are nouns, like the existing "Redukce šumu" (Reduce Color
Noise, Sharpen Details, Lighten Grout)
- grain and flame choices agree in gender with their neighbours; "Shadow"
matches "Highlight"/"Světla" as "Stíny"; "At" is "Pozice"; "Water Paper"
is "Mokrý papír"; "Leaves" is "listů" everywhere
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* contributors: add my own people.toml entry
Own entry only, as AGENTS.md describes: credited by the name on my public
GitHub profile (sync_github_name), otherwise by @username.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n(cs): drop three rows that main added in the meantime
"Columns", "Margin" and "Automatic" were added to cs.tsv on main while this
branch was open, with the same translations, so the appended block no longer
needs them (the catalog has no duplicate keys).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n (cs): translate two strings that are still missing from cs.tsv
"Drag (lasso) or click points (polygonal) ..." and "The current gradient
(pick one in Window > Gradients)" exist in the other catalogs but not yet
in cs.tsv on main.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* i18n (cs): translate context-menu, channel and layer-style labels
These labels are rendered through tl!() with a variable, so the literal-based coverage test does not see them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Localize Czech status messages and notice wrappers
Translate shell messages before interpolating file names and engine errors.
Use the existing plural API for counted objects and preserve English fallback.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Localize Czech UI labels and hardware information
Translate brush controls, accessibility names and rasterization prompts at
render sites. Preserve adapter, driver and display/profile identifiers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Localize Czech composed labels and distinguish Navigator zoom
Translate profile, preset and shortcut messages before interpolation, and use
a Navigator context for the zoom level. Keep shortcut warning state separate
from translated text and preserve document names and shortcut codes.
Cover remaining visible counts and add rendering/glyph regression tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Fix Czech UI localization review follow-ups
Keep copied System Info and API diagnostics in English while localizing the
on-screen text. Avoid double translation of accessible names, honor command-id
shortcut labels, and translate stored auto backend values only for display.
Refine Czech warning punctuation and catalog wording, simplify template blocks,
and correct the documented catalog count to 3,522 rows. Add four regression tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* people.toml: keep axlecoffee's sync_github_name with their entry
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Losminek <301412321+Losminek@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Dialog buttons follow the OS order through one shared widgets::dialog_buttons helper (fixes#776, #627). Supersedes #802, whose reordering of the unsaved-changes and Revert prompts this carries to every dialog.
Co-authored-by: Luna <luna161@users.noreply.github.com>
* Monitor profile: say which profile is in use and why; apply a late display reading (#569)
Color Settings › Monitor Profile used to fall back to sRGB without a
word when the display's profile was missing, unreadable, not RGB or
unusable as a destination, and dropped a macOS reading that took longer
than 2 s. `ColorState::monitor_status` now reports the profile actually
applied, its fingerprint, the source (auto / manual / fallback) and the
reason for a fallback. `edit.colorSettings` (`monitorStatus`),
Help › System Info and the Color Settings dialog show it, and a fallback
in auto posts a notice.
The macOS reader also names the display it read. On a dual-display Mac
it reports the menu-bar display ("Built-in Retina Display") while the
canvas is on the external monitor, which is the display-selection bug
in #569; following the window's display is the next step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Monitor profile auto: each window uses the profile of the display it is on (#569)
The macOS reader now lists every display (id, name, frame, ICC profile
and its System Settings name) instead of asking for the "main screen",
which from a windowless helper is the menu-bar display. Each window
picks the display it overlaps most, as NSWindow.screen does, and its
canvas is shown in that display's profile: moving a window to another
display switches the profile without a restart, and two windows of one
document on different displays each keep their own. The GPU canvas
shares the document texture and keeps a display LUT per (document,
display); CPU textures are per (document, display).
Displays are read again when PhotoCraft comes back to the front (a
profile changed in System Settings or by a calibration tool), and when
a window is on an unknown display or one whose size changed (connected,
removed, rearranged), at most every 5 s. A failed re-read keeps the
previous reading. Help › System Info lists each display's profile.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Monitor profile auto: act on the r1 review; no periodic re-read (#569)
Following the owner's decision on echo round r1:
- No periodic discovery: a read costs about 0.2 s of CPU, and a profile
reassigned while PhotoCraft stays in front with no window moving
practically doesn't happen. It is picked up at the next return to the
front, or when Edit › Color Settings opens, which now reads at once.
- Every document window, not only the main one, asks for a reading when
it is on an unknown display or one whose size changed.
- The focus re-read is throttled to one per 30 s; a trigger inside that
window is deferred, not dropped.
- Starting a reading schedules the frame that applies its result, so an
idle window doesn't wait for unrelated input.
- The osascript helper is stopped after 10 s (killed and reaped, both
pipes drained meanwhile), so a hung reading can't block later ones.
- The Color Settings dialog recomputes "Monitor profile in use" as it
draws, and the status says when an earlier reading is kept after a
failed re-read (detection `retained`).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Monitor profile auto: document windows count for focus and wake the main window (#569)
Echo round r2 on #591:
- Coming back to PhotoCraft through a document window (macOS reports
focus per window, so the main window stays unfocused) now counts as a
return to the front and reads the displays again, within the 30 s
throttle.
- A document window that is on an unknown or resized display, or that
brings PhotoCraft back to the front, asks the main window for a frame:
the reading starts there, so it no longer waits for unrelated input.
One frame per trigger, no continuous repaints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Monitor profile: pass the notice dismiss preference added on main
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Add rendering policy with legacy preference migration and safe adapter selection
* Add rendering mode preferences and GPU fallback recovery warning
* Complete CPU fallback recovery and validate rendering mode UI
* Simplify GPU recovery and reuse preference persistence
* Rendering modes: rebase onto the preferences merge/retry save path; drop docs screenshots
save_preferences now writes through write_prefs (merging other instances' changes) and
only marks the revision saved when the write succeeds, so a failed recovery choice is
still retried. Removes a leftover diff3 marker in es.tsv, the docs/images/rendering-*.png
screenshots and their ATTRIBUTION row (no binary assets in the repo), and regenerates
docs/scorecard.md for the new performance.renderingMode preference.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>