build.rs requires every *-formats.json to carry the same messages. The sv (7), fi (1),
id (1) and vi (49) format catalogs predate messages main has added; those entries use the
English text until they are translated.
The vi coverage test reports missing catalog messages and display labels instead of
failing on them, like the Italian and Finnish tests; the English-fallback check stays.
Conflicts:
- crates/ui-egui/src/i18n.rs, crates/ui-egui/src/menus.rs: language table and commands keep every language plus vi
- docs/localization.md: language list, note links and table excerpt keep every language plus vi
- docs/localization-parity.md: header keeps the newer date and both changes; the newer per-language measurements are kept, Vietnamese row from the PR; links and revision rows are the union
Conflicts:
- crates/ui-egui/src/i18n.rs, crates/ui-egui/src/menus.rs: language table and commands keep sv, fi and id
- crates/ui-egui/src/panels/keyword_list.rs: code list gains id; Locale::ALL.len() is 17
- docs/localization.md: language list and note links keep every language
- docs/localization-parity.md: header keeps the newer date and both changes; links and revision rows are the union
Conflicts:
- README.md, docs/localization.md: language lists keep every language, Finnish appended
- assets/ATTRIBUTION.md: union of rows
- crates/ui-egui/src/i18n.rs, crates/ui-egui/src/menus.rs: language table and commands keep sv and fi
- crates/ui-egui/src/panels/keyword_list.rs: code list gains fi; Locale::ALL.len() is 16
- docs/localization-parity.md: header keeps the newer date and both changes; revision rows are the union
Conflicts:
- README.md, docs/localization.md: language lists keep every language, Swedish appended
- assets/ATTRIBUTION.md: union of rows
- crates/ui-egui/src/i18n.rs: language table and command test list keep it, ka and sv; both tests kept
- crates/ui-egui/src/menus.rs: language commands keep it, ka and sv
- crates/ui-egui/src/panels/keyword_list.rs: code list gains sv; Locale::ALL.len() is 15
* Use chrono for UTC/calendar conversion instead of hand-rolled copies
Replaces the hand-rolled Howard Hinnant civil/days-from-civil copies and the
leap-year/days-in-month table with chrono as a workspace dependency:
- apps/lightcraft/src/logging.rs: timestamp (log line time)
- crates/catalog/src/dates.rs: civil + weekday
- crates/catalog/src/stacks.rs: iso_seconds (date -> seconds)
- crates/engine/src/cmd/face_recognize.rs: capture_secs (native only)
- crates/meta/src/datetime.rs: DateTime::unix_seconds and the valid() day check
Output formats and semantics are unchanged (pre-epoch clamp, day rollover that
normalize_iso relies on, leap-second tolerance, EXIF/XMP parsing). chrono uses
only std, so wasm stays safe. AGENTS.md/CLAUDE.md now say time goes through chrono.
Follow-up to storytold/cadcraft#1785
* Never panic on a clock outside chrono's range; keep 5-digit years unprefixed
chrono's From<SystemTime> unwraps, so a clock past year 262143 panicked
inside the logger. timestamp now uses the fallible DateTime::from_timestamp
and reads such a clock as the epoch, keeping the old pre-epoch clamp.
timestamp and catalog's civil write years past 9999 with {:04} like the old
{y:04} did, because %Y would add a '+'. Tests cover the out-of-range and
5-digit-year cases.
* Don't build an unrepresentable SystemTime in the far-future test
UNIX_EPOCH + Duration::from_secs(u64::MAX / 2) overflows Windows'
SystemTime and panicked before timestamp() ran, failing the Windows
test job. Build it with checked_add and only assert where it exists.
* time: preserve old out-of-range date conversion (no epoch fallback)
`DateTime::from_timestamp(...).unwrap_or_default()` silently reset any instant
past chrono's year range to 1970-01-01, changing output the old hand-rolled
Howard-Hinnant code converted exactly (e.g. 10_000_000_000_000 s was
318857-05-20 17:46:40, not the epoch). Restore the old conversion domain with a
cycle-based helper that routes the calendar through chrono (the calendar
repeats every 400 years = 146_097 days, so month/day/time come from chrono and
only the year is carried in i128, keeping every i64 exact).
- apps/lightcraft/src/logging.rs: `timestamp` via `utc_parts`; keep the
pre-1970 epoch clamp; u64 secs past i64::MAX saturate, not epoch.
- crates/catalog/src/dates.rs: `civil` cycle-based (the callers stay inside
0000-9999, but a far clock no longer resets to the epoch).
Pinned old outputs in tests:
logging u64::MAX/2 s -> 292277026596-12-04T15:30:07.000Z (was epoch),
10_000_000_000_000 s -> 318857-05-20T17:46:40.000Z
civil 10_000_000_000_000 -> 318857-05-20T17:46:40,
-10_000_000_000_000 -> -314918-08-13T06:13:20, i64 extremes
* Don't build a year-318857 SystemTime unchecked in the logging test
Windows' SystemTime only reaches about year 30828, so
UNIX_EPOCH + Duration::from_secs(10_000_000_000_000) panicked before
timestamp() ran and failed the Windows test job. Build it with
checked_add and assert where the platform can hold it. The year-10000
case fits everywhere and stays unconditional.
* Name the 400-year range extension as the one exception to the time rule
The rule said never to hand-roll calendar math, but the range fix
(shifting by whole 400-year Gregorian cycles so dates past chrono's year
range keep their old output) is cycle arithmetic, so the rule and the code
disagreed. The rule now names that exception: the library still computes
month, day and time, and only whole cycles are carried in the year.
* Say the library computes month and day; time-of-day is plain arithmetic
Narrows the range-extension wording: the date library computes the
calendar fields, and splitting seconds into hours, minutes and seconds
is ordinary arithmetic, which is what the code does.
Calls storytold/craft-common-actions/.github/workflows/sonarcloud.yml@main
with secrets: inherit. Adds SonarCloud badge to README where missing and
removes the redundant sonar-project.properties (config now passed by the
reusable workflow).
The web release now builds with craft-fonts too (web-craft-fonts), as VectorCraft's
does, so the published web app ships the Chinese and Japanese faces docs/releasing.md
promises.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
crates/ui-egui/locales/{cs,it,pl}-formats.json: kept the translations and added the track's new keys.
docs/parity-checklist.md: newer header line; revision rows from both sides.
docs/whats-new.md: both tracks' 2026-10-15 entries.
Held for the owner: #995 and #999 overlap, and #995's author offered on 10-12 to
close#995 once his commit was in #999. docs/parity-checklist.md: header keeps the
#1485 note and drops the #995 part.
crates/engine/src/lightroom_catalog.rs: test helpers chained; native_catalog_rows takes #842's last_page argument, native_catalog_with_last_table_page builds on it
docs/parity-checklist.md: LRC-LIB-CATALOG-IMPORT keeps both texts