Commit Graph
718 Commits
Author SHA1 Message Date
aiongg 548d700b9f Integration: #802 a document that can't be serialized fails Save and Save a Copy instead of writing an empty file; Open Recent's alert names the file without an undocumented param 2026-10-13 20:08:28 +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 a4ec5b31d6 Merge t13-gradients: gradient PRs #765 #763 #826 #823 #825 2026-10-13 19:55:40 +00:00
aiongg 72d7ea2e82 Integration: #823 a linear gradient across a straight line runs along its length, on screen and in PDF alike 2026-10-13 19:49:32 +00:00
aiongg 9b605cecd9 Integration: cargo fmt 2026-10-13 19:33:55 +00:00
aiongg 14e98419ad Integration: #825 PDF table-cell and footnote text lays a gradient across its own lines, as on screen 2026-10-13 19:33:08 +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 6db40bdbb5 Integration: #823 a radial gradient on a box with no width or height is a circle across it 2026-10-13 19:29:28 +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 ba95ae2dae Integration: #826 grey stops keep their value (model form); a stale ramp drag can't index past the stops 2026-10-13 19:26:54 +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 aad9db158f Integration: #763 New Swatch with nothing selected makes a colour swatch in the engine; the panel sends the Fill/Stroke target 2026-10-13 19:24:47 +00:00
aiongg 0c45a8420f Merge t13-epub: EPUB/HTML export PRs #785 #787 #783 #789 #779 #780 #782 #791 #121 2026-10-13 19:23:54 +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 1486f82212 Merge #782: EPUB: the navigation lists the headings, with a title in the book's language
# Conflicts:
#	crates/engine/src/cmd/export.rs
#	crates/epub/src/lib.rs
2026-10-13 19:14:13 +00:00
aiongg 033611d518 Integration: cargo fmt 2026-10-13 19:09:21 +00:00
aiongg 6477435e81 fix(transform): rotate and flip turn about the chosen reference point 2026-10-13 19:08:36 +00:00
aiongg ee2c450829 Merge #789: Styles: name the attributes in the params docs, and a wrong one in the command's error
# Conflicts:
#	crates/engine/src/cmd/style.rs
2026-10-13 19:07:52 +00:00
aiongg 6eae3620ed Integration: #783 reads the cover image with a size cap (256 MiB), devices included 2026-10-13 19:07:02 +00:00
aiongg 3cb3f342c8 Integration: #812's test (overset preflight names the page of a frame in a moved group) 2026-10-13 19:07:02 +00:00
aiongg 73112e882f Merge #783: EPUB: real modified time, a stable unique identifier, and a cover image file
# Conflicts:
#	crates/engine/src/cmd/export.rs
#	crates/epub/src/lib.rs
2026-10-13 19:06:49 +00:00
aiongg 5917b24aaa Integration: #819 a pasted missing graphic always gets a fresh asset id from the target 2026-10-13 19:06:46 +00:00
aiongg 0016b8260e Merge #819: Keep a missing graphic missing when it is pasted into another document (#720)
# Conflicts:
#	crates/engine/src/cmd/object.rs
2026-10-13 19:06:14 +00:00
aiongg 8f54d1cce4 Integration: #787 refuses bitmap-only fonts for embedding, and styles name the face's numeric weight 2026-10-13 19:05:28 +00:00
aiongg 11e4a7f60e Merge #787: EPUB/HTML: generic font fallback from the font's class, and optional font embedding
# Conflicts:
#	crates/engine/src/cmd/export.rs
#	crates/epub/src/lib.rs
2026-10-13 19:04:24 +00:00
aiongg 5f3a2fbb61 Merge #785: EPUB/HTML: no file name as alt text, readable image names in the package 2026-10-13 19:02:00 +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 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
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 ec4ff485df Keep a missing graphic missing when it is pasted into another document (#720) 2026-10-13 18:43:16 +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 fca75a3302 Merge locked-paths: tests for path edits on locked layers and groups (#735)
# Conflicts:
#	crates/ui-egui/src/panel_docking_tests.rs
2026-10-13 14:58:15 +00:00
aiongg e08f3c5997 test(paths): path edits on a locked layer change nothing (#735) 2026-10-13 14:51:53 +00:00
Gert Pellin 2a3d1dc183 Styles: name the attributes in the params docs, and a wrong one in the command's error (#777)
- describe style.paragraph.create (and .edit, style.character.create,
  .edit) gave chars?: {…} and para?: {…}. The descriptions now say they
  take type.char's and type.para's attributes and name the common ones.
- A wrong attribute gave "invalid parameters for `style`: unknown
  attribute `font`". The error now names the command and the group,
  and the attribute meant (nearest name, or one the key starts or ends)
  or else lists them all: "invalid parameters for
  `style.paragraph.create`: chars: unknown attribute `font`; did you
  mean `fontFamily`?". The suggestion comes from set_json, so
  type.char, type.para and find.change get it too.
- chars or para that isn't an object is an error instead of ignored.

Fixes #777
2026-10-13 16:48:27 +02:00
aiongg d9c766dd16 Integration (editor-undo): Story Editor test focuses the text input, not its text run; paste test sets its stale selection after Undo restores the selection 2026-10-13 14:47:23 +00:00
Gert Pellin 6fafa9b0a3 EPUB/HTML: generic font fallback from the font's class, and optional font embedding (#775)
- Every font-family fell back to serif, sans-serif typefaces included.
  The fallback now comes from the font's classification (PANOSE, OS/2
  class, name: serif, sans-serif, monospace, cursive, fantasy), passed
  in by the engine; without one, from the family name.
- file.exportEpub takes embedFonts: the faces the stylesheet names are
  packaged (OEBPS/fonts, @font-face, manifest items with font/ttf, otf,
  woff or woff2). Faces that aren't available, whose licence (OS/2
  fsType) forbids embedding, or that sit in a font collection are left
  out and listed in the new warnings array. Fonts are packaged whole;
  subsetting is a follow-up.

