* M8: typing the last page of a range keeps what is typed (#5574)
The page-range dialogs (Redact pages, Rotate pages, Duplicate field,
Number pages) raised the end to the start on every frame, so the
half-typed "1" of "12" became the start before the "2" arrived.
The end is now ordered only once its field is no longer being typed
into. Number pages no longer lowers the start to a half-typed end, and
its preview cannot underflow while the end is below the start.
* M8: a test types the last page of Rotate Pages' range (#5574)
Rotate Pages' From and to fields are labelled, and a test types "12" after a
first page of 5 (pages 5-12 are turned) and "3" left by clicking OK (page 5
alone), as croquete1 asked on #5579.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GbuSKAS2k5248LGFEapMUR
* M8: page ranges stay typed while the window is in the background, and OK orders them (#5574)
Review follow-up. The end field's typing check used Response::has_focus,
which is false while another window is in front, so switching apps
halfway through "12" still raised the typed "1" to the start. It now
asks egui's memory (widgets::typing_into). OK pressed while the end is
still focused (a screen reader's click) orders the range first in Rotate
pages, Duplicate field and Redact pages, and Number pages builds its edit
from the ordered end, so a reversed range never reaches the edit.
---------
Co-authored-by: Claude <noreply@anthropic.com>
* M14: the comments panel lays out only the cards in view (#5488)
With more than 100 comments, a card or page header out of view that
was measured at the current width only takes up its height instead of
being laid out every frame. The selected card is always drawn, so it
still scrolls itself into view. Shorter lists are laid out whole and
keep every card in the accessibility tree.
The 500-comment panel in tests/perf.rs drops from about 23 ms to 5 ms
per frame locally, well inside the 40 ms budget it overran on CI.
* M14: the comments panel measures a card again when it changes (#5488)
A card's kept height is now per document and per what the card shows (its
thread's text, author, date, status, colour and lock, and whether it is the
selected card), so another document at the same width, an edit, an undo or a
redo never lays the cards below it out at a stale height (CodeRabbit on #5583).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GbuSKAS2k5248LGFEapMUR
* M14: a comment card with an editor open is never skipped (#5488)
A card with an inline editor open on it or on one of its replies is laid out
wherever it is, and its kept height is apart from the one without the editor,
so the cards below make room for the editor (CodeRabbit on #5583).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GbuSKAS2k5248LGFEapMUR
---------
Co-authored-by: Claude <noreply@anthropic.com>
* M10: Create ▸ Blank page uses the system region's paper (#5644)
A new blank document was always US Letter. It now takes the paper of the
system region: Windows' own Region paper size (iPaperSize) first, then the
system locale on macOS or LC_ALL/LC_PAPER/LANG, A4 everywhere outside CLDR's
US-Letter regions, and Letter when the region can't be told (web).
The region parsing is shared with the Units preference.
* M10: the first locale variable set decides the paper; extensions are not regions (#5644)
LC_ALL, LC_PAPER and LANG are read in POSIX order and the first one set
decides, so LC_ALL=C no longer falls through to LANG's region; and a locale
tag's region is read from its base tag only, so en-US-u-nu-latn is US, not
NU (CodeRabbit on #5651).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GbuSKAS2k5248LGFEapMUR
* M10: the blank page test covers A4 as well as Letter (#5644)
The app keeps the paper Create ▸ Blank page makes (blank_paper, the system
region's at start), so the test makes a blank page on A4 and on Letter
whatever the host's region (CodeRabbit on #5651).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GbuSKAS2k5248LGFEapMUR
---------
Co-authored-by: Claude <noreply@anthropic.com>
After switching back to the window, the windowing layer sends no
pointer move until the mouse moves, so egui knew no pointer and the
wheel scrolled nothing. When a frame turns the wheel with no known
pointer and no move of its own, the raw-input hook now asks the system
where the pointer is (drag_pointer::in_window, already used for file
drags) and puts that move first.
The list was fixed at 12. Preferences ▸ Documents and view now takes a
number from 0 to 50, with 12 as the default. At 0 Home has no Recent
section and File ▸ Open Recent is disabled.
The list is cut when Preferences closes, so the number can pass through
0 on its way to another. Until then Home and saved settings already
leave out what is past it. A settings file can't ask for more than 50.
The control channel and launch options take it as recent-files. The two
new strings go in every catalog as English copies.
Convert context-paste user coordinates into AddImage display coordinates and
identify the inserted image through its new owning stream and XObject. Keep
legacy added-image metadata aligned with the active image controller, route
Delete to the visible selection, and clear hidden image selections when leaving
the controller while preserving text selections and drafts.
Separate selection-dependent Copy from whole-page Snapshot and document Export
permissions. Add synthetic UI regressions covering transformed page placement,
identity collisions, shared image references, saved deletion results, tool
transitions, and permission controls.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Project the visible user-space bounds centre for the drag preview, matching the RotateBy engine operation. The full image centre differs for cropped artwork and caused the preview to jump on release.
The regression compares actual painted preview corners with saved/reopened image corners and verifies a requested 90-degree turn on cropped and uncropped controls, including page rotation. Linux scoped clippy, 106 tests, wasm, formatting, layering and assets pass.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Invert the actual affine basis instead of projecting onto assumed orthogonal axes. Capture the displayed crop frame for handles and apply its relative transform to the full artwork, preserving author clips on rotated pages. Exact out-and-back gestures preserve bytes and undo state, including off-centre presses.
Synthetic headless regressions verify sheared and rotated images, crop-frame anchors, press-offset independence and saved/reopened placement. Degenerate and nonfinite bases are refused; the inverse guard has a negative-control test.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Only request a script conversion from the panel; the caller decides whether conversion changed the text. Do not create a paragraph rewrite merely because the button was clicked. A hidden QQQ fixture checks both script buttons preserve document bytes, undo history and saved rendering, while supported conversions remain covered. Actual reverted-fix raster failure and scoped Linux native, wasm, clippy and repository checks passed.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Measure Mixed with the active body font and include the input margins and caret allowance. Keep that minimum stable while typing so the full label is visible without shifting the row on each keystroke.
A themed panel regression fails against the old 40-pixel input, verifies the text fits inside the field margins and keeps the alignment control inside a 272-pixel pane. Linux scoped clippy, format tests, wasm, formatting, layering and assets pass.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
The pinned integration consumes native clipboard shortcut keys and delivers PasteImage pixels. Handle those payloads in order, keep text cache invalidation, and use the delivered image even if the OS clipboard changes before processing. Preserve alpha and validate dimensions and byte counts before allocation.
Remove release timestamps so Windows clipboard history cannot reuse a completed paste gesture. Keep explicit shortcut fallback for control-channel and browser internal-image paste, with modal, focus and permission guards unchanged. Tests cover both release orderings, history after prior paste, mixed batches and duplicate prevention.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Include the mixed-size flag in the font-size draft cache key. Selecting a mixed block can preserve the previous representative number, so a value-only key left the field showing 24 instead of Mixed. Unchanged formatting still preserves partially typed input across frames.
The headless regression covers uniform-to-mixed and mixed-to-uniform transitions with the same number, draft preservation and a single committed edit. Linux scoped clippy, tests, wasm, formatting, layering and assets pass; reverting the cache fix reproduces the stale display.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Offer Remove Crop only for editor-owned clips, reset draft cropping to the author-visible bounds, and remove only an owned crop when confirming the full image. Active crop mode owns presses on its page so underlying text and marquee selection cannot steal crop controls. Label the painted controls for accessibility and automation.
The author-clip regression opens the real menu, resets a draft crop and confirms it while preserving the authored clip. It failed before ownership checks and again before the router fix.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Use matrix resizing whenever image axes are turned or reflected on screen, including document and view rotation. Share the visible grip geometry for cursor claims and hit routing, and use only the drawn rotate knob. Map flip labels to the visible axis and defer Cut selection clearing to its result.
Regressions cover all page/view rotation combinations and 64 outward drags across every grip, asserting fixed anchors and pointer tracking for upright and rotated images.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Omit pinned tools from Recommended and keep unpin buttons in the accessibility tree without requiring pointer hover.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
An explicit size commit normalizes mixed-size selections even when the value matches the displayed size. Caret-only script commands format the whole draft. Mathematical minus uses reversible Unicode forms and ASCII hyphen-minus remains unchanged. Correct the applied-selection comment and document the character policy in parity and the tool.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Do not reuse stale native images for unsupported clipboard contents, while preserving web internal image paste. Observe text paste before focus gating and keep image-less key releases silent. Tie release fallback to V gestures. Check modification permission before copying and clear selection only after successful deletion.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Resolve explicitly configured font roots while skipping descendant links; retain all accepted fonts within the existing bounded scan. Allow an explicit Reload without duplicate workers or an app restart. Three actual reverted-fix regressions fail; scoped engine/UI clippy, 2400 tests, wasm, formatting, layers and assets pass on AI02.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Reconstruct moved edges from the fixed opposite anchor in f64 and round each coordinate once. This avoids cancellation introduced by adding a nearly full-width UV displacement to the old screen edge. The deterministic Linux CI case fails against the previous implementation and passes with the fix.
Check scale on both axes in screen space against a derived f32 coordinate-rounding bound, using the stored rectangle dimensions. Verify fixed edges for every handle and reject a deliberately stretched result. Both CI regression seeds and 10000 randomized cases pass; scoped UI clippy/tests, wasm, formatting, layering and assets are green on AI02.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Use the common image-selection constructor across owners, remove obsolete crop and copied-coordinate fields, keep only a shortcut timestamp, and make the internal UI modules private. Combined GGEO-AI02 validation: 4198 tests passed, scoped clippy, doctests, fmt, layers, assets, parity and wasm; 25-step CLI save/reopen smoke and real-shell screenshot checked.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Fill five notification/search entries in all catalogs and the missing Home tool labels in ckb, lo, nb and th. Correct Albanian crop labels and Italian/Kazakh unpin meanings. Catalog additions are AI-assisted and not separately native-reviewed.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Contain discovery panics and publish the empty result so the font menu leaves its pending state. A synthetic panic regression fails when containment is removed.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Menu paste refuses cached images when system text supersedes them. Keyboard and menu paste share the text check; image validation reuses Create. Expire shortcut context by elapsed time even when frames are sparse. Regression tests fail with the old checks restored.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Reuse the existing subset-tag parser through the engine facade and share the fallback formatting state.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Remove the rectangle helper whose only caller was the deleted crop test, and format the preserved text_script tool additions.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Use the shared content Matrix, one image-frame painter, texture-mesh builder, crop-pill style and rectangle comparison. Replace rotated resize arms with signed anchors and reduce crop arithmetic while preserving UV and screen scale. Reuse the edit-generation image cache for drag painting and image menus, and keep the menu flag temporary.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Keep invalid image geometry and drag coordinates out of crop clamps and discard an invalid in-progress gesture. The regression panics at f32::clamp without the guard (cropred) and passes with it (cropgreen2), alongside 81 image and crop checks.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Share click handling, set crop mode in the image menu, remove duplicate export and inactive editor entries, and format shortcuts for the platform. Add a selection constructor for callers.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
crop_rect was only ever reset to None outside tests, and crop_mode was
always set together with crop, so readers now test crop.is_some() and
the writers are gone. The fields stay until the last struct literals in
content_ui stop naming them. Tests that set crop_rect to drive the crop
now set the crop's box the same way crop_input did.
The rotated-drag match had a DragTarget::Rotate arm that the turning
branch above always pre-empts, and image_input's ImageAction parameter
was unused (let _ = &action).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HandleId duplicated ImageHandle with conversion tables both ways; the
image engine now takes ImageHandle, which also carries the cursor icon,
and the crop grips map through one GRIPS table instead of two index
matches. ImageObb loses from_center_size_angle, scale_proportional,
project_delta, new, handle_id, handles_with_ids and the width, height
and theta fields, which only the test of scale_proportional read.
reset_crop, RESIZE_ACCENT, image_owns_pointer and DragTarget::CropHandle
had no callers, and turn_degrees is test-only. The image engine is
private and its unused re-exports are gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The image panel crops through Edit::UpdateContent with the fractions it
reads, and nothing else called update_image_crop. Removed with its test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The four character match blocks become two const tables in a new engine
module, text_script, keeping the minus sign's mapping. One toggle_script in
Edit text replaces the three copies (paragraph editor, added-text draft and
the Format text button state).
A range is now 'already in the script' when at least one character is and
none of the others has a form in it. Before, one character with no Unicode
form (a space, q, H in H2O) kept the range from ever counting as converted,
so the second click converted again, changed nothing, and the text could not
be toggled back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
is_bulleted, is_numbered, toggle_bullet_list and toggle_numbered_list had no
caller outside their own tests: Format text shows bulleted and numbered lists
as planned. Removed with their tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Restore main's text where the PR changed things it did not need to:
intra-doc links turned into plain code spans (engine js.rs and
camera_edges.rs, forms raster.rs, fonts pdf.rs, sign keys.rs and
pkcs12.rs, ui-egui motion.rs and web_print.rs), the #[allow(dead_code)]
on render's byte_capacity and ui-egui's unit_for_tag (both are used on
every native target, as on main), the loosened combine_grid timings, the
forms test case rename (scratch() already numbers its folders), and the
computed xref size in a_recovered_page_tree_survives_a_full_save (always
5 there).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main's page-edge-shadow preference (#2088) added a fade-strength branch to
document_area, and this branch's image-menu work added others; together they
put it one over clippy's cognitive-complexity limit. page_scroll, its only
caller's scroll helper, now takes the fade flag and restores the spacing itself.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings the branch up to date with main (47f42ba0), which moved 37 commits since
the merge base, including page-edge shadows (#2088), full rewrite after page
delete (#4279) and the licence-check stage (#2212). No conflicts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- [P1] Arrange: track and replay inherited non-ExtGState drawing state (colors, line widths, etc.) across q/Q scopes for stencil and form artwork
- [P2] Flips: route ImageEdit::Flip through coordinate-aware transform logic to swap axes when rotated at odd quarter turns (90°/270°)
- [P2] Rotation knob: measure clockwise angle delta from drag start relative to the rotated image center to prevent reapplying existing angle
- [P2] Format panel: connect superscript/subscript controls to line editor and content draft selections, and update parity evidence
- Resolve all 13 merge conflicts between upstream/main and main
- Preserve full image editing suite: OBB, crop, proportional resize, arrange, and context menu actions
- Preserve text editing formatting tools: bullet/numbered lists, subscript/superscript, and line spacing
- Cleanly integrate upstream's canvas modularization, font updates, and styling changes
- Refactor arrange_image and run_edit_page_image helpers to satisfy clippy cognitive complexity limits
- All engine, edit, and UI integration test suites passing cleanly