252 Commits
Author SHA1 Message Date
Dylan M. Taylor 15d13dde20 Update prompt shows the app icon and accent color instead of a letter (#2023)
UPDATE_APP (native builds; craft-update is a native-only dependency) carries our 128 px icon and accent (#7bb51c, its colour in ArtCraft Launcher). The updater, the self-update
helper and craft-update's startup prompt all get them through crate::UPDATE_APP; the
prompt draws the icon, the accent and "by ArtCraft" (craft-update's default developer).

craft-update is pinned to storytold/craft-libs main at 1065653e7 (#434).
Clippy fixes for Rust 1.99 in untouched code (same logic):
- crates/engine/src/cmd/export/text_tests.rs
- crates/pdf/src/gradient_inks.rs
2026-10-30 01:28:06 -04:00
aiongg 855d606325 Show file and message dialogs without blocking the frame (fixes macOS crash) (#1961)
* Show file and message dialogs without blocking the frame

A blocking rfd dialog inside the egui frame runs AppKit's modal loop; a
file dragged from Finder and held over the window then re-enters winit's
handler and aborts the app. Dialogs now go through craft-dialogs: a
command without a path asks with a continuation and returns, the host
shows the dialog at the end of the frame, and the continuation runs with
the answer on a later frame, in the document it was asked for. Commands
given a path stay synchronous. The replace-file question is a message
box from the same queue. Quit and Close wait for a Save As dialog's
answer. clippy.toml disallows rfd::FileDialog and rfd::MessageDialog.

* Raise the open file panel when File > Open is chosen again

* Repin craft-dialogs to the craft-libs PR head

* Queue dialogs asked by different code or documents; hold answers behind newer dialogs

Two asks for the same file dialog were treated as one, so Place for one
document and then Choose Image, or Place again for another document, ran
the first ask's action. An ask is now skipped (and the open panel
raised) only when the same code asked for the same dialog for the same
document. An answer that would replace an egui dialog opened since the
asking (an unsaved-changes question, Preferences) waits until it closes;
Quit counts it as pending.

* Repin craft-dialogs to the craft-libs PR head

* Allow the file dialogs' existence checks in the file guard

* Repin craft-dialogs to storytold/craft-libs main
2026-10-28 17:13:45 -04:00
Dylan M. Taylor 251dabb2b5 Refactor designcraft-cli for strict Clippy checks 2026-10-28 11:39:26 +00:00
NetterSilverandClaude Opus 5.5 1db1635a88 Ask the composition cache once per story and page in PDF export
The PDF exporter called Cache::get twice for every text frame it drew.
Each call rebuilds the cache signature of the whole story, which looks up
every frame of the story with Document::find (a linear walk over all
spreads), and the anchored-frame placements walk them again. For a story
threaded through many frames this is O(frames^2 x items) per export.

The document doesn't change during an export, so the exporter now keeps
the composition it got for each (story, page) and reuses it. The cache
still decides what to compose; the output is byte-identical.

Add examples/pdf_export.rs to designcraft-cli: File > Export PDF through
a headless session on the `designcraft-cli perf` stress document (200
pages, 1M characters in 40 threaded stories, 500 placed images). The
stress document generator moves unchanged from src/perf.rs to a small
library module (src/stress.rs) that the binary and the example share.

Ryzen 16 threads, Linux, 12 runs each, interleaved:
  main     median 3485 ms, min 3427 ms
  patched  median 2016 ms, min 1656 ms  (-42 % median, -52 % min)
The exported PDF is byte-identical (12,075,744 bytes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 16:47:25 +01:00
aiongg ab48df68c8 refactor(designcraft): move the tests module of gpu.rs to gpu/tests.rs 2026-10-26 18:18:14 +00:00
aiongg 3fc0bf83fd Merge branch 'keymap-adoption' into keymap-shortcuts-dialog
# Conflicts:
#	Cargo.toml
#	xtask/src/pdf_deps.rs
2026-10-26 02:06:51 +00:00
aiongg 23173b9065 Merge remote-tracking branch 'upstream/main' into keymap-adoption
# Conflicts:
#	Cargo.lock
#	Cargo.toml
#	xtask/src/pdf_deps.rs
2026-10-26 02:06:25 +00:00
aiongg c6444a0471 A command's other shortcuts run while the macOS menu bar holds its first 2026-10-26 01:39:22 +00:00
aiongg d28271b489 Merge branch 'keymap-adoption' into keymap-shortcuts-dialog
# Conflicts:
#	Cargo.lock
#	Cargo.toml
2026-10-26 01:16:37 +00:00
aiongg 1a52593d0d Merge remote-tracking branch 'upstream/main' into keymap-adoption
# Conflicts:
#	Cargo.toml
2026-10-26 01:15:41 +00:00
aiongg c856db68f1 Merge branch 'print-color' into print-color-x1a
# Conflicts:
#	Cargo.toml
2026-10-26 01:11:11 +00:00
aiongg 240dbcf731 Merge remote-tracking branch 'upstream/main' into print-color
# Conflicts:
#	Cargo.toml
#	crates/idml/src/export.rs
2026-10-26 01:10:39 +00:00
aiongg 074db00079 Merge print-color into print-color-x1a
Brings in #1374 with upstream main merged (egui 0.37, craft-libs at
storytold/craft-libs e3b9e239).

Conflicts:
- Cargo.toml: print-color's pins (storytold/craft-libs e3b9e239 for
  craft-pdf, craft-prepress, krilla and the hayro crates; winit and egui
  patches), with craft-prepress keeping the `flatten` feature.
- xtask/src/pdf_deps.rs: print-color's REPO/REV.
- Cargo.lock: print-color's, relocked with cargo metadata; adds only
  craft-prepress and linesweeper.
- crates/pdf/src/export.rs: main's optional-content registration and
  add_form_fields helper; the helper leaves form fields and media out of
  PDF/X-1a with a warning.
- crates/engine/src/cmd/export.rs: the X-1a parameter docs plus main's
  `created`; the X-1a flatten and shadow-mask path on top of main's
  PDF/A-2b-only rasterize_layered.

Also: x1a_tests builds FlattenTotals through Default, since the struct is
non_exhaustive at e3b9e239.
2026-10-25 19:55:21 +00:00
aiongg 65c387732d Merge upstream/main into print-color
PDF/X and CMYK groups keep this branch's approach: krilla's serialize
settings (pdfx::print_settings) write the CMYK page and transparency
groups, the GTS_PDFX output intent and the PDF/X Info and XMP entries.
make_pdfx4 adds only the XMP dates, so main's title argument and its
output-intent, Info and pdfxid XMP edits are dropped, and both
cmyk_group_spaces calls after writer.finish() (Export PDF, Print
Booklet) stay removed. Main's new parts are kept: file_info_metadata
(also for Print Booklet, on top of the print settings), optional content
registration and layer errors, add_form_fields, and pdfx's fonts module.
Main's metadata panic test calls the two-argument make_pdfx4.

craft-libs and the krilla patch are pinned to storytold/craft-libs
e3b9e239; Cargo.lock is main's, relocked without updates.
2026-10-25 19:47:27 +00:00
aiongg 77c718aa68 Merge keymap-adoption (#1298, on main) into keymap-shortcuts-dialog
craft-keymap with the dialog comes from storytold/craft-libs main e3b9e239. egui 0.37's
shaper owns bidi, so the dialog's labels go to it in logical order: rtl::visual_text and the
dialog's visual hook are gone. ForeignSet keeps the dropped-entry count through its builder.
2026-10-25 19:39:58 +00:00
aiongg 073ac98694 Merge upstream main into keymap-adoption; pin craft-libs to main e3b9e239
Main's shortcut dispatch (shortcut_key, used for keys forwarded from floating panel windows)
becomes the keymap's: shortcuts and forwarded_shortcuts both take their presses through the
key dispatcher and run them once the input is released. craft-keymap comes from
storytold/craft-libs, ForeignSet is built through its constructor, and the panel shortcut test
asks the docking layout which panel shows.
2026-10-25 19:36:33 +00:00
Dylan M. Taylor d607c068b1 feat: startup update prompt modal and direct self-updating
The update flow (check at start or on request, the startup prompt, the self-update worker) is craft-update's Updater; the app only supplies the suite, its close path and its status window. The check at start is opt-in for the desktop app, and the Windows update helper runs before argument parsing.

Ref: storytold/craft-libs#319
2026-10-25 15:02:26 -04:00
aiongg 5c99863f12 Merge remote-tracking branch 'upstream/main' into docking-trial
# Conflicts:
#	Cargo.lock
#	Cargo.toml
#	apps/designcraft/src/main.rs
2026-10-25 18:18:36 +00:00
aiongg 02cb2876d2 Merge upstream main into docking-trial
Main's File Info PDF metadata gets craft-pdf's modified field (None, as before), and the
Notes panel ids use Id::unique (egui 0.37).
2026-10-25 17:52:22 +00:00
skylar taylor-barrick bd4b9dd1d2 egui-037: reconcile shared window integration with main 2026-10-25 18:34:10 +01:00
aiongg 1da1f7028b Merge designcraft#1810 (egui 0.37) into docking-trial; pin craft-libs to main a0916808 2026-10-25 17:16:25 +00:00
aiongg 56f15a600d Merge upstream main into docking-trial; pin craft-libs to main 5bbe9f62 2026-10-25 16:25:57 +00:00
Brandon Thomas a8b8b3eb62 Merge pull request #1779 from dotCooCoo/fix/automation-roots-tests-on-windows
MCP: the several-roots tests expect Windows paths without the \\?\ prefix
2026-10-25 12:25:47 -04:00
aiongg fec0897a5d Application bar: use craft-window's shared title bar (#1811)
* UI: the application bar uses craft-window's title bar

DesignCraft's title-bar code moved to craft-libs as craft_window::title_bar.
The application bar calls it instead of its own copy, and the macOS shell
takes sync_movable from it. Differences: a drag on an in-window panel lying
over the bar no longer moves the window, full screen ignores the bar's drag
and double-click, and floating panels in their own windows keep their own
movable flag.

* UI: craft-window from craft-libs main

* UI: the application bar ignores the title bar's response
2026-10-25 12:06:36 -04:00
skylartaylor 8a6ed75d41 egui037: reconcile current DesignCraft main and qualify CI 2026-10-25 03:38:13 +01:00
skylartaylor a2db858e7d egui037: Port DesignCraft to egui 0.37
Pin the final egui, winit and shared UI revisions and reconcile current main.
Preserve document-epoch input and screenshot boundaries, host action ordering,
and Windows path regression coverage. Add click/End batch characterization.

Validation: all nine Linux CI stages pass (3230 tests, zero failures); Windows
CI and bounded native editing workflows pass on the same production source.
2026-10-25 00:52:21 +02:00
skylar taylor-barrick 9fc02f8f7b Merge contributor branch codex/placed-graphic-layer-overrides (PR #1647) into main
Conflict resolutions:
- crates/pdf/src/export.rs: keep main's `graphic(.., hidden)` signature from
  #1241 and report retained graphic-layer overrides that drawing cannot apply.
- docs/gaps.md: keep main's change list and add the graphic-layer entry.
2026-10-24 15:30:03 -06:00
aiongg b3be40ed08 Merge remote-tracking branch 'upstream/main' into docking-trial
# Conflicts:
#	Cargo.toml
2026-10-24 18:46:43 +00:00
dotCooCoo bab76dcd11 MCP: the several-roots CLI test keeps a share path a share path
The test removed only the \\?\ prefix from the folder it resolved, so a
temporary folder on a network share became the relative path
UNC\server\share\... . It now removes the \\?\UNC\ prefix first and
keeps the two leading backslashes, as file_access::plain does, with a
test of both forms.
2026-10-24 10:44:08 -07:00
dotCooCoo 1f613f8c66 MCP: the several-roots tests expect Windows paths without the \\?\ prefix
Four tests from #1678 fail on Windows. Each one builds its paths, or the
text it expects, from a folder resolved with `std::fs::canonicalize`,
which adds the `\\?\` prefix on Windows. `AutomationRoots::describe` and
the rejection messages show the roots without that prefix, so the
expected text does not match. Windows does not resolve `..` in a path
with the prefix, so the checks refuse every such path as one they cannot
resolve, including the ones the tests expect to pass from one root into
another. The code the tests check is unchanged.

The engine tests take the resolved folder through `file_access::plain`,
which the roots use for the text they show. The CLI test strips the
prefix the same way.
2026-10-24 08:41:44 -07:00
Brandon Thomas b724698d98 Merge remote-tracking branch 'origin/main' into pr1678
# Conflicts:
#	apps/designcraft-cli/src/main.rs
2026-10-24 10:03:25 -04:00
andeecollard 7074dc8a85 M6: preserve per-placement graphic layer options 2026-10-24 13:22:08 +01:00
Dylan M. TaylorandClaude Sonnet 5.5 8299d26400 chore: replace trivial closures with method paths (Sonar S1612) (#1714)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 04:30:43 -04:00
Dylan M. TaylorandDylan Taylor f90d286721 chore: resolve SonarCloud maintainability issues (#1698)
* chore: resolve SonarCloud maintainability issues

- Replace lambda closures with method references (S1612)
- Replace wildcard imports with explicit imports (S2208)
- Refactor complex functions with extracted helpers (S3776)
- Replace OR pattern with range pattern in math parser (S9047)

* style: apply rustfmt to warichu helper

* fix(compose): revert slice conversion in tests and remove unused var in warichu

* Group warichu row layout scalars to satisfy S107 parameter limit

* style: satisfy rustfmt in warichu.rs

---------

Co-authored-by: Dylan Taylor <dylan@craftmaker.dev>
2026-10-24 03:54:24 -04:00
Brandon Thomas 058b369b53 Merge storytold/main into squad/pdfx-unembedded-fonts
# Conflicts:
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-23 17:30:20 -04:00
Brandon Thomas e3ac9936a6 Merge pull request #1371 from Hamza0101/fix/backend-log-says-what-happened
Graphics: the start-up log says which backend was chosen and how to change it
2026-10-23 17:28:35 -04:00
Marco Arnulfo b5342e48cd Save and export dialogs start in the document's folder, or the last one used (#1653) 2026-10-23 21:32:05 +02:00
Marco Arnulfo 7362530591 MCP: --automation-read-root and --automation-write-root may each be given more than once (#1552)
A path passes when it lies inside one of the roots of its kind. The roots'
common parent, a sibling named like a root, `..` or a link out of them and
the other kind's roots stay refused; a folder given twice counts once.
2026-10-23 20:30:07 +02:00
Dylan M. Taylor df1db1646b web: let the browser zoom the page (SonarCloud Web:S7926) (#1641)
The viewport no longer sets user-scalable=no, so people who need to zoom
the page can (WCAG 1.4.4). The canvas takes touch-action: none, so pinch
and pan gestures on it still go to the app's own zoom, as eframe's touch
handlers expect, instead of zooming the page.
2026-10-23 11:16:49 -04:00
aiongg 0409bbf9aa Merge branch 'print-color' into print-color-x1a
# Conflicts:
#	Cargo.lock
#	Cargo.toml
#	xtask/src/pdf_deps.rs
2026-10-23 08:16:07 +00:00
aiongg 7ac286e6d2 Merge branch 'keymap-adoption' into keymap-shortcuts-dialog
# Conflicts:
#	Cargo.toml
#	apps/designcraft/src/native_menu.rs
#	crates/ui-egui/src/i18n.rs
2026-10-23 05:41:18 +00:00
aiongg a30b71cd36 Merge remote-tracking branch 'upstream/main' into print-color
# Conflicts:
#	Cargo.lock
#	Cargo.toml
#	xtask/src/pdf_deps.rs
2026-10-23 05:40:13 +00:00
aiongg 3c38c84438 Merge remote-tracking branch 'upstream/main' into keymap-adoption
# Conflicts:
#	Cargo.toml
#	crates/ui-egui/src/menus.rs
#	crates/ui-egui/src/shortcut_import.rs
#	docs/target-app-parity.md
#	docs/ui-parity.md
2026-10-23 05:10:57 +00:00
aiongg bc1ccfa481 Merge remote-tracking branch 'upstream/main' into docking-trial
# Conflicts:
#	Cargo.toml
#	crates/ui-egui/src/i18n/fr.rs
2026-10-23 05:07:00 +00:00
aiongg 3ee11391ae CI: one integration-test binary per crate 2026-10-23 01:17:48 +00:00
KnottyDyes bba31c17b6 PDF: report visible unembedded fonts in PDF/X-4 (#909)
Track text state through graphics saves and drawn forms; exempt invisible text. Report font names and pages in export warnings and validation without embedding or outlining placed fonts.
2026-10-22 10:31:21 -06:00
aiongg 8f98f3dc8b Keyboard Shortcuts: import an InDesign shortcut set (#1296)
* #1180: import an InDesign keyboard shortcut set

Edit > Keyboard Shortcuts gets Import InDesign Set..., which reads the
.txt that InDesign's Show Set writes (window.importShortcuts {path|text}).
Matched commands take the set's Default shortcut, or lose theirs when
every occurrence says [none defined]; other commands give up keys the
set takes. The report lists changes, cleared conflicts, and the items
that could not be matched or read. Tool keys stay fixed.

* #1180: read the set file through the checked reader

* #1180: when two items want the same keys, the later one takes them
2026-10-22 09:28:11 -04:00
aiongg 2660395db5 macOS: a Language submenu in the app menu (#1541) 2026-10-22 01:31:36 -04:00
aiongg 496b1899a2 macOS menu bar: menus keep working after a language change; key equivalents follow the shortcuts (#1540)
* macOS menu bar: keep working after a rebuild; key equivalents follow the shortcuts in effect

* macOS menu bar: the shortcut test sets its key with window.setShortcut
2026-10-22 01:18:41 -04:00
Zuujis 438d7b6d79 Dedupe the 400-year-cycle UTC conversion and the EPUB package writer (#1537)
* Dedupe the 400-year-cycle UTC conversion and the EPUB package writer

SonarCloud flagged new-code duplication on main (17%): five copies of the
"move the instant into chrono's range by whole 400-year cycles" conversion
and two copies of the EPUB OPF/container/zip writer.

- designcraft-doc gains a `time` module: `date_parts(days)` and
  `utc_parts(secs, nanos)`. App logging, EPUB `iso8601`, PDF `civil`, PPTX
  `civil` and text-variable `civil` are now thin callers; each keeps its own
  format string and year clamp. The app reaches it through
  `designcraft_engine::doc`, so its direct chrono dependency is dropped.
- EPUB: `package_opf` + `write_package` shared by fixed-layout and
  reflowable export (same entry order and compression).
- Boundary tests on the helper: pre-1970, year 9999 -> 10000, i64 extremes,
  nanos past a second.

Output is unchanged: existing date tests pass, and fixed/reflowable EPUBs
(with/without cover, author, pre-1970 and i64::MAX dates) are byte-identical
to origin/main.

* time: drop the unreachable i128 clamp and match arms in utc_parts

Whole 400-year cycles times 400 stay under 2.6e16 for any i64 day count,
so the year can't overflow i64 and the i128 + saturate was dead code
(carried over from the copies this replaced). The date and time lookups
can't fail either; each keeps one unwrap_or_default instead of a match
with a fake 1970 fallback. Output and tests unchanged.
2026-10-21 21:46:38 -04:00