- copied_file: check the read policy on the real path when it is
valid UTF-8; skip non-UTF-8 paths while automation roots are
active instead of checking a lossy name (path traversal via
symlink, CodeRabbit #3872 thread 1).
- full_bytes_cached: collect matching cache candidates under the
FULL lock, then run the read checks after releasing it, so
filesystem walks no longer stall other FULL users (thread 2).
SonarCloud flags every bare [ conditional in bash scripts under packaging/ as a reliability smell (rule S7688). Convert the flagged [ ... ] tests to [[ ... ]]: safer word-splitting and no glob surprises, same semantics for these string and file tests.
* 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 tables with chrono as a workspace dependency:
- apps/vectorcraft/src/logging.rs: timestamp (log line time)
- crates/doc/src/metadata.rs: civil (Unix seconds -> [y,m,d,h,mi,s]) and iso8601
- crates/pdf/src/file_info.rs: unix (date -> seconds) and in_month, used by the
PDF/XMP date parsers (pdf_date, xmp_date)
Output formats and parsing semantics are unchanged and pinned by the existing
tests (PDF D:... and XMP dates, offsets, leap-year rejection, year clamp 0-9999,
pre-epoch clamp). 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 (or a pre-epoch one) as the epoch instead. Years past
9999 are written with {:04} like before, 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. 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/vectorcraft/src/logging.rs: `timestamp` via `utc_parts`; keep the
pre-1970 epoch clamp; u64 secs past i64::MAX saturate, not epoch.
- crates/doc/src/metadata.rs: `civil` cycle-based, keeping its clamp EXACTLY
as the old code had it: the input is clamped to 0000-01-01..9999-12-31 first
and the year is clamped to 0..=9999 afterwards. (With the input clamp the old
`unwrap_or_default()` was already unreachable; this drops it and makes the
conversion explicit, with identical output.)
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
metadata civil(i64::MIN)[0]=0, civil(i64::MAX)[0]=9999;
iso8601(i64::MIN)=0000-01-01T00:00:00Z,
iso8601(i64::MAX)=9999-12-31T23:59:59Z (year clamp, as before)
* 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).
* feat(window): adopt shared craft-window for Linux decorations
Decide the window frame on Linux/BSD via the shared craft-window crate
(storytold/craft-libs) instead of VectorCraft's unconditional
`CUSTOM_TITLEBAR = !macos`, so the app matches the rest of the Craft
suite per desktop.
- Probe `craft_window::Choice::probe(Prefix::new("VECTORCRAFT"))` once at
startup; custom titlebar is on when the frame is `Frame::App(_)` and the
user has not opted into the system title bar.
- Preserve the `systemTitleBar` preference: opting in forces the native
(desktop) frame back on, as before.
- Keep macOS and Windows behavior exactly as today (decided by
`CUSTOM_TITLEBAR`, not the probe): macOS integrated title strip,
Windows app-drawn caption. craft-window returns `Frame::Desktop` for
both; VectorCraft's overdraw is its own choice.
- The only behavioral change: KDE/Wayland now keeps KWin's native
decorations (Frame::Desktop) instead of always drawing a custom
titlebar; GNOME still draws its own (Frame::App).
- `VECTORCRAFT_TITLEBAR` override and the `systemTitleBar` pref both still
work (the env var is craft-window's override for the `VECTORCRAFT` prefix).
NOT MERGEABLE YET: craft-window is open PR storytold/craft-libs#217; the
dep points at the PR branch and must be repointed to a merged `rev`
before this can merge.
* fix(window): native frame when X11 is forced; pin craft-window to merged rev
- When Preferences > Linux Display Server > X11 forces the window onto
XWayland, treat the frame as the desktop's (native X11 decorations)
rather than letting a GNOME/Wayland probe pick Frame::App and draw our
own title bar on what is really an X11 window. An explicit
VECTORCRAFT_TITLEBAR override still wins (checked via Choice::overridden).
- Repoint craft-window from the (now-merged) feature branch to the squash
commit on storytold/craft-libs (craft-libs#217); regenerate Cargo.lock.
- The WAYLAND_SOCKET concern raised in review is handled in the merged
craft-window crate itself (the probe skips when WAYLAND_SOCKET is set).
Verified: cargo build + test -p vectorcraft (131 tests) pass against the
merged dep; windows7 (--no-default-features) still compiles; rustfmt clean.
* fix(window): only force native frame when X11 libs are actually present
Follow-up to review: the XWayland title-bar override now also checks
xwayland::x11_libraries_found(), matching the launch path's own condition.
If X11 is requested but the libraries are missing, startup stays on
Wayland, so the frame decision must keep the probe's app-drawn result
rather than flipping to native. Verified: tests pass, rustfmt clean.