Commit Graph
511 Commits
Author SHA1 Message Date
aiongg d30cefe166 Integration: renaming a swatch reaches endnote text (#84 × #416)
#84's Add to Swatches renamed the colour in footnotes and endnotes through its own helpers; next's Document::rename_swatch_refs (#416), which add_to_swatches calls after the merge, covered footnotes but not endnotes. rename_swatch_refs now walks each story's endnotes too, and #84's test checks an endnote.
2026-10-12 04:23:54 +00:00
aiongg 70fe825045 Integration: autoflow page-break test names its command (#266 × #84)
#84 gives autoflow the id of the command that autoflows, for its errors; #266's odd/even page-break test called autoflow with the old three arguments. The test passes "file.place".
2026-10-12 04:20:54 +00:00
aiongg 89abf6b6c9 Merge #84 (update): Text editing, snapping and text threading (#23)
Review fixes for text editing, paste and threading (fix84-text,
fix84-thread), the Italian/Japanese/Portuguese/Ukrainian Swatches hint
and #83's update merged into the PR branch.

Conflict: ROADMAP.md: header combines #84's threading change with next's 2026-10-11 changes; progress lines and change-log rows both (#84's 2026-10-12 first); stage line #84's (alpha ~5–7 h away)
Conflict: docs/gaps.md: #84's header text plus next's kerning/fonts/print changes
Conflict: docs/roadmap.md: next's M1 row (#186 done), #84's M2 row
Conflict: crates/engine/src/cmd/style.rs: next's side; add_to_swatches calls next's Document::rename_swatch_refs, #84's rename_in_story/rename_in dropped (next's covers text, tables, cells, notes and items); #84's test kept
Conflict: crates/idml/src/tests.rs: next's side (#84 carries #83's tests.rs, already merged in 9196a164)
Conflict: crates/tools/src/select.rs: overlays keeps next's content-move ghosts and live corners, plus #84's snapped loaded-cursor drag frame; a Place click without a drag shows nothing
Conflict: crates/ui-egui/src/i18n/it.rs: next's side (the Swatches hint entry is already further down)
2026-10-12 04:20:34 +00:00
aiongg d2a6dd39a2 Integration: character fill swatch errors go through known_swatch (#403 × #520)
#403 added validate_char_fill with its own message; #520 added known_swatch for object fills and strokes, which also suggests the intended name. validate_char_fill now calls known_swatch with key "fill", so one error path covers both. #403's tests still pass (the message names the fill, swatch.create and the returned name).
2026-10-12 04:17:15 +00:00
aiongg 8f1c60e4fc Merge upstream main 835edae6
Brings #514 (MCP request replay), #523 final (already in next), #403
(character fill must name a swatch), #520 and #521 finals (already in
next as their PR heads).

Conflict: crates/engine/src/cmd/object.rs: next's side throughout (transform.move doc with content?, object.fill with tint alone from #525, move_content, reorder/content_move/group/tint_alone test modules); upstream's copy_ids_tests already in next
Conflict: crates/engine/src/cmd/text.rs: both; #403's validate_char_fill after next's ruby_cmd
Conflict: crates/pdf/src/export.rs: next's side (item/item_guarded cycle guard, frame_objects; upstream's anchored loop dropped as in d11ccad3)
Conflict: crates/render/src/lib.rs: next's side (drawing_items, draw_item/draw_item_inner)
2026-10-12 04:15:40 +00:00
aiongg dd044dd8b1 Merge review fixes: threading 2026-10-12 04:10:06 +00:00
aiongg 9964025ffb Swatches: Add to Swatches renames the colour in text, tables and notes 2026-10-12 04:04:07 +00:00
aiongg 52b58c0519 Text: Bold and Italic that change nothing add no undo step 2026-10-12 04:01:12 +00:00
aiongg d163837781 Text: text.selectAt needs a point 2026-10-12 04:00:19 +00:00
aiongg 6a0f0cf386 Text: a quadruple click selects the paragraph with its return 2026-10-12 03:59:53 +00:00
aiongg 45144c5044 Text: moving the caret drops the pending typing format 2026-10-12 03:59:21 +00:00
moppeeer 835edae659 feat(commands): return IDs from move, duplicate and repeat
Return ordered item IDs from Move, Duplicate and Step and Repeat while preserving the existing moved and created counts, selection and undo behavior. Scripts can use the IDs to operate on copies.

