The window docks as its groups at a frame's edge zones, as a group drag would; a body's
center still takes no whole window. Tests for Escape during a bar drag and for the
window focused last being in front.
The window where a drag starts owns it and writes its pointer with its desktop origin.
The window under that point shows the preview with its own targets, and the release
docks there, or floats a new window over the desktop. Leaving a window is no release
with native viewports. Dragging the only tab or group of a window moves it, and its
bar docks the whole window.
* Use chrono for UTC/calendar conversion instead of hand-rolled copies
Replaces the hand-rolled civil_from_days and Day::to_days in
crates/ai-core/src/history/report.rs with chrono, added as a workspace
dependency (std-only, so ai-core still compiles for wasm). The day/month
grouping, ISO and CSV timestamps and the Day/Month display are byte-identical
and pinned by the existing tests. AGENTS.md now says time goes through chrono.
Follow-up to storytold/cadcraft#1785
* history: preserve out-of-range conversion failure in civil_from_days
civil_from_days used `i32::try_from(..).unwrap_or(0)` before calling
NaiveDate::from_num_days_from_ce_opt, so a CE day count past i32 (e.g.
from Day::from_days(i64::MAX)) passed a valid day 0 to chrono and read
as 0000-12-31 instead of the 0000-01-01 fallback the None arm gives for
every other out-of-range date. Convert with `.ok().and_then(..)` so both
the integer-conversion and NaiveDate-construction failures share the one
fallback. Regression test covers i64::MAX and i64::MIN.
Doc comments on the chrono-touched to_days/from_days/civil_from_days now
state the out-of-range behaviour (0 / 0000-01-01) for the docstring gate.
* Convert every i32 year, not only chrono's range
Day holds any i32 year, but NaiveDate stops near ±262143, so years like
262144 read as 0 / 0000-01-01. The Gregorian calendar repeats every 400
years: chrono now converts the day within its cycle and whole cycles are
added back, in both directions. Values match the pre-chrono formula
(tested for 262144, -262145, i32::MAX and i32::MIN). Nonexistent days
still read as 0, and day counts whose year doesn't fit i32 still read as 0000-01-01.
* 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.
* fix(hayro-syntax): read full /W[0] width for xref stream entry type (follow-up to #239)
* hayro-syntax: loader-path test for oversized /W[0]; document patch; drop committed build output (CodeRabbit)
Maintainer review on storytold/wordcraft#3562: spelling looks words up composed (NFC), but
add_word, remove_word, ignore_word, custom_word (so loading the dictionary
file) and save_user_dictionary's removed list kept the raw lower-cased
spelling. A word added or ignored decomposed (`zqprue\u{301}ba`) therefore
stayed flagged.
- dictionary_key(): lower case, then composed; every write uses it, and
in_user_dictionary() looks up composed, so either form adds, ignores,
removes and finds the same entry; NFD entries in an existing file load
and save composed
- English's known_core() reuses in_user_dictionary() instead of a copy of it
- issue byte ranges still cover the text as typed
- regressions: proof (add, Ignore All, remove, save/load with an NFD entry,
both forms rechecked) and engine (review.addToDictionary and
review.ignoreAll with the issue's NFD text)
* security: bound vendored hayro xref, object-stream, JBIG2 and graphics-state work; krilla PNG size
hayro-syntax:
- xref-stream subsections must be backed by decoded bytes: zero /W widths and counts
longer than the data are refused before inserting (CodeRabbit CWE-400, xref.rs:962)
- object-stream offset tables are parsed once per document and cached in Data
(CodeRabbit CWE-407, xref.rs:1079)
- embedded JBIG2 pages over 2^28 pixels are refused before allocating, with checked,
fallible reservation (CodeRabbit CWE-789, filter/jbig2.rs:34)
- a JBIG2Globals stream with an image filter is ignored, so globals cannot recurse
(CodeRabbit CWE-674, filter/jbig2.rs:20)
hayro-interpret:
- at most 4096 saved graphics states / 64 MiB retained; dash arrays over 256 entries stroke
solid (CodeRabbit CWE-400, context.rs:226)
krilla:
- PNGs implying more than 1 GiB of decoded output are refused at Image::from_png and in
the deferred decoder; the buffer is reserved fallibly (CodeRabbit CWE-789,
graphics/image.rs:565)
SOURCE.json local hashes updated; patches recorded in vendor/README.craft-reader.md and
vendor/krilla/README.craft.md.
* vendor: fix SOURCE.json local hashes; reserve krilla's colour/alpha split buffers fallibly
- SOURCE.json: only the local sha256 changes; the source/upstream hashes of the patched
files are restored (CodeRabbit review)
- krilla handle_u8_image/handle_u16_image reserve their split buffers with try_reserve_exact
and return an error, so a valid image under the 1 GiB decode cap cannot abort the process
on the second allocation (CodeRabbit review)
- es/mod.rs: one variety() match gives the dictionary slot, log name and file
names (variety_name() folded in); load_installed() destructures it
- es/grammar.rs: the sentence-start rule for the missing ¿/¡ is one named
function, ends_clause(), replacing is_boundary() and three closures
rebuilt per mark; in_address() is shared; repeated_words() builds its
helpers once instead of per word pair
- es/suggest.rs reuses the Czech ranking's MAX_LETTERS instead of a copy
Same tests pass unchanged; a temporary old-vs-new differential proptest
(20,000+ generated inputs with URLs and punctuation) found identical output.
Maintainer review on storytold/wordcraft#3562:
- words() keeps combining marks with the word they follow, so a word typed
decomposed (NFD, `tambie\u{301}n`) is one word with its own byte range;
is_correct_in_with and suggest_in look it up composed (NFC), so it is
checked like `también` in every language (unicode-normalization, MIT/Apache)
- the Spanish missing-¿/¡ rule skips marks in web and e-mail addresses
(`Ver https://example.com/?`), and punctuation inside an address or a word
(`example.com`, `https:`, `10:30`) no longer starts the sentence the
opening mark is offered for; a mark ending an address still ends a sentence
- Spanish repeated-word check matches composed and decomposed spellings
- regressions: check_text with the Spanish fixture and NFC/NFD equivalents
(acceptance and ranges), words() and composed(), URLs ending in /? beside
ordinary questions
CodeRabbit review: Session::region_spanish takes the ISO 639-2 `spa` as
Spanish, so a `spa-MX` region writes es-MX documents.
with_native_host draws native windows as their own egui frames with a
desktop rect, focus, close requests and the commands they receive. Tests
cover each way into a native window, closing, stable ids, place and
monitor, restore, the bar drag, hiding and forwarded keys.
With native-windows on a backend with viewports, each floating window
whose panels allow it is native: in-window floats open over their place
in the app window, and restored windows go back to their monitor or onto
the main window's. Windows stay on top and hide while the app is
inactive. A thin bar with title and close replaces the OS title bar and
moves the window; closing the window closes its panels. Keys a native
window leaves unused come back in DockOutput::forwarded_events, and
DockArea::monitors takes the monitor list. Move Panel to New Window goes,
since every float is native.
Floating::id names a window across panel moves: fresh from Floating::new
and on load, unique within a loaded layout, kept by a whole window that
moves and by the receiver of a merge. Floating::monitor and monitor_size
record where a native window is; Action::SetMonitor sets them. Equality
ignores the id.
A luminosity soft mask whose group is DeviceCMYK draws the group's four process plates over the backdrop and applies 1 - min(1, 0.3c + 0.59m + 0.11y + k) (ISO 32000-2 §10.4.2.2), so K 100% hides the masked object. Spot colorants on those plates paint through their alternate space (Plate::spots_as_process).
Floating::stack builds a window from groups stacked top to bottom, so an
app's own stacked layouts migrate without building the split chain. The
four-way handle no longer takes keyboard focus: it has no label or key
action, and the two dividers it joins take arrow keys.