mirror of
https://github.com/storytold/lightcraft.git
synced 2026-11-13 17:31:09 -06:00
* ui: first-run onboarding tour and What's New after an update The very first launch now opens a short Welcome tour (six pages over the real views: import, the Photo Grid, Detail, Assisted Culling, export and print, and driving the app with agents over MCP), and after the app has been updated the existing What's New dialog opens once on its own. Both are skipped once the running version has been seen. - UiState.seen_version: the last version whose startup dialog was shown (serde default/skip, so older ui.json files read as None = first run). - new startup module: parse_version, startup_dialog (FirstRun only when nothing was ever seen; WhatsNew when the version changed, comparing through parse_version and falling back to string difference when either side is unparseable), and open_at_startup — headless hosts (snapshots, scripted runs, tests: session-local state) never gain a modal. - UiState::record_startup_dialog_seen records on show, so a crash mid-dialog does not re-trigger it on every launch; ui.json saves every few seconds and at exit, as before. - Dialog::Welcome renders the tour with Back / Skip / Next (Get Started on the last page); every string goes through tr(), every menu path names a real command (file.addPhotos, view.photoGrid, view.detail, dialog.cull, dialog.export, dialog.print). - the decision runs once on the first frame (ui-egui, so desktop and web both get it); headless hosts and already-seen versions open nothing. * ui: the integration harnesses are headless hosts The accessibility and docking tests build the app directly and drive it through logic() and ui(). On a fresh UI state the first frame therefore opened the Welcome tour: its modal took the Tab focus the accessibility tests walk (canvas_tool_sliders_are_reachable_with_tab, tab_walks_the_right_panels_top_to_bottom) and the pointer the docking drag aims at (real_drag_float_close_reopen_and_state_reload_keep_photo_tool_mode). Mark both harnesses as headless hosts, as the Headless harness already does: a headless host never gains a first-run modal. * ui: translate the first-run tour into the 13 catalogs The 21 strings of the first-run tour (its six pages, its window title and its Next and Get Started labels) were in no catalog, so every language showed the tour in English. They are now in each of the 13 catalogs under crates/ui-egui/locales/, with the placeholders and punctuation of the English kept: the ellipses, the menu separators, the middle dots, the parentheses around shortcuts and the final full stop. ka.json stays key-sorted; the other catalogs get the keys appended. WELCOME_TITLE, WELCOME_NEXT and WELCOME_START name the three labels that are not part of WELCOME_PAGES, so one test can check all 21 keys: the_welcome_tour_is_translated_in_every_catalog. It fails with the catalogs as they were. The translations were written without a native speaker's review and need one for each language before release. * ui: translate the first-run tour into the four new catalogs main added sv, fi, id and vi after this branch was cut, and the_welcome_tour_is_translated_in_every_catalog iterates Locale::ALL, so the tour's 21 keys were missing there. Same block, same place (after "Visualize HDR"), in each catalog's own terms. * ui: the lifecycle gesture test is a headless host main's egui-037 lifecycle tests (added after this branch was cut) build the app directly and drive it through logic() and ui(), so the first frame opened the Welcome tour there: its modal took the dialog the delivered gesture under test opens (a_delivered_gesture_may_change_the_state_it_acted_on saw the tour where it expected View Options). Mark that one test's app as a headless host, as the accessibility and docking harnesses already are: a headless host never gains a first-run modal. The flag stays scoped to this test because the file's screenshot tests exercise the headless-capture fallback, which a headless host short-circuits. * ui: build the tour's shortcut chords from the platform bindings WELCOME_PAGES hard-coded Cmd+Shift+I, D and Cmd+P in its prose, and the renderer translated those sentences verbatim, so Windows, Linux and FreeBSD users were shown keys the app does not bind. The three pages that name a shortcut now take the command's live binding at render time (shortcuts::shortcut_of) and paint it the way the menus paint theirs (menubar::shortcut_text: a symbol chord on macOS, Ctrl+... elsewhere); no key is written in the source strings any more. A command whose binding was removed shows the menu path alone. The chorded sentences move from the message catalogs into tr_format! messages (placeholders are format!-checked in every language), translated in all 17 catalogs' -formats.json; the catalog keys stay, chordless, as the no-binding fallback, and the_tour_is_translated_in_every_catalog pins both per language. * ui: the tour's Develop page names the Before/After command, not the Y key The page's prose still hard-coded Y as the before/after key: remap or remove that binding and the tour sends users to a command that no longer runs. The body now names View › Before/After Side by Side the way the other pages name their commands, and every catalog translates the new sentence with its own menu label. * ui: record the startup dialog once its input is not blocked * Keep headless UI tests independent of onboarding Assisted-by: OpenAI Codex * Tests: integration harnesses mark the app as a headless host Services has no headless field; the app does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tests: Find Similar waits for the worker to set the row total It lost that race twice on the Windows gate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: eliets2 <287273517+eliets2@users.noreply.github.com> Co-authored-by: FredApps <FredApps@users.noreply.github.com> Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>