Validation: eight independent probes covering both swatch validation and returned IDs, plus all six cargo xtask ci gates on main after #520 (1,188 native tests passed, 3 ignored; all 15 applicable crates passed WASM compilation checks). Both ROADMAP entries and command regression modules are retained.

Task: #521
2026-10-11 21:58:49 -06:00
aiongg 7b803f1ddc Text: double-click word selection keeps grapheme clusters whole 2026-10-12 03:58:28 +00:00
aiongg 710ab70abe Threading: locked frames, locked layers and a locked or hidden active layer refuse threads
check_thread rejects a locked frame or a frame on a locked layer, and
text.thread refuses to make a new frame on a locked or hidden active
layer.
2026-10-12 03:57:41 +00:00
aiongg b6622fd581 Paste: copied text pasted as a new frame keeps its formatting 2026-10-12 03:57:09 +00:00
aiongg ddc93d8457 Threading: autoflow errors name the command that autoflows
text.thread with autoflow reported its autoflow errors as file.place.
2026-10-12 03:57:01 +00:00
aiongg 4fbd867337 Threading: Shift-click autoflow from a frame on a parent spread is an error
Autoflow fell back to the last document page when the story's last
frame was on a parent spread, threading a parent frame to new document
pages. It now refuses a story that ends on a parent spread.
2026-10-12 03:56:14 +00:00
aiongg 0349fcf38b Paste: drop marker characters from text pasted from other apps 2026-10-12 03:56:13 +00:00
moppeeer 5789caaa60 fix(objects): reject unknown fill and stroke swatches
Reject unknown object fill, stroke and gap swatch names before editing the document, and suggest an existing name where possible.

Validation: three independent atomicity and compatibility probes, plus all six cargo xtask ci gates against current main (1,187 native tests passed, 3 ignored; all 15 applicable crates passed WASM compilation checks). ROADMAP progress entries are retained.

Task: #520
2026-10-11 21:54:43 -06:00
aiongg 0fae2e4b5a Merge #83 into feat/text-editing-and-frames
Conflict: ROADMAP.md: took #83's (main's restructured roadmap); the Threading inventory line goes into docs/target-app-parity.md
Conflict: crates/compose/src/lib.rs: kept this branch's undrawn trailing spaces after warichu; comment covers both the spaces and the drop cap
Conflict: crates/idml/src/tests.rs: took #83's (drop cap style as a Dropcap nested style, table style fills through the cascade); this branch has no changes of its own there
2026-10-12 03:31:39 +00:00
KnottyDyes fae9bc84ce fix(text): reject unknown character fill swatches
Reject unknown character fill swatches before modifying text or undo history. Registered swatch names remain valid, including names that resemble hex colours.

Validation: invalid-name atomicity and save/reopen regressions, plus all six cargo xtask ci gates on main after #514 and #523 (1,186 native tests passed, 3 ignored; 15 crates passed WASM compilation checks).

Task: #403
2026-10-11 21:30:21 -06:00
seanth a21e8db14f fix(compose): key parent-page compositions by absolute page index
Parent-page text now uses the absolute page index as its cache key, so repeated page numbers and renumbering do not reuse another page's composition.

Validation: targeted canvas and PDF regressions, plus all six cargo xtask ci gates on main after #514 (1,179 native tests passed, 3 ignored; 15 crates passed WASM compilation checks).

Task: #523
2026-10-11 21:27:52 -06:00
aiongg 0032d57525 Merge upstream main into feat/layout-parity
Conflict: ROADMAP.md: took main's restructured roadmap; this branch's inventory changes (Keep Lines Together default, IDML defaults, drop caps, IDML Kashidas) go into docs/target-app-parity.md, where the inventory lives now
Conflict: crates/format/src/lib.rs: load runs the format upgrade, then merges duplicate assets; kept both sets of tests
2026-10-12 03:24:27 +00:00
aionggandskylartaylor f70f93de44 fix(idml): report missing parts and show import warnings
Match IDML part names leniently, report missing parts, and show import warnings when opening a file. Cover the malformed URI crash with a regression test.

