Commit Graph
2702 Commits
Author SHA1 Message Date
Florent Siegenthaler cd64dd734d feat(i18n): translate the French interface
The French column of the shared table repeated the English text on 1,217 rows; they now carry a French translation (60 rows stay identical on purpose: format names, Japanese composition terms, words French shares with English). Strings the shared table has no row for (248, already translated in ja/it/ar) go in a new i18n/fr.rs, looked up before the shared column, as ja.rs is. Keys of fr.rs and of the shared table are disjoint, so cached_lookup_preserves_every_translation_across_language_switches still holds by construction.
2026-10-12 23:17:17 +02:00
Jeffrey ThayneandClaude Opus 5.5 bb7e81425f Links: never read a linked text file that isn't a regular file
Checking a linked text file's status (every links.list, which the
Links panel polls) compared sizes and then read the whole file, so a
link to a FIFO or a device such as /dev/zero (size 0) never returned.
Only regular files are read now, and no more than the Data Merge
source limit (256 MiB): anything else shows as missing; a file over
the limit shows as modified when its size changed and as tooLarge
("Too large to check") when it didn't. Update and Relink read through
the same guard, and placing a file too large to check doesn't link it.
Unix test: a FIFO, /dev/zero, a folder and an over-limit file each
return promptly without being read (each call runs on a thread with a
timeout, so a regression fails instead of hanging). Thanks @aiongg.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:15:01 -06:00
moppeeerandClaude Opus 5.5 4769cadb77 feat(ui): The Effects dialog, as in InDesign
Object > Effects (Transparency..., Drop Shadow... and the rest, and their
shortcuts) and the Effects panel's fx button open the Effects dialog on
the chosen effect, turned on: the effect list with checkboxes on the left
and a summary line, the chosen page's settings on the right in the
document's units (Blending, Position with X/Y offsets, Options...),
Preview on the page while it is open, Cancel putting the page back, and
OK as one undo step.

