Commit Graph
587 Commits
Author SHA1 Message Date
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 <[email protected]>
2026-10-11 18:49:46 -06: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 66860bd1de UI: Return and Escape in the font menu stay in the menu, never reach the dialog under it (#447)
Every font field's menu (Control bar, Contextual Task Bar, Properties > Character,
Character and Glyphs panels, Paragraph/Character Style Options, Find/Replace Font)
keeps its search focused and open on a click and filters as you type. In a dialog,
Return picked the highlighted family and also pressed OK, and Escape closed the
menu and cancelled the dialog: the menu now consumes the keys it uses. The
drawn font fields report themselves as combo boxes to screen readers.
2026-10-11 17:40:06 -06:00
japbcoelhoandClaude Opus 5.5 2b46ca664c Hyphenation: Portuguese text is hyphenated with hyph-utf8's Portuguese patterns (#512)
Text set in Portuguese ("Portuguese: Brazilian", "Portuguese", pt_BR) got
no hyphenation, so justified Portuguese columns could only stretch their
word spaces (up to 335 % of the normal space in a 56 mm column of 10 pt
Times).

Turn on hypher's "portuguese" feature, beside Spanish: hyph-pt.tex 1.4
(2024-07-13) from hyph-utf8, BSD-3-Clause, credited in NOTICE.
Lang::for_language already maps the Portuguese names and locale codes.

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-11 17:39:53 -06: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 <[email protected]>
2026-10-11 17:39:40 -06:00
skylar taylor-barrick 957890223a M6: Pin craft-print with native driver capability validation 2026-10-12 01:20:53 +02:00
skylar taylor-barrick 3ec5cef6a2 M6: Adopt merged craft-print with scaling and queue duplex defaults 2026-10-11 22:14:24 +02:00
Dylan M. Taylor ca343e8985 ci: refactor workflows to use storytold/craft-common-actions (#489)
* ci: refactor workflows to use reusable artcraft-common-actions

* ci: point reusable workflows to storytold/craft-common-actions

* ci: pin storytold/craft-common-actions to commit 27a978f and pass secrets explicitly

* ci: use @main (unpinned) for craft-common-actions with explanation comment
2026-10-11 11:52:22 -04:00
skylar taylor-barrick 9f7da3f6ab M6: Adopt craft-print for CUPS and Windows PDF printing 2026-10-11 07:29:54 +02:00
skylar taylor-barrick 16a7365701 Merge pull request #409 from skylartaylor/codex/ui-suite-integration-20261010
Adopt shared panel docking and preserve workspace commands
2026-10-10 19:46:47 -06:00
skylar taylor-barrick 2bdb0ea01d UI.9: recover saved docking metadata and declare frontend MSRV 2026-10-10 18:52:20 -06:00
skylar taylor-barrick a77bf8bff5 UI.9: reconcile upstream before shared UI landing 2026-10-10 18:00:07 -06:00
Brandon ThomasandClaude Opus 5.5 a9124a0517 Release prep: version 0.6.0; Linux riscv64 tar.gz in releases (#398)
- release.yml: new linux-riscv64 job (cross-compiled on ubuntu-24.04 against
  ports.ubuntu.com riscv64 libs, tar.gz only, CLI smoke-tested under QEMU),
  ported from GridCraft; the draft release job needs it, so SHA256SUMS covers it.
- packaging/linux/package.sh: CROSS_ARCH/CROSS_TARGET/CROSS_COMPILE/EMULATOR
  support, as in GridCraft.
- cargo xtask version set 0.6.0; README, hardware parity and ROADMAP log note
  the new platform.

Co-authored-by: Claude Opus 5.5 <[email protected]>
v0.6.0
2026-10-10 19:33:30 -04:00
skylar taylor-barrick c62866443c UI.9: integrate shared panel docking and preserve workspace behavior 2026-10-10 14:42:40 -06:00
Brandon ThomasandClaude Opus 5.5 a127c6d5c3 Progress docs: target-app parity, gaps, alpha gate, readiness by audience (#355)
Progress docs per craftrules standards/progress-docs.md: target-app parity, gaps, roadmap with alpha gate, readiness by audience (full / mainstream / essentials) with Opus 5.5 hours, and feature, format, hardware, UI and localization parity docs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 14:09:17 -04:00
92649ffdb0 Security: validate engine inputs and package paths (#155)
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 04:50:26 -04:00
fc2ce5556f Text: Split Columns sets a paragraph in sub-columns within its column (#295)
Consecutive paragraphs with the same Split Columns settings form a split
block: their text fills the sub-columns of the column one after the
other from the block's top, inset by the outside gutter and separated by
the inside gutter, then moves on to the next column. When text follows
the block, its last part is balanced across the sub-columns and the text
continues below the deepest one at the full column width; a block that
ends the story fills its sub-columns in turn. Keep options don't apply
between a block's lines.

IDML: SplitColumnInsideGutter and SplitColumnOutsideGutter import and
export (paragraph attributes split_inside_gutter, split_outside_gutter).

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 04:27:59 -04:00
c657d8545e PDF export: options dialog with remembered settings (#196)
* PDF export: Export Adobe PDF options dialog with remembered settings

File > Export PDF... now opens an options dialog (General / Compression /
Marks and Bleeds / Advanced) that exposes only the parameters the engine's
file.exportPdf already supports: PDF standard, image compression, flattener
preset, spreads, document bleed, crop/bleed/page-info marks with weight and
offset, tagged PDF, page range, and the title. Choosing a preset fills the
dependent fields; editing any field switches the preset to Custom.

The last-used settings persist in ui.json (UiState.pdfExport) and are reused
across exports and sessions. Programmatic callers that pass explicit params
(path, options) still export directly without the dialog; cancelling the save
picker writes nothing.

Adds English and ar/pt-br strings, unit tests for the settings serde, the
dialog defaults and preset mapping, the open/confirm flow (stub writer), and
the menu dispatch, plus a ROADMAP note.

* PDF export: rename dialog title to "Export PDF"

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 04:23:32 -04:00
0b54cc3d66 Text: a word longer than the line breaks at the column edge; a line that exactly fills the measure fits (#293)
* Text: a line that exactly fills the measure fits

A line whose natural width overruns its measure by less than 0.01 pt
(rounded frame widths and advances) fits, in the paragraph and the
single-line composer. A one-word line that exactly filled its frame was
hyphenated and lost its last letters.

* Text: a word longer than the line breaks at the column edge

The paragraph composer gets emergency break points inside any word (or
part between hyphenation points) that is wider than the narrowest
measure even fully shrunk. They cost more than a hyphen, so a legal
hyphenation point wins where one fits; justified pieces are rated by the
space they leave, so each line takes as many letters as fit. Tracking is
part of the width.

Justified lines shrink no further than the paragraph's minimum word and
letter spacing and glyph scaling: an overfull line stays overfull
instead of overlapping its glyphs, and an overfull single word shrinks
within the letter and glyph limits.

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 04:18:49 -04:00
736acffe5f Desktop: pick the graphics backend before the window exists; DirectX 12 on Windows (#297)
* Desktop: pick the graphics backend before the window exists; DirectX 12 on Windows

On an AMD hybrid-graphics laptop (Radeon RX 6800M beside an integrated
Radeon, with two versions of amdvlk64.dll loaded into one process) the
app died a few seconds after launch with an access violation inside
AMD's Vulkan driver at the first swapchain present. Nothing reached the
log: a fault inside a driver is a native crash the engine's panic guard
never sees. wgpu's default backend set (PRIMARY | GL) enumerates Vulkan
first, so every start on that machine went down the same path, while
WGPU_BACKEND=dx12 and WGPU_BACKEND=gl ran fine and the headless CPU
renderer was never affected.

The desktop app now chooses the backend before creating the window
(apps/designcraft/src/gpu.rs): DirectX 12 first on Windows, then Vulkan,
then OpenGL; Vulkan then OpenGL on Linux; Metal on macOS. OpenGL stays
in the set as the in-process fallback when the chosen backend has no
adapter. WGPU_BACKEND still overrides everything and turns the fallback
off.

Start-up fallback: gpu.json in the settings directory records the
backend being tried until the third frame begins (the first has been
presented by then) or the app exits normally. A record left behind means
that backend never showed a frame, so the next start skips it, tries the
next candidate and says so in the status bar. The list starts over when
every candidate has failed or the app version changes;
DESIGNCRAFT_NO_PREFS records nothing. The adapter, backend and driver
version are logged at start-up for bug reports.

Verified on the affected machine: a default start runs on DirectX 12; a
simulated crashed DX12 start moved to Vulkan, which faulted for real,
and the start after that came up on OpenGL with the status line. Nine
unit tests cover the chooser and the record; cargo xtask ci passes.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* Merge origin/main; on Windows OpenGL isn't added to the DX12 or Vulkan instance (#167)

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Fable 5.1 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
2026-10-10 04:11:47 -04:00
aionggandBrandon Thomas 70974afc66 Lists: numbering formats and expressions from IDML; an empty paragraph gets no bullet or number (#298)
* Lists: numbering formats from IDML (A, B, C; i, ii, iii; 01, 02 …)

A paragraph's numbering format, number expression, bullet character and
text after the bullet import from IDML and export back. Numbered labels
come from the expression ("^#.^t", "Tabel ^#^t") with its
metacharacters expanded, in place of a fixed "n." label. Adds 001 and
0001 number styles, keeps letter and roman labels short for huge
numbers, and counts saturate instead of overflowing.

* Lists: an empty paragraph gets no bullet or number

A list paragraph with no characters before its return draws no label,
and numbering carries on past it: in a story's own list, in named lists
(also across stories) and in cross-reference paragraph numbers.

---------

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-10 04:08:56 -04:00
aionggandBrandon Thomas 0b69d6856c PDF: a placed image whose file is missing is reported as missing (#299)
A linked image that wasn't found on open has no data; the export warning said it could not be decoded. It now says it is missing and names the link.

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-10 04:01:49 -04:00
37803c8176 PDF: reuse image conversions and index page objects (#176)
Co-authored-by: skylar taylor-barrick <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 04:00:40 -04:00
1bccd0c9e4 Keep menus and dialogs usable at larger UI scales (#281)
* ci: simplify testkit dependency match for Clippy

* UI: keep menus and dialogs reachable at larger scales

* UI: share viewport-aware scrolling across menus

Incorporate @aiongg's shared menu-button, anchor-aware sizing, and
reopen-reset work from #279 (717ddb6), retaining #281's clipped-submenu
and scrollbar interaction fixes.

Use the wrapper for workspace, zoom, and panel menus as well as the
menu bar. Preserve checkbox dismissal and cover anchor placement,
scroll reset, and very short viewports with regression tests.

---------

Co-authored-by: skylartaylor <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 03:53:23 -04:00
b581031d91 IDML: resolve table styles and correct cell height fitting (#288)
* ci: simplify testkit dependency match for Clippy

* IDML: resolve table styles and shared cell edges

* Compose: size table cells by baseline and physical bounds

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 03:50:09 -04:00
d0d4812ada Set a text selection as warichu inside the parent line (#280)
* Set warichu as smaller lines inside the parent line.

Type › Warichu stacks the selected run after the line is broken, closes up the rest of the line, and round-trips the IDML warichu properties. The line break itself still sees the full-size run.

* Keep clicks, the caret, and Up/Down on each warichu row.

The glyphs were already stacked inside the parent line, but hit testing, the caret, and vertical movement still used that parent line. Break minimums, row widths, spacing, and tab leaders now follow the small lines, and underlines sit on the small baseline.

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 03:47:14 -04:00
74a81f9fb0 Harden temporary files and release inputs (#153)
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 03:46:19 -04:00
8146737e97 Text: a column, frame or page break ends its paragraph (#285)
A break character is stored as the last character of the paragraph it
ends, followed by the usual '\n' separator. The story model, paragraph
ranges, styles, editing and every exporter keep splitting paragraphs on
'\n' only, so the change stays in the places that create, lay out or
write break characters:

- IDML import writes the break and ends the paragraph, so the range that
  follows keeps its own paragraph style. NextOddPage and NextEvenPage map
  to new odd and even page break characters; IDML export writes a
  paragraph that ends in a break as that one Br with its
  ParagraphBreakType, so export and re-import give the same story.
- Word and RTF import, Type > Insert Break Character (with odd and even
  page breaks added) and typing end the paragraph after a break. Typing
  between a break and its return goes to the next paragraph; deleting
  either deletes both.
- Layout: a break that ends a paragraph leaves no empty line; the next
  paragraph starts in the next column, frame or page, as a paragraph start
  option would. Page breaks and Next Page start options move to the next
  frame on a later page; odd and even ones to the next page with that
  page number parity (no such frame: overset). A break inside a paragraph
  from an older document still moves the rest of that paragraph on.

Storing the break as a paragraph separator of its own would have changed
every place that counts or splits paragraphs on '\n'.

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 03:36:47 -04:00
a50d279cba test(datamerge): pin facing-page record placement (#198 no longer reproduces) (#258)
* test(datamerge): pin facing-page record placement (#198)

#198 reported that with `facingPages: true` the merged records after the
first landed at identical coordinates on one spread, leaving a later page
empty. That no longer reproduces: `data.merge` now builds a new document
rather than duplicating pages inside the template, and the three records
land on absolute pages 0, 1 and 2 with no overlap.

Nothing in the engine changed here. These two tests pin the behaviour the
issue asked for, both at the one-record-per-page plan and at the short
block that tiles, so a future refactor of `tile_records` can't quietly
bring the stacking back.

Refs #198

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>

* Merge origin/main; the facing-pages merge tests find each frame's page with page_of_item

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Codebuff <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 03:35:34 -04:00
9e4f69428f I18n italian (#218)
* i18n: add Italian (it) UI translation

Adds an Italian interface catalog (crates/ui-egui/src/i18n/it.rs) covering every
key of the shared table and of the Arabic table (1,430 strings), written from the
English source using ordinary Italian desktop-publishing vocabulary.

Registers "Italiano" in Edit > Interface Language, wires it into tr(), and extends
the i18n and menu tests (lookup, no duplicates, full key coverage, translated menu
titles, language switch frames).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* contributors: add emanuelebonaventura (display name insanityxoff)

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
2026-10-10 03:24:12 -04:00
56479fb9ea Text: a justified line with a tab justifies the text after its last tab (#284)
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 03:14:06 -04:00
a01b00da8f UI: closing a document with unsaved changes asks first (#141) (#146)
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) <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
2026-10-10 03:12:48 -04:00
99af9f513f Text: a line with a tab wraps at the column edge (#282)
* Footnotes: the first line wraps at the column edge after the number

Line breaking gave tabs no width: a tab only reached its stop when the
line was laid out, so a line holding a tab ran past the measure by the
tab's advance. A footnote whose separator is a tab overflowed its first
line by the gap between the number and the tab stop, and body lines
with tabs did the same.

The greedy breaker now asks for each tab's advance at its position on
the line, computed by the same tab_advance helper line layout uses.

* Lists: the first line after a bullet or number wraps at the column edge

In a hanging indent (a negative first-line indent), the tab after a
bullet or number went to the next default tab stop, past the left
indent: the first line's text started late and pushed the line past the
column edge.

The left indent is now a left tab stop of its own. A tab before it
reaches it unless an explicit stop comes first, so the first line's
text lines up with the lines below.

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 03:03:25 -04:00
60ad82497b Text frames: column rules on screen, in PDF and in IDML; Column Rules in Text Frame Options (#274)
* Text frames: column rules draw between columns on screen and in PDF

A frame with Column Rule on gets a bar centred in each gutter, inside the
insets, broken around paragraphs that span columns; right-to-left and
vertical frames included. object.textFrameOptions takes columnRuleWeight
and columnRuleColor (unknown swatches are an error) and an inset array
whose null sides stay; document.inspect reports the rule.

* Text Frame Options: Column Rules fields; OK changes only edited fields

The dialog shows the selected frame's options, with an inset field per
side, and sends only what the user changed, so unequal insets survive.
The rule weight is read in points.

* Column rules: tint, horizontal offset and top/bottom insets; IDML import and export

Text Frame Options gains Tint %, Top Inset, Bottom Inset and Horizontal
Offset for the rule, and object.textFrameOptions and document.inspect
take and report them. IDML reads the ColumnRule attributes of
TextFramePreference on frames and object styles and writes them when a
rule is set.

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 02:49:00 -04:00
Brandon ThomasandClaude Opus 5.5 1d996da82a i18n: one Tint: row per catalog (two PRs each added it) (#296)
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 02:38:46 -04:00
01500ceb04 Paragraph Rules: a dialog for the selected paragraphs and a section in Paragraph Style Options (#263)
* Paragraph rules: a partial ruleAbove/ruleBelow edit keeps the other rule fields; Rule Above honours Text width

* Paragraph Rules: dialog for the selected paragraphs and a Paragraph Style Options section

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 02:28:09 -04:00
464a442802 PDF/X-4: RGB images carry sRGB, no image interpolation, pdf:Trapped in XMP (#223)
- 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 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
2026-10-10 02:22:52 -04:00
e161878e4c Hyphenation: Spanish text is hyphenated with Javier Bezos's patterns (#222)
Text set in Spanish was not hyphenated at all. It now uses Javier
Bezos's Liang patterns, hyph-es.tex 5.0 from hyph-utf8 under MIT,
through the hypher crate 0.1.8 with only its alloc and spanish
features. The full feature, which embeds 48 languages under several
licences, stays off.

- hyphen::Lang names the hyphenator for a run: English, or a hypher
  language. Lang::for_language maps a language name or locale code.
- word_breaks_in and hyphen_points_in take the language. word_breaks
  and hyphen_points stay as the English entry points.
- Composition hyphenates a non-English run whose language has
  patterns and skips the rest. The word cache key includes the
  language.
- NOTICE carries the Bezos copyright line and the MIT text.

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
2026-10-10 02:21:25 -04:00
06b4e4e860 Add A6 sheet preset to the welcome page (#248)
Co-authored-by: moppeeer <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 02:18:47 -04:00
096ddf6085 Swatch menus: make the whole row (chip included) clickable and show chips in the Contextual Task Bar (#199)
Add one shared swatch_menu_row (full-width clickable chip + name, current
highlight, hover) and use it in the Properties Appearance/text menus, the
shared swatch_picker (Control bar, Stroke, Character, Paragraph Border/
Shading) and both Contextual Task Bar fill/stroke menus. The Task Bar popups
gain a colour chip per swatch, and every swatch menu now applies on a click
anywhere on the row, including the chip.

Cap the popups at 280 pt so a full-width row keeps the menus InDesign-sized.
Label the chip controls and rows for accessibility.

Test: clicking a swatch chip in the Appearance Fill menu applies it (the Task
Bar uses the same swatch_menu_row helper).

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 02:16:24 -04:00
617e487dff Styles: Character Style Options dialog (#144)
* Styles: editing a character style renames it everywhere it is used, changes its Based On and unsets attributes; [None] can't be edited

* 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…

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 02:14:30 -04:00
Brandon ThomasandClaude Opus 5.5 b9febf68c1 i18n: a new English string doesn't fail the Ukrainian catalog test (#294)
Untranslated strings fall back to English, as in every other language;
only stale Ukrainian entries fail. Otherwise every PR adding UI text
breaks the build until someone translates it.

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 02:13:04 -04:00
e585f9495d Preserve browser import intent and document targets (#119)
* #89: preserve browser import context and PDF options

* #91: reject PDF pages before narrowing on Wasm

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 02:02:57 -04:00
b61715889a UI: one searchable font menu everywhere; the Contextual Task Bar can be moved (#143)
* UI: every font field opens the same searchable font menu, with the search ready for typing and hidden system fonts left out

* 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

* Merge origin/main; one test_window harness with wheel, panel_rect and click_at

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 01:56:01 -04:00
Ikki NishiharaandClaude Sonnet 5.5 620a27d645 i18n: translate the whole interface into Japanese (#272)
Japanese covered menu titles, about 140 menu items and panel names. Of the
1,391 rows of the shared table, about 1,240 carried the English text in
their Japanese column, so every dialog, panel field, tooltip and status
line was still English.

Add i18n/ja.rs, a full catalog in the shape of ar.rs and pt_br.rs. It has a
row for every key of the shared table and of the Arabic and Brazilian
Portuguese tables (1,430 strings), keeps the existing Japanese menu words
unchanged, and adds 213 rows for labels the code passes to tr() but no table
had: command labels without their trailing ellipsis (the shortcut list), menu
paths (the menu list), special characters, colour-profile names and a few
field labels.

tr("ja", ..) looks in ja::TABLE first and falls back to the shared table's
Japanese column. The shared table is left as it is.

Co-authored-by: Claude Sonnet 5.5 <[email protected]>
2026-10-10 01:51:39 -04:00
e904bd6057 Add Ukrainian interface translation (#236)
* UK1: add Ukrainian interface translation

Translate existing interface catalogs and wire Ukrainian menu selection, contextual publishing labels and grammatical counters. Validate persisted selection, untouched document data, placeholder/key coverage and bundled Cyrillic glyphs.

Co-Authored-By: Codex <[email protected]>

* i18n uk: translate the open and place alert titles

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Codex <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 01:51:35 -04:00
Dylan M. Taylor c30340f60f chore: bump github actions to fix Node 20 deprecation warnings (#268) 2026-10-10 01:51:30 -04:00
aiongg e6b5a52a85 Text: the line before a page break is a paragraph's last line (#283)
A line ended by a column, frame or page break takes the paragraph's
last-line alignment, so a Left Justify paragraph sets it flush left.
A line ended by a forced line break stays justified.

The break that moves the following text to the next column or frame
is the line's last glyph, so a space after the break no longer keeps
the text in the same column.
2026-10-10 01:51:26 -04:00