Found while testing apps/designcraft-cli/src/main.rs:361 (`--scale` parse) and :386 (export); crates/render/src/lib.rs:422-423 (render_page)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Merges upstream/main into the branch, resolving 12 conflicted files, and reconciles where the two sides meant different things — notably the format version, where both had independently defined version 2 (EPS sources here, pinned paragraph defaults on main), so a document without EPS was being written as version 1 and re-upgraded on load.
Verified locally against main b17efb2d: cargo test --workspace --no-fail-fast 2869 passed / 0 failed, cargo fmt --check clean, cargo clippy --workspace --all-targets -D warnings clean, xtask layers and wasm ok.
Builds on the fix in this branch, which spotted that a failed OpenGL was
being forgotten because `failed` is filtered to this platform's
candidates and OpenGL is no candidate off Windows.
Rather than spare OpenGL from the filter, run the filter before the
previous start's backend is added. A stored entry that is no candidate
here came from another machine and still says nothing, so it is dropped
as before; the backend a start was actually running on is added
afterwards and stays, whichever backend it is. That keeps the same crash
out of the next instance without naming OpenGL or the platform, and
without holding a failure recorded on someone else's machine against
this one.
Adds the regression test: on macOS, where Metal is the only candidate, a
start left on OpenGL has to keep it out of the instance, while a stored
OpenGL entry that never ran here is dropped.
Resolve 12 conflicted files. Where both sides changed the same code:
- format: keep main's bounded-read load_zip (MAX_ENTRIES, read_entry) and
this branch's u64 version reporting, so a version above u32::MAX is named
as written rather than clamped. Both sides had fixed the same truncation.
- render: main's natural_size supersedes image_natural_size (it already
reads an EPS bounding box and also handles PDF pages); callers in table
and data merge now use it.
- images: keep the DSC-aware header scan and (atend) trailer lookup, take
main's classic-Mac line endings, and carry its overflow guard into
parse_box. eps_proxy draws main's simple rectangles before falling back
to the placeholder, still over a retained EPS source.
- engine/file: take main's placement shape (add_asset, crop box geometry,
place cursor, finite_rect, object effects) and keep this branch's
validate_placement in both branches; edit() works on a clone and commits
only on Ok, so validating inside it stays all-or-nothing.
- engine/links: keep building the replacement before edit() so a file that
cannot be read leaves the document untouched, and move main's crop_pdf
refresh into relinked_asset, which both relink and update go through.
- engine/export: EPS preview warnings join the font-embedding warnings
rather than shadowing them.
- idml: an EPS element keeps ActualPpi (its pixels are a preview raster);
main's PDF exclusion is unchanged.
Cargo.lock regenerated. Tests and doc strings from both sides kept.
`choose` pruned the failed list to this platform's candidates, which dropped an
OpenGL entry on macOS, where Metal is the only candidate. But OpenGL is not only
a candidate: off Windows it is the in-process fallback that `instance_backends`
adds whenever it has not failed. So a start that recorded OpenGL and never
showed a frame had the entry pruned a line later, and OpenGL went straight back
into the instance — the backend that had just failed was offered again on every
start, with nothing able to retire it.
An entry that can still be reached is kept now. A non-candidate entry still
cannot be chosen as a backend, since `pick` only looks at candidates, so which
backend starts is unchanged; it only stops a failed OpenGL being handed back to
the instance.
This is what `a_start_records_its_backend_until_a_frame_is_presented` asserts,
and that test failed on macOS: `cargo xtask ci` stopped in the `designcraft` bin
before the rest of the workspace ran, so the gate could not go green there at
all.
Graphic literals (cli perf, doc assets/item, engine datamerge/export/file/links/mathexpr/media/table/sample, epub, format, pdf, pptx, render, tools, ui-egui properties, and tests): name the branch's clip and wrap (#561, #381) and the PR's fill.
crates/doc/src/item.rs: Graphic keeps wrap and gains fill.
crates/idml/src/import.rs: read the image swatch, then the branch's styled FrameFittingOption lookup; the graphic keeps its wrap.
crates/idml/src/tests/assets.rs: keep both new tests.
crates/render/src/lib.rs: keep #362's crop-box sizing of placed PDFs and colour the resulting pixmap.
crates/render/tests/colorize.rs, crates/epub/tests/grouped_reading_order.rs, crates/compose/src/tests.rs: literals name the branch's Asset and Graphic fields.
crates/pdf/src/export.rs: the soft mask interpolates by the standard's raster mode (the branch has no per-exporter interpolate switch).
docs/gaps.md, docs/target-app-parity.md: status lines list both sides' changes; gap text keeps the clipping-path items and adds the image swatch.
The merge took next's winit patch (rust-windowing/winit#4694, the Windows mixed-DPI drag fix) and
left out main's (skylartaylor/winit 3d51077f, with_close_requested_on_terminate), so Quit from the
Dock stopped asking to save (#623, released in 0.8.0). Cargo takes one winit patch, so the batch
keeps main's until one fork holds both commits; the Windows fix (#631) follows then.
main (#731) and next fixed the same panic separately; main reads null as no
options and tests that the export succeeds, next's test listed null among the
errors. The CLI keeps main's reading.
Conflicts and how each was resolved:
- crates/idml/src/import.rs: main's linked_assets and insert_asset kept; the
crop is read beside main's page (PDFAttribute as a child or a property), and
linked_assets is keyed by (path, page, PdfCrop) so two crops of one page are
two assets.
- crates/idml/src/export.rs: PDFAttribute with PageNumber, PDFCrop and
TransparentBackground is written for every placed PDF, replacing main's
page > 0 block.
- crates/idml/src/tests.rs: main's file plus the `$ID/` font tests.
- crates/engine/src/cmd/file.rs: main's exact PDF page size, place cursor and
add_asset kept; Crop to stores the choice on the asset (the graphic spans
the box), so the place cursor's frame crop box is gone.
- crates/engine/src/cmd/{book,interchange}.rs: main's confined and reporting
imports, with the PDF crops resolved after them.
- crates/engine/src/cmd/print.rs: main's print_with, exporting with the
measured PDF boxes.
- crates/engine/src/cmd/links.rs: main's text links plus pdfCrop.
- crates/engine/src/cmd/{datamerge/fill,mathexpr,media,table}.rs,
crates/format/src/lib.rs: main's add_asset with the asset defaults.
- crates/engine/src/cmd/package.rs, crates/engine/src/tests_idml.rs: both
sides' tests.
- crates/fonts/src/fontdb.rs: main's style traits, looked up through
plain_family.
- crates/render/src/lib.rs: pdf_crop_box replaces pdf_page_box; main's
UserUnit test checks pdf_crop_box.
- crates/ui-egui/src/dialogs.rs: main's dialog with the content-bounds crops.
Also: Asset::same_file compares the PDF crop, so placing or saving no longer
merges two crops of one page; Asset literals added on main take the defaults;
main's PageNumber test checks the crop and that page 1 is written; new tests
a_pdf_page_placed_with_two_crops_imports_as_two_assets and
pdf_assets_with_different_crops_stay_apart.
#84 landed on main squashed (0f4f382b0), so the merge was resolved against #84's
tip as the base: main's version of #84 stands and only this branch's IME changes
are applied on top.
Conflicts and how each was resolved:
- crates/engine/src/cmd/text.rs: removed main's text.compose command, compose_text
and its compose_tests; text.insert keeps main's reset of the typing format after a
successful edit, skipped while an IME preview is open; reselection bumps the
revision.
- crates/engine/src/lib.rs: removed Session.composition and the hook that ended it
in execute_unguarded; set_active/add_document end the composition and reset the
Find scope; both test modules kept.
- crates/engine/src/tooling.rs: removed main's composition() and its
end_composition (the opposite semantics); begin_interaction no longer ends a
composition.
- crates/engine/src/cmd/edit.rs, file.rs, guard.rs, recovery.rs: main's code with
bump_revision in place of revision arithmetic; Revert takes the fresh state's
revision.
- crates/ui-egui/src/canvas.rs: removed main's IME block, composition highlight and
remembered caret rect; this branch's ime_event/ime_output stay. Main's dead-key
test is ported to these events (the accent stays as typed when the caret moves).
- crates/ui-egui/src/lib.rs, menus.rs: both sides' fields and checks.
- apps/designcraft/src/main.rs, apps/designcraft-web/src/web.rs: both sides.
- docs/control-protocol.md: ui.ime row plus main's ui.click/ui.drag params.
Also: the remaining `revision += 1` sites on main use bump_revision, and two tests
compare revisions with `>`; the IME candidate window follows the caret on type on a
path; IME status lines in ROADMAP.md, gaps, ui-, localization- and target-app-parity
docs and roadmap.md.
Conflicts:
- README.md: one WGPU_VALIDATION_INDIRECT_CALL row (this branch's wording).
- apps/designcraft/src/gpu.rs: main's only change is a test tweak in the backend-fallback tests, which now live in gpu/backend.rs; kept this branch's file and ported the tweak there (the check depends on the chosen backend, not the candidate count).
- apps/designcraft/src/main.rs: dropped main's inline VALIDATION_INDIRECT_CALL block (#664) in favour of gpu::instance_flags(); kept the adapter selection, restart and first-frame watch, and took main's macOS Dock Quit handling, preserve_documents_on_exit in on_exit, and profile::config_dir/recovery_dir.
- crates/engine/src/recovery.rs: took main's version; its retained recovered checkpoints do what this branch's recovery change did.
- ROADMAP.md: main's file plus this PR's one progress-log line.
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>
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
to_bytes returns a Result and every caller propagates it, so a document
that can't be written is an error: the existing file, the unsaved state
and the recovery checkpoint stay. Asset entries near or over 4 GiB are
written with zip64 sizes. On the web, Save marks the document saved only
after the browser download has started.
Opening or saving a document moves it to the front of File › Open Recent
(no duplicates; Save a Copy and exports don't count). The submenu lists the
files by name with the full path as tooltip, then Clear Recent Files; the
start screen shows the last eight with their folders. A file that is gone is
dimmed and, when chosen, says so in an alert and stays listed (a disk that
isn't connected comes back). Preferences › File Handling sets how many are
kept (default 20, 0–100).
Engine: Prefs.recent_files / recent_files_max (saved in prefs.json),
file.openRecent {index}, file.recentFiles, file.clearRecent. UI:
app.openRecent; the macOS menu bar rebuilds the submenu when the list
changes.
wgpu validates indirect draw and dispatch arguments with a compute shader it
compiles when the device is created. Metal on an Intel Iris Pro (MacBookPro11,3,
macOS 12.7.6) fails to compile it ("Compiler encountered an internal error" in
MTLCompilerService), the device is lost, and the window never opens.
DesignCraft draws and dispatches nothing indirectly, so leave the check out of the
instance flags. WGPU_VALIDATION_INDIRECT_CALL=1 turns it back on. VectorCraft
does the same (storytold/vectorcraft#651).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Route AppKit termination through the existing unsaved-document close transaction using the immutable winit patch. Restore pending prompts from hidden or minimized windows and preserve answers across repeated requests.
Validation: all six local gates, native cancellation/retry/save smoke, and hosted platform checks.
Fixes#544.
PR #623.
win-mixed-dpi 38dad0fb. Cargo.lock: next's lock plus the winit git source and the dpi 0.1.1 it brings (merged clean; cargo metadata --offline leaves it unchanged).
Conflict: apps/designcraft/src/main.rs: next's startup (adapter selection, catch_unwind, apple events) with log_uncaptured_errors at the top of its wgpu_render_state block; request_adapter_texture_size and mod gpu_limits merged clean
Conflict: ROADMAP.md: progress log both
Conflict: docs/hardware-parity.md: header keeps next's change list plus the mixed-DPI note
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
winit 0.30.13 mishandles WM_DPICHANGED during a live drag on Windows 11:
the window flips between the two scales and grows past the GPU surface
limit. Pin winit to the one commit of rust-windowing/winit#4694, its
0.30.x backport of the fix.
The device asks for the adapter's largest texture side instead of
eframe's fixed 8192, and an uncaptured wgpu error is logged instead of
panicking, so a window larger than the surface limit draws wrongly
until it shrinks rather than closing the app.