Commit Graph
465 Commits
Author SHA1 Message Date
aiongg f4f0896f85 Merge #144: Character Style Options dialog; style.character.edit renames everywhere, validates Based On, null unsets; New Character Style opens the dialog
Conflict: crates/ui-egui/src/dialogs.rs: took #144's structure (shared basic_character_formats and character_color sections); paragraph_style_options reads a clone of the document as #143 does, so the font menu can borrow the app; basic_character_formats takes &mut DesignApp and uses #143's font_combo (font_menu_rows is gone) with a × button that clears a style's family
2026-10-09 22:35:29 +00:00
aiongg 97edaf461a Merge #143: One searchable font menu for every font field; hidden .Name fonts left out; Contextual Task Bar draggable, with Pin and Reset
Conflict: crates/ui-egui/src/lib.rs: one test_window module with #112's wheel and panel_rect and #143's click_at
2026-10-09 22:34:30 +00:00
aiongg e9fa6a72e3 Merge #111: macOS menu Select All/Copy/Cut/Paste/Undo/Redo go to the focused text field 2026-10-09 22:34:17 +00:00
aiongg e80e4a656a Merge #146: File › Close, ⌘W and the tab × ask Save / Don't Save / Cancel for unsaved documents
Conflict: crates/ui-egui/src/lib.rs: kept both sides' tests in mod tests (#142/#138 and #146)
2026-10-09 22:33:55 +00:00
aiongg 9ca7d3f663 Merge #112: Tools panel uses two columns or scrolls when short; Control bar and app bar scroll sideways; tool button labels
Conflict: crates/ui-egui/src/chrome.rs: kept #112's horizontal ScrollArea in control_bar and put #142's horizontal_top inside it
2026-10-09 22:32:57 +00:00
aiongg 7ff03ade34 Merge #142: Control bar row is horizontal_top, so the second row of two-row groups shows 2026-10-09 22:32:43 +00:00
aiongg c18912db69 Merge #128: PDF/A keeps placed images; right-to-left tables and columns
Conflict: crates/pdf/src/export.rs: took #223's side (RasterMode with #147's decode check) and dropped #128's Exporter interpolate field; #128's RTL story/table work and its PDF/A test are kept
2026-10-09 22:16:55 +00:00
aiongg 8b5e1a909d Merge #147: PDF: a damaged PNG, GIF or WebP is skipped, not fatal
Conflict: crates/pdf/src/export.rs: kept #223's lossless() (RasterMode, sRGB tagging) with #147's full-decode check at its top; the fallback reuses the decoded raster
2026-10-09 22:16:33 +00:00
aiongg 7b813d70ad Merge #223: PDF/X-4: RGB images carry sRGB, no image interpolation, pdf:Trapped in XMP 2026-10-09 22:16:16 +00:00
aiongg ff38aede13 Merge #127: CLI: export options after the last export are an error; MCP and README docs match the code 2026-10-09 22:16:13 +00:00
aiongg 7a0407f8a7 Merge #118: Honor MCP typing, story, and page contracts 2026-10-09 22:16:13 +00:00
aiongg 8cd7bdc88b Merge #117: Fix browser printing, startup flags, and document MIME 2026-10-09 22:16:13 +00:00
aiongg e46e173d90 Merge #120: Preserve typed values in generic command dialogs 2026-10-09 22:16:13 +00:00
aiongg 800b2332f6 Merge #139: Docs, About: the Illustrator-class sibling is VectorCraft (#59) 2026-10-09 22:16:13 +00:00
aiongg 40a8ba3c53 Merge #138: Web: AltGr and other named keys no longer type their names (#134) 2026-10-09 22:16:13 +00:00
aiongg 8300519bd5 Merge #249: Make menu entries selectable across the full row 2026-10-09 22:16:13 +00:00
aiongg 9b3b085770 Merge #248: Add A6 sheet preset to the welcome page 2026-10-09 22:16:13 +00:00
aiongg 8fe589586b Merge #224: Distribute selected table rows evenly and keep the edit undoable 2026-10-09 22:16:13 +00:00
aiongg 5729796705 Merge #122: Desktop: write log records to standard error and a rotating log file
Conflict: apps/designcraft/src/main.rs: kept both module declarations (#168's graphics, #122's logging)
2026-10-09 22:16:13 +00:00
aiongg 6e8f33c386 Merge #168: Fix Windows startup crash by defaulting to DirectX 12 2026-10-09 22:15:59 +00:00
aiongg de5d32c90f Merge #244: Rebind embedded image links when opening relocated packages (#235) 2026-10-09 22:15:59 +00:00
aiongg 553d017065 Merge #229: Prevent mixed gradient color spaces from aborting PDF export 2026-10-09 22:15:59 +00:00
aiongg 29e33dde3d Merge #152: Apply contextual escaping to HTML and EPUB exports 2026-10-09 22:15:59 +00:00
aiongg eedbaf6717 Merge #137: Commands: huge row and column counts can't exhaust memory 2026-10-09 22:15:59 +00:00
aiongg 997d18dcac Merge #149: Import: RTF and Tagged Text keep characters past U+FFFF 2026-10-09 22:15:59 +00:00
aiongg 69fc1c381f Merge #116: Images: set SVG text in the shared fonts when the bundled ones lack it 2026-10-09 22:15:59 +00:00
aiongg 13cc11b041 Merge #115: Compose: don't panic when a hyphenated paragraph starts with a no-break letter 2026-10-09 22:15:59 +00:00
aiongg c64e703495 Merge #136: Images: PSD decode passes clippy on Rust 1.98 2026-10-09 22:15:59 +00:00
moppeeer 218dc42e5d Make menu entries selectable across the full row 2026-10-09 23:33:42 +02:00
moppeeer 84be68d9c2 Add A6 sheet preset to the welcome page 2026-10-09 23:28:49 +02:00
DWAA1660andCodex 658d8e1784 M5: rebind embedded image links when opening relocated packages (#235)
Reuse packaged-link candidates for embedded IDML images and native files.
Resolve missing original paths beside the opened document, retaining the
embedded image bytes and any original link that still exists. Bound path
lookup allocations and cover actual package relocation through commands.

Make existing data-merge test path assertions portable to Windows.

Co-authored-by: Codex <noreply@openai.com>
2026-10-09 15:07:20 -06:00
Vikram Bhaskaran 6aaa4f0b75 Distribute selected table rows evenly and keep the edit undoable.
The rows share one fixed height that preserves their composed total. Merged rows and rows that are not laid out are refused, and the table stays unchanged.
2026-10-10 04:37:44 +11:00
Skylar Taylor-Barrick ee33fe3c35 FID1: prevent mixed gradient color spaces from aborting PDF export 2026-10-09 17:33:18 +00:00
ROSSAandClaude Opus 5.5 6aa92e70b4 PDF/X-4: RGB images carry sRGB, no image interpolation, pdf:Trapped in XMP
- An RGB image with no ICC profile of its own was written as DeviceRGB
  under the CMYK output intent, and the export only warned. In PDF/X-4
  it now carries the sRGB profile (assets/icc/sRGB-v2-magic.icc, ICC
  v2.1, CC0). JPEGs stay passed through. Untagged PNG, GIF and WebP,
  and the decoded TIFF, BMP and PSD fallback, go in as RGB with the
  profile. A re-encoded JPEG keeps the source's RGB profile or gets
  sRGB. Grey images and images with their own profile are unchanged.
- Every image constructor asked for interpolation. PDF/X-4 and PDF/A
  exports now write images without it, which also removes the
  ImageInterpolation error that stopped PDF/A exports with a placed
  image. Plain exports keep it.
- make_pdfx4 writes pdf:Trapped False into the XMP, matching
  /Trapped/False in the document information.
- check_pdfx4 reports an image that asks for interpolation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 12:18:18 -05:00
Brandon Thomas 66c7ce7e96 Merge pull request #131 from XusBadia/dmg-background
macOS DMG: branded window background and icon layout
2026-10-09 09:20:37 -04:00
Xus BadiaandClaude Opus 5.5 ec42e542e9 macOS DMG: branded window background and icon layout
The DMG opened as a plain Finder window. It now shows the app icon's figure on
the DesignCraft colour, with DesignCraft.app and the Applications link side by side.
Same approach as PhotoCraft's and VectorCraft's (storytold/photocraft#1201,
storytold/vectorcraft#493).

- packaging/macos/dmg/: background.svg (source; text outlined, no paper grain,
  the figure an <image> of assets/app-icon), background.tiff (1x + 2x HiDPI,
  16-colour palette, Deflate, sRGB; ~90 KB)
  and dmg-layout.DS_Store (Finder view settings: window, icon size 128,
  positions, background).
- generate.py writes the TIFF and the layout on any OS, byte-for-byte
  reproducible, with nothing from the machine that ran it (no paths, users,
  disk names or volume UUIDs).
- package.sh copies them into the staging folder; the image is still built with
  hdiutil makehybrid. Nothing extra runs in the signing job.
- The volume name drops the version: the layout finds the background through
  an alias that includes the volume name. The DMG file name keeps the version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 07:43:40 +02:00
DWAA1660 412962d9f8 Fix #167: default Windows startup to DirectX 12 2026-10-08 14:56:37 -06:00
Jan Hallvard LarsenandClaude Opus 5.5 c63ca54227 Import: RTF and Tagged Text keep characters past U+FFFF
Placing an RTF or Tagged Text file dropped every character outside the
Basic Multilingual Plane: emoji, mathematical letters and the rarer CJK
ideographs such as 𠮷 (U+20BB7, used in Japanese names). The text
around them came in, so the loss was silent. It also broke our own round
trip: Export Text writes these characters correctly and Place lost them.

Both formats carry such a character as a UTF-16 surrogate pair, two
units in a row: \u-10179?\u-8704? in RTF, <0xD83D><0xDE00> in Tagged
Text. Both readers passed each unit to char::from_u32 on its own. Half a
pair is not a character, so each half was dropped.

A small shared Utf16Units joins the units: a first half waits for the
second, and the pair becomes one character. Half a pair with no partner
is still left out, as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 16:33:44 +01:00
Skylar Taylor-Barrick 6a53d95895 Security: escape HTML and EPUB metadata 2026-10-08 15:31:35 +00:00
aiongg 7d064f268e UI: the Contextual Task Bar moves by its grip and stays where it is dropped, with Pin Bar Position and Reset Bar Position in its menu 2026-10-08 11:27:01 -04:00
Jan Hallvard LarsenandClaude Opus 5.5 1ed206642f PDF: a damaged PNG, GIF or WebP is skipped, not fatal
A placed PNG, GIF or WebP that is cut off (an interrupted download) or
has a bad checksum made the whole PDF export fail with
"PDF writer error: Image(Image(ImageRepr {..}), None, "unexpected end of
file")", which names no file. A damaged TIFF or BMP in the same place is
skipped with the warning "image `name` could not be decoded and was
skipped", and the rest of the document is exported.

The exporter decides whether an image can be embedded by asking krilla
to open it. krilla opens PNG, GIF and WebP lazily: it reads the header,
accepts the file, and decodes the pixels only when the PDF is written,
where an error ends the export. TIFF and BMP are decoded by us first, so
they took the skip path.

lossless() now decodes the image first for every format. A file that
does not decode returns None and takes the existing skip-with-warning
path. This costs one extra decode per PNG, GIF or WebP asset: the sample
magazine (two PNGs, 820 KB) exports in 0.19 s instead of 0.17 s in a
debug build.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 16:26:30 +01:00
Jan Hallvard LarsenandClaude Opus 5.5 c84eb56258 UI: closing a document with unsaved changes asks first (#141)
File ▸ Close, its shortcut (Ctrl/Cmd+W) and the tab's × closed a document
with unsaved changes at once. Closing also discards the document's
recovery data, so the edits were gone for good.

The three user paths now go through menus::close_document. A document
with unsaved changes opens a dialog with Save, Don't Save and Cancel:

- Save runs the same Save the menu does, then closes. If the document
  is still unsaved afterwards (the file picker was cancelled), it stays
  open.
- Don't Save closes it.
- Cancel (or Escape) does nothing.

A document with nothing to save closes as before. The engine command
file.close is unchanged and never asks, so scripts, MCP and the control
channel's engine.execute keep working unattended. ui.menu.invoke without
params acts like the menu item, as it does for other dialogs.

Quitting the app is not covered here. Recovery data survives a quit, so
unsaved documents come back on the next start.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 16:20:51 +01:00
Jan Hallvard LarsenandClaude Opus 5.5 20422c4725 UI: the Control panel shows its second row
In every workspace that shows the Control panel (Advanced, Typography,
Printing and Proofing, Book), the second row of fields was cut off at the
panel's lower edge: Y and H in object mode, leading, tracking and the
indents in text mode.

The panel lays its groups out in a row that centres each child
vertically. The two-row grids were placed as if they were one row tall
and then grew downward, so they started 13.5 pt below the panel's top
instead of at it and ended 5.5 pt past the 59 pt panel.

The row is now top-aligned, which is where the font menus, the
separators and the reference-point proxy already sat.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 16:14:24 +01:00
Jan Hallvard LarsenandClaude Opus 5.5 e8ee4cffb4 Docs, About: the Illustrator-class sibling is VectorCraft (#59)
DrawCraft was renamed VectorCraft, and its repository is now
storytold/vectorcraft. Three places still used the old name:

- AGENTS.md and CLAUDE.md told contributors and agents that the sibling
  checkout lives at ../drawcraft. A clone made from the README link lands
  in ../vectorcraft, so that path does not exist.
- The About window's sibling list used the app-page slug "drawcraft".
  getartcraft.com/apps/drawcraft still redirects to /apps/vectorcraft,
  so the link worked, but the README (which the list says it matches)
  uses /apps/vectorcraft.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 16:04:52 +01:00
Jan Hallvard LarsenandClaude Opus 5.5 9ec7858dc9 Web: AltGr and other named keys no longer type their names (#134)
In the browser, pressing AltGr while editing text on the canvas inserted
the word "AltGraph" into the story.

eframe's web backend (0.36.2, web/input.rs text_from_keyboard_event)
turns a keydown into a text event unless the key is on a fixed list of
names. Browsers report many more named keys than that list holds:
AltGraph, Dead, Unidentified, the media keys. Each one arrived as text.
Panel text fields are not affected, because a focused egui text field
takes its text from the browser's input events instead of keydown.

The web shell now drops a text event that is a key name (ASCII letters
and digits, starting with a capital, longer than one character) unless
an egui text field has the keyboard. One key press never types such a
word; a text field's input events can, so they are left alone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 15:54:52 +01:00
aiongg 9fec15876a UI: captions in a sideways-scrolled bar stay on one line
The last group of a scrolled Control bar row got only the width left
over, so a caption like Columns wrapped one letter per line.
2026-10-08 10:51:46 -04:00
Jan Hallvard LarsenandClaude Opus 5.5 7c739feb6f Commands: huge row and column counts can't exhaust memory
Three commands took a count from their parameters and used it unchecked.
Parameters come from MCP, the control channel and scripts, so the count
is hostile input (never-crash: cap input-sized allocations, checked
arithmetic).

- layout.createGuides made one or two guides per row and column with no
  upper limit: 30 million rows took 1.2 GB, and 2^32 - 1 would exhaust
  memory, which aborts the process past the command guard. A count over
  2^32 wrapped to a small one. Both counts are now capped at 100.
- edit.stepAndRepeat multiplied rows by columns as usize. In debug builds
  the overflow panicked; in release builds 2^32 x 2^32 wraps to 0, passes
  the 1000-copy limit and starts a grid of 2^64 copies. The product is
  now checked, and a grid that large falls back to `count` copies like
  any other grid over 1000.
- file.new stored any column count in the page (4 billion, or a wrapped
  value past 2^32). Page::column_rects builds one rect per column each
  time the columns are laid out. The count is now clamped to 1..=216,
  the range layout.marginsAndColumns already uses.

Each case has a regression test that failed before the fix.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 15:45:15 +01:00
Jan Hallvard LarsenandClaude Opus 5.5 27b5c871bd Images: PSD decode passes clippy on Rust 1.98
Rust 1.98 adds the clippy lint chunks_exact_to_as_chunks. It flags two
loops in the PSD decoder, so `cargo xtask ci` stops at the clippy step on
unmodified main for anyone on current stable.

Both loops now use as_chunks / as_chunks_mut (stable since 1.88, below the
workspace rust-version 1.90). The chunks are arrays, so the try_from that
could never fail goes away too. Decoded pixels are unchanged; the existing
PSD tests cover both paths.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 15:32:20 +01:00
aiongg ca0ab6e551 UI: Character Style Options edits a character style's name, Based On and character formats, opened by double-clicking the style, Edit in its menu, or New Character Style… 2026-10-08 10:03:57 -04:00
Brandon ThomasandClaude Opus 5.5 14e677b242 Bump version to 0.4.0 (from 0.3.0) for release
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.4.0
2026-10-08 10:02:08 -04:00