Commit Graph
1054 Commits
Author SHA1 Message Date
aiongg 9ed4aba3da Merge #456: Wrap saved story revision counters consistently
Conflict: crates/doc/src/edit.rs: next's thread_frames (append_story); every rev += 1 is rev.wrapping_add(1)
Conflict: crates/engine/src/cmd/fonts.rs: next's replace (the local-formatting pass moved out of this hunk)
Conflict: crates/engine/src/cmd/text.rs: next's format_chars (apply_char_attrs, typing format); rev += 1 is rev.wrapping_add(1)
2026-10-11 12:20:08 +00:00
aiongg d5a6eca36d Merge #427 update: Text: Optical kerning spaces pairs from their outlines
Conflict: Cargo.lock: next's (the PR's dependency is already on next)
Conflict: ROADMAP.md: next's (the PR's earlier head is already merged)
Conflict: docs/gaps.md: next's (the PR's earlier head is already merged)
Conflict: docs/typography-parity.md: next's, test count 170 -> 172 as in d0120bf6
Conflict: crates/compose/src/optical.rs: next's plus d0120bf6's kernable() dash and fullwidth exclusion and its unit test
Conflict: crates/compose/src/tests.rs: next's plus d0120bf6's optical_kerning_leaves_dash_pairs_alone
2026-10-11 12:19:35 +00:00
aiongg ec245dda53 Merge #394 update: UI: CJK interface text from installed fonts, on the Latin baseline 2026-10-11 12:19:17 +00:00
Abdelrehman Fouad d0120bf6a6 Text: Optical kerning leaves dashes and fullwidth forms alone
Two dashes end to end have no ink gap to measure, so -- and the double
em dash opened up by the loosen cap; Chinese sets —— as one continuous
rule. Fullwidth Latin (U+FF21–FF5A) was kernable through its Latin
script and moved CJK text off its em grid. Both are now excluded from
kernable(), with a unit test and a compose test that failed before.
2026-10-11 11:45:29 +02:00
Abdelrehman Fouad d02a220b4e Text: Optical kerning spaces pairs from their outlines
Optical kerning was treated exactly like Metrics: the font's kern pairs
and nothing else, so text set with Optical in InDesign came out wider,
rewrapped and went overset in tightly fitted frames.

Now the font's pairs are turned off for Optical text and each pair is
spaced from its outlines (crates/compose/src/optical.rs): every glyph's
ink is sampled into 28 bands across the em, the pair's mean and smallest
ink gap over the bands both glyphs ink are compared with the font's
stem-to-stem gap (the pair nn, or HH then oo), and the pair moves 60% of
the way towards it, keeping 30% of that gap and moving at most -0.12 or
+0.06 em. Latin, Greek and Cyrillic letters, digits and Western
punctuation take part; spaces, marks, missing glyphs, right-to-left
runs, upright and tate-chu-yoko glyphs and pairs across a fallback font
keep their font spacing. Profiles and references are cached, bounded.

Tests: AV, To and r. close up and nn stays; HH closes up under Optical
but not Metrics (fails on main); every change on a pangram stays within
the bounds and spaces keep their width; profile, gap, adjustment and
character-class unit tests.

Docs: typography parity Kerning row and remaining effort, gap 4,
ROADMAP milestone 6 and progress log.