Validation: all six cargo xtask ci gates passed on the current main merge candidate; 1,176 native tests passed and all 15 WASM packages checked.
Hosted FreeBSD tests and Windows ARM64 packaging and install/run checks passed.

Task: #356

Co-authored-by: skylartaylor <26588250+skylartaylor@users.noreply.github.com>
2026-10-11 19:39:34 -06:00
aiongg 223dedfe5a feat(assets): deduplicate files and omit unused assets on save
Share duplicate file data while keeping distinct link paths independent, and leave unused assets out of saved documents.

Validation: all six cargo xtask ci gates passed on the merge candidate; 1,170 native tests passed and all 15 WASM packages checked. Independent regression checks cover updates, relinking, embedding, undo and save/reopen.

Task: #420
2026-10-11 19:23:58 -06:00
aiongg ca1a8a12bc Place: an image placed into a frame leaves Auto-Fit off (#528)
* Place: an image placed into a frame fills it once and leaves Auto-Fit off

* Place: the fitting test names its fitting, since a placed image no longer auto-fits
2026-10-11 18:49:56 -06:00
japbcoelhoandClaude Opus 5.5 597f0e649f Color parsing: one scale per colour, not per component (#510)
A CMYK component of 1 or less was read as a fraction even when the other
components were percentages, so {"c": 0.7, "m": 0, "y": 12.2, "k": 0}
(a pale cream) became C 70 % Y 12.2 % and exported as
`0.7019608 0 0.12156863 0 k`. The same applied to RGB arrays.

Now a colour is read as percentages (0..100, or 0..255 for RGB) when any
component is above 1, and as fractions only when every component is
within 0..1.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:49:46 -06:00
aiongg edaea097e8 Merge asset-dedupe: files with different paths stay separate assets (#420)
Conflict: crates/doc/src/assets.rs: same_file doc comment names both the PDF crop box (next) and the link (asset-dedupe)
2026-10-12 00:32:16 +00:00
aiongg 10546a6e66 Assets: files with different paths stay separate assets
Two linked files with identical bytes were merged into one asset, so updating, relinking or embedding one changed both placements and saves dropped one path. Assets now match only with the same link; identical bytes still share one buffer in memory.
2026-10-12 00:22:34 +00:00
aiongg 978a9b9b98 Merge #452: M6: Adopt craft-print and support Windows PDF printing
Conflict: crates/engine/src/cmd/print.rs: the PR's print_with/craft_print::submit; print_with and the test's expected bytes export through next's interchange::with_pdf_boxes
Conflict: Cargo.lock: next's lock plus only craft-print (the auto-merge had moved windows-sys back to 0.59/0.60 for several crates)
Conflict: Cargo.toml: craft-print entry moved beside craft-ui with a comment
Conflict: ROADMAP.md: status Change texts combined; both progress-log lines (the PR's 2026-10-11 line under the 2026-10-12 ones)
Conflict: docs/gaps.md: status Change texts combined
Conflict: docs/hardware-parity.md: status Change texts combined
2026-10-12 00:21:22 +00:00
aiongg 6c8ba2d75d Merge #539: One underline bar per stretch of text, no seams between letters
Conflict: ROADMAP.md: both progress-log lines
2026-10-12 00:18:21 +00:00
aiongg bbd6a6c277 Merge #537: Engine: report the selected text; remember recently used fonts
Conflict: crates/engine/src/cmd/text.rs: next's selection_attrs (mixed, typing format, mixed_para_attrs, link_style) with the PR's selectedText added to its output; both parameter descriptions
2026-10-12 00:17:43 +00:00
aiongg 52d42ebdd0 Merge #525: Tint and Apply To in the Properties Fill and Stroke menus
Conflict: ROADMAP.md: both progress-log lines
Conflict: crates/engine/src/cmd/object.rs: object.fill takes the PR's swatch?/tint? form with next's known_swatch check (#520) on a given swatch; params doc names the swatch format; both test modules
Conflict: crates/ui-egui/src/i18n/it.rs: both sets of rows
Conflict: crates/ui-egui/src/panels/properties.rs: swatch_menu keeps the PR's popup (Apply To, Tint) with next's swatch_list for the swatches (its own width); tests keep next's Pathfinder test and the PR's three
2026-10-12 00:15:19 +00:00
aiongg d11ccad30a Merge #523: Parent-page items are composed for their page by index, not page name
Conflict: crates/pdf/src/export.rs: next's cycle guard (item -> item_guarded) and frame_objects (notes and cells) with the PR's page: Option<usize>; the PR's anchored-object loop dropped (frame_objects covers it)
Conflict: crates/render/src/lib.rs: stories keyed by (StoryId, Option<usize>) plus next's drawing_items; next's draw_item -> draw_item_inner guard with page: Option<usize>
2026-10-12 00:14:33 +00:00
aiongg 117420a829 Merge #522: Every command that writes story text reads CR as a paragraph return
Conflict: ROADMAP.md: both progress-log lines
Conflict: crates/engine/src/cmd/table.rs: next's setCell (StrokeEdit/EdgeSel, edit_table with ID) with the PR's text_param for the cell text
2026-10-12 00:12:48 +00:00
skylar taylor-barrick 65ef027f9f Merge pull request #452 from storytold/codex/craft-print
M6: Adopt craft-print and support Windows PDF printing
2026-10-11 18:12:43 -06:00
aiongg aee3748be2 Merge #411: Undo: a command that runs another is one undo step
Conflict: crates/engine/src/guard.rs: next's snapshot tuple without revision (bump_revision on restore) + the PR's saved in_undo_step
2026-10-12 00:12:13 +00:00
japbcoelhoandClaude Opus 5.5 3dc0a1563a Place: PDF pages and SVGs keep their exact size (#511)
file.place sized a placed PDF page (and SVG) from its size rounded to
whole points, while the export draws the page into that size: a page of
91.774 x 613.276 pt became 92 x 613 and was drawn 0.25 % wider and
0.3 % shorter, so pieces laid edge to edge left gaps of up to 1.9 pt.
A 100.4 x 50.4 pt page placed 100.4 pt wide came out 50.2 pt tall:
`Rect { x0: 0.0, y0: 0.0, x1: 100.4, y1: 50.2 }`.

The graphic's natural size is now the exact page (crop box) or SVG size;
the rounded pixel size stays for display and preflight. place.load (the
place cursor) gets the same exact size.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:39:40 -06:00
moppeeerandClaude Opus 5.5 1e34026a79 fix(render): One underline bar per stretch of text, no seams
The canvas drew an underline (and a strikethrough) as one rectangle per
glyph; where two anti-aliased edges met at a fractional pixel they left
a lighter seam between the letters. Contiguous bars of the same colour
and position are merged into one, as the PDF export already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 00:29:17 +02:00
moppeeerandClaude Opus 5.5 83584feb03 feat(engine): Report the selected text; remember recently used fonts
type.selectionAttrs reports the start of the selected text on one line
(`selectedText`, up to 60 characters), for previews such as the font
menu's Selected Text sample. The preferences keep `recentFonts`, the
fonts last picked in the font menus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 00:14:13 +02:00
moppeeerandClaude Opus 5.5 60bf43b9e8 fix(ui): A tint keeps each object's swatch, and no stroke stays none
Changing the Tint in the Fill menu sent the first object's swatch with
it, so with several objects selected every fill became that swatch; and
a Stroke tint on a frame without a stroke gave it the default 1 pt black
stroke.

object.fill now takes a tint alone and keeps each object's swatch, and
object.stroke with only a tint leaves a [None] stroke alone; the menu
sends just the tint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 00:06:18 +02:00
moppeeerandClaude Opus 5.5 95a5317b0b fix(engine): note.edit reads CR as a paragraph return too
note.edit was the one story-text command still reading `text` without
text_param. The progress-log line held real CR and CRLF characters where
it meant to show the escapes; it reads `\r` and `\r\n` now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 00:00:30 +02:00
Sean HammondandClaude Opus 5.5 5898c084b6 Parent-page composition: the page's name is part of the cache signature
Keying parent-page compositions by page index (previous commit) dropped the
page name from the cache key, and the signature only counts sections, so a
page-number marker on a parent page kept its old text after a section's
start number, style or prefix changed (renaming the page without changing
its index). The page's name is now hashed into the signature whenever a page
is given, so renumbering recomposes it as it did before, while two pages that
share a name stay separate.

Test: parent_page_number_follows_section_renumbering (marker on a parent
page, composed for page 3; the first section restarted at 100) fails before
(still "3", 1 glyph) and passes after ("102").

Reported by aiongg on #523.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CgGsJkVe5GYG9qK6VFdECa
2026-10-11 21:17:42 +00:00
aiongg f5ee268962 Merge justification-dialog: a Justification dialog for the selected paragraphs (#531)
Conflict: ROADMAP.md: both progress-log entries, #531's above next's 2026-10-11 lines.
Conflict: docs/typography-parity.md: status line lists next's changes and the Justification dialog.
Conflict: crates/engine/src/cmd/text.rs: selection_attrs keeps next's typing-format chars and adds paraMixed for {mixed: true}; mixed_para_attrs sits beside next's apply_typing_format and link_style.
Conflict: crates/ui-egui/src/dialogs.rs: titles, draw and confirm arms for both paragraphLocal and justification; the style dialog's justify arm calls the shared justification(), which uses next's composer_choice (Japanese composers) and selects neither composer when mixed; the style dialog's justification check moved into next's confirm_paragraph_style_options (section set to justify, reason in next's status line); tests from both sides.
Conflict: crates/ui-egui/src/i18n/ja.rs: union of rows.
Conflict: crates/ui-egui/src/i18n/uk.rs: union of rows.
Conflict: crates/ui-egui/src/menus.rs: UI_COMMANDS, run_ui arms and Type menu keep next's Paragraph Borders and Shading, Span Columns and Bullets and Numbering plus Justification… (Cmd+Alt+Shift+J, free on next) right after Paragraph Rules…; menu_enabled keeps next's IME check before the type.para check.
Auto-merged crates/ui-egui/src/panels/properties.rs: Justification… joins the Paragraph panel menu list after Paragraph Rules….
2026-10-11 20:49:57 +00:00
aiongg 3f752312d6 Type: Justification dialog for the selected paragraphs (#531)
Type > Justification... and the Paragraph panel menu (Cmd+Alt+Shift+J) open
the Justification controls of Paragraph Style Options for the selected
paragraphs: word and letter spacing, glyph scaling, composer, kashidas and
Korean line breaks. Values that differ across the selection show empty, and
OK applies only the edited fields through type.para as one undo step.
Spacing values outside their range or out of minimum/desired/maximum order
keep the dialog open with the reason, in both dialogs.

type.selectionAttrs takes {mixed: true} and lists the paragraph attributes
that differ across the selected paragraphs as paraMixed.
2026-10-11 20:46:38 +00:00
skylar taylor-barrick 3ec5cef6a2 M6: Adopt merged craft-print with scaling and queue duplex defaults 2026-10-11 22:14:24 +02:00
aiongg 1ff299adf2 Merge place-no-autofit update: the fitting test names its fitting 2026-10-11 19:18:07 +00:00
aiongg 54b607525e Place: the fitting test names its fitting, since a placed image no longer auto-fits 2026-10-11 19:18:07 +00:00
aiongg 13fb00fd08 Integration: transform.move on content reports the ids it moved (#521)
#521 makes transform.move return {moved, ids}; next's move_content (the content-selection path, content?) returned only {moved}. It returns the ids of the items whose content or frame moved, and copies_report_their_ids checks it.
2026-10-11 19:11:53 +00:00
aiongg 3af8c6275d Merge #521: Copies report their ids
Conflict: ROADMAP.md: both progress lines kept, the PR's first
Conflict: crates/engine/src/cmd/object.rs: transform.move params doc is next's (with content?) plus the PR's return text; next's test modules and the PR's copy_ids_tests both kept
2026-10-11 19:11:43 +00:00