Commit Graph
920 Commits
Author SHA1 Message Date
aiongg 9d8bdaef2c Merge wrap-placed-graphic: the CLI's performance document 2026-10-10 21:26:25 +00:00
aiongg d32134d337 Merge wrap-placed-graphic: text wrap set on a placed graphic wraps text
Conflicts: compose lib.rs imports (both; Composer unused), idml tests.rs (both test modules), ui-egui
panels/mod.rs (next's ruler origin with the branch's graphic wrap for Direct Selection).
2026-10-10 21:26:25 +00:00
aiongg 70a04944a5 Layout: the CLI's performance document builds its graphics with a wrap 2026-10-10 21:26:25 +00:00
aiongg 720165a709 Merge layers-drag: group members move with their group, no-op drops record nothing, locked layers need Cmd 2026-10-10 21:23:20 +00:00
aiongg 7f2b5cfa68 Layout: text wrap set on a placed graphic wraps text
A placed graphic carries its own text wrap (Graphic.wrap). IDML import
reads TextWrapPreference on the Image/PDF element and export writes it
back there. Layout treats it as an obstacle around the part of the
graphic its frame shows, plus the offsets, and also collects wraps of
items inside groups. object.textWrap acts on the graphic when the
selection is the frame's content, and the Text Wrap panel shows its wrap.
2026-10-10 21:23:00 +00:00
aiongg b84a58391f Layers: group members move with their group; no-op reorders leave history alone; Cmd for locked layers
- object.setLayer moves a group member's top-level group (and its members)
  and rejects an unknown or missing layer; object.reorder sets the layer on
  moved groups' members too.
- object.reorder returns without editing when the order and layers would
  not change, so no undo step is recorded.
- Layers panel: locked and hidden layers take object and selection drops
  only with Cmd/Ctrl held; the selection square drops the selected
  members' top-level groups.