Fixes #181.
2026-10-11 11:39:20 +02:00
sanyamsingh001andCodex f58c212289 QA1: wrap saved story revisions consistently
Co-Authored-By: Codex <codex@openai.com>
2026-10-11 16:30:58 +10:00
aiongg 7eb3b2ac5a Integration: the Tabs panel floats outside the docking layout
atomic-say brings upstream's shared panel docking (#409), which sends
float_panel and dock_panel through the docking registry; next's Tabs panel
is float-only and not registered there, so Type > Tabs failed with "panel
must name a registered panel". Float-only panels keep the legacy floating
list: they float and close without the registry, a customized layout
keeps them in that list, and dock::floating still draws them then.
2026-10-11 05:35:53 +00:00
aiongg e828cd6c13 Integration: indd-utils at 08c6b78b 2026-10-11 05:19:21 +00:00
aiongg 924a78a2dd Merge feat/layout-parity: Layout: only CJK fonts take the em box top from a one-em typo box
Conflict: crates/compose/src/tests.rs: next's baseline-fit and keep tests, then the branch's OS/2 offset doc and code-page constant
Conflict: crates/fonts/src/tests_vertical.rs: testing imports from both (with_base, with_code_pages)
2026-10-11 05:19:16 +00:00
aiongg c94d7ce8c8 Merge mojikumi-base: CJK: mojikumi tables based on Nothing and the one-em line-end set apply their rules
Conflict: crates/compose/src/tests.rs: next's tests, plus the branch's one-em line-end test after its neighbour
2026-10-11 05:18:58 +00:00
aiongg b77ed8c300 Merge fix/idml-interchange: PDF: Bounding Box crops are measured from the page's drawing, not a render 2026-10-11 05:18:36 +00:00
aiongg 0d05035e65 Integration: thread cursors have system cursors in the tool cursor table
tool-cursors moves the cursor-to-system-icon table into cursors.rs; next's
text threading adds Cursor::ThreadLink and Cursor::Unthread. They keep the
icons next gave them (alias, no-drop) under the loaded-text badge.
2026-10-11 05:18:36 +00:00
aiongg 7c6bb39656 Integration: close the live-corner test before the pen cursor tests
The tool-cursors merge (ba6fcbe2) appended the branch's pen cursor tests
after next's live_corner_diamonds_follow_a_rotated_frame without that
test's closing brace.
2026-10-11 05:18:07 +00:00
aiongg ba6fcbe26a Merge tool-cursors: Canvas: tools show their own cursors (Direct Selection, Pen states)
Conflict: crates/tools/src/tests.rs: both sides' tests (thread ports, pen add/delete)
Conflict: crates/ui-egui/src/canvas.rs: the branch's cursors::show for the icon, then next's loaded-text/thread badge; next's cursor_icon dropped for cursors::system
Conflict: crates/ui-egui/src/lib.rs: DesignApp fields from both sides
2026-10-11 05:17:24 +00:00
aiongg 2352a58b7d Merge tool-icons-swap: Tools panel: Selection is the filled arrow, Direct Selection the hollow one 2026-10-11 05:16:40 +00:00
aiongg 2fae23fcc6 Merge atomic-save: MCP: exported files replace the target atomically (#436)
Conflict: ROADMAP.md: both progress-log entries, next's newer ones first
Conflict: apps/designcraft/src/gpu.rs: next's side; the branch's test fix targets the startup record tests next replaced
Conflict: apps/designcraft/src/main.rs: next's gpu startup and macOS IME helper; config_dir() dropped for the branch's profile::config_dir(), recovery_dir from profile::recovery_dir()
Conflict: crates/ui-egui/src/canvas.rs: next's guard (menu_open, keyboard_pane already limits keys to the focused pane)
Conflict: crates/ui-egui/src/lib.rs: modules from both sides
2026-10-11 05:16:10 +00:00
aiongg 0437ab27f5 PDF: Bounding Box crops are measured from the page's drawing, not a render
Finding a placed PDF's Bounding Box crop rendered its page at 2048
pixels and took the bounds of the painted pixels. PDF export measures
every such crop that import left unmeasured, so exporting a 17-page
document that places one complex illustration took about 4 s longer
than before crops were supported. The box is now the union of the
page's painted paths (strokes included), glyphs and images, each cut
to its clip, collected by interpreting the page without rasterizing
it: the same box, exact instead of to the nearest pixel, in
milliseconds.

Test: content_crop_box_is_measured_without_rendering_the_page.
2026-10-11 04:56:32 +00:00
aiongg 45032b527f CJK: mojikumi tables based on Nothing and the one-em line-end set apply their rules 2026-10-11 04:38:04 +00:00
aiongg 58f0e8c9b7 Layout: only CJK fonts take the em box top from a one-em typo box
A font's em box is its BASE em box; else, for a CJK font (its font
menu group isn't Western: meta or name languages, CJK code pages, else
kana, Hangul or ideograph coverage), the OS/2 typo ascender when the
typo ascender and descender span exactly one em; else 0.5 em + cap
height / 2; else 0.88 em. Latin fonts with a one-em typo box, such as
DejaVu Serif, centre on the cap height.
2026-10-11 04:28:46 +00:00
aiongg 56c8ed9667 Canvas: tools show their own cursors (Direct Selection, Pen states)
Tool cursors are glyphs drawn in code (black ink, white halo): the filled
arrow for Selection, the hollow arrow for Direct Selection, the pen nib
with an add, delete, close or convert badge, and a curved double arrow
for rotating. Linux and Windows show them as OS cursor bitmaps,
rasterized per pixels-per-point and cached; macOS paints them at the
pointer, since it sizes cursor bitmaps in points; the web build keeps
the system cursors. Text, hand, zoom and resize cursors stay system
cursors.

