* Fix Links panel stalls on network paths
* Fix Links review findings
* Fix Links worker gating and wasm build
A link check blocked on another document's unreachable share no longer
holds back the document now shown: after 200 ms a fresh worker takes
over (at most four alive at once) and the stale result is dropped.
relinked_asset used the native-only fingerprint_from, breaking the
wasm build; it now uses fingerprint_of. The EPS relink and IDML link
reach tests build Windows paths correctly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Fix Links worker repaint and graphic read retry
Switching documents before the 200 ms takeover now schedules a repaint
for it: a blocked worker never asks for one, so without other input the
panel kept waiting. A failed graphic read is no longer cached as
missing, so the next check reads again. The legacy EPS fixture clears
its source fingerprint, as documents from before fingerprints have
none, so the test no longer depends on running within two seconds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Keep touched links OK and poll Links within seconds
Only a new size now marks a graphic Modified from its fingerprint. A
file with the same size but a new time (saved again, copied, restored
or checked out) is compared byte for byte, once per new time, and stays
OK when unchanged.
External changes are polled every two seconds instead of every five
minutes. The next check waits at least as long as the last one took
(up to five minutes), so a slow share stays idle half the time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Wake Links when a worker slot frees; translate check strings
A worker thread asks for a repaint when it ends, so a document waiting
at the four-worker limit gets a worker as soon as a superseded one
returns from its blocked call, without other input.
Rows from a check keep a weak reference to their document. The key
names it by address, which a later document can't reuse while the
allocation is kept.
The Links panel's new strings ("Checking", "Checking links…", "Link
status is unavailable.") use the UI's ellipsis and are translated in
every interface language. The comments on Session::text_link_checks and
on the byte comparison in links::status describe what they do now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Links: rows from the document, statuses from a check that can stop
The panel lists links from the document on the UI thread (no file is
looked at) and fills in the statuses a worker check last found, kept
per document. An edit shows at once, and a check of an earlier edit
still supplies statuses instead of being discarded.
- A check of another document is told to stop and ends before its next
file; only one blocked in a file system call is replaced, so blocked
workers no longer hold their slots for a whole document.
- Checks are timed on the worker, and what they remember passes to the
worker that replaces them.
- A fingerprint is kept only for a file whose time was settled when it
was read, as for linked text. A file found unchanged by its bytes
gets its current size and time stored with the document
(remember_link_files), without marking a saved document as changed.
- Asset::set_link keeps link and fingerprint together; copies made by
Package and Copy Links To carry none.
- The Session holds one LinkListChecks. Update Link lists files only
when a linked story was edited in the layout.
- The unused "Checking links..." caption is removed.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
* Text: adjust hyperlink and placeholder source ranges on story edits (#1219)
Text-anchored hyperlinks and data-merge placeholders store byte ranges
(`start..end`) pointing into story text. When text earlier in the story is
inserted or deleted, these offsets remained unchanged, causing hyperlink
targets and data-merge placeholders to drift across unrelated text or
point to invalid UTF-8 boundaries.
This change:
- Implements `Document::adjust_text_ranges` in `designcraft-doc` to shift
or resize text hyperlinks and placeholders when story text is edited.
Pure insertions at or before the link shift it forward; insertions
inside expand the link's span; deletions before shift it back; deletions
consuming or overlapping the link clamp to surviving text or prune it if
entirely consumed.
- Cleans up obsolete hyperlinks and placeholders in `set_story_text`.
- Wires range adjustment into engine text commands: typing, deleting,
selection replacement, autocorrect, `story.replaceRange`, find/change,
and spelling change.
- Adds comprehensive unit tests in `designcraft-doc` and 7 integration
regression tests in `crates/engine/tests/hyperlink_offsets.rs`.
* fix(engine): adjust hyperlink and placeholder ranges for physically removed text under track changes
* fix(engine): adjust text ranges for formatted paste, case change, and drag move
* fix(engine): record change case adjustments per character to preserve enclosed hyperlinks
* fix(engine): adjust text ranges for track changes, inline markers, and preserve zero-length ranges
- Route Track Changes accept/reject deletions through adjust_text_ranges, and transfer replaced text hyperlinks to inserted text so links survive accept.
- Hook inline marker operations to adjust_text_ranges (notes, endnotes, xref, anchored objects, index markers, tables, editorial notes, unvar, and place text).
- Replace datamerge::shift_after with adjust_text_ranges.
- Only prune ranges that the edit emptied (s < e && s_new >= e_new), preserving zero-length ranges.
- Drop inverted ranges first and use saturating arithmetic in remap_range.
- Preserve doc.hyperlinks and placeholders ordering in move_text_ranges by mutating in place.
- Remove dead wrapper mark_deleted and call mark_deleted_reporting.
- Fix changes.track invocation parameter to 'on'.
- Add comprehensive regression tests in doc and engine covering all review items.
* fix(text): guard cell find/change, pre-record tracked replaces, shift para xref anchors
* fix(text): record stable IDs for tracked replacement redirects
* fix(text): reconcile range adjustments for review feedback
* fix(text): adjust ranges on delete surrounding and clean up dead auto edit
* Prune text ranges on TOC index and live caption regeneration
* test(engine): format regenerating_index_prunes_story_hyperlinks line wrap
Both exports walk Document::placed_items: document and parent spreads, groups, and the objects
anchored in a story. That is the walk the parent-page and anchored fix introduced. The
nested-story traversal (table cells, notes, endnotes), Document::update_placed_items and the
fail-closed refusals that went with it are not part of it: that is the traversal change on its
own, not the placements this pass carries. update_items keeps its reach, so the two exports clear
the choice where the placement actually is again.
A choice anchored inside a table cell's or a note's text therefore stays on the document and is
not carried into the embedded IDML copy, as on main; the refusal that turned that silent
difference into an export error goes with the traversal it guarded, since asking the walk's own
predicate it could never fire. The stale-field half keeps: has_applicable_pdf_layer_overrides
still asks for a graphic whose asset is a PDF, so relinking a placed PDF to an image no longer
makes the package report name a choice that does not exist.
Tests: the nested-placement regression and the relink integration test of the removed refusal
go; the parent-page regression, the stale-field predicate test and the format 4 cell-only reader
test stay. The comments no longer claim every placement counts.
The workspace file guard scans test modules too when they are declared `pub(crate) mod` (it only
skips a plain `mod`), so a temp file written inside pdflayers.rs' tests failed
no_crate_touches_the_file_system_outside_the_checked_readers. The unit test now builds the state a
relink leaves behind - layer fields on an item whose asset is an image - and asserts the refusal
predicate ignores it while all three exports run. The user path (a real links.relink to a PNG) moves
to tests/pdf_optional_content.rs, where the guard exempts fixtures written by test files.
The graphic-layer test covered a mixed document. A save whose only layer settings sit in a
table cell has to choose version 4 too, asserted against the saved metadata.
Graphic gained an optional layer_options field, so the test-only literals in the PDF
crate and in the engine's optional-content support need it. No behaviour changes.
Main's insert_pages_bounds.rs and a_page_count_insert_pages_cannot_mean_is_refused
pin that an `after` past the last page appends. This branch had made it an error;
`before` (new here) still has to name a page, and both together are refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main now aborts an export when a stored hidden layer can no longer be
applied (#876). Vector export follows it: a layer hidden in Object Layer
Options that the placed PDF doesn't have, because the file has no layers
or was relinked, fails with an error naming the file instead of exporting
everything visible. The image-path tests for oversized images and
undecodable content export as PDF/A-2b, which still rasterizes placed
PDFs with hidden layers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep source defaults and per-frame hidden layers in plain and PDF/X-4 vector exports, including independent repeated placements, booklet output and book merging. Accept usage-application arrays and resource-only groups without a catalog configuration.
Use file-named warnings and all-ON defaults for recoverable default-configuration problems and unsupported base states. Add readable groups omitted from OCGs without discarding their visibility, combine concurrent recovery warnings, and generate missing names only when no frame override needs name matching. Retain errors for contradictory states, unresolved references and ambiguous name overrides.
Cache prepared variants per asset and hidden-name set while replaying recovery warnings through the existing deduplicating channel. Keep original source bytes when every group is visible and named, so reader-repaired cross-reference tables remain usable without an incremental layer update. When hidden groups or generated names require writing, reject an unusable cross-reference table with a file-named damage error and retain the first placement failure.
Cover visibility, recovery, names, independent placements, repaired cross-reference tables and vector output with 46 optional-content integration tests, plus preparation-cache and shared-buffer unit tests.
Integrate current main (d8d78851) without rewriting the contributor's commits, retaining craft-libs 9f75d3dc.
Accept literal null trailer entries while rejecting actual or ambiguous encryption/hybrid markers. Structurally inspect bounded xref/trailer history instead of adopting trailer-looking comments. Preflight all selected source variants before editing/undo; preserve reset-to-defaults. Suppress stale unrewritable canvas overrides rather than revealing source content. Keep conservative all-content crop fallback without overcropping layerless/null stubs.
Validation: cargo xtask ci, all 9 gates passed; 3062 tests passed, 6 skipped; all 16 WASM-safe crates checked. 17 supported variants passed independent qpdf, logical preservation and Poppler pixel checks; 6 unsupported/repair-only cases refused. Direct-harness Claude Opus 5 reviewed the exact main-relative delta with no blocking findings.
No native-format changes or unrelated layer features. Synthetic regression fixtures are generated in test code.
`after` or `before` past the last page is refused without overflowing and
changes nothing; inserting after the last page still appends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/object.rs:299-313 (object.stroke: `st.tint = v as f32`, `st.gap_tint = v as f32`; the no-selection path at :273 clamps, this one doesn't)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reject page selections that cannot fit the target index type before conversion, and return an error for the largest direct PPTX page index.
Validated the combined main and PR tree with all seven cargo xtask ci stages (1,967 passing tests), four actual WASM export-page regressions, and successful FreeBSD, Windows ARM64, and packaging workflows.
Found while testing crates/doc/src/build.rs:377 (Document::insert_pages), called from crates/engine/src/cmd/layout.rs:91
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Add multi-click text selection, pending caret formatting and clipboard support, text-aware colour controls, guide/grid snapping, and frame threading with loaded-port interactions.
Include the layout, drop-cap and IDML updates carried from #83, with safeguards for generated and linked stories, external text references, local IDs, locked frames and atomic colour edits.
Fixes#23.
Co-authored-by: Sean Hammond <sean.hammond@und.edu>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge current main into the text editing and layout work, retaining the original contributor commits. Preserve generated and linked stories, incoming text references, local IDs and locked frames when threading.
Keep pending formatting and text colours consistent, isolate deferred control clipboard input, and handle inherited IDML frame properties with bounded column allocations. Add focused state-preservation and round-trip regressions and correct the socket and swatch test fixtures.
M2; refs #84.
Callers from outside the app now get a parameter error for a key a
command's params description doesn't name, instead of the command
running with its defaults: `text.select {story, start, end}` and
`style.list {bogus: 1}` are refused with "unknown parameter `…`" and the
nearest or the accepted keys.
- engine: `cmd::params` (ported from VectorCraft #1442) reads the keys
from each params description; `check_params` / `check_keys`.
`Session::execute` itself stays lenient so old journals replay; in the
engine's own tests it checks too, as the audit of the descriptions.
- MCP: tools/call answers -32602 for an argument a tool doesn't take and
for params a command doesn't take (`execute`, every `batch` step before
the first runs); the headless backend checks as well.
- Control channel (`engine.execute`) and `designcraft-cli run --cmd`.
- Descriptions now name every key their commands read (table target
cells, anchored objects, gap.move, file.place, data merge, prefs.set,
…); type.char / type.para stay unchecked (their attributes may be the
params object itself; the setters refuse unknown attributes).
- Tests that passed keys the commands never read are fixed, and the pen
tool no longer sends the unread `notifyPen`.
Fixes#776
upstream/main c3859c82 (#614e198eb5d, #6159509b2bc, #6168e151a2f, #617c3859c82).
Conflict: ROADMAP.md: header keeps next's change list plus orderly native exit recovery; progress log both
Conflict: apps/designcraft-cli/src/main.rs: next's case-insensitive .jpg/.jpeg check with main's write_atomic
Conflict: apps/designcraft/src/main.rs: on_exit keeps next's startup.presented() (not an Option) and adds preserve_documents_on_exit; next's discard_marked_text and main's preserve_documents_on_exit both; Services write uses write_atomic and keeps next's clipboard_text; tests both
Conflict: crates/engine/src/cmd/book.rs: main's ChapterBatch paginate with next's chapter-number resolution and {pages, chapters} result and doc
Conflict: crates/engine/src/cmd/file.rs: revert takes main's discard_document ordering without the revision + 1 (next's DocState::new hands out a fresh revision)
Conflict: crates/engine/src/recovery.rs: main's save_tracked, write_entry and open (st.bump_revision() for next's global revisions); next's reopen-twice test assertions kept
Conflict: docs/gaps.md, docs/target-app-parity.md: headers combined; next's Language, Books and Numbering lines plus main's book update safety line
Conflict: crates/engine/src/cmd/file.rs: spec strings keep next's (extension, return value, .indd); Save a Copy adds the extension then rebases data-source paths on a copy; file_save takes main's prepare-then-commit order with next's bump_revision
Conflict: ROADMAP.md: status line lists both changes; progress log keeps both sides
Prepare and stage every chapter before a book update writes files. Preserve imported IDML sources, reject open native chapters and report completed paths after a later write failure.
Follow-up to #485.
PR #615.
Preserve document paths, revisions and collection contents when persistence fails. Rebase Save a Copy paths without changing the open document.
Follow-up to #485.
PR #613.