Commit Graph
1028 Commits
Author SHA1 Message Date
Dylan M. Taylor ba145e9405 security: enforce HTTPS on curl downloads (SonarCloud S6506)
Add --proto '=https' to curl invocations so redirects cannot downgrade to
plaintext HTTP. Resolves SonarCloud S6506 (shell/docker/githubactions).
2026-10-21 17:07:24 -04:00
Dylan M. Taylor f7d7c0288c ci: add sonar-project.properties for CI-based analysis
Marks the project ineligible for SonarCloud Automatic Analysis, resolving the
CI-vs-automatic-analysis conflict so the SonarCloud workflow succeeds.
2026-10-21 16:00:51 -04:00
Dylan M. Taylor e65d188747 ci: add SonarCloud scan via shared reusable workflow
Calls storytold/craft-common-actions/.github/workflows/sonarcloud.yml@main
with secrets: inherit. Adds SonarCloud badge to README where missing and
removes the redundant sonar-project.properties (config now passed by the
reusable workflow).
2026-10-21 15:47:09 -04:00
Dylan M. Taylor e05b7fe86f Merge pull request #2381 from dylanmtaylor/ci/shared-nix-workflow
ci: switch to shared nix workflow from craft-common-actions
2026-10-21 02:39:43 -04:00
skylar taylor-barrick e3bb06a298 Merge pull request #800 from isaac-levine/ci-xtask-ci-workflow
CI: run cargo xtask ci on pull requests and pushes to main (#754)
2026-10-20 22:11:52 -06:00
skylar taylor-barrick cd709ea0a9 Merge current main into CI workflow branch 2026-10-20 21:31:13 -06:00
Brandon ThomasandClaude Opus 5.5 d1b0bfc3f9 Skip CodeRabbit reviews on Dependabot PRs (#3302)
Adds .coderabbit.yaml so CodeRabbit no longer reviews or comments on PRs opened by dependabot[bot].

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 11:39:49 -04:00
Dylan M. Taylor fd24c1ab3f Merge pull request #2935 from dylanmtaylor/docs/add-sonar-coderabbit-badges
docs: add SonarCloud and CodeRabbit badges to README
2026-10-20 11:31:10 -04:00
Brandon ThomasandClaude Opus 5.5 2e6db0bd88 Release 0.9.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.9.0
2026-10-19 22:18:50 -04:00
Dylan M. Taylor 9b4b948c6e Merge pull request #2854 from dylanmtaylor/chore/drop-competitor-references
docs: drop references to competing commercial software
2026-10-19 19:22:22 -04:00
e6c5a701dd The caret and clicks land between the letters of a ligature (#1974) (#2006)
* Fix #1974: the caret and clicks land between the letters of a ligature

The glyph's advance is shared evenly among its letters (mirrored in
right-to-left text); ligature letters keep their character spacing.

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

* Fix #1974: a long word never wraps inside a ligature

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

* Fix #1974: a hyphenation point never splits a ligature

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

* #1974: ligature tests use a bundled font that draws fi and ffi as ligatures

They assumed the installed default-font stand-in draws ligatures; Liberation Sans (Linux) draws
none, so they failed there.

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

---------

Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-19 13:09:22 -04:00
4e2568e2da RTF import: heading and code formatting compared with the file's own stylesheet (gap 19 follow-up) (#2236)
* gap 19: RTF import compares heading and code run formatting with the file's stylesheet

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

* gap 19: review round 1: a style chain with formatting words is complete; unnumbered entries are Normal

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

---------

Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-19 12:57:16 -04:00
b8debb3050 Content controls: typed text takes the control's formatting, in any language (#3116)
* Content controls: typed text takes the control's formatting, in any language

Text typed in place of a content control's placeholder takes the
control's own run properties (w:sdtPr/w:rPr, ECMA-376 §17.5.2.27), as
in Word: in a protected form filled in Word for Mac, all 17 entered
values carry the style their control's rPr names. WordCraft kept that
rPr only as XML to write back and gave typed text the placeholder's
formatting minus the placeholder style.

It also knew the placeholder style only by its English id,
PlaceholderText. German Word writes Platzhaltertext (the name stays
"Placeholder Text"), so in forms from German Word every entry stayed
grey, on screen and in the saved file, and restoring a placeholder
added a second Placeholder Text style. The style is now found by its
built-in name.

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

* Content controls: typing drops a showing placeholder's own style

A control without run properties that shows its placeholder: text typed
over it drops the placeholder run's character style whatever its id
and name. Word localizes only the placeholder style's id, but another
program may localize its name too, and the name lookup then misses it.
Restoring a placeholder and new controls keep the name lookup.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-19 12:53:44 -04:00
f1b4be8a04 Restrict Editing: forms protection keeps a form's content controls (#3112)
* Restrict Editing: forms protection keeps a form's content controls

With Filling in forms enforced (w:edit="forms"), Word greys out the
Developer tab's control buttons and Properties, and a field's shortcut
menu offers no Remove Content Control: only the fields' content can be
filled in. WordCraft refused these only outside the fillable fields, so
with the caret in one it removed the control (its content left as
plain, no longer fillable text), inserted new controls inside it, and
renamed it; only lock changes were refused.

Under forms protection, inserting any content control, Properties
(every setting) and Remove Content Control are now refused before they
run. Typing, Toggle Check Box, Choose List Item and Set Date still fill
the fields. Other protection modes are unchanged.

Observed in Word for Mac 16.113 on a protected real-world form
(72 content controls: rich text, check boxes, date pickers); the same
form filled in WordCraft opens in Word still protected, with its values,
ticks and dates.

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

* Restrict Editing: parity row states what forms protection refuses

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

* Restrict Editing: correct the comment on content-control property edits in forms mode

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-19 12:49:05 -04:00
2304bf6ac6 Fix Paragraph::sentence_at at the first character of a sentence (#3105)
* Fix Paragraph::sentence_at at the first character of a sentence

Found while testing crates/doc/src/para.rs:1397-1427

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

* sentence_at: a terminator followed directly by text doesn't end the sentence at the caret

"Hello.World" at offset 6 gave an empty (6,6) range; only treat the caret as just past a
sentence end when the character at the caret is a space.

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

---------

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-19 12:45:25 -04:00
NetterSilverandClaude Haiku 5.5 e8866ea58b Keep an ordered list's start number in Markdown import (#3103)
Found while testing crates/formats/src/markdown.rs:160-161,463 and crates/formats/src/model.rs:111 (ListInfo)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-19 12:32:33 -04:00
NetterSilverandClaude Haiku 5.5 74ab8d2819 Keep a single leading space in HTML export paragraphs (#3102)
Found while testing crates/formats/src/html.rs (export, around line 1744; paragraph text writer)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-19 12:32:27 -04:00
NetterSilverandClaude Haiku 5.5 517ba09250 Write large whole numbers without decimals in format_number (#3107)
Found while testing crates/doc/src/locale_number.rs:114

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-19 12:32:21 -04:00
Diego GueriniandClaude Sonnet 5.5 49a934fba7 #3067: script parse test compares paths with Path::join (#3106)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 12:29:17 -04:00
Gabriel MatozoandBrandon Thomas 117aea31a8 Navigation pane: a long heading wraps within the pane instead of ending in an ellipsis (#3015) (#3029)
The pane drew each heading with `Button::selectable(..).truncate()`, so a heading wider than the
pane ended in an ellipsis and its text had to be read from the hover tooltip. Word wraps the row
instead.

`nav_heading` now wraps: the button takes `Button::wrap()` and a minimum size of the available width
and one row, so the label continues on further lines inside the pane, the row grows with the text,
and the pane still keeps its width. The trailing grow atom still keeps the text flush left under its
parent (#1199). The hover tooltip goes with the ellipsis: nothing is hidden now, and a word wider
than the pane is broken by the wrap instead of overflowing.

The navigation test asserts the wrap instead of the ellipsis. For a heading well past the width and
for a single word wider than the pane, the label has more than one line, is not elided, stays inside
the pane, does not widen it, and takes a taller row than a one-line heading in the same pane.

`docs/ui-parity.md` and `ROADMAP.md` record the new behaviour.

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
2026-10-19 03:08:47 -04:00
SlavenDj fdf791a8b2 #3045: the empty window's ribbon tabs stay live, so File reaches the Start screen (#3064)
With no document open, the empty window greys the Share / Editing /
Comments buttons out by disabling the whole right-to-left line, and the
tab strip is a child of that line: egui gates interactions on a widget's
enabled state, so every tab was dead. File did nothing, and the empty
window had no way back to the Start screen, or to making a document.

Only those three buttons are disabled now. The test presses Escape on
the Start screen (the empty window, as #1129 has it), clicks the File
tab and asserts the Start screen, with its Blank document, is back.
2026-10-19 03:05:41 -04:00
MarekSpodBiedry dbc5c3407b Size the text cursor by the font, not the line spacing (#2920) (#3011) 2026-10-19 03:05:17 -04:00
MarekSpodBiedry 3f9c19d9f3 Opening a missing recent file keeps File open and shows the error (#2938) (#3012) 2026-10-19 03:05:11 -04:00
Martin BallandClaude Opus 5.5 435be0a4cb #2972: regression tests for File page AccessKit focus paths (mac chevron, Options back, Blank document) (#3019)
* #2972: regression tests for File page / sidebar AccessKit focus paths

The macOS back chevron beside the traffic lights (the report's "sidebar
collapse toggle"; there is no other toggle there), File > New/Home > Blank
document, Escape, and File > Options > back (both layouts, with a document
and from the Start screen) all run under kittest's AccessKit. The chevron
and Options tests panic with "Focused ID ... is not in the node list"
before #2856 and pass on main.

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

* #2972: review — fresh document per Blank document path; Options back asserts closing and focus in every case

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 03:05:06 -04:00
Martin BallandClaude Opus 5.5 6950c22d73 #2999: a file dialog that "cancels" at once asks again without a parent, then shows the built-in browser (#3030)
* #2999: a file dialog that "cancels" at once asks again without a parent, then shows the built-in browser

rfd folds the xdg-desktop-portal's failure response into "cancelled", so on desktops whose
FileChooser portal fails to show a dialog, Save As, Export and Open did nothing and logged
nothing. A cancel quicker than anyone can make one (300 ms) is now taken as that failure: the
dialog is asked for once more without the parent window (GridCraft, which passes none, works on
the reporter's machine), and if that too comes back at once the app shows its built-in browser.
Each step logs a warning.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr

* #2999: review — the quick-cancel retry runs only on Linux and the BSDs; Windows and macOS answer a quick cancel as a cancel

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 03:04:59 -04:00
Juan Camilo Agudelo e0ff4b0c3d Paste: Ctrl+V pastes a clipboard picture even when Ctrl is released before V (#3049) (#3058)
* Paste: Ctrl+V pastes a clipboard picture even when Ctrl is released before V (#3049)

egui-winit keeps the Mod+V press, so a clipboard picture is pasted on V's
release, which required Mod still held. Letting Ctrl up first (the natural
one-hand Left Ctrl+V) pasted nothing. A V release with no V press seen was
Mod+V: use the modifiers held with Mod then, so Mod+Shift+V and Mod+Alt+V
stay apart.

Fixes #3049

* Paste: record the modifiers held with Mod in event order (#3049)

Mod can go down and up within one frame, so the frame's modifiers may no
longer have it when the canvas reads them. Take them from each
ModifiersChanged and key press as they arrive. Review feedback on #3058.

* Paste: document the Mod+V release helpers and tests (#3049)
2026-10-19 03:04:54 -04:00
SlavenDjandBrandon Thomas ebabb57e5e #3039: mini toolbar font colour Automatic returns focus to the document (#3043)
Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
2026-10-19 03:04:49 -04:00
SlavenDj 25ad07f868 #3038: mini toolbar highlight swatch returns focus to the document (#3042) 2026-10-19 02:59:34 -04:00
SlavenDj e1e6c31571 #3040: undo-list row returns focus to the document (#3044) 2026-10-19 02:59:29 -04:00
SlavenDj 396344c31e #3031: Tabs dialog says the list is empty instead of a blank box (#3050) 2026-10-19 02:59:24 -04:00
SlavenDj 6e3f800a8b #3032: Styles pane greys out Delete for built-in styles (#3051) 2026-10-19 02:59:19 -04:00
SlavenDj 83014016c6 #3036: AutoCorrect Replace list says when the filter matches nothing (#3053) 2026-10-19 02:59:14 -04:00
570b992947 #3037: file browser says when a folder (or the filter) shows no items (#3054)
* #3037: file browser says when a folder (or the filter) shows no items

* #3037: Czech and Vietnamese catalogs carry the file browser's no-items string

* #3037: show the no-items hint only for a folder that was read

An unreadable folder already shows its error below the list.

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

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 02:59:09 -04:00
SlavenDj 76d8347f77 #2892: rules end flat on screen, like the PDF export (#3061) 2026-10-19 02:44:02 -04:00
SlavenDj 19e5bb48cf #3033: file browser Open/Save uses the themed primary button (#3047) 2026-10-19 02:43:57 -04:00
SlavenDj ab968b072d #3034: SmartArt Pyramid separators follow the theme line colour (#3052) 2026-10-19 02:43:52 -04:00
SlavenDj d26b8dcae7 #3035: WordArt reflection swatch follows the theme's dimmed text colour (#3055) 2026-10-19 02:43:46 -04:00
SlavenDj 68bc118a68 Add @SlavenDj to contributors/people.toml (#3048) 2026-10-19 02:43:34 -04:00
Diego GueriniandClaude Opus 5.5 18c7c1d114 #2845 follow-up: clippy passes on Windows and Linux again (#3006)
`graphics.rs` allowed dead code only on macOS (where the Windows items are unused). Since #2845
the two indirect-validation functions are called only on macOS, so on Windows and Linux
`cargo clippy --workspace --all-targets -- -D warnings` stopped with "function is never used".
They now carry `allow(dead_code)` off macOS.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 02:43:28 -04:00
NetterSilverandClaude Haiku 5.5 60ad86acb3 Keep a single trailing space when exporting a paragraph to ODT (#3066)
Found while testing crates/formats/src/odt.rs:354-390 (plain_inlines)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-19 02:43:22 -04:00
Fin Warman d588e0b873 Limit table row deferral to genuine widow splits (#2925) 2026-10-18 17:11:52 -04:00
ReesandBrandon Thomas 61e565cd1c fix(ui): keep recent fonts visible after an unmatched query (#2068) (#2929)
Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
2026-10-18 17:10:01 -04:00
Diego GueriniandClaude Opus 5.5 2d0169e718 A Word 2003 XML or web page saved as .doc opens by what it holds (#2853) (#2942)
* #2853: Word XML, web pages and MHTML saved as .doc open by what they hold

A Word 2003 XML Document named `.doc` went to the Word 97-2003 reader, which refused it ("Invalid
CFB file"), and the window showed a blank page. Word XML (2003 or Flat OPC), a web page and a
Single File Web Page (MHTML) under a `.doc`, `.docx` or `.rtf` name now open by what they hold,
as in Word. MHTML is read for the first time (`.mht`/`.mhtml` too): the MIME parts are split and
decoded, the page is imported as HTML in its charset and its pictures load from the other parts.

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

* #2853: leave MHTML to #1104, keep Word XML and web pages saved as .doc

#1104 adds an MHT reader (`wordcraft_formats::mht`), so this branch drops its own MHTML reader and
the `.mht`/`.mhtml` extensions instead of adding a second one. A Word XML Document or a web page
under a `.doc`, `.docx` or `.rtf` name still opens by what it holds (the #2853 fix).

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

* #2853: a web page's signature needs its tag name to end (`<htmlish>` is other XML)

CodeRabbit: `starts_with("<html")` took XML with a root like `<htmlish>` under a `.doc` name for a
web page, though XML that isn't a Word document stays an error. `<html` and `<!DOCTYPE html` now
need white space, `>` or `/` after them.

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

* #2853: rustfmt

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:04:33 -04:00
dhruvLearner8 384d5ef307 Cross-reference: caption labels of your own are Reference types (#2940) (#2957)
* Cross-reference: caption labels of your own are Reference types (#2940)

* Caption labels come from the SEQ field's caption, not a digit (#2940, review)

* Caption label after a chapter number field (#2940, review)
2026-10-18 17:01:41 -04:00
dfgodvananddfgodvan 99ff497180 Layout: a roman page number in the footer stays roman when the body shows the page count (#2966)
A paragraph with a PAGE field is cached by the page number, the totals, the
section and the bookmarks, but not by the number format the field shows the
page number in. Paginating lays the footer out to know where the body ends,
before the section's number format is set in the field context, so as
decimal. When the body shows NUMPAGES or SECTIONPAGES, the pass that has the
right totals made that layout's key equal to the page's own footer's, and the
cached decimal one was drawn: page 2 of a section numbered in lower-case
roman showed "2", not "ii". The number format is now part of the key.

Co-authored-by: dfgodvan <180286157+dfgodvan@users.noreply.github.com>
2026-10-18 16:55:08 -04:00
dfgodvananddfgodvan 4b18b307be Selection rectangles walk only the paragraphs the range reaches (#2967)
Document::paths_between listed every paragraph of the story (every cell
paragraph of every table) and then kept those between the two paths, so each
selection_rects call cost the whole document. The canvas calls it every frame
for each Find reading highlight (up to 5,000), each content control frame,
Read Aloud and the hyperlink under the pointer. It now walks only the
blocks, rows and cells between the two paths: for a one-paragraph range in a
document of 26,000 paragraphs (four 1,500-row tables), 1.0 ms -> 2.6 us.

Co-authored-by: dfgodvan <180286157+dfgodvan@users.noreply.github.com>
2026-10-18 16:53:39 -04:00
dhruvLearner8 972252d0b4 Ruler: margin handles on even mirrored pages edit the visible edge's stored margin (#2914) (#2975) 2026-10-18 16:49:22 -04:00
100dfb37c8 Layout: a word after a slash wraps whole, like Word (#2963) (#2985)
* Layout: a word after a slash wraps whole, like Word (#2963)

UAX #14 (LB13) forbids a break between a space and a slash, and the break
after a slash is dropped on purpose (Word keeps "and/or" whole). Together
"caption /word" had no break opportunity at all, so a line too short for
both filled letter by letter and split the word. Add the break before a
slash that follows whitespace and starts a word.

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

* Layout: no break before a slash after a no-break space (#2963)

The break before a slash word took any whitespace, U+00A0, U+2007 and
U+202F included, so "Nr. /12" could wrap after the no-break space.
Only a breakable space counts now. The parity checklist lists the rule
on its own row as untested against Word.

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

* Layout: test the slash word in a fixed-width table cell (#2963)

The report came from a table cell, where the cell's own width is the
line width.

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

* Docs: gaps and typing parity for the slash-word break (#2963)

Doc wording taken from #2994 (maxm11), with the test names of this PR.

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

---------

Co-authored-by: Claude (ThinkSystem) <claude@thinksystem.local>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 16:41:44 -04:00
Max Murphy 00abb59a85 render: keep the legacy font effect test off antialiasing (#2898 x #2899) (#2990)
The shadow copy is solid RGB 128; the old 100-160 grey band also counted the glyph's antialiasing, whose centroid hid the copy's offset with some fonts. This was already failing on origin/main on macOS; count the copy's own grey.
2026-10-18 16:40:22 -04:00
Max Murphy 72328ab521 cli: keep the non-UTF-8 file name test off macOS (#2993)
* cli: keep the non-UTF-8 file name test off macOS

APFS refuses file names that aren't valid UTF-8, so the fixture can't be
made there (as in tests/non_utf8_args.rs); the unit test runs on the
other unixes.

* chore: add @maxm11 to About-window contributors with their own name
2026-10-18 16:37:59 -04:00