"the user name can't change while the document is protected" was the
wrong term twice over: it is the session author — the name new tracked
changes and comments are recorded under, and the one a named exception
is matched against — and it is `file.setAuthor` that refuses to change
it. The CLI's `--author` names the session directly, so it works on a
protected document.
The parity row, the two status lines and the two progress rows now say
that.
Co-Authored-By: opencode <noreply@opencode.ai>
Resolves the conflicts with the changes that landed since the branch was cut:
- crates/engine/src/cmd/protect.rs: the forms-mode refusals stay behind
upstream's `greyed_out()` (so the ribbon still greys the Developer tab's
control buttons out), and the refusal of the document's properties and its
privacy options is added on top of it.
- ROADMAP.md, docs/file-format-parity.md, docs/gaps.md: both sides' entries
are kept; the status lines carry the merge's date and this change at the
front.
Co-Authored-By: opencode <noreply@opencode.ai>
crates/ui-egui/src/backstage.rs: #3125's t.nav fill kept, without the recovery black. Docs: status entries prepended to main's lines; the recovery paragraph placed after the status line.
crates/ui-egui/src/backstage.rs: t03's bidi_display on the Recent names over t09's borrowed name/path. people.toml: union. Tests: both tracks' tests kept whole. Docs: status lines combined, rows unioned or word-merged.
docs/gaps.md row 4: one row (t06 had kept a second, older copy of it); t05's #1840 and t06's #2964 clauses both in, main's #2963 clause kept. docs/recovery.md: t06's retry sentence with t01's base64 size text. Test files: both tracks' tests kept whole.
crates/pdf/src/lib.rs: t01's vector_text kept before t05's watermark (box area signature). Test files and canvas.rs tests: both tracks' tests kept whole. Docs: status lines combined, rows unioned or word-merged.
crates/formats/src/odt.rs: StyleInfo keeps both cell_valign (#2416) and column_width.
ROADMAP.md, docs/file-format-parity.md, docs/gaps.md: main's status lines kept; the PR's log rows added.
ROADMAP.md: status line once (this change first, then main's); progress-log entries and history rows union
docs/file-format-parity.md: status line once; history rows union in date order
docs/gaps.md: status line once (this change first, then main's)
ROADMAP.md: kept main's status lines; progress-log entries from both sides
docs/gaps.md: kept main's status line
docs/ui-parity.md: kept main's status line; change-log rows from both sides
crates/ui-egui/src/objects.rs: main's tests kept; the PR's rotation_window helper and two rotation tests appended after them.
ROADMAP.md, docs/ui-parity.md: main's status lines kept; the PR's log rows added.
docs/gaps.md: main's status line and row 10 kept, #2277 clause added to the row; log row added.
ROADMAP.md: both progress-log rows, #2692's under main's newest 2026-10-17 row.
docs/gaps.md: status line kept main's, #2692's change added as a previous change.
crates/proof/src/lang.rs: main's English-dialect helpers kept; code() doc lists `de`.
crates/proof/src/lib.rs: hostile-input test loops over main's languages plus De.
crates/ui-egui/src/options.rs: language_name maps EnGb/EnCa and De to no dictionary name.
ROADMAP.md: main's status lines; both progress-log and revision rows kept.
docs/gaps.md: main's status line; gap 8 row is main's with German hyphenation added; revision rows unioned.
docs/layout-parity.md: main's status line, Tabs and Drop caps rows; the PR's Hyphenation row; revision rows unioned.
docs/localization-parity.md: main's status line; revision rows unioned.
* #2852: personal templates in File › New, Save as Word Template
File › New lists the .dotx/.dotm files in the user templates folder under
Personal (a subfolder per category, search covers them); a click makes an
untitled document attached to the template. Save As › Save as Word Template
starts in that folder (<settings>/Templates, made on first use; File ›
Options › Advanced › File locations › User templates overrides it).
Engine: file.listTemplates (bounded, network folders refused, read root
applies), file.newFromTemplate sets the attached template, file.options
userTemplatesFolder, file.saveTemplate keeps a .dotm name. UI:
ui.saveAsTemplate; Save dialog requests carry a start folder.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #2852: review — a personal template asks about unsaved changes; network templates folder refused before it's looked at; New lists the folder once per visit with Refresh; .dotx/.dotm labels alike
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>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
* #1281: English dialects in proofing — en-GB, en-CA, en-AU… flag each other's spellings (#1045)
English was one proofing language: en-GB, en-CA and en-AU all proofed like
US English, and US documents accepted British spellings. Now ProofLang has
three Englishes, picked by the tag's region:
- En (en-US, other regions, no language): British spellings flagged
(colour → color).
- EnGb (en-GB, en-AU, en-NZ, en-IE, en-ZA, en-IN…): American spellings
flagged (color → colour, center → centre, analyze → analyse); -ize and
-ise both right (Oxford spelling).
- EnCa (en-CA): British -our/-re/-ence, American -ize, tire, program.
Data: assets/spelling/en-variants.txt, our own ~280 American/British
pairs with a policy per English (MIT OR Apache-2.0, ATTRIBUTION.md row).
Inflections by rule (crates/proof/src/en_variants.rs). A dialect's form
is right when the shared list knows it or its other spelling, so
"realisation" is right in en-GB. No system dictionary needed.
Suggestions put the dialect's own form first. Review › Language lists
English (Australia) and (Canada); the Language dialog adds India,
Ireland, New Zealand and South Africa (names in every catalog, not
native-reviewed). An English interface on an en-GB/en-CA/en-AU… region
writes new documents in that English. Options › Language shows the
document's language instead of a fixed "English (United States)".
Tests: per-dialect accept/flag/suggest, tag mapping, no-panic proptests,
engine end to end through review.language, DOCX w:lang round trip,
region-based editing language, language menu.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P8veLveuUsJJm6UEcQPEuW
* #1281: review — pin the #40 words (judgement, defence, licence, programme, catalogue) in en-GB and en-US
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #1281: review — harness picks every proofing language in Review › Language and checks it is ticked
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #1281: review — pin more British spellings in en-GB (goitre, sceptre, enrol, fulfil, instalment, skilful, wilful, encyclopaedia…)
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>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
* #1500: shape fills keep gradients and transparency (model, DOCX read/write)
A shape's, text box's and canvas's fill is now a `Fill` (solid with
transparency, or a gradient kept as the file states it) instead of one
opaque colour, so gradients and transparency survive opening and saving.
- doc: `fill::Fill`/`Gradient` with sanitizing for hostile input; `.json`
keeps writing opaque solid fills as `[r,g,b]` and reads both forms;
`ShapeExtra::outline_fill` keeps an outline gradient.
- docx read: `a:solidFill` with `a:alpha`, `a:gradFill` (stops, `a:lin`,
`a:path`/`fillToRect`, `tileRect`, `rotWithShape`, `flip`), theme fill
styles through `a:fillRef` 1-999/1001+ with `phClr`, `a:grpFill`,
`satMod`, outline gradients, VML `v:fill` gradients and opacity, canvas
backgrounds.
- docx write: `a:solidFill` + `a:alpha`, `a:gradFill` as read.
- engine/docbin/layout: call sites; drawing still uses the main colour
(next commit draws gradients).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1500: draw shape gradients and transparency (layout, render, PDF)
- layout: WordArt's paint becomes a shared `display::Paint` (solid,
linear, radial); `paint_for` lays a fill over the shape's frame
(linear at its angle, `scaled` honoured; path gradients circular from
their focus to the farthest corner). Shapes, freeforms and canvas
backgrounds use it.
- render: one `set_paint` for shapes, paths and WordArt; radial
gradients; soft edges fade the real fill.
- pdf: axial and radial shadings with per-stop opacity.
- Review fixes: nested groups without a fill pass theirs on to
`a:grpFill`, text-box contents don't take a group's fill, `phClr`
transparency only on `phClr` colours, VML focus ends consistent and
opacity clamped, `rotWithShape`/`scaled` written only when set,
Shape Outline clears an outline gradient, shared stop sanitizing.
- docs: file-format, layout parity, gaps #1, roadmap progress log.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1500: shape.fill sets transparency and gradients on every selected shape
- shape.fill (now cmd/shape_fill.rs) takes {"color", "transparency"?} or {"gradient": {stops,
type, angle, focus, rotateWithShape}}; a colour alone keeps the transparency; values are
sanitized; applies to every selected shape, text box and canvas as one undo step.
- Formatting (shape.fill, shape.effects) keeps a Shift+click multi-selection, as in Word.
- view.formatShapePane toggles the Format Shape pane.
- Session::undo_mark/merge_undo_since: a pane drag that applies as it goes undoes in one step.
- Helpers for the pane: params(fill), stop add/remove/move, directions, variations and
presets (variation and preset colours provisional until compared with Word).
- Catalog: Shape Fill Gradient, Format Shape Pane.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1500: Shape Fill › Gradient and the Format Shape pane
- Shape Fill › Gradient: No Gradient, Light and Dark Variations of the shape's colour, More
Gradients… (opens the pane).
- Format Shape pane (right): No fill / Solid fill (colour, transparency) / Gradient fill
(presets, type, direction, angle, stops bar with add/remove/drag and arrow keys, per-stop
colour, position, transparency, rotate with shape). Drags preview live, one undo step.
- Right-click › Format Shape… on shapes, text boxes and canvases.
- Gradient swatches have accessible names; new labels translated (own wording, clean room).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1500: preset gradients are our own design (clean room)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1500: Gradient variations, starting gradient and directions as Word 365 does them
Observed in Word 365 (behaviour and values only):
- Shape Fill › Gradient: a No Gradient swatch, then 13 Light and 13 Dark Variations in a
grid five wide, in Word's order; light stops tinted 66/44.5/23.5% with saturation ×1.6,
dark shaded 30/67.5/100% with ×1.15; linear ones scaled; corners with their tile rectangle.
- Format Shape › Gradient fill starts from the theme's first accent (four stops, 0/74/83/100%).
- Corner directions in the pane get the same tile rectangle; Add/Remove sit by the stops bar.
- shape.fill carries "scaled" and "tile"; docx exports color_modified (lumMod, lumOff, tint,
shade, satMod) so variations use the reader's own colour maths.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1500: review fixes for the shape fill ribbon controls
- Gradient variations are built from the colour the shown variation came from, so picking
variations in turn, or No Gradient, doesn't drift the colour.
- A drag's undo mark is forgotten while the Format Shape pane is closed (a stale mark could
merge later undo steps).
- Dragging on the stops bar away from a handle no longer moves the selected stop; the arrow
keys are taken by the bar (no focus moves or shape nudges).
- Direction and Type changes keep a linear gradient's "scaled" and an imported tile
rectangle; only a corner direction sets its tile.
- Colour modifiers (lumMod, lumOff, tint, shade, satMod) live on Rgb in the doc crate, shared
by the DOCX reader and the gradient builders.
- Previews build their stop list once per swatch, not per vertex.
- Translations for the pane's hint, direction and preset names.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1500: adapt to main: caret contrast samples gradient fills; upstream tests use Fill/Paint
- Paint::at gives a paint's colour and opacity at a point; the caret's background sampling
(#299) uses it for shape fills, now gradients or transparent, and the Format Shape
previews use it too.
- Upstream tests (#886 shapes survive saving, render) build fills as Fill/Paint.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1500: adapt to main after the rebase: VML presets, rounded corners, new catalogs
- vml_fill takes the shape's shapetype: a VML preset shape (#1377) takes its fill, opacity and
gradient from its shapetype when it states none of its own, as it already took its colour.
- Rounded-rectangle corners (#1476) and the gradient paints meet in render, PDF and the caret's
background sampling; ShapeExtra keeps both `corner` and `outline_fill`.
- Tests that landed on main since build fills as Fill/Paint (VML presets, cover pages, VML
freeforms, path arrowheads, PDF and render shapes).
- The 40 Shape Fill / Format Shape strings in the hr, ko, sv and vi catalogs, which main
gained since.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1500: docs: shape gradients and transparency, Shape Fill › Gradient and the Format Shape pane
Re-applied on main's docs after the rebase: progress log, file-format, layout and UI parity
rows, gaps #1, status lines and revision history; parity checklist regenerated.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1500: a theme picture fill falls back on the reference colour; VML freeform opacity note
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1500: adapt to main after the second rebase: VML shapetype tests compare Fill; parity checklist regenerated
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1500: one VML fraction parser; a preset takes its gradient from its shapetype
Review notes: story.rs's watermark/freeform opacity reuses shape_style's vml_fraction (clamped at the callers) instead of a second copy; a test pins a VML preset whose v:fill gradient comes from its v:shapetype, and the 65536ths form of a fraction.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1500: adapt to main after the third rebase: the shape duplicate preview test fills with Fill
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1500: review — harness test: right-click a shape, Format Shape… opens the pane and keeps the shape selected
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #1500: review — theme keeps every fill style a fillRef can reach (999 per list), still bounded
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>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
The font name box and the font size box announced themselves as bare "edit"
controls, and the dropdown arrows beside them as bare "button" controls: a screen
reader had nothing to say about them.
The combo helper names its field with the hint it already shows, and its arrow
with the same "{name} options" the split buttons' arrow uses; the split button's
arrow now says whose options it opens. No new strings: every name is one the
widget already shows.
Measured the way #3172 measured it — every keyboard-focusable element of the
Home tab and its name: 43 of the 111 had no name before, 31 after.
The repository's convention is that landed work updates the gap document, the
parity document and the progress log, with each touched document's status line
and revision history bumped. This does that for the fix in the commit before:
- docs/file-format-parity.md: a section says what saving many content
controls costs, with the measurement before and after (32,000 controls:
3.6 s -> 0.1 s), and names the tests.
- docs/gaps.md: gap 20 (performance on large real-world documents) is
narrowed by it.
- ROADMAP.md: the progress log and its revision history gain the entry.
Fixes#3437.
Co-Authored-By: opencode <noreply@opencode.ai>
Roll back the new preference on final command rejection, including protected-region validation after handler success. Cover prior/default-free preferences, document and undo rollback, serialization and restart. Qualify interface-language fallback documentation and add missing function documentation.
`always_allowed` waved through every `file.*` id, so on a protected document
the file commands that do change it went unchecked: the privacy options and
the properties wrote document settings, and the inspector's removals deleted
content. The privacy options' own comment already said they were "refused on
a protected document"; that wasn't true.
The allow-list now names the file commands that don't change the open
document, and the ones that do are checked like any other command. Reading
the document stays allowed: the inspector without removals, the privacy
settings, the info, and saving a version snapshot.
Restoring a stored version replaces the whole document, and a snapshot's own
protection is stale — restoring one lifted the protection and dropped its
password. The live document's protection, password and formatting limit now
survive a restore.
Co-Authored-By: opencode <noreply@opencode.ai>