68 Commits
Author SHA1 Message Date
be7474566b Layout: blank pages put sections on the right side, and paragraph spacing is the larger of after and before (#733, rebased) (#964)
Rebased #733 (paragraph spacing larger of after and before; blank pages for odd/even section starts) onto main.

Co-authored-by: Naxvog <4963183+naxvog@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 11:18:57 -04:00
Brandon ThomasandClaude Opus 5.5 7dfa6b0ee2 Release 0.6.1: rebuild with the fixed shared release workflow
The previous release build failed in craft-common-actions, not in this
repo: riscv64 packaging got --formats deb,rpm,tar (fixed in
craft-common-actions#3), Linux jobs lacked the ALSA headers
(craft-common-actions#5), and Windows signing needed the AZURE_* secrets
listed in the caller.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:11:12 -04:00
Brandon ThomasandClaude Opus 5.5 3e7f355e5a Ctrl+F searches in the Navigation pane; one Find and Replace dialog (#712) (#921)
* Find, Replace and Go To work like Word (#712)

Before: Ctrl+F opened both the Navigation pane and a Find window,
Advanced Find opened the same window, Ctrl+H failed with nothing to
replace, and Go To was a separate little window that only took a page.

After, as in Word:
- Ctrl+F and Home > Find open only the Navigation pane, with its search
  box focused so typing searches; selected text is searched for.
- Find, Replace and Go To are one Find and Replace dialog with three
  tabs: Advanced Find opens Find, Ctrl+H (edit.findReplace, which never
  marks the document edited) opens Replace, Ctrl+G / F5 open Go To.
- Go To moves to a page (a number, or +2 / -1 from the page the caret
  is on; empty, Next and Previous step a page) or picks a bookmark.
  edit.goto takes pageBy for scripts and agents.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Go To: list bookmarks only while Bookmark is chosen (#712)

Finding a document's bookmarks walks every paragraph; the Go To tab did it every frame even
while it showed pages.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 10:45:23 -04:00
Brandon ThomasandClaude Opus 5.5 03274b3b4f Alt+Q opens Search like Word (#819) (#919)
* Search: Alt+Q opens it from anywhere, like Word (#819)

- Alt+Q (and Alt, then Q on the keytips) opens the Search box with its
  text field focused, from the page or anywhere else; the key's own "q"
  is never typed into the box or the document. The title-bar box shows
  its Q keytip and its shortcut in the tooltip.
- An empty box suggests common commands; every word of a query has to
  match (so "insert table" finds Insert > Tables > Table, as the box's
  own hint promises); Enter runs the top match; "Find ... in the
  document" opens the Navigation pane on the results. Matching is
  about 2x faster than before.
- tools.search lists matching commands for scripts and agents.

Observed in Word for Windows (Microsoft 365): Alt+Q and Alt, Q focus
Search with nothing typed; multi-word queries match commands; the list
also offers finding the words in the document.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Search: Bulgarian, Czech, German, French, Hungarian, Italian and Polish strings

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 10:38:53 -04:00
Brandon ThomasandClaude Opus 5.5 994162fdff Shift+F10 opens the page's context menu at the caret (#821) (#920)
The right-click menu could only be opened with the mouse. Shift+F10,
Windows' keyboard way to the context menu (the Menu key's equivalent),
now opens the same menu just below the caret, with the spelling
suggestions and synonyms for the word there; Escape closes it.
ui.contextMenu opens it for scripts and agents.

The Menu key itself still does nothing: egui-winit drops that key
before the app sees it, so it needs an upstream change (or a winit
hook) to reach the same path.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 10:28:22 -04:00
Brandon ThomasandClaude Opus 5.5 d863811511 No automatic spacing between list items (#924)
* R17: no automatic paragraph spacing between list items

Word drops automatic (HTML-style) space before and after between two list
items, of one list or two, at any level; the first item keeps its space
before and the last its space after, and measured spacing stays. Measured in
Word for Windows: 16 synthetic cases. NIH's bulleted IND report items now
touch as in Word.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* ROADMAP: keep the progress log separator under its header

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 10:22:53 -04:00
Brandon ThomasandClaude Opus 5.5 681a57fc1a Rendering: kerning and automatic paragraph spacing like Word (#923)
Found by rendering 12 US government Word documents (NIH, EPA, MCC) in
Word for Windows and WordCraft and comparing the pages side by side,
then pinning each rule down with synthetic documents measured in Word.

- Kerning: the shaper kerned every run, so WordCraft's lines held more
  text than Word's and broke differently. Word kerns only a run with
  Kerning for fonts (w:kern) at or above that size; no w:kern and
  w:kern="0" are unkerned. New documents keep kerning from 1 pt, like
  Word's Normal template.
- Automatic paragraph spacing (w:beforeAutospacing/w:afterAutospacing,
  common in text pasted from web pages) was ignored. It is 14 pt at
  any size, none before the document's or a cell's first paragraph,
  none after a cell's last, and the larger of two neighbouring spaces
  counts. Read, written back, and settable through para.spacing
  (beforeAuto/afterAuto); a number, Ctrl+0 and Remove Space After
  replace it as in Word.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 10:18:05 -04:00
Brandon ThomasandClaude Opus 5.5 3ede32e80e Space before at the top of a page like Word (#932)
* Layout: space before at the top of a page after page breaks, as in Word

Found comparing Microsoft's specification documents with Word for
Windows, then measured with 32 synthetic documents in compatibility
modes 12 (Word 2007) and 15 (Word 2013+):

- A paragraph that ends with a page break keeps its mark with the
  break: WordCraft put an empty line at the top of the next page.
- The paragraph after it, first on the new page, gets no space before.
- A paragraph that starts with a page break starts on the new page
  with its space before there (WordCraft put the space on the old page).
- A page break before keeps the space before in modes up to 14 and
  drops it in mode 15.
- Natural breaks, section breaks and the first page were already right.

All 32 cases now land where Word puts them (within 0.3 pt), and the
caret still reaches a paragraph mark kept with its break.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Layout: after a page break before or a section break, the space after above counts

Measured in Word for Windows (10 more cases): at the top of a page after a page break before
(modes up to 14) or a next-page section break, the previous paragraph's space after comes off the
space before (24 pt before after 10 pt after leaves 14 pt; after 30 pt leaves none). This put the
numbered Heading 1s of Microsoft's specification documents 9 pt too low. All 42 page-top cases now
match Word.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 10:12:54 -04:00
Brandon ThomasandClaude Opus 5.5 97ade895d7 Layout: text after a column break takes its new column's width (#934)
Found in NIH's SARP form compared with Word for Windows: the form's
second section has a narrow label column (67 pt) and a wide text column
(438 pt), and each answer starts with a column break into the wide one.

Before: the paragraph's lines were broken once, for the column it
started in, so the text after the break ran a word or two wide down the
wide column. After: when a column break lands the rest of a paragraph
in a column of another width, that rest is broken again for it, as in
Word. Equal columns are unaffected (nothing is laid out twice).

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 10:09:42 -04:00
Brandon ThomasandClaude Opus 5.5 672522b2ed R22: paragraph-relative floats start above the space before (#941)
Word positions a floating object "relative to paragraph" (and to its line)
from the paragraph's start above its space before, below the previous
paragraph's space after. WordCraft measured from the first line, so floats
in spaced paragraphs sat lower by the space before. Measured in Word for
Windows: 10 synthetic cases.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 10:05:48 -04:00
Brandon ThomasandClaude Opus 5.5 5383d80f75 An empty continuous section mark takes no room (#940)
* R21: an empty continuous section mark takes no room

An empty paragraph that only carries a section break before a continuous
section takes no room in Word: the text after it follows right on, and
columns balanced above it don't count it. A break on a paragraph with text
keeps its line. Measured in Word for Windows: 10 synthetic cases. The SARP
form's fields drifted a line lower at each of its continuous breaks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* ROADMAP: keep the progress log separator under its header

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 10:02:07 -04:00
Brandon ThomasandClaude Opus 5.5 fc52079f58 R19: keep with next splits a paragraph that doesn't fit anyway (#933)
Word moves a "keep with next" paragraph to the next page only when it would
fit on this one by itself; one that doesn't fit anyway breaks as usual, its
last lines going over with the next paragraph (with keep lines together it
moves whole). A chain of kept paragraphs moves together. Measured in Word
for Windows: 20 synthetic cases. NIH's five-line kept paragraph no longer
leaves a third of a page empty.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 09:58:26 -04:00
Brandon ThomasandClaude Opus 5.5 7d37b14679 R16: list indents and style indents combine like Word (#927)
A numbered paragraph style's own indents (and those of styles based on it)
now beat the list level's, its base styles' don't, and the paragraph's own
list beats every style. A paragraph that switches its list off (numId 0)
drops the styles' indents, as Word does; the first Backspace in a List
Paragraph item makes the item's indent the paragraph's own so the text stays
put. Measured in Word for Windows: 27 synthetic cases, all match. NIH's
Heading 3s sit at their 0.9" and its "sample text" paragraphs at the margin.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 09:55:41 -04:00
Brandon ThomasandClaude Opus 5.5 5aa8151a53 Text frames in headers, footers and cells, kept on save (#935)
Found in NIH's monitoring-plan template compared with Word for Windows:
its footer puts the page number in a text frame (w:framePr: centred,
text wrapping around it), and Word draws "NCCIH DSMP Template" ending
just left of the number on the same line.

Before: frames other than drop caps were ignored on import, so the
number took a line of its own above the footer text (making every page
a line shorter), and the frame was lost on save.

After: a paragraph's text frame is read (size, anchors, alignment,
offsets, wrap, distances) and written back. In headers, footers, table
cells and text boxes it is placed like a floating object, sized to its
text unless it has a width, and the text after it wraps around it; the
DSMP footer now matches Word's. Frames in the body text still lay out
in line.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 09:52:26 -04:00
Brandon ThomasandClaude Opus 5.5 74548cff57 layout tests: iterate the cells in a_hidden_cell_mark_takes_no_room (clippy) (#948)
clippy::needless_range_loop failed cargo clippy --all-targets -D warnings on
main after #939.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:52:20 -04:00
Brandon ThomasandClaude Opus 5.5 9f0776a70b ROADMAP: progress log separator back under the header row (#947)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 09:47:00 -04:00
Brandon ThomasandClaude Opus 5.5 541e10e23a R20: hidden end-of-cell marks (w:hideMark) (#939)
Read and write w:hideMark on table cells, and lay it out like Word: a cell's
empty last paragraph takes no room, spacing and all, so a row of only such
empty cells is just its borders; an ordinary empty cell keeps the row a line
tall. Measured in Word for Windows: 9 synthetic cases. NIH's Table A no
longer shows a blank row.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 09:45:40 -04:00
Brandon ThomasandClaude Opus 5.5 2e66cbb261 R18: a list label adds ascent, not descent, to its line (#928)
Word sizes a list item's first line with the label's ascent when it is
taller, but keeps the text's descent below the baseline: a Symbol bullet's
deeper descent (and a Courier New label's) doesn't count. Measured in Word
for Windows: 6 synthetic cases. NIH's bulleted items were a fifth of a point
taller each, enough to push lines over a page bottom.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 09:41:37 -04:00
Brandon ThomasandClaude Opus 5.5 99561097d5 DOCX: a list level without a list takes its style chain's list (#926)
Found in NIH's monitoring-plan template, compared with Word for Windows.

Before: a w:numPr that gives a level (w:ilvl) but no list (w:numId) was
dropped. Outline-numbered templates do this all the time: Heading 3 is
based on Heading 2 (which names the list) and only says "level 3", so
every Heading 3 numbered as a Heading 2 (6.15 where Word shows 6.4.5).

After: such a level takes the list of the nearest style in its based-on
chain that names one (ECMA-376 17.3.1.19), in styles and in paragraphs;
with no list anywhere in the chain there is no numbering. Saved files
carry an ordinary list reference.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 09:37:38 -04:00
Brandon ThomasandClaude Opus 5.5 906e0f8aca .doc import: tab stops, and sections that end at their section marks (#925)
Found by comparing EPA's .doc templates with Word for Windows.

Before: custom tab stops in .doc files were skipped, so centred titles,
right-aligned dates and dot leaders set with tabs lost their alignment;
and a section mark (0x0C) at a section's end became a page break inside
a paragraph, so the file's sections (continuous breaks, footers) were
lost and every section break started a new page.

After: sprmPChgTabsPapx and sprmPChgTabs are read in styles and
paragraphs (removed stops clear inherited ones; each added stop keeps
its alignment and leader), and sprmPChgTabs' one-byte size is decoded
per [MS-DOC] (it was read as two bytes, swallowing the sprms after it).
A section mark at a section's end now ends that section's last
paragraph, so the section's properties and footers are kept. Both EPA
.doc templates in the comparison set now have Word's page count.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 09:34:10 -04:00
Brandon ThomasandClaude Opus 5.5 64f656a8bc Layout: spaces and tabs don't make a line taller, as in Word (#931)
Found comparing Microsoft's own specification documents with Word for
Windows: their tables of contents were a third taller in WordCraft
(13.4 pt per entry against Word's 10.9). Each entry's 9 pt text has tab
runs in an 11 pt font, and WordCraft sized the line by them.

Measured in Word with 13 synthetic documents: 11-20 pt tabs, spaces and
ideographic spaces next to 9 pt text leave the 9 pt line as it is,
wherever they are on the line; a line of only such whitespace takes the
paragraph mark's height; a big letter or no-break space does grow the
line. WordCraft now skips spaces and tabs when it sizes a line, and all
13 cases match.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 09:30:34 -04:00
Brandon ThomasandClaude Opus 5.5 26cdcfd4c6 DOCX: table-of-contents links look like Word's (#930)
Found comparing Microsoft's own specification documents with Word for
Windows: their table of contents was blue and underlined in WordCraft
and plain in Word.

Measured with nine synthetic documents in Word: a run with the
Hyperlink character style, inside a hyperlink, inside a TOC field's
result, is drawn plain (with or without the TOC's \h switch). A
Hyperlink-styled run outside a hyperlink, another character style,
direct formatting, or a hyperlink in another field keep their look.

The import now drops the Hyperlink style from exactly those runs (the
links stay), so all nine cases render as Word does.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 09:26:56 -04:00
Brandon ThomasandClaude Opus 5.5 90515abd1e Keytips: Word for Windows' tab letters (#922)
Observed in Word for Windows (Microsoft 365): pressing Alt badges the
tabs File F, Home H, Insert N, Design G, Layout P, References S,
Mailings M, Review R, View W, Help Y, and the Search box Q.

Before, WordCraft used Design L, References R, Review S and View V,
and File and Help had no letter, so Alt, W or Alt, R went to the wrong
tab for anyone used to Word. Now the letters match; Alt, F opens the
Backstage and ends keytips.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 09:23:21 -04:00
Brandon ThomasandClaude Opus 5.5 912d49f38f PDF export: look internal links up in a map, not the whole document (#937)
Found while timing US government documents against Word for Windows.

Before: every internal link (#bookmark: a linked table of contents,
cross-references) found its bookmark by walking every paragraph of the
document, so export time grew with links x length: NIH's 67-page
protocol template (297 links) took 10.6 s to export as PDF.

After: the bookmark positions are gathered once per export (the first
bookmark of a name still wins). The same file exports in 0.9 s with
identical text and links. internal_links_export_in_linear_time guards
it (the old code took 12x as long for 4x the document).

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 09:19:41 -04:00
Brandon ThomasandClaude Opus 5.5 b7aa7639a9 Release prep: 0.6.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:44:42 -04:00
Brandon ThomasandClaude Opus 5.5 956963e9d9 ui-egui: fix Windows clippy in desktop_theme (#872)
Bus is a unit struct off Linux, so Bus::default() tripped
clippy::default_constructed_unit_structs on Windows and macOS. Each
platform's Bus now has a plain constructor.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:36:02 -04:00
Brandon ThomasandClaude Opus 5.5 715f85c612 Tests: linear-time checks measure CPU time, not wall clock under load (#850)
The five tests that assert linear layout/export/hyphenation by comparing
the cost of an n-sized and a larger input (layout: unequal formats, hidden
separators with auto hyphenation, hidden float anchors; pdf: fragmented
hidden headings; proof: hyphenating a long word) timed both with wall
clock, fastest of three. On a loaded machine (CI, shared dev boxes, the
test harness running other tests in parallel) a preempted big run, timer
resolution on the small run, or a tight 4x/8.0 limit (hyphenation measured
~7.2 when linear) made them fail now and then.

Each now uses a shared cost_ratio helper (test-only, no production change):
- CPU time of the test thread from /proc/thread-self/schedstat on Linux, so
  time spent waiting for a CPU is left out (wall clock elsewhere);
- the small input runs `scale` times per timing so both timings are long
  against the clock's resolution;
- small and big runs alternate, five rounds after a warm-up, cheapest kept;
- 8x the input everywhere: linear ~8x, quadratic ~64x, limit 24x.

Measured under load (load avg ~15-35 on 32 cores): 60 linear ratios in
7.7-8.8. With the old quadratic code injected locally each test fails:
unequal formats (uncapped style bucket scan) ~90-104x, hidden separators
(hyphenator rescan per point) ~36x, float anchors (run walk restarted per
anchor) ~188x, PDF headings (left-out range scan per char) ~37x,
hyphenation 50-65x.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 04:37:03 -04:00
Brandon ThomasandClaude Opus 5.5 19684cff26 layout: make Myanmar fallback tests deterministic (#807)
tests_complex::myanmar_syllables_are_single_clusters and
myanmar_in_a_latin_font_falls_back_to_a_regular_myanmar_face failed about
1 run in 4 of the full layout suite (cluster starts [0, 3, 6, 12, 18], "one
face for the word") but passed alone.

Root cause: a race in FontDb's system font fallback, not the tests. Tests
share FontDb::global(), and layout asks for a fallback per character from
several threads. A search marks a preferred family ("Padauk" for Myanmar) as
tried before loading it; a second search racing it skipped the family as
already tried, fell through to the coverage index, and loaded the first
cataloged file in path order that maps the character (Padauk-Bold.ttf on
its own). Characters of one syllable then fell back to different faces, so
the shaper split the syllable and the word's faces differed. Separately, a
search's answer was cached under the face count read after the search, so
an answer chosen before another thread's load could be cached as current.

Fix (crates/fonts):
- System fallback searches run one at a time (a `searching` lock). A search
  that waited re-checks the loaded faces first, so it uses the family the
  previous search loaded instead of loading another file.
- The fallback cache keys an answer by the face count it was chosen from,
  and doesn't cache an answer from before a concurrent load.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 00:29:58 -04:00
Brandon ThomasandClaude Opus 5.5 46e694f5f5 Release workflow: pass the Azure signing secrets so Windows builds are signed (#752)
Since the release workflow moved to storytold/craft-common-actions, it has
passed only the Apple and WINDOWS_CERTIFICATE secrets. The signing job reads
values from the release environment, but only for names the caller lists, so
the AZURE_* names that Trusted Signing uses came through empty and Windows
builds would ship unsigned. Same fix as storytold/craft-launcher#174.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 00:27:33 -04:00
Brandon ThomasandClaude Opus 5.5 7d1f6a07c1 docx: fix clippy large_enum_variant on main (#779)
A field's buffered result items held an InlineObject inline, which grew past
clippy's large_enum_variant limit with today's object changes. Box it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:01:43 -04:00
Brandon ThomasandClaude Opus 5.5 8fb782ea02 CI: run FreeBSD and Windows ARM64 checks nightly and on release, not per PR (#530)
They ran on every push to main and on any PR touching Cargo.toml/Cargo.lock,
which is nearly every PR. See craftrules release/playbook.md §2.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:34:27 -04:00
Brandon ThomasandClaude Opus 5.5 e4b7d155f1 Bump version to 0.5.0; add Linux riscv64 release build (#394)
- release.yml: new `linux-riscv64` job ported from GridCraft. Cross-compiles
  wordcraft and wordcraft-cli on ubuntu-24.04 with riscv64 libs from
  ports.ubuntu.com (riscv64 variants of the Linux job's -dev packages),
  packages a tar.gz, and smoke-tests the CLI under QEMU. The draft-release
  job needs it, so its tar.gz is uploaded and covered by SHA256SUMS.
- packaging/linux/package.sh: CROSS_ARCH/CROSS_TARGET/CROSS_COMPILE/EMULATOR
  support (as in GridCraft).
- README and docs/releasing.md list the riscv64 tarball.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:41:30 -04:00
Brandon ThomasandClaude Opus 5.5 2545b170ba Tests: the equation-wrap test accepts plain y where no math font is installed (FreeBSD CI) (#348)
The first glyph is math italic 𝑦 only when a font has it; FreeBSD CI has none, so
it shapes as plain y. The test is about line breaks and indents, not the glyph.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 17:04:08 -04:00
29e978d6d8 Re-land #264, #263, #270, #271, #267, #280: clipboard pane, table styles, Manage Styles, Asian Typography, Group, Shape Effects (#323)
The original squash merges of these six PRs were empty. Integrated and tested together on current main.

Co-authored-by: Valentin Klein <vangel1976@googlemail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:09:04 -04:00
Brandon ThomasandClaude Opus 5.5 6b40846789 i18n: translate the remaining untranslated interface strings; test the font fallback cache with legacy names (#276)
* i18n: translate the remaining untranslated interface strings; test the font fallback cache with legacy family names (#233, #239)

Follow-ups from integrating #233 and #239: every tl! string now has a row in every
catalog, and a test pins that loading a face by its legacy family name drops the
fallback cache.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* i18n: Serbian rows for the newly translated strings

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:31:59 -04:00
Brandon ThomasandClaude Opus 5.5 eb31860917 Progress docs: target-app parity, gaps, alpha gate, readiness by audience (#277)
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, layout, hardware, UI and localization parity docs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:11:53 -04:00
Brandon ThomasandClaude Opus 5.5 ed5a29f1b9 Fonts: load an installed family's faces all at once, one load at a time
load_cataloged added a family's files one by one, so a lookup on another thread could find the family half-loaded, resolve the closest style it saw (say Bold for Regular) and word::resolve cached that face for the rest of the process. Faces from a family's files are now added under one write lock, family loads are serialised (re-checking once the lock is held), and add_font no longer checks and inserts under separate locks.

This made layout tests flaky under parallel test threads (space_shrinking_is_only_for_justified_text failed 4/20 runs on main; 0/40 with this change). In the app it could give a document opened during the first font lookups a wrong face.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 08:09:52 -04:00
Brandon ThomasandClaude Opus 5.5 d47f2f47fe Tests: the PDF text extractor counts a synthetic-bold glyph once
Synthetic bold fills and then strokes each glyph, and the test helper appended text on both draws, so bold text read "bboolldd". Whether a run gets synthetic bold depended on font resolution under parallel tests, which made exports_pages_text_and_structure flaky (3/40 runs once #133's tests were added). Regression test: synthetic_bold_text_is_extracted_once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:43:43 -04:00
Brandon ThomasandClaude Opus 5.5 bbe89c2d86 i18n: pt-BR strings for the dark page option and the link scheme message
#128's coverage test requires every English string; #72 and #194 added three in the same batch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:17:59 -04:00
Brandon ThomasandClaude Opus 5.5 464557ada7 Format the desktop app's imports with rustfmt (follow-up to #76)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:15:37 -04:00
Brandon ThomasandClaude Opus 5.5 869ac16110 Typing parity with Word: lists, AutoFormat and AutoCorrect as you type (#204)
* Typing parity with Word: lists, AutoFormat and AutoCorrect as you type

Typed side by side with Word (same keystrokes, results read back) and matched it:

- Enter after a list no longer gets stuck: the paragraph that leaves a list goes back to
  Normal, and later Enters make blank paragraphs (the `num: 0` marker counted as a list item,
  so every Enter re-ran "leave the list" and did nothing; the list indent also stuck to every
  following paragraph).
- Enter on an empty nested item moves it up a level before leaving the list.
- Second Backspace at the start of an un-numbered list paragraph returns it to Normal
  instead of joining it to the paragraph above.
- "1. " always starts numbering at 1; "a. " and "I. " start lettered/roman lists; "- " makes
  a dash bullet.
- Undo right after an AutoFormat/AutoCorrect change undoes only that change.
- AutoCorrect: sentence capitals (with abbreviation/initial exceptions), runs on Enter too,
  "word -- word" en dash, "wait..." ellipsis, 1/2 1/4 3/4, ordinal superscripts, web
  addresses become links.
- Border lines: Enter after ---, ===, ***, ___, ~~~, ### draws a bottom border.
- Layout: no line break right after a slash ("and/or", URLs), as in Word.
- ui_shot: UI_SHOT_FULL=1 prints whole replies for scripted checks.

Regression tests in crates/engine/src/tests_typing.rs and the layout tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Typing parity: record Word's observed typing rules, two more tests

docs/typing-parity.md lists what Word does for each key sequence (lists, Backspace, Tab,
AutoCorrect, AutoFormat, slash line breaks) with the test that pins it, the known gaps, and how
to compare without taking over the shared machine. CLAUDE.md/AGENTS.md point at it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 04:52:10 -04:00
Brandon ThomasandClaude Opus 5.5 a1caf22a0b Bump version to 0.4.0 (from 0.3.0) for release; refresh contributors
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:02:32 -04:00
e04372aa83 README Downloads: where to get it first, then builds with Windows before macOS (#26)
Also fix the .deb/.rpm description in packaging/linux/nfpm.yaml, which still described PhotoCraft.

Co-authored-by: Brandon Thomas <shared2@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 09:51:15 -04:00
b830fae017 Release: Flatpak x86_64 + aarch64 bundles, AppImage zsync auto-update, README Downloads (#22)
- packaging/linux/package.sh: embed gh-releases-zsync update information in each AppImage
  (appimagetool -u) and write the .zsync beside it; the Linux job installs zsync and checks both.
- New flatpak job (x86_64 on ubuntu-24.04, aarch64 on ubuntu-24.04-arm) repackages the Linux
  tarball with packaging/linux/flatpak-bundle.sh and ai.storyteller.wordcraft.bundle.yml; no Rust
  build, no release environment. The release job needs it.
- version, Linux and web jobs drop `environment: release` (they sign nothing), so a branch
  workflow_dispatch dry-runs every unsigned job; the release job still needs the signed jobs.
- FreeBSD release build embeds craft-fonts like the other desktop builds and ships its licences.
- Flathub manifest: same finish-args as the bundle (adds xdg-documents), builds the CLI too;
  packaging-lint checks both manifests agree.
- README: Downloads section; docs/releasing.md updated.

Co-authored-by: Brandon Thomas <shared2@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 09:38:25 -04:00
Brandon ThomasandClaude Opus 5.5 db0cfed1f7 Release CI: macOS job on macos-15-xlarge
Custom macOS larger runners in a runner group do not pick up jobs; the
built-in M2 Pro label does. Only release.yml uses it, and the
actions_macos_xl budget caps the monthly spend.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 22:08:09 -04:00
Brandon ThomasandClaude Opus 5.5 0d4eadcdbe Release CI: macOS job on the org macos-release runner
The standard macOS runners are shared with all CI and queue for an hour or
more. The macos-release runner (GitHub-hosted M2 Pro) is in a runner group
usable only by release.yml on the release branch, with a monthly spend cap
on the actions_macos_xl SKU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 21:55:02 -04:00
Brandon ThomasandClaude Opus 5.5 906d4faca3 About: contributor and model credits compiled in (grab bag + table, self-submitted names) (#6)
* About: contributors compiled in from contributors.json (grab bag + table, sortable, name toggle)

Adds About ▸ Contributors and About ▸ Models tabs per the craftrules standard
(standards/contributors.md): contributors/contributors.json is generated by
craftrules/scripts/contributors.py and baked into the binary by crates/ui-egui/build.rs as
static tables. Each contributor line: @username, opt-in display/real name, PRs, commits,
lines +/−/Δ, binary assets +/−, first/last commit. Sortable by every column; alphabetical
ignores case and the leading @. `ui.dialog` also accepts `contributors` / `models` to open
the About window on that tab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Contributors: self-submitted names in contributors/people.toml; AGENTS.md credit-yourself section

build.rs reads contributors/people.toml next to the generated contributors.json and shows a name only
for entries the generator verified as written by that person (craftrules standards/contributors.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Contributors: regenerate (verified people.toml entries)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 21:17:02 -04:00
Brandon ThomasandClaude Opus 5.5 5dce7f5ed1 Rename PrintCraft to PdfCraft (storytold/pdfcraft) (#3)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:46:02 -04:00
Brandon ThomasandClaude Opus 5.5 c9824abfc2 M14: .shellcheckrc so packaging lint follows the shared env.sh
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 02:37:25 -04:00
Brandon ThomasandClaude Opus 5.5 56942da7a1 M14: clean wasm build warnings; macOS CLI zip without AppleDouble files
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 02:14:18 -04:00
Brandon ThomasandClaude Opus 5.5 6e9dc4bab4 M7/M14: comment balloons in the margin; ROADMAP alpha and parity estimates; README Review and Layout screenshots; cargo xtask alias
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:38:55 -04:00
Brandon ThomasandClaude Opus 5.5 f38710fcb6 Layout: automatic hyphenation (dictionary + patterns, 0.25" zone), soft hyphens show a hyphen at line end
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:27:02 -04:00
Brandon ThomasandClaude Opus 5.5 4c63bb0230 Layout: drop caps (dropped letter sized to N lines, text indented beside it)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:17:56 -04:00
Brandon ThomasandClaude Opus 5.5 e8c50e9dde Layout: table rows split across pages between lines (cantSplit honoured, header rows repeat)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:09:16 -04:00
Brandon ThomasandClaude Opus 5.5 6379a25ce6 Layout: text wrap around floating objects, text box contents, page borders (from page edge), column separators, vertical alignment, line numbers; cli run --page/--scale
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:04:48 -04:00
Brandon ThomasandClaude Opus 5.5 702aeb4ff4 Release: FreeBSD artifact job, docs/releasing.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 00:51:20 -04:00
Brandon ThomasandClaude Opus 5.5 7b4a526fdf Release pipeline (macOS universal signed/notarized DMG, Windows x64/x86 MSI, Linux AppImage/deb/rpm/tar/Flatpak, FreeBSD, web zip) adapted from DesignCraft; Windows exe resources; version in --version/About; multithreaded page raster
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 00:50:50 -04:00
Brandon ThomasandClaude Opus 5.5 f3dbc31a57 README with screenshots, Crafting Apps table, community links; proofing ignores deletions/objects; comment shading; hhea line metrics; watermark tuning
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 00:18:42 -04:00
Brandon ThomasandClaude Opus 5.5 d95e09ba25 Clippy/fmt clean workspace, ROADMAP with honest parity estimate; xtask ci green
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 00:11:08 -04:00
Brandon ThomasandClaude Opus 5.5 72262a2ea9 M3/M6/M12: PDF export (krilla), TXT/MD/HTML/RTF/ODT import+export, citations & bibliography, index, table of figures, cross-references, tools (repeat, macros, AutoCorrect, compare, accessibility, inspector, protection, versions, Quick Parts)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 23:57:36 -04:00
Brandon ThomasandClaude Opus 5.5 c29182e8bc M1: DOCX read/write (OOXML) wired into open/save; floating objects; picture & shape tools; mail merge, envelopes, labels; contextual spacing; export without proofing marks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 23:46:40 -04:00
Brandon ThomasandClaude Opus 5.5 5a56dda633 M6.1: footnotes at page bottom, endnotes at document end, click-to-edit notes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 23:40:59 -04:00
Brandon ThomasandClaude Opus 5.5 d1a0ed39c0 M7.1: proofing — spelling with suggestions, grammar checks, squiggles, F7, right-click menu, thesaurus; web app; hyphenation dictionary
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 23:38:37 -04:00
Brandon ThomasandClaude Opus 5.5 435194f7c2 M0.9–M0.12: CLI, MCP server, xtask (layers/assets/parity/ci), icon placeholder, AGENTS/ATTRIBUTION/NOTICE, protocol docs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 23:21:35 -04:00
Brandon ThomasandClaude Opus 5.5 98bbfbed4e M0.8: Word-style egui UI — ribbon (all tabs), page canvas, rulers, panes, dialogs, Backstage, control channel; desktop app
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 23:16:02 -04:00
Brandon ThomasandClaude Opus 5.5 b8dd46ccb5 M0.7: engine — session, undo, 200+ commands, Word feature catalog, samples
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 22:47:26 -04:00
Brandon ThomasandClaude Opus 5.5 d1e80df4da M0.1–M0.6: workspace, geom, document model, fonts, layout, render
Clean-room foundations for WordCraft: Arc-shared document model with
paragraph/run editing, styles, lists, sections and tables; Word-style
font substitution; first-fit line breaking with tabs, lists, alignment,
pagination, tables, headers/footers and hit testing; vello_cpu page
rasteriser.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 22:30:16 -04:00
Brandon Thomas 4e1fb3aba9 first commit 2026-10-05 20:23:39 -04:00