The locked-reply check in delete_annotation also reached stamp_preview
and signature_image, which only take the annotation off a copy to draw
the page beneath it: an unlocked stamp with a locked reply lost its drag
preview. They now use lift_annotation, which checks no locks.
The Comments panel's Delete (comment and reply menus) and the page's
context menu are greyed out while a reply below is locked
(comments::locked_below), rather than refusing after the click.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deleting a comment or a reply took every reply to it, locked or not: only
the target's own Locked flag was checked. A locked reply can't be deleted
on its own, so delete_annotation now refuses while anything that would go
with the target is locked (pop-ups follow their comment).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The preview cloned and wrote the whole document on the UI thread for every
page it stepped to, and showed a blank sheet while the page rendered.
Document::without_marks now removes the marks of a kind from every page
once per dialog (UnmarkedPages), each page shown renders from it off the
UI thread, and the page's thumbnail stays until it arrives.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The scrollbar floats over the right edge of the tool list and covered the
right-hand rounded corners of a hovered row. The list now keeps an 18 px
gutter, so the highlight has the same 15 px gap to the panel edge on the
left and to the scrollbar on the right.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
main split canvas.rs, comments.rs, edit_text_ui.rs and moved the page
modules under canvas/ and tools/. The cursor claims are re-applied where
that code now lives:
- page_cursor.rs moves to canvas/, beside page_pointer.rs, re-exported
from lib.rs the same way.
- canvas.rs: claims in the stamp, hand, text-cursor and link code;
over_field and badge_hover are gone; the cursor is resolved once after
the page loop in draw_pages. annotation_hover_text (canvas/area.rs) and
its test give way to comments::describe and hover_tip.
- comments: describe, hover_tip and on_link go to tools/comments/input.rs,
the hover_tip test to tools/comments/tests.rs.
- Edit text's picture claims go to tools/edit_text_ui/image.rs.
- The cursor guard test goes to canvas/page_pointer/tests.rs, with
NOT_PAGE_CURSOR on main's paths.
- Tools that took ui only to set the cursor stop binding it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main split comments.rs into tools/comments/: annotation_deleted,
after_delete and on_page go to state.rs, their test to tests.rs. main
pulled the post-edit selection into select_after_edit, which now takes the
deleted page's annotations from before the delete. ROADMAP.md and
docs/roadmap.md keep both entries, main's newer one first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main split lib.rs: the unmarked_page field stays on PdfCraftApp in lib.rs,
its init and the render_pending check moved to app.rs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main split and refactored the text-rewrite code this PR changes
(automation dispatch.rs, edit added/ and text/, text_regions/styled.rs).
Conflicts in added/store.rs, text/rewrite.rs and text_regions/styled.rs
take main's structure, with per-row alignment applied in it:
- rewrite.rs: LineWriter keeps the paragraph's alignment and the rows'
own (align, aligns); each line looks up its row's alignment
(align_of) for justify and for its offset. Alignment::offset takes the
row's alignment. Wrapped lines carry their row, as wrap returns it.
- styled.rs: wrap_styled_rows also returns each row's draft line
(row_lines), which the kept-rows check counts aligns by.
- Imports for the moved modules (alignment_frame, BlockStyle,
align_code, align_from, content in dispatch.rs).
# Conflicts:
# crates/ui-egui/src/commands.rs
# crates/ui-egui/src/dialogs.rs
Main split the command and dialog dispatch for its stricter clippy
rules; New bookmarks from text styles goes into the command list after
New bookmarks from structure, and its dialog into the dialog dispatch
next to the other dialogs with their own buttons.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GbuSKAS2k5248LGFEapMUR
redraw_field after the join redrew every button of the target group, so a
button with its own appearance states lost them to the default marks. Only
the moved buttons are now redrawn under the group's name.
Test: joining_a_group_leaves_its_own_buttons_appearances_as_they_are
(fails before: the group's own button gets new /AP streams).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
Sorting a field's own items carries its /I positions through the sort
(ISO 32000-2 12.7.5.4) instead of re-deriving them from /V, which names
only the export value. Values no carried position covers still take their
first position. items_to_sort and write_sorted_opt keep set_props under
the complexity limit.
Test: sorting_keeps_which_of_two_items_with_one_export_value_is_selected
(fails before: /I [0], Alpha, instead of [2], Zeta).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
Adopt upstream's split set_props: rename_field returns the group a renamed
radio button joins, and redraw_or_join keeps set_props under the new
cognitive_complexity limit. join.rs imports explicitly (wildcard_imports).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
Upstream moved the interpreter state into a Walk struct; the saved-state
stack now carries (Gs, tm, tlm) there. Re-proved: the redact test fails
with upstream's interp.rs and passes with this one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
Adopt upstream's split set_props and explicit import lists. The list box
scroll after a property edit moves into scroll_list, keeping set_props under
the new cognitive_complexity limit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9