Fixes #775
2026-10-13 16:41:08 +02:00
Gert Pellin f420639276 EPUB/HTML: no file name as alt text, readable image names in the package (#774)
- An image without alt text (Object Export Options) got its file name as
  alt, which a screen reader then reads aloud. It is now decorative
  (alt=""), and file.exportEpub lists those images in a new warnings
  array so they can be given alt text.
- Images and media were packaged as images/<asset id>.<ext>. They are
  now <name>-<asset id>.<ext>, the name from the original file name
  (lowercase ASCII); the id keeps two files of the same name apart.

Fixes #774
2026-10-13 16:35:36 +02:00
Gert Pellin 7be88b4484 EPUB: real modified time, a stable unique identifier, and a cover image file (#771, #773)
- dcterms:modified was always 2026-01-01T00:00:00Z. It is now the
  export time (from the command; on the web, and in the crate without
  one, the document's last save or creation time).
- The default dc:identifier was urn:designcraft:<title>, shared by
  books with the same title. It is now a urn:uuid derived from the
  title and the document's creation time: stable from export to export,
  different between documents. file.exportEpub takes identifier to set
  it (an ISBN, say).
- file.exportEpub takes coverImage, a PNG or JPEG file used as the
  cover (cover-image); a JPEG is packaged as cover.jpg/image/jpeg.

Fixes #771
Fixes #773
2026-10-13 16:33:34 +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 4e3a5364e5 Merge docx-tables: fix(textimport): Word table cells skip deleted text and field codes; Normal comes in as its own style (#721, #722) 2026-10-13 14:31:03 +00:00
aiongg a39bb70c5b Merge locked-paths: fix(paths): path edits refuse a path on a locked layer or in a locked group (#735); fix(properties): links to other panels open them when panel docking is on (#736) 2026-10-13 14:28:19 +00:00
aiongg a418a8a309 Merge editor-undo: fix(editing): Story Editor stays with its document; Undo restores the selection from before the command (#729, #716) 2026-10-13 14:27:42 +00:00
aiongg 3d3f02e621 Merge save-result: fix(file): saving never reports success without a file (#741, #708) 2026-10-13 14:27:03 +00:00
Gert Pellin 1fc302f695 EPUB: the navigation lists the headings, with a title in the book's language (#769, #770)
nav.xhtml had one entry per placed story, labelled with its first
paragraph (body text too), and always the title "Contents".

- Headings (h1-h6) get an id in the EPUB, and the navigation lists the
  h1-h3 headings of the stories in reading order, nested by level and
  linked to them. A document without headings keeps one entry per
  story (its first paragraph).
- The navigation's title follows the language (Inhoud, Inhalt, Table
  des matières, Índice, Indice, 目次, 目录; Contents otherwise), and
  file.exportEpub takes tocTitle to set it.

Fixes #769
Fixes #770
2026-10-13 16:26:32 +02:00
aiongg debec75166 fix(textimport): Word table cells skip deleted text and field codes; Normal comes in as its own style (#721, #722)
Table cell paragraphs go through the same run and inline path as body paragraphs, so deleted and moved-from text and field instructions stay out of cells, and field results, tabs and breaks come in.

Word's Normal imports as a paragraph style named Normal whose definition is w:docDefaults with Normal's own properties on top; paragraphs without a style use Word's default paragraph style. A styleMap entry can still map Normal to [Basic Paragraph].
2026-10-13 14:23:08 +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 eb83d23040 fix(file): saving never reports success without a file (#741, #708)
to_bytes returns a Result and every caller propagates it, so a document
that can't be written is an error: the existing file, the unsaved state
and the recovery checkpoint stay. Asset entries near or over 4 GiB are
written with zip64 sizes. On the web, Save marks the document saved only
after the browser download has started.
2026-10-13 14:22:27 +00:00