2026-10-10 21:22:57 +00:00
aiongg c403cae4c7 Merge #83 update (feat/layout-parity f386a98d): Em Box Height first baseline offset
Conflicts: fonts fontdb.rs (the branch's em box with its from-BASE flag beside next's ICF cache); idml
tests.rs (the branch's copies of tests already on next, and the mojikumi test #129 replaced, dropped;
the Em Box Height test kept). German entry for Em Box Height.
2026-10-10 21:16:13 +00:00
aiongg f386a98d7c Layout: Em Box Height first baseline offset
The first baseline sits the em box top below the frame's top inset: the
font's BASE em box (idtp, else ideo + 1 em), else 0.88 em, for the
largest size on the first line. IDML FirstBaselineOffset="EmboxHeight"
imports and exports as itself; the footnote options list it and the
frame and footnote option commands take emboxHeight.
2026-10-10 21:04:33 +00:00
aiongg 754d09ac2d Merge table-cell-strokes: the style test names the [Black] swatch 2026-10-10 20:38:00 +00:00
aiongg 43d6a5264d Tables: the style test names the [Black] swatch, which style strokes now check 2026-10-10 20:37:55 +00:00
aiongg 16bb39e531 Merge gpu-startup: pick the GPU that drives the display, fall back across adapters and backends, and log startup errors (#273, #334) 2026-10-10 20:30:59 +00:00
aiongg 23da490a42 Merge table-cell-strokes: cell edge strokes in the Stroke panel and Cell Options, with gap colour and tint (#370)
Conflicts: ui-egui dialogs.rs (next's numberingSection arm and the branch's cellOptions arm; the branch's
copies of the dialog test helpers already on next dropped, its cell options test kept); idml tests.rs and
the i18n tables appended at the end on both sides (both kept; the branch's Gap Tint: and Tint: rows that
next already had dropped).
2026-10-10 20:30:54 +00:00
aiongg 279336c445 Startup: pick the GPU that drives the display, restart on the next adapter or backend, and log startup errors
Ported from VectorCraft's start-up fixes (#502, #545, #651, the display-GPU
choice, #806, #964) and reconciled with the backend fallback of #297:

- The app picks the window's adapter itself (eframe's native_adapter_selector,
  apps/designcraft/src/gpu.rs): adapters that can't present never, then
  WGPU_ADAPTER_NAME, hardware before software, native backends before OpenGL,
  the GPU that drives the primary display (Windows EnumDisplayDevices through
  winsafe, Linux /sys/class/drm), the power preference (WGPU_POWER_PREF;
  otherwise power saving on Windows and macOS, the system's order elsewhere),
  then DX12 before Vulkan. Every adapter and the choice are logged.
- wgpu's indirect-call validation is off (WGPU_VALIDATION_INDIRECT_CALL=1 turns
  it back on): its compute shader failed to compile on an Intel Mac and lost
  the Metal device (#334, first error).
- run_native runs under catch_unwind. A wgpu error or a panic in wgpu,
  egui-wgpu or naga during the first frames, or a first frame that doesn't
  reach the screen in 20 s, starts the app again without that adapter
  (DESIGNCRAFT_GPU_SKIP; exec on Unix, a new process on Windows). When no
  adapter of the start's backends is left, it starts again with the next
  backend. Each restart leaves out one more adapter or backend, so they end.
  Adapters without PCI ids (Metal) are keyed by name.
- The backend fallback (gpu/backend.rs, gpu.json) stays for driver faults that
  take the process down. Windows tries DirectX 12, then OpenGL, then Vulkan,
  each in its own instance: Vulkan last because creating its instance loads
  every installed Vulkan driver, and OpenGL kept out of the DX12 instance
  because creating it crashed in AMD's atio6axx.dll (#167).
- main returns an ExitCode and logs why the app stopped to standard error and
  the log file, which is all a Windows start without a console leaves (#273).

README, architecture, gaps, hardware and target-app parity docs and ROADMAP
describe the choice, the restarts and the variables.
2026-10-10 20:26:06 +00:00
aiongg cce6f986c5 Tables: cell and table style strokes take type, gap colour and gap tint
style.cell.create/edit stroke and style.table.create/edit border use the
table.setCell stroke parser: type, gapColor (alias gapSwatch) and gapTint, with
the same validation. A bad value is an error and changes nothing.
2026-10-10 20:25:40 +00:00
aiongg 57d9761c69 Tables: cell strokes in the Stroke panel and Cell Options › Strokes and Fills
With cells selected, or the text cursor in a cell, the Stroke panel edits the
cell edges chosen on a drawn edge proxy: weight, type, colour, tint, gap colour
and gap tint. A click toggles a line, a double-click chooses all outer or all
inner lines, a triple-click chooses or clears all. Table › Cell Options ›
Strokes and Fills… has the same proxy and fields plus the cell fill colour and
tint, and applies the changed values in one undoable step. Translations for
the new strings in every interface language.
2026-10-10 20:16:53 +00:00
aiongg c1c44a3a2a Integration: German entry for the Layers panel's selection square 2026-10-10 20:07:31 +00:00
aiongg e33cb52248 Merge layers-drag: drag layers and objects to reorder them in the Layers panel (#371) 2026-10-10 20:07:20 +00:00
aiongg 1b9125ceea Layers: drag layers and objects to reorder them
- object.reorder {ids, above|below: id | index, layer?}: restacks sibling
  objects in front of or behind a target, or at an index counted from the
  top of a layer, moving them to that layer. Group members stay in their
  group. Bad input is an error and leaves the document unchanged.
- layer.move rejects a missing id or an out-of-range index.
- Layers panel: drag a layer row to reorder layers, an object row to
  restack it or move it to another layer, and a filled selection square
  onto a layer to move the selection there; a line or box shows the drop.
  Groups expand to list their members. Layers list top (frontmost) first,
  matching the stacking order.
2026-10-10 20:06:34 +00:00
aiongg 20d4178c43 Tables: cell edge gap colour and tint, all stroke types on cell edges, inner horizontal/vertical edge choice
CellStroke and cell/table style edge attributes carry gap colour and gap tint
(IDML ...EdgeStrokeGapColor/GapTint, ...BorderStrokeGapColor/GapTint). The table
renderer and PDF export draw every stroke type on cell edges, with the gap colour
under dashes, dots and stripes.

table.setCell validates stroke values, takes gapColor/gapTint and an edges list
(top/left/bottom/right are the target range's outer sides; innerHorizontal and
innerVertical the lines between its rows and columns). table.getCellStroke reports
the shown values of the chosen edges, null where they differ.
2026-10-10 20:04:57 +00:00
aiongg 74eda5579f Merge canvas-context-menu: the menu reopens on a second right-click, blocks both panes, and follows menu hiding 2026-10-10 19:29:37 +00:00
aiongg 1fe1ec9cc6 UI: canvas context menu reopens on a second right-click; menu keeps both panes' input
- A right-click while the menu is open closes it as a click outside;
  the request now stays until a frame ends with the menu open, so the
  menu reopens at the new click with the new target selected.
- While a context menu is open in either split-view pane, neither pane
  takes pointer presses, guide drags or keys. The open state is read
  once per frame, so the Escape or click that closes pane 0's menu does
  not reach pane 1.
- Submenus copied from the menu bar (Arrange, Select, Fitting, Type
  submenus...) are keyed by their menu bar path, so rows hidden with
  Edit > Menus are hidden in the context menu too, with Show All Menu
  Items below.

Tests: reopening on another object, Escape in split view, Escape with a
non-selection tool and a selection, the Type tool's keep-or-place-caret
rule, and hidden rows in a context submenu.
2026-10-10 19:29:13 +00:00
aiongg d5fb84d5e1 Merge nobreak-justify: jidori runs keep their width; tests for nonbreaking-space lines 2026-10-10 19:22:40 +00:00
aiongg 0474a8b5ec Compose: jidori runs keep their width when their spaces could stretch; tests for nonbreaking-space lines 2026-10-10 19:22:22 +00:00
aiongg d7d6fa8302 Integration: German entry for Rotate 180° 2026-10-10 19:13:45 +00:00
aiongg 1553acb6be Merge canvas-context-menu: a context menu on the canvas for objects, text and the page
Conflicts in ui-egui canvas.rs (fmt_tick stays pub(crate) for the ruler zero point; the open menu also
blocks the zero point drag; #84's press counting keeps its clicks with the branch's in_view; the tool
keyboard return keeps keyboard_pane; tests: both) and menus.rs (shortcuts return while an IME composes or
a canvas context menu is open).
2026-10-10 19:13:41 +00:00
aiongg 1194119c3e UI: a context menu on the canvas for objects, text and the page
Right-clicking the canvas opens a menu built from the menu bar's rows
(labels, shortcuts, check marks, greyed entries, translations):

- On an object: the object is selected first unless it already is, as
  the Selection tool would. Undo/Redo, Cut/Copy/Paste/Paste Into/Paste
  in Place, Transform (rotate 90° both ways, 180°, flip), Arrange,
  Select, Group/Ungroup, Lock/Unlock All, Hide/Show All, Fitting for
  placed graphics, Content, Effects, and Fill with Placeholder Text and
  Text Frame Options for text frames.
- While typing: a click in a text frame places the caret there, except
  inside the selected text, which stays selected. Cut/Copy/Paste/Paste
  without Formatting, the Insert Special Character, White Space and
  Break Character submenus, Fill with Placeholder Text, Change Case,
  Hidden Characters, Story Editor, Select All.
- On the page or pasteboard: Undo/Redo, Paste, Paste in Place, zoom,
  Rulers, Grids & Guides, Select All.

Tools only see the primary button, so a right-click or right-drag
starts no gesture. While the menu is open, Escape and the click that
closes it go to the menu, not to the tools or shortcuts. Each split-view
pane has its own menu.
2026-10-10 19:11:39 +00:00
aiongg 5b060a8de2 Merge nobreak-justify: No Break runs and nonbreaking spaces stretch on justified lines (#368)
Conflicts in compose breaker.rs (box_elastic keeps next's mojikumi stretch and shrink and adds them to
the word-space elasticity of No Break spaces; space_elastic uses is_word_space) and lib.rs (word spacing
uses is_word_space). next's leftover-stretch sharing and space_overflow also use is_word_space, so U+00A0
shares the stretch past the maximum; breaker::elastic_space is gone.
2026-10-10 18:56:46 +00:00
aiongg 42cfc8b06c Compose: No Break runs and nonbreaking spaces stretch on justified lines (#368) 2026-10-10 18:54:54 +00:00
aiongg 6d30b39a41 Merge #369: copied objects paste on the spread in view
Conflict in DesignApp::run: the spread is added after the system clipboard step from #84.
2026-10-10 18:48:47 +00:00
aiongg 5fae1ac2cc Merge upstream main a127c6d: progress docs (#355); ROADMAP.md takes upstream's rewrite 2026-10-10 18:47:02 +00: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 <noreply@anthropic.com>
2026-10-10 14:09:17 -04:00
noah1400andClaude Opus 5.5 dfdc650318 Paste: copied objects land on the spread in view (#348)
Paste and Paste in Place always put the objects on the first spread,
whatever page was showing. They now go to the spread in view, or to
the one a caller names with `spread`. With facing pages the objects
keep their distance from the spine, so they stay on their side of the
spread, also on a first or last page that sits alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:08:43 +02:00
aiongg c234a61ed4 Merge feat/cjk-typography-ui: the CJK style dialog tests install the UI fonts 2026-10-10 17:06:21 +00:00
aiongg e2ffa89817 UI: the CJK style dialog tests draw with the UI fonts installed
The section headings use the semibold family, which a bare egui context has no fonts
for; the tests install the UI fonts as the other dialog tests do.
2026-10-10 17:06:21 +00:00
aiongg b7efb71c05 Merge feat/ruler-zero-point: the page-origin ruler test sets the page origin 2026-10-10 16:58:28 +00:00
aiongg d59687bf96 Rulers: the page-origin ruler test sets the page origin before it reads the pieces
The loop above ends on the spine origin, which has a single ruler piece.
2026-10-10 16:58:24 +00:00
aiongg 0cd3a309da Integration: German entries for the ruby, kenten, shatai and grid strings and the drop cap strings
feat/cjk-typography-ui adds shared-table keys without German rows in de.rs, and eight
drop cap keys had none before. The rows copy the shared table's German column. The
Japanese table's 'Select an object to color its fill or stroke.' gets a German row of its
own, so de.rs covers every key its header lists.
2026-10-10 16:53:49 +00:00
aiongg b3cb8ed570 Integration: one Em Box Top and Em Box Bottom row in the shared and Japanese tables
feat/cjk-typography-ui (grid reference choices) and feat/paragraph-settings-full (border
and shading offsets) both add these keys; the tables hold each key once. The paragraph
settings rows stay.
2026-10-10 16:53:49 +00:00
aiongg b98d54ee38 Integration: IDML import warns when it repairs a tab list
#343's TabStop::sanitized_list repairs a file's tab list (dropped, clamped, shortened or
duplicate stops, more than MAX_TAB_STOPS); fix/idml-missing-spread's import_idml_report
carries the import warnings. The import counts the lists it had to repair and reports
them in one warning; sorting and a char stop's default '.' don't count.
2026-10-10 16:48:01 +00:00
aiongg 2589699057 Integration: the em box top/bottom leading test sets the Japanese composer
feat/cjk-typography's cjk_aki_below_measures_em_tops_and_aki_above_em_bottoms used the
default composer. #330's line (5dc8383a) applies leading models only under the Japanese
composers, so the test sets one.
2026-10-10 16:45:56 +00:00
aiongg fb727aa1b9 Integration: #330's Aki Below test measures the em box tops
#330 wrote cjk_aki_below_sets_the_upper_lines_leading_below_it on a base where Aki
Below measured from the em box bottom. feat/cjk-typography measures it from the em box
top (as the corpus shows), so the test asserts that the lower line's em box top sits the
upper line's leading below the upper line's.
2026-10-10 16:45:36 +00:00
aiongg a4ac6b039c Merge #343 update: Tabs commands return the stored list; tab lists from files are sanitized
Conflict: crates/idml/src/tests.rs: both tests (next's CJK settings and ruby round trips, the branch's hostile tab list import)
2026-10-10 16:44:40 +00:00
aiongg ba46313fc7 Integration: a Jump Object wrap steps by the Aki Below advance
#330 advances a line under the Japanese composers' Aki Below model by the upper line's
leading; the Jump Object wrap from the layout-parity chain stepped by the lower line's
leading. Both now use the same advance.
2026-10-10 16:44:13 +00:00
aiongg c0dc1503c8 Integration: format the kenten character test (rustfmt) 2026-10-10 16:44:06 +00:00
aiongg 9f1f41fb7b Integration: the kenten character test reads the mark from the run style's kenten mark
#84's kenten_character_change_keeps_kerning read RunStyle::kenten_character, which
feat/cjk-typography replaces with kenten_mark (kind, character, size, placement). The
test reads the mark text from it; the unchanged character draws the default sesame dot.
2026-10-10 16:43:32 +00:00
aiongg 0f6f96ae6d Merge feat/cjk-typography-ui: ruby, kenten, shatai, auto tate-chu-yoko, rotate roman, roman word break, grid references and the CJK style dialog panes
Conflict: ROADMAP.md: next's FONT1-3 lines with the branch's CJK1 line
Conflict: docs/cjk.md: the branch's rows, with next's aki below and Japanese composer text kept in the leading model row
Conflict: crates/fonts/src/fontdb.rs: both fields (family/style aliases and the ICF cache)
Conflict: crates/compose/src/ruby.rs: the branch's ruby module; its PlacedGlyph (and kenten.rs's) carry next's rendered_char
Conflict: crates/compose/src/lib.rs: modules both; rtl and para_block both; the branch's grid placement (gyoudori, grid references) replaces next's snap, its paragraph-gyoudori span uses next's line_metrics with mark leading and #330's aki below advance; a wrap push moves the line by whole grid lines and its grid end with it; next's trimmed spaces and drop cap, then the branch's ruby and kenten annotation
Conflict: crates/compose/src/shape.rs: next's pieces loop and StyleRuns with the branch's auto tate-chu-yoko split (after marks join their base) and fit, and Upright mode in every shape_segment call; next's label fallback passes the mode on
Conflict: crates/compose/src/tests.rs: both sets of tests appended at the end
Conflict: crates/idml/src/export.rs: next's metadata and text variable writers, then the branch's CJK settings writers
Conflict: crates/idml/src/tests.rs: both tests
Conflict: crates/ui-egui/src/dialogs.rs: the branch's Ruby dialog default section (next's duplicate characterStyleOptions default dropped); next's section arms with the branch's style_cjk::section_ui
2026-10-10 16:42:01 +00:00
aiongg bac5b091ae Merge #330 update: under the Japanese composers a line sits the upper line's leading below it
Conflict: crates/compose/src/lib.rs: next's line_metrics (tops, mark leading, last_line_mark) and baseline grid snap with the branch's (reference, model) and aki_below advance passed to next_baseline; Above keeps next's span space after plus the branch's aki_below
2026-10-10 16:36:22 +00:00
aiongg 370084909e Integration: collapse the kinsoku set choice's nested if in style_cjk.rs
clippy -D warnings (collapsible_if) rejects feat/style-sections-cjk's nested if around serde_json::to_value in the Kinsoku Set menu on next; the condition and the let join in one let chain.
2026-10-10 16:33:32 +00:00
aiongg 3d3bed63cf Integration: drop blank lines at the end of ja.rs's table
The German entry commits a237e639 and 9cacd53d added no Japanese rows but left an empty line each before the table's end; cargo fmt removes them.
2026-10-10 16:33:32 +00:00
aiongg 9cacd53d1b Integration: German entries for the CJK style section strings
feat/style-sections-cjk adds shared-table keys for the CJK sections of Paragraph and Character Style Options. de.rs holds a row for every key of the shared table; the rows copy the shared table's German column. ja.rs has rows for them from the branch.
2026-10-10 16:31:54 +00:00