Commit Graph
445 Commits
Author SHA1 Message Date
aiongg a4b8ee994f Merge #866: Page tool: liquid pins and resize locks on the canvas for object-based pages
# Conflicts:
#	ROADMAP.md
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/i18n.rs
#	docs/target-app-parity.md
2026-10-14 01:40:21 +00:00
aiongg 47b51d6377 Integration: #864 drops the Controlled by Parent rule from the menus and the Objects Move with Page checkbox (neither has an effect yet) 2026-10-14 01:39:08 +00:00
aiongg d0035c0722 Merge #864: Control panel for the Page tool (W/H, preset, orientation, Liquid Page Rule); Controlled by Parent; Show Parent Page Overlay
# Conflicts:
#	crates/idml/src/import.rs
#	crates/ui-egui/src/chrome.rs
#	crates/ui-egui/src/i18n.rs
2026-10-14 01:37:34 +00:00
aiongg f1f8aaa9f7 Merge #862: Page tool: click selects a page; its handles preview the liquid rule, Alt-drag resizes, Shift keeps proportions
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/tests.rs
#	crates/tools/src/lib.rs
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/i18n.rs
2026-10-14 01:36:53 +00:00
aiongg 063856cc4c Merge t14-b: #836 #840 #846 #854, Find/Change first:true
# Conflicts:
#	ROADMAP.md
2026-10-14 00:09:13 +00:00
moppeeerandClaude Opus 5.5 8f71219533 feat(tools): Page tool pins and locks objects on object-based pages, on the canvas
Chris Cicchino's "Adobe InDesign – Page Tool and Liquid Layout" (6:32–
9:58) clicks an object with the Page tool on an object-based page and
gets InDesign's on-canvas liquid controls: a pin outside each edge
("Object is pinned relative to the page edge. Click to release.") and a
padlock on the width and height lines ("The object's width is locked.
Click to let the object change width as the page changes width."). The
pins and resize settings existed (liquid.object, the Liquid Layout
panel) but the canvas showed none of it.

- tools: on an object-based page a Page tool click selects the object
  with its page (layout.selectPage takes `ids`); its pins (14 px outside
  each edge's middle) and width/height locks (beside the centre) toggle
  liquid.object on click, show InDesign's tooltips on hover and a hand
  cursor.
- canvas: LiquidPins (orange dashed width/height lines with padlocks,
  open when the object follows the page; hollow pins, or a line to the
  page edge when pinned) and Tip overlays; tooltip translations.

Builds on the Page tool PR (#862).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 01:21:51 +02:00
moppeeerandClaude Opus 5.5 8864307d7c docs: the Page tool's Control panel in the parity notes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 00:52:06 +02:00
aiongg 5ff7c4f956 Merge #854: index duplicate asset candidates by content (#727)
# Conflicts:
#	docs/file-format-parity.md
2026-10-13 22:41:06 +00:00
aiongg cd317a235a Merge #553: docs: add user docs in docs/user 2026-10-13 22:36:18 +00:00
aiongg 283d04e14f Integration: #859 drop the control-channel safety note from the editing gap, which lists missing features 2026-10-13 22:36:08 +00:00
aiongg 450bee203a Merge #859: Control: bound click and drag input before queuing events 2026-10-13 22:36:03 +00:00
aiongg dc2dda7717 Merge #836: gradients between process colours blend as inks
# Conflicts:
#	ROADMAP.md
2026-10-13 22:31:16 +00:00
moppeeerandClaude Opus 5.5 cc13e80c9d feat(tools): Page tool selects pages; handles preview the liquid rule, Alt-drag resizes
Chris Cicchino's "Adobe InDesign – Page Tool and Liquid Layout" (0:42–
2:06) selects a page with the Page tool, gets its bounding box with
handles, drags a handle (the page springs back: a liquid-rule preview),
holds Option to really resize it, Shift for proportions, and reads the
size in a tooltip by the cursor. DesignCraft's Page tool only opened a
generic Page Size dialog on click.

- tools: the Page tool selects the page under a click (none on the
  pasteboard); the selected page shows its outline, eight handles and
  its liquid page rule ("Scale", "Object-Based"…) above it. Dragging a
  handle previews layout.pageSize (the liquid rule re-flows the
  objects) and springs back on release; Alt resizes, Shift keeps the
  proportions; a W/H readout follows the pointer; resize cursors on the
  handles. Double-click a page for the Page Size dialog.
- doc: Selection.page (the Page tool's page); engine: layout.selectPage
  (no undo step).
- layout.pageSize `anchor` (0-8): the page grows away from the
  reference point and its objects and guides keep their place relative
  to it (a handle drag anchors the opposite edge); liquid rules work
  from that.
- canvas: PageHandles and Tag overlays; translations for the tags and
  the Resize Page undo label.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 00:29:55 +02:00
Andreas Haraminac 5024ed8870 #732: bound control-channel click and drag event queues 2026-10-13 21:55:44 +00:00
LabDennis 0fd627aa44 perf(assets): index duplicate candidates by content (#727) 2026-10-13 23:12:09 +02:00
aiongg e19b6b1fd9 Merge toc-dialog: Table of Contents dialog, entry options and IDML TOC styles (#755)
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/tests_idml.rs
#	crates/idml/src/import.rs
#	crates/ui-egui/src/dialogs.rs
2026-10-13 20:44:30 +00:00
aiongg 129adfa429 Layout: Table of Contents dialog with entry styles, page number position and IDML TOC styles (#755) 2026-10-13 20:41:56 +00:00
aiongg d5c26cc781 Merge t13-commands: #764 #757 #802 #803
# Conflicts:
#	crates/engine/src/cmd/object.rs
2026-10-13 20:28:49 +00:00
aiongg b9060d0350 Merge #803: Refuse command params the description doesn't name
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/file.rs
#	crates/engine/src/cmd/mod.rs
#	crates/engine/src/cmd/object.rs
#	crates/engine/src/cmd/prefs.rs
#	crates/engine/src/cmd/table.rs
#	crates/engine/src/lib.rs
2026-10-13 20:13:51 +00:00
aiongg 16c2ac46c6 Merge #829: Polish UI language
# Conflicts:
#	ROADMAP.md
#	crates/ui-egui/src/chrome.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/menus.rs
#	docs/localization-parity.md
#	docs/target-app-parity.md
2026-10-13 20:06:53 +00:00
aiongg c6921a77cb Merge #802: File › Open Recent: recent documents in the menu and on the start screen
# Conflicts:
#	ROADMAP.md
#	apps/designcraft/src/native_menu.rs
#	crates/engine/src/cmd/file.rs
#	crates/engine/src/cmd/mod.rs
#	crates/engine/src/cmd/prefs.rs
#	crates/engine/src/lib.rs
#	crates/format/src/lib.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/menus.rs
#	docs/target-app-parity.md
2026-10-13 20:06:29 +00:00
aiongg 7f0d7f1f32 Merge t13-moppeeer: #253 #832 #753 #754 (a3b97165) #588
# Conflicts:
#	ROADMAP.md
#	crates/idml/src/tests.rs
2026-10-13 19:57:37 +00:00
aiongg a4ec5b31d6 Merge t13-gradients: gradient PRs #765 #763 #826 #823 #825 2026-10-13 19:55:40 +00:00
aiongg 6039be0526 Merge #588: CMYK blend space: blend modes blend in CMYK on screen
# Conflicts:
#	ROADMAP.md
#	crates/render/src/fx.rs
#	crates/render/src/lib.rs
2026-10-13 19:37:18 +00:00
aiongg 1847b8cef5 Merge #825: Gradient swatches on text, laid across the text frame
# Conflicts:
#	ROADMAP.md
#	crates/pdf/src/export.rs
#	crates/pdf/src/text.rs
#	crates/render/src/text.rs
#	docs/target-app-parity.md
2026-10-13 19:30:47 +00:00
aiongg 42f000e95a Merge #832: Window menu in InDesign's order; Window > Workspace with Essentials Classic
# Conflicts:
#	ROADMAP.md
#	crates/ui-egui/src/chrome.rs
#	crates/ui-egui/src/dock.rs
#	crates/ui-egui/src/menus.rs
2026-10-13 19:28:59 +00:00
aiongg f60eb24656 Merge #823: Gradient strokes: on screen, in PDF, in IDML and the Gradient panel
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/object.rs
#	crates/engine/src/cmd/style.rs
#	crates/idml/src/tests.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/i18n/ar.rs
#	crates/ui-egui/src/i18n/it.rs
#	crates/ui-egui/src/i18n/ja.rs
#	crates/ui-egui/src/i18n/pt_br.rs
#	crates/ui-egui/src/i18n/uk.rs
#	crates/ui-egui/src/panels/swatches.rs
#	docs/target-app-parity.md
2026-10-13 19:28:11 +00:00
aiongg da8aaa9cdc Merge #826: Gradient panel layout; drop swatches from the Swatches panel onto its stops
# Conflicts:
#	ROADMAP.md
#	crates/ui-egui/src/panels/swatches.rs
#	docs/target-app-parity.md
2026-10-13 19:25:12 +00:00
aiongg fce28331ec Merge #763: New Swatch names a gradient "New Gradient Swatch"; gradient swatches from colour swatches
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/style.rs
#	docs/target-app-parity.md
2026-10-13 19:23:33 +00:00
aiongg 9d5d341a7d Merge #765: Radial gradients stretch with the object's box (an ellipse) 2026-10-13 19:21:58 +00:00
aiongg 8a4dac7faa Merge #815: fix(pdf): leave form fields and media out of PDF/A-2b exports (#743) 2026-10-13 19:03:42 +00:00
aiongg b4f7286482 Merge text-autosize: opening a file keeps its auto-size frame sizes (#632)
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/interchange.rs
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-13 18:40:15 +00:00
aiongg 935741d47c Text frames: opening a file keeps its auto-size frame sizes 2026-10-13 18:40:01 +00:00
moppeeerandClaude Opus 5.5 dc712baeca fix(color): gradients between process colours blend as inks, as InDesign shows them
LYH Studio episode 6 builds every gradient from C=0 M=100 Y=0 K=0 to
C=100 M=0 Y=0 K=0. InDesign blends a gradient's process stops as inks:
halfway is C 50 M 50, a light lilac. DesignCraft converted both ends to
RGB and blended there, so the middle came out a darker violet (117, 88,
189 instead of 135, 119, 181), and every Episode 6 gradient looked
different. A midpoint between CMYK stops also became an RGB colour, so
the PDF shading turned RGB with a conversion warning.

- color: `Color::lerp` blends CMYK with CMYK (or grey), and grey with
  grey, in ink space; anything with RGB still blends in RGB.
- `Gradient::display_stops`: the expanded stops plus samples through
  each span that isn't RGB at both ends, for renderers that interpolate
  in RGB; the screen renderer uses it.
- pdf: a midpoint's expanded stop now keeps the stops' space; the
  colour-space test expects no conversion for CMYK or grey midpoints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:38:38 +02:00
moppeeerandClaude Opus 5.5 0a34316b82 feat(ui): Window menu in InDesign's order; Window > Workspace with Essentials Classic
LYH Studio episode 6 starts with "Window > Workspace > Essential
Classic" and opens panels with "Window > Color > Gradient & Swatches".
DesignCraft's Window menu had no Workspace or Color submenu (panels sat
in one "Panels" list) and no Essentials Classic workspace.

- Window menu: Arrange, Workspace, then the panels alphabetically with
  InDesign's groups (Color: Color, Gradient, Swatches; Editorial;
  Interactive; Object & Layout; Output; Styles; Type & Tables;
  Utilities). Every panel stays reachable; Next/Previous Document moved
  into Arrange.
- Window > Workspace: the built-in workspaces as "[Essentials]", the
  current one checked, Reset <current>, New Workspace. The app bar's
  switcher uses the same list and order.
- Essentials Classic: the Control panel, no Properties stack, and an
  icon column of named panel groups ([Pages, Layers, Links] [Stroke,
  Color] [Swatches] [Effects] [Paragraph] [Text Wrap] [Character
  Styles, Paragraph Styles] [Gradient]); Window > Pages opens Pages
  from its group. Saved workspaces keep the groups.
- Flyouts and floating panels name Pages, Layers and Properties.
- Translations for Editorial, Object & Layout, Styles and Essentials
  Classic.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:16:51 +02:00
moppeeerandClaude Opus 5.5 bdf66d7749 feat(ui): Gradient panel in InDesign's layout; swatches drop onto its stops
LYH Studio episode 6 (1:36-2:05) builds its gradient in the Gradient
panel: it drags C=0 M=100 Y=0 K=0 from the Swatches panel onto the left
stop, drops C=100 M=0 Y=0 K=0 on the ramp (a new stop at 68.636 %), then
drags the black stop off. DesignCraft's Gradient panel took no swatches,
showed only a hint until the fill was a gradient, and laid out its
fields differently.

- Gradient panel: InDesign's layout (thumbnail; Type; Location % and
  Angle; Reverse; the ramp with midpoint diamonds above and stops below).
  Until the fill is a gradient it shows the default White -> Black ramp;
  clicking the thumbnail or using the ramp starts one. Midpoints drag
  and take a Location. A stop's colour picker moved to its right-click
  menu, so selecting a stop no longer grows the panel.
- Swatches panel rows are drag sources (the chip follows the pointer):
  dropped on a stop a swatch colours it, elsewhere on the ramp it adds
  a stop there.
- Stops keep their colour model through panel edits (CMYK used to turn
  into hex RGB whenever a stop moved).
- engine: `object.gradient` stops accept `swatch` (its colour; gradient
  swatches and [None] are refused) and colours accept `{gray}`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:02:45 +02:00
moppeeerandClaude Opus 5.5 ac54c8faf7 feat(text): gradient swatches on text, laid across the frame as in InDesign
LYH Studio episode 6 (8:28) gives the "1969" headline the "New Gradient
Swatch" (magenta → cyan) from the Swatches panel. InDesign runs a gradient
on text across its text frame's box. DesignCraft resolved a character
fill to one solid colour, so a gradient swatch drew no text at all.

- render: a character fill or stroke that names a gradient swatch is
  painted with that gradient over the frame's box in text space (type on
  a path: the path's box).
- pdf: text runs take a gradient paint: the shading is laid out in the
  frame's text space and mapped into each run's origin, so skew and
  horizontal scale don't bend it; outlined runs and type on a path too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:30:55 +02:00
Tomasz LewandowskiandClaude Opus 5.5 226991cb28 i18n: add Polish interface language
Adds a Polish catalog (i18n/pl.rs, 1,698 strings), registers Polski in
Edit › Interface Language, uses category-first count captions for Polish
(as for Ukrainian) and adds catalog and language-switch tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DukHNvUY45rjRnvtz91x33
2026-10-13 19:16:32 +02:00
moppeeerandClaude Opus 5.5 370dca2ca2 feat(render): gradient strokes, on screen, in PDF and in the Gradient panel
A gradient swatch on a stroke drew nothing: strokes were painted with a
single resolved colour. LYH Studio episode 6 gives the loop on the right
page (9:13) and, at first by accident, the circle (1:19, "right now it
is on the stroke") a gradient stroke.

- doc: Stroke::gradient_angle / gradient_vector (as Fill's) and
  Stroke::as_fill(), the stroke's paint as a fill.
- render and PDF: a gradient stroke is painted like a gradient fill,
  across the path's box (dash gaps and arrowheads keep their paints).
- engine: object.gradient {target: stroke} edits the stroke's
  gradient (kind, stops, angle, the Gradient Swatch tool's from/to);
  scaling moves the stroke's vector with the shape.
- IDML: GradientStrokeStart/Length/Angle round-trip.
- Gradient panel: acts on the stroke when the toolbox's Stroke box is
  in front, as InDesign's follows the Fill/Stroke proxy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:11:58 +02:00
MarekSpodBiedry 79b9971939 fix(pdf): leave form fields and media out of PDF/A-2b exports (#743) 2026-10-13 18:44:45 +02:00
aiongg 7ad3d12883 Merge indd-import: open .indd and .indt files through indd-utils 2026-10-13 16:18:53 +00:00
aiongg a7c3e330ec INDD: open .indd and .indt files through indd-utils (#21, #114)
.indd and .indt documents open through File › Open, drop, the web inbox,
file.open, file.openBytes, file.openIdml and the CLI. The engine converts
them to IDML in memory with storytold/indd-utils (pinned by rev, MIT OR
Apache-2.0, no dependencies, builds for wasm) and imports the result
through the existing IDML path. The converter's warnings come first in the
open result's `warnings`, before the IDML importer's and the font
warnings. A file the converter can't read is an open error, shown in the
Can't Open the File alert.

Routing: a path or name ending in .indd/.indt, or bytes starting with the
INDD signature (the web inbox passes the name without its extension).
opens_as_document and the desktop and web Open dialogs list indd/indt.

Tests use synthetic bytes only: signature-bearing junk, a zero-filled
.indd and a short .indt all fail as open errors with no document added;
a dropped unreadable .indd raises the open alert.

(cherry picked from commit bb8a6a23fa)
2026-10-13 16:15:15 +00:00
Gert Pellin 68596ea696 Refuse command params the description doesn't name
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
2026-10-13 17:25:27 +02:00
PiMinusMax 0fb3d677cc Start screen: recent files show a picture of the first page (#706)
Save and Save a Copy put a picture of the first page in the .designcraft
zip as preview.png (320 px on the longer side). The start screen reads
only that entry and shows the last eight recent files as cards with the
picture and the name, below the community card. Files saved before have
a blank page until they are saved again.

The package fallback-font test writes its fixture without Save's preview:
drawing it before the document's fonts load pulled in a system font that
covers the test's characters (Unifont CSUR) ahead of the document's own.
2026-10-13 17:01:57 +02:00
aiongg d087c035b2 Merge recovery-dirs: fix(recovery): an explicit recovery folder never overwrites or deletes another run's checkpoints (#715) 2026-10-13 14:32:52 +00:00
aiongg e04cce9838 Merge epub-tables: fix(epub): EPUB and HTML export include tables, footnotes and endnotes (#724) 2026-10-13 14:31:31 +00:00
aiongg 4eb786e880 fix(recovery): an explicit recovery folder never overwrites or deletes another run's checkpoints (#715) 2026-10-13 14:22:58 +00:00
aiongg c91b9af16d fix(epub): EPUB and HTML export include tables, footnotes and endnotes (#724) 2026-10-13 14:19:45 +00:00
moppeeerandClaude Opus 5.5 7a30e5427e fix(render): radial gradients stretch with the object's box, as in InDesign
InDesign draws a radial gradient in the object's bounding box: on a
non-square frame it's an ellipse reaching the frame's sides, and a
Gradient Swatch tool drag keeps those proportions. LYH Studio episode 6
(3:16) gives the left page's background a radial gradient: magenta in
the middle, cyan out to every edge. DesignCraft drew a circle reaching
only the longer side, so a portrait page stayed purple at its sides.

- doc: `Fill::radial_ellipse(bounds)`: centre and radii (default: the
  box's centre and half its width and height; with a tool vector, its
  start, and its length measured with the box made square).
- render: the radial paint is that circle, stretched by the paint
  transform; PDF: the shading's transform does the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:58:05 +02:00
moppeeerandClaude Opus 5.5 19aa1c6f4c feat(swatches): New Swatch names a gradient "New Gradient Swatch"; gradient swatches from colour swatches
InDesign's Swatches panel New Swatch takes what is current: a gradient
built in the Gradient panel becomes "New Gradient Swatch" (applied, and
listed), an unnamed colour a listed swatch, a selected named swatch a
"… copy". DesignCraft's button always added the same orange swatch, and
gradient swatches could not be created at all (LYH Studio episode 6,
2:12: "this is our Swatch … create a new swatch for it").

- engine: `swatch.new {swatch?, target?}` does the above for the
  selection's fill (or stroke) or a named swatch; `swatch.create` also
  takes `gradient: {kind?, stops: [{location, swatch | color, opacity?,
  midpoint?}]}`, its stops taking colour swatches by name, as swatches
  dragged onto the Gradient panel's stops.
- Swatches panel: New Swatch runs `swatch.new`, and with nothing
  selected still adds a colour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:46:31 +02:00