The Pen tool reports what a click does: over the first anchor of the
path being drawn it closes the path, and between paths a click on a
selected path's anchor deletes it and a click on its outline adds one.
The Add, Delete and Convert Direction Point tools show the matching pen
badges.

The glyph geometry and the rasterizer are adapted from VectorCraft's
code-drawn tool cursors (same organisation, original work).

Fixes #438
2026-10-11 04:12:43 +00:00
aiongg 1a8df25fdd MCP: exported files replace the target atomically (#436) 2026-10-11 04:12:32 +00:00
aiongg 11f34a07f3 Files: saves and exports replace the file atomically
Documents, books, libraries, snippets, swatches and every export are
written to a temporary file beside the target, flushed, renamed over it
and the folder flushed, so a crash, kill or full disk mid-write leaves
the old file intact. Replaced files keep their permissions, symbolic
links keep pointing at their file, and Windows retries a rename that a
scanner briefly blocks.

Fixes #436
2026-10-11 04:01:22 +00:00
aiongg 63375bc84d Tools panel: Selection is the filled arrow, Direct Selection the hollow one
Fixes #437
2026-10-11 03:54:51 +00:00
aiongg 1e1b0bcce0 Merge #362 update: placed PDFs cropped to their bounding box render only when drawn or exported 2026-10-11 03:01:04 +00:00
aiongg 29d8d56e67 Integration: no live-corner widget while the text cursor is loaded (#186)
A press with the loaded text cursor places or threads text, so the selected frame's live-corner widget and diamonds are not drawn meanwhile (pointer and cursor already give the loaded cursor priority).
2026-10-11 03:00:26 +00:00
aiongg 54520e11e8 Merge canvas-scrollbars: UI: document views have scrollbars, and Alt pans while editing text
Conflicts:
- crates/ui-egui/src/canvas.rs (handle_input): the Alt-hand block comes
  first, then next's zero point and ruler guide drags, which keep their
  menu_open check. The Alt-hand press also waits while a canvas context
  menu is open, like the other canvas presses.
- docs/ui-parity.md: the status line names both live corners and the
  scrollbars.

show() reserves the 10 px strip on the right and bottom only, so the
rulers, the zero-point corner and guide drags keep their positions.
2026-10-11 02:55:53 +00:00
aiongg 7ad4558560 Integration: German row for the macOS app menu's Settings… (#397)
de.rs has a row for every key of the shared and other full tables; mac-app-menu added Settings… to those.
2026-10-11 02:54:44 +00:00
aiongg 7935c60617 Merge mac-app-menu: macOS: Settings… is in the app menu
No conflicts. native_menu.rs merged automatically: install() builds the
app menu from MAC_APP_MENU and the bar from native_menu_tree(), and
next's poll() rebuilds through install() when the language or Show CJK
Features changes, so a rebuilt menu has the same app menu and keeps
dc-settings in its items map.
2026-10-11 02:53:33 +00:00
aiongg 607b2f4a34 Merge live-corners: Tools: live corners round a frame's corners by dragging
Conflicts:
- crates/tools/src/lib.rs: Overlay keeps next's ContentGhost and adds
  LiveCornerWidget and LiveCornerDiamond.
- crates/tools/src/select.rs: the module doc keeps next's threading and
  content paragraphs and adds the live-corner one. Drag keeps Place and
  adds Corner; SelectionTool holds both `loaded` and `live`. pointer()
  first ends live-corner editing when the selection changed, then runs
  next's loaded-text-cursor and double-click handling. An empty-canvas
  press clears `live` and keeps next's `content: false` Pending. drag()
  handles Place and Corner. overlays() keeps next's Place marquee and
  content ghost and appends the widget or diamonds.
- crates/ui-egui/src/canvas.rs: the overlay pass keeps next's doc/layout
  for ContentGhost and next's Path drawing, and adds the two live-corner
  arms (the branch's no-op Path arm is dropped). The live-corner render
  test goes before next's fast_harness tests.
- crates/engine/src/cmd/object.rs: next's text_wrap stays; the branch's
  corner_options function follows it.
- crates/engine/src/tests.rs, crates/tools/src/tests.rs: both sides'
  tests kept.
- ROADMAP.md: the live corners progress-log line goes first; the status
  line names it too.

Action::Handled is matched in engine tooling.rs; no other crate matches
Action or Overlay exhaustively.
2026-10-11 02:50:45 +00:00
aiongg d6f7cb719f Merge mac-titlebar: macOS: the window buttons sit centred in the title bar; double-clicking the title bar zooms or minimizes, and dragging it moves the window
No conflicts. chrome.rs, lib.rs and the new title_bar.rs merged
automatically; app_bar keeps next's controls (welcome, menus, scrolling
bar) inside the branch's 28 pt macOS bar.
2026-10-11 02:47:43 +00:00
aiongg 0f3f4e0634 Import: placed PDFs cropped to their bounding box render only when drawn or exported
IDML import found every Bounding Box crop by rendering the PDF page at
2048 pixels, once per placement, so opening a 17-page document that
places one Illustrator file 16 times took about 54 s of CPU. Import now
measures the box only for a graphic without GraphicBounds; drawing finds
it through designcraft_render::shown_box (cached per file, page and crop)
and PDF export through with_pdf_boxes.
2026-10-11 02:46:07 +00:00
aiongg db2c6a1717 Integration: Find/Replace Font search test starts from a set Replace With family
next fills an empty Replace With family with the fallback family (Source Sans 3), so the field never shows the placeholder the font-search test looked for, and picking Source Sans 3 would change nothing. The test opens the dialog with Replace With set to the default family instead.
2026-10-11 02:46:02 +00:00
aiongg 94fa7dc2ad macOS: double-clicking the title bar zooms or minimizes, and dragging it moves the window
The window has a full-size content view, so every click in the title-bar
band reaches the app and AppKit neither drags nor zooms the window. An
empty part of the application bar now acts as the title bar:

- A double-click follows System Settings > Desktop & Dock > "Double-click
  a window's title bar to": zoom (the default), minimize or do nothing.
  The setting (AppleActionOnDoubleClick) is read once per run with
  `defaults read` on a background thread; until then, or if reading
  fails, the bar zooms.
- A drag moves the window (ViewportCommand::StartDrag).

The empty area senses clicks only and sits behind the bar's controls, so
Home, the menus, search, the workspace menu, Share and Discord keep their
clicks, and a drag that starts on one of them does not move the window.
2026-10-11 02:42:25 +00:00
aiongg eab0ac7f4b UI: document views have scrollbars, and Alt pans while editing text
Each document view (and each split pane) has thin, theme-coloured scrollbars on its right
and bottom edges, outside the rulers and the view. They span the pasteboard: the thumb is
the visible fraction, dragging it pans, clicking the track pages by one view, and they
follow panning and zooming by any other means. Rotated views scroll in screen axes.

With the Type tool editing text, Alt held at the press gives the Hand tool for that drag:
the cursor turns into the hand, the drag pans, and the caret stays. Alt pressed after a
text drag starts still copies the dropped text.
2026-10-11 02:40:00 +00:00
aiongg 60f2c597a5 macOS: Settings… is in the app menu
The macOS menu bar shows Settings… (app.preferences, Cmd+K) in the application menu after About and leaves it out of Edit. The app menu labels go through the interface language. Windows, Linux and the web keep Edit › Preferences….
2026-10-11 02:34:49 +00:00
aiongg ff34ad3d14 Integration: INDD import follows indd-utils main
The indd dependency tracks indd-utils' main branch; Cargo.lock pins 68308f6f021a58901e82db4659705b8b96939b3b (was 9cf456d9). The API is unchanged: designcraft-engine builds as it was.
2026-10-11 02:33:11 +00:00
aiongg f12b3443a0 Merge #427: Text: Optical kerning spaces pairs from their outlines
Conflicts:
- Cargo.lock: designcraft-compose depends on both unicode-script (#427)
  and unicode-segmentation (next).
- ROADMAP.md: the optical kerning progress-log line goes above next's two
  2026-10-11 lines. Status line keeps next's Last reviewed date and names
  both changes.
- docs/gaps.md, docs/typography-parity.md: same status-line merge.
2026-10-11 02:30:53 +00:00
aiongg edcb3d2036 Merge font-search: Return and Escape in the font menu stay in the menu, never reach the dialog under it
No conflicts; crates/ui-egui/src/panels/mod.rs merged automatically.
2026-10-11 02:25:28 +00:00
aiongg 4b3dca6fff Integration: fonts listed by the platform keep their name aliases (#362) 2026-10-11 02:21:18 +00:00
aiongg 5e51392ac4 Merge mac-font-activation: macOS fonts that font services and managers activate are listed (#261, #327)
Conflict in ROADMAP.md: kept both 2026-10-11 progress-log lines (New
Document from next, then the font activation line).
2026-10-11 02:20:28 +00:00
aiongg cffff885bb Integration: GREP lookbehind overlay test passes the drop-cap end (#425)
next's overlays() takes the drop cap's end as a fifth argument; the test from #316 calls it with 0, like the other overlay tests.
2026-10-11 02:20:00 +00:00
aiongg 037771057e Merge #316 update: GREP styles with a pattern that can't compile are rejected, and imports warn
Conflicts:
- engine cmd/file.rs, cmd/interchange.rs: kept next's file.open and
  file.openIdml docs (content detection, INDD conversion, package parts)
  and added "GREP styles whose pattern can't compile" to their warnings.
  open_idml keeps next's conversion and import-report warnings and appends
  grep_style_warnings after the font warnings.
- engine cmd/style.rs: style.paragraph.create/edit docs combine next's
  startAt and kinsoku notes with the grepStyles rule. create_para and
  edit_para keep next's set_json_over, continue_numbering and
  explicit_default_kinsoku, then call check_grep_styles. The branch's
  grep_style_error, check_grep_styles and grep_style_warnings follow
  explicit_default_kinsoku. Kept next's delete_tests and
  swatch_options_tests; grep_style_tests goes at the end of the file.
- ui-egui dialogs.rs: kept next's style_section_edit tests and added the
  branch's bad-GREP-pattern test.

confirm_paragraph_style_options, grep_error and the dialog status line
merged without conflicts; next had not changed the OK arm they replace.
2026-10-11 02:19:10 +00:00
aiongg 3bda745a52 Tools: live corners round a frame's corners by dragging
A selected, unlocked rectangle frame at least 40 px on screen shows the
yellow widget near its top-right corner. Clicking it shows a diamond at
each corner. Dragging a diamond along its edge sets the size of all four
corners as one undo step (Shift: that corner only), clamped to half the
shorter side. Alt-click steps that corner's shape; Shift+Alt-click steps
every corner. Escape, a click on empty canvas, another selection or
another tool ends corner editing. Positions follow the frame's transform.

object.cornerOptions takes corners: four {shape, size} entries. Tools
gain Action::Handled and the LiveCornerWidget/LiveCornerDiamond overlays,
drawn by the canvas in the live_corner theme token. Fixes #186.
2026-10-11 02:16:21 +00:00
aiongg cf0ff4a034 Merge group-nested-panic: Objects: group siblings inside a group; refuse mixed levels and spreads
Conflicts in crates/engine/src/cmd/object.rs:
- Helpers: kept next's opt_u64, siblings_mut(d, spread, parent), reorder,
  set_layer_deep and set_layer, then added the branch's GroupPlan,
  group_plan, GROUP_SAME_LEVEL and can_group. The branch's
  siblings_mut(d, &GroupPlan) is dropped; group() calls next's helper as
  siblings_mut(d, plan.spread, &plan.parent).
- Tests: kept next's reorder_tests and the other test modules next added,
  followed by the branch's group_tests module.

group() and ungroup() were unchanged on next, so group() is the branch's
version. cmd/mod.rs (CANT_GROUP_SELECTION, selection_reason) and lib.rs
(execute_unguarded) merged without conflicts.
2026-10-11 02:15:58 +00:00
aiongg c537f1ae46 UI: Return and Escape in the font menu stay in the menu, never reach the dialog under it
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 02:10:36 +00:00
aiongg aa6446336b Paragraph Style Options: after a later step fails, OK edits the style it created
Once style.paragraph.create (or edit with a rename) succeeds, the dialog
switches to editing that style, so when export tagging then fails the
dialog stays open with the error and a second OK edits the style instead
of creating another.
2026-10-11 01:58:37 +00:00
aiongg 80b9b1d037 macOS: the window buttons sit centred in the title bar
On macOS the application bar is the window's title bar. AppKit centres
the traffic lights in the standard 28 pt title-bar band and they cannot
be moved without unsafe code, so the bar is 28 pt there: the lights sit
vertically centred in it, and the bar's content starts after them with
the same 8 pt gap the lights keep from the top. The Home button shrinks
to 22 pt to keep a margin in the shorter bar.

The bar's frame margin leaves room for the panel separator, so the
content row is the full bar height and the title and controls share one
centre line on every platform.

Fixes #333.
2026-10-11 01:58:12 +00:00
aiongg ae50689e27 GREP: styles with a pattern that can't compile are rejected, and imports warn
style.paragraph.create, style.paragraph.edit and type.para return the
compile error (bad GREP: ...) for a GREP style pattern that can't compile
and change nothing. Paragraph Style Options shows the error under the
pattern as it is typed, and on OK keeps the dialog open on the GREP Style
section with the command's error.

Opening an IDML or native document keeps such a style and reports a
warning naming the style (or paragraph) and the pattern; composition
keeps skipping it. Adds a lookbehind GREP style test.
2026-10-11 01:51:51 +00:00
aiongg 833c0ec8d6 Fonts: macOS fonts that font services and managers activate are listed
The system font scan also reads the font files CoreText has available
(core-text's collection of all available fonts, safe API), so fonts that
Adobe Fonts, RightFont and other font managers register from their own
folders are cataloged. Files the font folders already hold are read once;
paths that lead to no file are skipped. FontDb::global serves the desktop
app and designcraft-cli alike; the scan stays in the background.

Fixes #261, #327.
2026-10-11 01:50:32 +00:00
aiongg 21a230e426 Merge pdf-effects-wrap: PDF: items with soft effects keep their text wrap 2026-10-11 01:47:49 +00:00