- engine: object.effects {effects (merged, kept in range), opacity, blend}
- doc: drop and inner shadows default to InDesign's 120 degree light
- ui: dialogs::cancel (a previewing dialog reverts)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:14:15 +02: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
Marco Arnulfo de31bd5647 fix(fonts): fall back to a font made for Malayalam (part of #628)
Malayalam took the first installed file mapping it: on macOS Arial Unicode MS, which has no Malayalam shaping, so conjuncts like ക്ഷ fell apart. Fonts made for Malayalam are tried first, loaded on demand through the CJK chain lookup.
2026-10-12 23:03:22 +02: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
webdevtodayjason 98f1340f61 fix(index): keep level 3 and 4 topics in a run-in index 2026-10-12 15:50:26 -05:00
webdevtodayjason f605ca3157 chore(contributors): add Jason Brashear 2026-10-12 15:50:26 -05:00
Isaac Levine 5473f42de4 Engine: guard test flags bare fs calls and Path probes, and scans every crate that reads paths
The scan now reads crates/*/src instead of only the engine, and flags fs::
after use std::fs or use std::{fs, ...}, std::fs, File::open/create,
OpenOptions, direct atomic writes and the Path probes (metadata,
symlink_metadata, canonicalize, read_dir, try_exists, exists, is_file, is_dir,
read_link). The files it leaves out are listed with the reason for each, and a
second test fails when an entry no longer needs its exception.
2026-10-12 16:50:04 -04:00
Isaac Levine d66f2c47ae Engine: route book chapters and atomic saves through the checked writers, and take file access out of fonts, IDML and ICC loading
Rebased onto the atomic save work: book chapter replacement, canonicalize and
the writability probe, and every command write now check the roots before
they reach crate::atomic. IDML import no longer reads linked images itself
(the engine passes its checked reader), load_icc_file is gone (profiles
arrive as bytes), the script loader goes through the host reader, and
FontDb::load_document_fonts asks a caller-supplied check about the folder and
each font file, so a link that leads out of the read root is skipped.
2026-10-12 16:50:04 -04: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
moppeeerandClaude Opus 5.5 cded7d997e feat(tools): Drag type-on-a-path start and end brackets
Selected type on a path shows its start and end brackets across the path,
as in InDesign, and the Selection and Direct Selection tools drag them
along it (Move Bracket, one undo step): the text starts and ends where
they are, and text past the end bracket is overset.

- doc: PathType.end (None: the path's end)
- geom: PathWarp::nearest, the arc length of the nearest point
- engine: type.pathOptions {end}; brackets stay on the path and can't cross
- compose: the line runs from the start to the end bracket
- idml: EndBracket read and written
- tools: Cursor::PathBracket

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:32:56 +02:00
Isaac Levine 3650f36927 Docs: document --automation-read-root and --automation-write-root for mcp 2026-10-12 16:22:15 -04:00
Isaac Levine 8939925075 CLI: add --automation-read-root and --automation-write-root to mcp 2026-10-12 16:22:15 -04:00
Isaac Levine 54330aa632 MCP: confine the headless session to the automation roots 2026-10-12 16:22:06 -04:00
Isaac Levine 401fe3bb67 Engine: check automation read and write roots on every path a command reads or writes 2026-10-12 16:22:00 -04:00
moppeeerandClaude Opus 5.5 c35515c54d feat(tools): The Pen continues an open path from either end point
As in InDesign, the Pen over an end point of an open path (selected, or
under the pointer) shows a continue cursor; clicking it carries on drawing
that path, new anchors going on its subpath, and clicking its other end
closes it. Dragging from the end point first reshapes its direction
lines. From the first anchor the subpath is reversed first.

- engine: path.reverse (Object > Paths > Reverse Path), path.setAnchor;
  path.appendAnchor and path.close take a subpath, and appendAnchor
  refuses a closed subpath
- tools: Cursor::PenContinue

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:18:14 +02:00
aiongg dfd98fca13 Merge parent-pages-panel: Pages panel: double-clicking a parent shows that parent; parent thumbnails (#624)
parent-pages-panel 4e7b3360.

Conflict: crates/engine/src/cmd/layout.rs: branch's layout.parents.edit {on?, parent?} with bump_revision; branch's Result-returning parent_ref (pages.insert and applyParent callers merged clean) next to next's set_margins_columns and parent_spread_index (a stray parent_ref doc line above set_margins_columns dropped); next's test modules plus the branch's parent_tests
Conflict: crates/ui-egui/src/panels/pages.rs: branch's page and parent thumbnails through next's widgets::load_texture
Conflict: ROADMAP.md: header keeps next's change list plus Pages panel parents; the branch's progress line first
2026-10-12 20:11:02 +00:00
aiongg e3f444ad10 Merge #603: document tabs shorten long names so the close button stays visible
doc-tab-overflow 84158e36. Merged clean onto next's chrome.rs.
2026-10-12 20:07:01 +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 069fe47b0d Integration: Fit Frame to Content leaves an auto-size text frame as auto-size fitted it (#591 x #180); test fit_frame_to_content_leaves_an_auto_size_frame_as_auto_size_set_it 2026-10-12 20:00:39 +00:00
aiongg c124e0ebcf Integration: auto-size tests that keep the top edge name the top-centre reference point (#180 x next's centre default) 2026-10-12 20:00:34 +00:00
skylar taylor-barrick 17cf5b4e91 test(recovery): make failed republishing fixtures deterministic
Force actual failed recovery publication at the reserved destination so privileged runners exercise the intended retention path. Keep the normal writer, publication order and existing assertions; enable these fixtures across platforms.

Validation: all six local gates and the FreeBSD root VM workspace build/tests.

PR #627.
2026-10-12 13:57:37 -06: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 698a87febc Integration: wgpu errors are logged only after the start-up frames, so a failing adapter still starts the app again on the next one (#612 x adapter restart) 2026-10-12 19:50:35 +00:00
aiongg 4e7b33607e Pages panel: double-clicking a parent shows that parent; parents are identified by id
- layout.parents.edit takes `parent` (id, full name, or a prefix only one
  parent has) and asks the canvas to fit that parent's spread; turning
  editing on without one shows the first parent. The result names the
  parent shown.
- Commands resolve parents by id, then full name, then a unique prefix.
  Unknown or ambiguous names are errors, so a typo no longer clears a
  page's parent; null or "[None]" clears it.
- Pages panel: only the row of the parent the canvas shows is selected;
  rows, drag payloads and the Apply Parent menu carry the parent id.
  Parent rows show rendered thumbnails of the parent spread, cached by
  document revision like page thumbnails.
- Delete All Guides on a parent acts on the parent the canvas shows.

Fixes #624.
2026-10-12 19:49:59 +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
hasan-sdaif d8d66a2757 Complete the Arabic (العربية) catalog: every missing key translated
ar.rs covered 1,456 of the interface strings; the newer catalogs (it, ja,
pt-br, uk) had since grown to a 1,690-key union. This adds the 238 keys
Arabic was missing, in the same clean-room Modern Standard Arabic and
InDesign-style vocabulary the file already uses (كائن مرسّى، مسار مغلق،
خطوة وتكرار، الفحص المسبق، دمج البيانات، الكائن الأصل…):

- the whole menu-path family (نص/…، كائن/…، جدول/…، نافذة/…) — Type on a
  Path, Track Changes, Story Direction, Transform Again, Pathfinder…
- the special-character and white-space inventory (شرطة كاملة، مسافة
  شعرية، محرف نهاية الفقرة، علامات الاقتباس المستقيمة…) and their
  symbols (رمز حقوق النشر، رمز العلامة التجارية…)
- data merge (سجل، سجلات في الصفحة، تحديد مصدر بيانات…), EPUB/HTML/XML/
  IDML exports, proof setups and colour-blindness previews, anchored
  objects, parent pages, slug and bleed fields, quick apply, preflight.

Same table, same lookups — Arabic now covers the union of every
language's keys, matching Japanese's completeness (1,694 keys).
2026-10-12 19:47:15 +00:00
aiongg 774ecf4fa5 Integration: book chapter-number test closes its chapters before Update Numbering (#341 x #615 open-chapter refusal) 2026-10-12 19:46:34 +00:00
aiongg 527e5cbc0c Merge main: atomic desktop and CLI exports, staged book updates, recovery checkpoint retention, exit recovery
upstream/main c3859c82 (#614 e198eb5d, #615 9509b2bc, #616 8e151a2f, #617 c3859c82).

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
2026-10-12 19:46:34 +00:00
LosminekandClaude Opus 5.5 2c0ca990b8 i18n(cs): Quit DesignCraft
Czech for the app-menu entry added on main since this PR was opened.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:41:25 +02:00
Losminek a91c720526 Merge remote-tracking branch 'upstream/main' into cs-pr 2026-10-12 21:37:51 +02:00
aiongg 84158e36d3 Document tabs: long names are shortened so the close button stays visible
A tab is at most 260 px wide. A longer document name ends in an ellipsis
(cut at a grapheme boundary, RTL names included), the dirty marker and
zoom stay readable, and the full name shows in the tab's tooltip. Tabs
and their close buttons carry accessible labels.

Fixes #603
2026-10-12 19:32:28 +00:00
aiongg 30ba47211e Text frames: Balance Columns evens out the last frame's columns (#609)
In the frame a story ends in, Balance Columns lowers the bottom of the
columns after the last explicit column break as far as the text still
fits, reusing the bounded search that balances columns above spanning
paragraphs. Frames the text runs on from fill normally. Text Frame
Options gets a Balance Columns checkbox.
2026-10-12 19:18:45 +00:00
aiongg 4be988f335 Integration: format 2026-10-12 18:57:40 +00:00
aiongg 203870ed64 Merge #591: Double-click a frame handle to fit the frame to its content; Fit Frame to Content fits text frames
Conflict: crates/engine/src/cmd/object.rs: object.fit's doc keeps next's text and adds the `axis` clause; test modules from both sides
Conflict: crates/ui-egui/src/canvas.rs: kept next's input code (it sends the second press as Down with clicks: 2) and dropped the PR's release-time DoubleClick; the PR's UI test stays
Conflict: crates/tools/src/select.rs: the handle double-click joins next's second-press preamble, after the port checks and before editing text and the frame/content toggle (Down or DoubleClick with click_count 2 on a handle runs object.fit along fitting_axis); the DoubleClick arm stays empty; module doc from both
Conflict: crates/tools/src/tests.rs, ROADMAP.md: both sides
2026-10-12 18:56:35 +00:00
aiongg 605513228a Text frames: auto-size grows and shrinks frames to fit their text
Height Only, Width Only, Height and Width and Keep Proportions frames fit
their text after every undoable command (same undo step) and after IDML
import, around the reference point, with minimum height/width and No Line
Breaks. Text Frame Options gains an Auto-Size section; IDML reads and writes
the minimum and no-line-break attributes. Applies to unthreaded frames and
the last frame of a thread; vertical frames and type on a path are left as
they are. (#180)
2026-10-12 18:55:57 +00:00
aiongg 3b295ceed0 Integration: drop #402's JSON-parser overflow test (it tests serde_json, not Place) 2026-10-12 18:50:15 +00:00
aiongg f6a03984d4 Merge #402: Place: an explicit x, y, spread or width puts the image in a new frame
Conflict: crates/engine/src/cmd/file.rs: the spec keeps next's pdfCrop boxes and loadCursor with the PR's width bound and placement precedence; the PR validates `width` earlier, so next's later want_w line goes; next's loadCursor branch stays
2026-10-12 18:50:08 +00:00
aiongg 796e9d713d Integration: the Eyedropper matches the clicked frame where no image pixel shows; sampling skips clipped-out pixels and hidden states (#572, #561)
color.sample takes `matchFrom`: with no opaque image pixel under the point (an empty part of an image frame, a transparent pixel), it applies that object's attributes as object.matchAttributes does, in the same command; the tool passes the object under the pointer. sample_item and the tool walk shown_children(), so only a multi-state object's active state is sampled, and sample_item skips points outside the graphic's clipping path. object.matchAttributes's body is a function both commands share.
2026-10-12 18:47:51 +00:00
aiongg 20a14d95f0 Merge #572: Eyedropper: sample a placed image's colour into the selected text or objects
Conflict: crates/engine/src/cmd/style.rs: object.color keeps next's apply_color, spec and enabled check (#84); takes the PR's color.sample (with next's enabled check), image_color_at and sample_item; drops the PR's color_swatch, color_items and has_text_or_selection; explicit `id`/`ids` win over a text selection in apply_color; the PR's tests follow next's caret behaviour (a caret takes the sampled colour for the next typing)
Conflict: crates/tools/src/tests.rs: both sides; the eyedropper test's Asset and Graphic literals gain next's pdf_crop, pdf_box, clip and wrap fields
Conflict: crates/ui-egui/src/i18n.rs, i18n/{ar,it,ja,pt_br,uk}.rs, ROADMAP.md: both sides
2026-10-12 18:43:58 +00:00
aiongg f11097c3f8 Merge #608: UI: spring-loaded zoom (Cmd+Space / Cmd+Alt+Space)
Conflict: crates/engine/src/tooling.rs: every with_ctx caller (IME preedit, commit, end, caret) takes the PR's `|s, cx| s.tool.…` form; pointer keeps next's end_composition before dispatch and tool_key its composing() early return
Conflict: crates/ui-egui/src/canvas.rs: the cursor block takes `space` from navigation_space, then the zoom branch, then `(space && !cmd) || alt_text`; the spring-zoom pointer events carry next's `clicks`; the PR's tests name their button helper `press` (next's tests have a `primary`) and compare cursors with what cursors::show sets for the engine cursor (next paints or rasterizes cursors per platform)
Conflict: ROADMAP.md, docs/target-app-parity.md, docs/ui-parity.md: status lines list both sides; progress and change-log rows keep both
2026-10-12 18:36:04 +00:00
aiongg 38dad0fbde Windows: dragging across monitors with different scales doesn't grow or crash the window
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.
2026-10-12 18:29:54 +00:00
aiongg 26fce37a47 Merge #611: Add a Nix flake (NixOS, GNOME and Hyprland) 2026-10-12 18:29:52 +00:00
aiongg 0d1fa3bf43 Merge #607: MCP: reconnect closed sockets before sending the next request 2026-10-12 18:29:52 +00:00
aiongg a680f2c937 Merge #606: Styles: selection capture accepts deleted-swatch fills 2026-10-12 18:29:52 +00:00
aiongg 3f6f8453ca Merge #577: fix(render): A group's gradient feather fades the group, on screen and in PDF
Conflict: crates/doc/src/item.rs: both tests (next's rotated group bounds, the PR's feather bounds)
Conflict: ROADMAP.md: both progress lines
2026-10-12 18:29:39 +00:00
moppeeerandClaude Opus 5.5 f25cae4780 feat(edit): Paste vector art from other apps as editable paths
Illustrator, VectorCraft, Figma and Inkscape put copied objects on the
system clipboard as SVG markup. Pasting it now creates editable paths, as
InDesign does with Illustrator art: fills, strokes (weight, caps, joins,
dashes) and opacity come through as unnamed colours, groups stay groups,
text comes in as outlines, CSS pixels become points (0.75 pt/px) and the
art is centred in the view. One undo step; garbage is refused.

- engine: edit.pasteSvg {svg, spread?, at?}
- canvas: Ctrl/Cmd+V with SVG on the clipboard (not while typing)
- Edit > Paste with nothing copied here asks for the system clipboard
- control channel: ui.paste {text}

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:23:47 +02:00