Commit Graph
385 Commits
Author SHA1 Message Date
aiongg 578818466e Merge craft-cms: Colour management on craft-cms 2026-10-13 13:08:18 +00:00
aiongg fc31f7f14d Merge craft-cms-adoption: colour management on craft-cms (next's image path) 2026-10-13 13:07:32 +00:00
aiongg 73af5d7f63 Merge script-dollar: GREP replacements like $1 stay literal in scripts (#749)
# Conflicts:
#	ROADMAP.md
2026-10-13 13:01:31 +00:00
aiongg d227ee84e9 fix(script): only a well-formed $N reference is replaced; $$ is a literal $ (#749) 2026-10-13 13:00:34 +00:00
aiongg b6c63d89c2 Colour: craft-cms for ICC transforms; Coated FOGRA39 default CMYK, BPC display, FOGRA39 output intent
- designcraft-color: ICC profiles (built-in RGB working spaces, user .icc files) go through
  craft-cms from storytold/craft-libs. The default CMYK working space is craft-cms's Coated
  FOGRA39; CMYK swatches show relative colorimetric with the settings' black point
  compensation. Generic CMYK and Device CMYK stay selectable. A CMYK profile name resolves
  through Builtin::stand_in_for; a name with no stand-in is an UnknownProfile error.
  ColorSettings::with_known_profiles replaces unavailable saved names with the defaults; the UI
  uses it when it restores saved settings at startup. Lab, gamut error and proofing of ICC CMYK
  spaces use craft-cms transforms. The Generic CMYK output profile is encoded by craft-cms.
- designcraft-pdf: the PDF/X output intent is the working CMYK space's profile: the FOGRA39
  built-in with identifier FOGRA39, user profiles as loaded, Generic CMYK otherwise.
- moxcms is no longer a direct dependency of designcraft-color.
2026-10-13 12:35:17 +00:00
aiongg afd2f45b69 Merge pr/673: fix(import): preserve XLSX numeric and cached text values (#673) 2026-10-13 12:31:43 +00:00
aiongg d234ede3a2 Merge craft-atomic: Save: delegate atomic writes to craft-atomic 2026-10-13 12:12:16 +00:00
aiongg e81a21e645 refactor(save): delegate atomic writes to craft-atomic
The engine's atomic module keeps its functions and signatures and delegates to craft-libs/craft-atomic (pinned to e9546e19), so every save and export caller is unchanged. write_atomic_with also turns a panicking writer into an error, so its temporary file is removed. Recovery checkpoints and package output keep their own writers, which replace links instead of following them.

On Unix, new files follow the umask and replaced files keep their permissions.
2026-10-13 11:56:42 +00:00
Brandon Thomas 89e9abbc8d Merge branch 'pr/662' into batch1
# Conflicts:
#	crates/engine/src/cmd/object.rs
2026-10-13 02:23:56 -04:00
Brandon Thomas ea8dc959d1 Merge branch 'pr/573' into batch1 2026-10-13 02:23:12 -04:00
Brandon Thomas 6e551af3e7 Merge branch 'pr/663' into batch1 2026-10-13 02:23:11 -04:00
Brandon Thomas 878169130e Merge branch 'pr/600' into batch1 2026-10-13 02:21:53 -04:00
aiongg 8bccf1de6e Merge new-doc-preview: New Document: Preview shows the new document behind the window (#679)
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/file.rs
#	crates/engine/src/lib.rs
#	crates/ui-egui/src/control.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/i18n/it.rs
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/new_document.rs
#	docs/target-app-parity.md
2026-10-13 04:11:58 +00:00
aiongg 5da112a9cb feat(ui): Preview in the New Document window
A Preview checkbox beside Close and Create shows a document with the
window's settings in the workspace and rebuilds it as they change: size,
orientation, pages, facing pages, margins, columns, gutter, bleed and slug.
It is off by default and kept for the session. Fields that don't hold a
value yet, and sizes file.new refuses, keep the last preview.

The engine keeps the preview document (Session::preview_new_document,
end_new_document_preview): it replaces itself in its tab, takes no
Untitled number and is never written as recovery data. Create removes it
and makes the document with file.new, so it starts with a clean undo
history. Close, Escape, ui.dialog.cancel, turning Preview off or another
dialog replacing the window remove it and show the previously active
document again, unchanged.
2026-10-13 03:47:57 +00:00
aiongg eef4648175 Merge next into craft-cms-adoption 2026-10-13 01:55:21 +00:00
storybored379andOpenAI Codex d27eff31cd fix(import): preserve XLSX numeric and cached text values
Keep small and high-precision numbers intact in table placement and data merge. Honor cached formula strings as text, including leading zeros and whitespace.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
2026-10-12 18:29:37 -07:00
aiongg b2aa5c1ec8 Merge object-style-effects: Object styles carry effects (#653) 2026-10-13 01:05:37 +00:00
aiongg ebae0c7805 Merge parent-based-on: Parents: a parent can be based on another parent (#652)
# Conflicts:
#	ROADMAP.md
#	crates/compose/src/cache.rs
#	crates/doc/src/build.rs
#	crates/engine/src/cmd/file.rs
#	crates/engine/src/cmd/layout.rs
#	crates/idml/src/import.rs
#	crates/idml/src/tests.rs
#	crates/pdf/src/tests.rs
#	crates/render/src/lib.rs
#	crates/render/src/text.rs
#	crates/tools/src/snap.rs
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-13 01:00:08 +00:00
aiongg 462831bf81 fix(styles): object style effects keep local effects, validate values, follow BasedOn in IDML (#653) 2026-10-13 00:58:47 +00:00
aiongg 182db943c2 fix(parents): bound every based-on walk; base items follow the parent showing them (#652)
Cycle checks walk based_on with a seen-set over all parents, so a cycle
longer than the drawing depth cap is still found. Snapping lists parent
items through shown_parent_pages instead of recursing, and offers base
items on a parent spread. A base parent's text on a parent spread is
composed for that spread, so page-number markers show its prefix.
Cmd+Shift-click hits what is drawn: it honours Show Parent Items, hidden
and locked layers, the facing-page filter and layer order. IDML import
warns when a parent spread's pages name different bases or their own
spread.
2026-10-13 00:51:41 +00:00
aiongg 98ff430022 feat(styles): object styles carry effects (#653) 2026-10-13 00:40:59 +00:00
aiongg 80803e3ae3 feat(parents): a parent can be based on another parent (#652)
Pages draw the base parent's items, then the parent's, then their own,
on the canvas, in PDF and image export, and on the parent spread itself.
Chains are followed base first with a depth bound. Overrides, parent hit
testing and the flattener's transparency check include base items.

layout.parents.options {parent, basedOn} sets or clears the base and
refuses cycles; document.inspect reports basedOn per parent. Opening a
.designcraft or IDML file clears a cyclic or dangling base with a warning.
IDML already carried the base as the parent pages' AppliedMaster; a test
covers the round trip.
2026-10-13 00:21:48 +00:00
aiongg d7d71380ad Merge pr/610: Links: placed text files can stay linked (Word, RTF, text) (#610)
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/file.rs
#	crates/engine/src/cmd/linked.rs
#	crates/engine/src/cmd/links.rs
#	crates/engine/src/cmd/place_text.rs
#	crates/engine/src/cmd/prefs.rs
#	crates/engine/src/lib.rs
#	crates/ui-egui/src/dialogs.rs
2026-10-13 00:00:51 +00:00
aiongg 7e4c75fce9 Merge pr/662: Commands: reject unknown spread values instead of falling back to page 1 (#662)
# Conflicts:
#	crates/engine/src/cmd/file.rs
#	crates/engine/src/cmd/guides.rs
#	crates/engine/src/cmd/object.rs
2026-10-12 23:59:28 +00:00
aiongg 605b31c6d6 Merge pr/663: CLI: accept a UTF-8 BOM in scripts and document the syntax in script --help (#663) 2026-10-12 23:56:41 +00:00
aiongg 52637295f0 Colour management on craft-cms: Coated FOGRA39 default CMYK, BPC display, FOGRA39 output intent
- designcraft-color: ICC profiles (built-in RGB working spaces, user .icc files) go through
  craft-cms. The default CMYK working space is craft-cms's Coated FOGRA39; CMYK swatches show
  relative colorimetric with the settings' black point compensation, as placed images do.
  Generic CMYK and Device CMYK stay selectable; names of the profiles FOGRA39 stands in for
  resolve to it; ColorSettings::with_known_profiles replaces unavailable saved names with the
  defaults (used when the UI restores saved settings). Lab, gamut error and proofing of ICC
  CMYK spaces use craft-cms transforms. The Generic CMYK output profile is encoded by craft-cms.
- designcraft-images: Profile, Transform, TransformOptions, Intent and Builtin are craft-cms's;
  its black point compensation replaces the module's sRGB table. Untrusted profiles are still
  ignored with a log line.
- designcraft-pdf: the PDF/X output intent is the working CMYK space's profile: the FOGRA39
  built-in with identifier FOGRA39, user profiles as loaded, Generic CMYK otherwise.
- moxcms is no longer a direct dependency.
2026-10-12 23:50:57 +00:00
Marco Arnulfo 9e15d5a17e fix(cli): accept a UTF-8 BOM in scripts and document the syntax in script --help (fixes #654) 2026-10-13 00:41:46 +02:00
Marco Arnulfo 3d59dd5d97 fix(cmd): reject unknown spread values instead of falling back to page 1 (part of #651) 2026-10-13 00:38:54 +02:00
aiongg 5b01f90baf Merge shaped-text-frames: Text frames: lines follow an oval or polygon frame's shape (#646)
Conflict: crates/compose/src/lib.rs: modules and exports both (outline, optical, parabox, hit_note_with); placement = next's carry/grid/wrap_slot code, entered for outlines too (and the first-word `need`); free_slots starts from the outline's intervals with cut set (1.5 em filter, segments in reading order, reversed for RTL); the branch's free_slot dropped
Conflict: crates/compose/src/lib.rs: next's bands are top..baseline (descenders hang into wraps); a shaped frame's outline holds the descenders too: wrap_slot and free_slots take the line's descent, used only for the outline's intervals (the branch's oval tests check descender corners)
Conflict: crates/compose/src/lib.rs: estimate_slots takes the band (the paragraph's ascent from line_metrics, max 0.8 em, and descent, max 0.25 em, for a shaped frame; next's 0.8 em and no descent otherwise), returns early only without wraps and outline; est_first uses that ascent; wrap_slot's 1 pt step for outlines (and contours) returns None when it makes no progress or is not finite
Conflict: crates/compose/src/tests.rs: tests both
Conflict: ROADMAP.md: logs both
2026-10-12 22:23:22 +00:00
aiongg 283f3f5bbc Merge colour-entry: Color Picker and Color panel keep typed values; colours read on a fixed scale (#323)
Conflict: crates/engine/src/cmd/style.rs: object.color params doc and enabled = next's (text?, has_text or selection) + the model form; apply_color = next's text targets and caret typing format + the branch's rounding-based swatch reuse
Conflict: crates/ui-egui/src/dialogs.rs: next's deleteCharacterStyle / swatchOptions arms; colorPicker = the branch's (no-op without edits, model-form colour) with next's `text`
Conflict: crates/ui-egui/src/panels/swatches.rs: double-click opens the picker with picker_fields + next's `text`; spectrum = the branch's model form + next's `text`
Conflict: docs/target-app-parity.md: the branch's Color panel line + next's Threading and Place text lines
Conflict: ROADMAP.md: logs both
2026-10-12 22:17:14 +00:00
aiongg cb141d6a2b Merge image-colour-profiles: Images: CMYK and profiled photos show in their colours
Conflict: ROADMAP.md: logs both
2026-10-12 22:10:50 +00:00
aiongg 116b3b4ec9 Text frames: lines follow an oval or polygon frame's shape (#646) 2026-10-12 22:07:43 +00:00
aiongg 6483a43ba8 Integration (docx-lists x #360 list levels): Word levels set list_level (ilvl + 1) and lvlRestart; parent numbers in a label are ^k level references, written as text only where the model's level numbers differ from Word's (legal labels, unnumbered levels); Start At from a simulated per-level ListCounter; tests nested_levels_and_restarts adjusted, parent_numbers_the_model_lacks_are_text 2026-10-12 22:07:29 +00:00
aiongg 8ce45eaef6 Merge docx-lists: Word import: bullets and numbering from numbering.xml (#626)
Conflict: crates/textimport/src/docx.rs: tests both
Conflict: ROADMAP.md: logs both
2026-10-12 22:04:39 +00:00
aiongg e391002be6 Merge text-stroke-behind: Text: stroke behind fill (#619)
Conflict: crates/render/src/text.rs: the branch's paint_glyphs with next's fill and stroke overprint (overprint_fill / overprint_stroke, an overprinting() Multiply layer around each paint); next's cell_stroke(kind, weight, length); path text strokes don't overprint, as before
Conflict: crates/pdf/src/text.rs: the branch's text_run / paint_outlines / glyph_outlines split with next's skew tan, vertical mark offsets and split_clusters glyph order (#421)
Conflict: crates/engine/src/cmd/paths.rs: next's shatai transform + the branch's stroke_behind_fill key
Conflict: crates/idml/src/tests.rs: tests both
Conflict: docs/typography-parity.md, ROADMAP.md: headers combined, logs both
2026-10-12 22:04:10 +00:00
aiongg 8811669ee0 Merge alt-arrow-kerning: Type: Alt+Left/Right kerns at the caret and tracks a selection (#621)
Conflict: crates/engine/src/cmd/prefs.rs: next's document.preferences params doc + kerningIncrement
Conflict: crates/engine/src/tests.rs: tests both
Conflict: docs/target-app-parity.md: next's UI and Pen lines + the branch's Tools line
Conflict: ROADMAP.md: logs both
2026-10-12 21:58:22 +00:00
aiongg 86848092ed Merge autoflow-columns: Autoflow: one frame per column, as the page's columns are set (#625)
Conflict: crates/engine/src/cmd/place_text.rs: autoflow(d, sid, max_pages, cmd, primary): next's parent-spread refusal and cmd-named errors + the branch's per-column body; callers file.place, text.thread (primary false) and Smart Text Reflow (primary true)
Conflict: crates/engine/src/lib.rs: Smart Text Reflow passes next's "Smart Text Reflow" and primary
Conflict: crates/engine/src/cmd/file.rs: next's file.place params doc with the branch's autoflow wording
Conflict: docs/target-app-parity.md: next's Threading line + the branch's Place text line
Conflict: ROADMAP.md: logs both
2026-10-12 21:54:55 +00:00
aiongg 7f494e4f3f Color Picker: hex and number fields keep typed text; CMYK colours stay CMYK; the page is not dimmed
The hex field keeps its text while focused, commits each time it parses
(#rgb, rrggbb, any case) and shows the colour again when focus leaves. R/G/B
and editable C/M/Y/K fields hold their values as typed. Picking on the field
or by hex keeps a CMYK colour CMYK through the CMYK working space; typed R/G/B
values make it RGB. OK without edits leaves the fill or stroke alone. Dialogs
draw no backdrop, so the page keeps its colours behind them (#323).
2026-10-12 21:53:06 +00:00
aiongg 09e8118b21 Merge #618: Paste vector art from Illustrator, VectorCraft or Figma as editable paths
Conflict: crates/engine/src/cmd/mod.rs: next's file/layout exports + is_svg_markup
Conflict: crates/ui-egui/src/lib.rs: next's logic(); the PR's RequestPaste hook and request_system_paste field dropped (next reads the system clipboard through Services::clipboard_text); run() routes edit.paste with SVG markup and no insertion point to edit.pasteSvg at next's view centre (with_system_clipboard's spread/at)
Conflict: crates/ui-egui/src/menus.rs: next's activate/menu_enabled; pastes_from_system dropped
Conflict: crates/ui-egui/src/canvas.rs: PR's view_centre dropped (next's canvas::view_centre); PR's Event::Paste SVG arm dropped (the paste event reaches run() through edit.paste); tests both, the PR's two UI tests rewritten against run() (paste event in the middle of the view; native-menu Edit > Paste reading SVG from the system clipboard, markup stays text at an insertion point)
Conflict: docs/control-protocol.md: ui.ime and ui.paste rows
Conflict: ROADMAP.md: logs both
2026-10-12 21:47:29 +00:00
aiongg f4e769369c Merge main: fix(macos): make Dock Quit cancelable; test(recovery): make failed republishing fixtures deterministic
Conflict: Cargo.toml: next's winit patch (rust-windowing/winit#4694, Windows mixed-DPI drags) kept; main's skylartaylor/winit patch held back (Cargo takes one patch per crate; both are single commits on v0.30.13 for different platforms, no published commit has both)
Conflict: Cargo.lock: next's winit source; main's ui-egui tempfile dev-dependency taken
Conflict: apps/designcraft/src/main.rs: next's startup; main's macOS with_close_requested_on_terminate block left out (needs main's winit patch); apps/designcraft/Cargo.toml keeps next's (no macOS winit dependency)
Conflict: ROADMAP.md: headers combined, logs both
Conflict: docs/target-app-parity.md: headers combined
2026-10-12 21:40:47 +00:00
aiongg b20ddf87ec Images: CMYK and profiled photos show in their colours
Placed rasters are converted to display sRGB once, at decode time, in one
module (designcraft-images colour):
- embedded ICC profiles in JPEG, PNG, TIFF, WebP and PSD (RGB and grey) are
  applied; sRGB-like profiles leave the pixels exactly as decoded
- CMYK JPEGs (incl. YCCK, Adobe-inverted values), CMYK TIFFs and CMYK PSD
  composites go through their embedded CMYK profile, else the working CMYK
  space, with the colour settings' intent and black-point compensation
- oversized (> 16 MiB), unreadable or mismatched profiles are ignored with a
  log line

The render image cache is rebuilt when the colour settings change. PDF/A
exports of CMYK TIFFs get the same managed RGB as the screen.
2026-10-12 21:30:41 +00:00
Jeffrey Thayne d3c1d3d029 Merge remote-tracking branch 'origin/main' into linked-docx
# Conflicts:
#	ROADMAP.md
2026-10-12 15:21:00 -06:00
aiongg 270dd46b50 Text: stroke behind fill (#619)
A character attribute strokeBehindFill paints a text stroke under the fill
at double width, so its full weight lies outside the letters and they keep
their shape. Screen and PNG rendering, PDF export (stroke as outlines
first, then the filled text, so the text is not duplicated; clipped to the
outside of the glyphs when there is no fill), Create Outlines (outside-
aligned stroke), the Properties panel and Character Style Options.

IDML StrokeAlignment on CharacterStyleRange, CharacterStyle and
ParagraphStyle imports and exports as the attribute (OutsideAlignment =
on, CenterAlignment = off).
2026-10-12 21:06:42 +00:00
aiongg 7515dfeaf5 Word import: bullets and numbering from numbering.xml (#626)
Paragraphs with w:numPr, directly or through their paragraph style, become
bullet or numbered paragraphs: Word's number formats, label text, start
numbers and overrides, level restarts, the text after the label and the
level's indents. Parent levels in a label are written as text. Unreadable or
hostile list definitions leave plain paragraphs.
2026-10-12 20:59:17 +00:00
skylar taylor-barrick 64f0ca8fa6 fix(macos): make Dock Quit cancelable
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.
2026-10-12 14:52:12 -06:00
aiongg 86a0ce2cf7 Type: Alt+Left/Right kerns at the caret and tracks a selection
With the Type tool, Option/Alt+Left/Right with a caret changes the manual kerning of the pair at the caret by the Kerning/Tracking increment (a new document preference, 20/1000 em by default, in Preferences > Units & Increments); with a selection it changes the tracking of the selected text. Cmd (Ctrl on Windows and Linux) multiplies the step by five. A pair without manual kerning starts from 0. The new type.kernStep command does the work, one undo step per press.

Moving by words is Cmd+Left/Right on macOS and Ctrl+Left/Right on Windows and Linux; Option/Alt+Shift+Left/Right still extends the selection by words. Fixes #621.
2026-10-12 20:48:14 +00:00
aiongg cb38862846 Autoflow: one frame per column, as the page's columns are set
Autoflow on Place threads one frame per page column (right to left in a right-to-left story) and adds pages until the story fits; a frame the user drew or selected stays as it is, and later columns of its page are filled when it sits in one. Empty frames autoflow made at the end of the thread are removed. Pages Smart Text Reflow adds get a primary frame with the page's columns, and it removes trailing pages that hold only empty frames of the story. Fixes #625.
2026-10-12 20:39:04 +00:00
aiongg e2dd71378e Merge #609: Balance Columns evens out the last frame's columns
balance-columns 30ba4721.

Conflict: crates/compose/src/lib.rs: story-end check (balance_end) before next's deleted-paragraph filler, then prange; next's `go` closure with the branch's band tail on Next Column; next's Cursor::clip replaced by the branch's tail-aware clip (the auto-placed duplicate dropped)
Conflict: crates/compose/src/tests.rs, crates/idml/src/tests.rs: tests both
Conflict: ROADMAP.md, docs/target-app-parity.md: both (auto-size and Balance Columns lines)
2026-10-12 20:06:06 +00:00
aiongg 38bdc19232 Merge #180: text frame auto-size grows and shrinks frames to fit their text
text-autosize 60551322.

Conflict: crates/doc/src/item.rs: next's reference-point doc and default auto_size_ref 4 (centre, as IDML leaves it) plus the branch's minimum-size and No Line Breaks fields
Conflict: crates/engine/src/lib.rs: autosize pass before next's record_undo_step on the success path; bump_revision for next's global revisions
Conflict: crates/engine/src/cmd/interchange.rs: next's grep_style_warnings and .indd comment plus the branch's autosize::apply on import
Conflict: crates/engine/src/cmd/object.rs: branch's textFrameOptions params doc with next's firstBaseline values
Conflict: ROADMAP.md: progress log both
Conflict: docs/gaps.md: next's optical kerning wording with the branch's auto-size note on #180
2026-10-12 19:57:02 +00:00
aiongg b32deda339 Merge #612: Windows mixed-DPI drag keeps the window's size; GPU surface up to the adapter's texture limit
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
2026-10-12 19:49:59 +00:00