Like Word: the pointer turns into a resize arrow over a column border or a
row's bottom border, and a small handle shows past the table's bottom-right
corner. Dragging shows a dashed guide; releasing runs the new
`table.resizeDrag` command (page coordinates), so a drag is one undo step
and the selection is kept. Esc cancels a drag.
- Column border: the right neighbour gives or takes the difference, so the
table keeps its width; Shift, or the table's right edge, changes only the
column on the left. Columns stay at least 6 pt wide.
- Row border: sets the row's "at least" height; the margins and border bands
the layout adds are measured and taken off, so the row is drawn at the
dragged height.
- Corner handle: scales every column and the rows on that page.
- Widths come from the drawn layout, so autofit and percentage widths don't
make the result jump; a merged cell hides the border it covers.
- Borders are hit-tested in the existing canvas mouse handler, only within a
few pixels of a border, so clicks in cells still place the caret.
Engine: crates/engine/src/cmd/table_resize.rs (7 tests).
UI: crates/ui-egui/src/table_resize.rs, wired into canvas.rs; kittest tests
drive real pointer events through the whole window (one undo step per drag,
selection kept, clicks in cells still move the caret, Esc cancels).
The command label is translated in all nine interface catalogs.
Progress docs updated: gaps, ui-parity, target-app-parity, ROADMAP.
`cargo xtask ci` passes (fmt, clippy, test, assets, layers, wasm).
Co-authored-by: rabis <340973947+rabis7575@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Options: Animation effects setting that follows the system
The app eased wheel scrolling, Ctrl+wheel zoom and its fades whatever the
system's animation setting was: with Windows' Animation effects off, the
page still smooth-scrolled.
File > Options > Accessibility > Animation effects: Use system setting
(the default), On or Off; `ui.animations` for agents; kept in ui.json.
- Off: a wheel notch scrolls the page its whole distance at once, egui's
own scroll areas get the notch whole instead of eased in, Ctrl+wheel
zoom steps at once, and egui's fades and scroll-to glides are off.
Touchpad momentum and the caret blink are unchanged.
- Use system setting asks the host (`Services::animations_enabled`) at
start-up and when the window regains focus. Windows: the Windows
Runtime's UISettings, a safe API of the `windows` crate already in the
build. Web: `prefers-reduced-motion`. macOS and Linux/BSD aren't read
yet (docs/gaps.md); there it means on.
- The eight new strings are in all eleven interface catalogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Animation effects off: zoom steps land finished in one frame
Follow-up to the Animation effects setting: zooming could still look
animated with the setting off.
- Ctrl+wheel zooms in whole steps, each at once. A mouse-wheel notch is
two steps in its frame, the same zoom as before. Turns that arrive in
small pieces (a touchpad pinch that Windows reports as Ctrl+wheel, a
high-resolution or free-spinning wheel) add up to a step of about 10%
first, where each piece used to zoom a little and the zoom glided.
- A page re-rendered for a zoom step is drawn on the pixel grid in that
frame. It used to be drawn off the grid, slightly soft, and snap sharp
a frame later.
- The frame a zoom changes in asks for the next one at once, so the
status bar's percentage and slider follow in the next frame. They
could lag until the caret's next blink, up to half a second.
The last two apply whatever the setting.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Animation effects: follow the system's setting on macOS, Linux and the BSDs
"Use system setting" only read Windows and the browser; elsewhere it
meant on.
- macOS: System Settings > Accessibility > Display > Reduce motion,
through NSWorkspace (a safe call in objc2-app-kit 0.3, already in the
build).
- Linux and the BSDs: the XDG settings portal over D-Bus (zbus, already
in the build through AccessKit). Three settings say whether animations
play, and any one saying no turns them off: the cross-desktop
reduced-motion preference, GNOME's enable-animations and KDE Plasma's
animation speed. A thread reads them at start-up and follows the
portal's change signal. Where no portal answers, or it knows none of
them, KDE's kdeglobals and GTK's settings.ini are read instead.
- The UI now asks the host every frame instead of at start-up and on
focus, so a change in the system's settings is followed while the app
runs. Hosts answer from memory: Windows and macOS are asked again at
most once a second, the web keeps its media query, and Linux/BSD is
kept current by its thread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Animation setting on Linux: ask every setting again when one changes
KDE's portal announces a new animation speed only as the cross-desktop
reduced-motion preference, not under its own kdeglobals name. Keeping
each setting's last announced value left KDE's own one stale: after
going from instant back to normal speed, animations stayed off.
Every setting is now read afresh on any relevant change, and the
settings files' answer stands whenever the portal has none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Caret glide follows Animation effects; its canvas test turns the setting off, then on
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Linux: built-in file browser when the portal file dialog is unavailable (#576)
rfd 0.17 on Linux/BSD asks the xdg-desktop-portal FileChooser and falls back to
zenity; with neither (labwc with only xdg-desktop-portal-wlr, no zenity) it
returns None, the same as Cancel, so Open/Save As/Insert Picture silently did
nothing. The dialog thread now checks first (zenity on PATH, or the portal's
FileChooser interface via dbus-send) and answers with an error when neither is
there; the app then finishes the job in its own egui file browser (folder
field, Up/Home, folders and files of the chosen type, file name, type filter,
replace confirmation) with a hint on how to get the system dialog.
File > Open > Browse in WordCraft... (Linux/BSD) and `ui.fileBrowser` reach
it at any time, even while a system dialog never showed up;
WORDCRAFT_FILE_DIALOG=builtin forces it. Native only (not wasm32).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Turkish and Russian strings for the file browser (#576)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Add arcanii to the contributor names
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Caret: glide and fade like Word for Windows; File › Options › Animation turns it off
The caret no longer jumps and switches on and off. A key or typing glides
it to its new place in 100 ms along cubic-bezier(0, 0, 0, 1), the same for
one character and for a whole line; a second key before it arrives makes
it snap to where it was heading and set off from there. Clicks, and moves
made by scripts or the control channel, don't glide. The blink is a pair
of 100 ms fades (530 ms on, 530 ms off), and a key or a click turns the
caret on from however visible it was.
These are Word for Windows' numbers (16.0.20326, 100% zoom, 144 Hz),
measured black-box: a synthetic document in a separate Word instance, its
window captured frame by frame while key and mouse messages moved the
caret. Fourteen consecutive frames match the curve to 0.003. Word for Mac
was not measured.
Nothing glides when the view changed (zoom, a resized window, scrolling,
another document), when the caret wasn't drawn the frame before, or when
a move starts or ends off screen.
File › Options › Animation › "Animate the caret (it glides and fades)"
turns it off: ui.caretAnimation {"value"} for agents, kept in ui.json, on
by default, translated in the nine catalogs. Off, and whenever egui's
animations are off (the headless harness, so ui_shot and scripted
screenshots), the caret jumps and blinks exactly as before.
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>
File > Options > Advanced > Measurement units (#485) missed these two dialogs:
the Line Numbers "From text" distance and the Hyphenation zone read and
sent inches whatever the preference was. They now convert through
options::unit() like every other dialog; params still send points.
Reported by @Ledity.
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Add fonts at run time: Add Font…, drop a font file, font.add (#582)
Rebased onto main as one commit (the branch had merge commits from main).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #582: font test no longer depends on the system's fallback font
The FreeBSD runner has no system fonts, so a missing family fell back to
Source Serif 4, the font the test compared against. The test now adds a
renamed JetBrains Mono under a name without Serif/Sans/Mono (which picks a
fallback of that kind), with narrow-letter sample text: monospaced, it
measures unlike any proportional fallback.
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>
* Font list: installed Chinese fonts are listed, by their Chinese names in a Chinese interface (#505)
The font database read only the English family name of each font, so a
Chinese interface listed SimSun, Microsoft YaHei and WenQuanYi Zen Hei
by their English names and never as 宋体, 微软雅黑 or 文泉驿正黑, and a
document naming a font by its Chinese name fell back to another font.
- fonts: keep each face's localized family names (name IDs 16 and 1,
every face of a collection, read during the scan without loading the
file); FontDb::families_in(lang) lists them in place of the English
names for a matching interface language (zh-hans, zh-hant, ja, ko);
face, has_family and styles accept either name.
- ui: the ribbon and mini toolbar font boxes show the interface
language's names and store the English name.
- East Asian font: choosing a Chinese, Japanese or Korean font also sets
it as the East Asian font (w:eastAsia), read and written in DOCX;
layout draws CJK text in a run's installed East Asian font.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Font dialog: separate Asian and Latin text fonts (#505)
Word's Font dialog sets the font of Chinese, Japanese and Korean text
apart from the font of Latin text; WordCraft's had one font box.
- Font dialog: an "Asian text font" box (the installed Chinese, Japanese
and Korean fonts, by their interface-language names, type to find)
above the "Latin text font" box, shown for a Chinese or Japanese
interface, East Asian text in the selection or a selection with an
Asian font. OK sets only the fonts changed, in one undo step.
- format.font takes "eastAsia" (w:eastAsia) besides "name"; either or
both; format.state reports fontEastAsia.
- fonts: the scan reads each font's OS/2 code pages, so
FontDb::east_asian_families lists CJK fonts without loading them.
- Tests: dialog sets each font on its own; layout sets CJK text in the
run's Asian font and Latin text in its Latin font (skipped without an
installed CJK font); command and undo; the scan finds a CJK font.
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>
- Two rows like Word's mini toolbar (font, size, grow, shrink; B I U S, highlight,
font colour, four alignments), sized from the ribbon widgets' real metrics so no
control is clipped any more (the one-row bar cut off everything after Align Left).
- Combo lists drop from the box's left edge instead of from the arrow.
- The dropdown triangle fills its box, so it no longer shrinks to a dot beside
combos and split buttons.
- Font list entries are an ink mask tinted with the theme's text colour, so they no
longer sit on white strips in dark mode.
Tests: the bar drawn over a real selection contains every control and is exactly as
wide as them; font entries take the theme text colour (dark and light) and paint no
background; the ink mask keeps coverage and drops the paper.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
- Find what understands Word's ^ codes (^p ^t ^? ^# ^$ ^^ ^l ^m ^n ^b ^+ ^= ^s ^~ ^- ^w ^g ^% ^v,
^nnn / ^unnnn); Replace with adds ^& (found text) and ^c (clipboard). `special: false` reads
the text literally.
- Use wildcards: ? * [abc] [a-z] [!x] {n} {n,} {n,m} @ < > ( ), \1-\9 in Replace with; always
matches case, like Word. Patterns are translated to the linear-time regex engine (no
catastrophic backtracking), length and counts capped, bad patterns are errors.
- ^p matches paragraph marks between sibling paragraphs; a ^p in the replacement splits the
paragraph. Replace All stays one undo step.
- Match prefix / suffix, Ignore punctuation / white-space characters.
- Dialog: Use wildcards checkbox, Search Options with the new options and a Special menu that
inserts codes into the focused field (a shorter list with wildcards); strings in all 9 catalogs.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
styles.updateToMatch copied only the direct run/paragraph formatting at the
caret onto the style, so text whose look came from its own style (Normal,
Title, ...) changed nothing; it then wiped that paragraph's direct formatting
without applying the style, so the selection lost its look too.
Now the style takes the selection's effective formatting (paragraph style
chain, character style chain, direct formatting) as a diff against what the
style currently gives, keeping its outline level and numbering. The selected
paragraphs take the style and drop only the direct formatting it now carries.
The Home styles gallery tiles get the right-click menu (Update to Match
Selection, Modify...).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Outline view: Outlining tab, levels, collapse/expand, Show Level (#493)
- View › Outline is its own view mode: the body on one long sheet, each paragraph indented
by its outline level behind a symbol drawn in code (circled plus for a heading with content,
circled minus for one without, a dot for body text); paragraph indents and alignment hidden.
- Collapsed headings hide their subtree and get a wavy line; Show Level 1-9 / All; Show First
Line Only; Show Text Formatting off draws everything in the Normal style. All of it is view
state (ViewState.outline), not saved; collapsed headings are found again by their text after
edits, and a caret entering hidden text moves on to the next shown paragraph.
- Commands: outline.promoteToHeading1/promote/demote/demoteToBody/level/moveUp/moveDown,
outline.expand/collapse/toggle/selectSubtree, outline.showLevel (+ showLevel1-9, showAll),
outline.firstLineOnly, outline.showFormatting, outline.close, outline.state. In Outline view
Move Up/Down and Promote/Demote take a collapsed heading's hidden content along.
- Keys: Alt+Shift+Plus/Minus expand/collapse, Alt+Shift+1-9 and Alt+Shift+A Show Level.
- Outlining tab (next to File while in Outline view): level box, promote/demote, move,
expand/collapse, Show Level, the two check boxes, Close Outline View. Clicking a symbol
selects the heading with its content; double-clicking expands or collapses it.
- Catalog: Outlining tab rows (421/453, 92.9%); translations in all nine catalogs; docs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Docs: drop the stale shortcut and dialog rows left by the rebase (#493)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Templates and Add-ins: attached template, update styles, Organizer (#377)
- Model + DOCX: Settings keep the attached template (w:attachedTemplate's external
relationship of the settings part) and w:linkStyles; both round-trip, written in
CT_Settings order (after w:mirrorMargins, before w:trackRevisions).
- Engine: tools.templates (state), tools.attachTemplate, tools.detachTemplate,
tools.linkStyles, tools.updateStyles (copy the template's styles and defaults, one
undo step; path or base64 data), styles.organizer (list), styles.copyFrom (copies
missing bases, linked halves and the lists a style numbers with), styles.rename.
The template path comes from the document, so it is never read automatically (not on
open either): only an explicit tools.updateStyles / attach with update reads it.
- UI: View > Templates > Templates and Add-ins dialog (path, Attach..., automatically
update document styles, Organizer...) and a two-list Organizer (Copy, Delete,
Rename...); strings in every catalog, Estonian included.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Review fixes for #382: never follow a template path to another machine
A document's w:attachedTemplate can name \\host\share\T.dotx, //host/share or
file://host/share. Opening it (Update Styles, Organizer) would make Windows
authenticate to that host over SMB and leak the user's NTLM credentials.
local_path now returns None for network paths; regression test added.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #377: refresh catalog count and parity checklist after rebase
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
File › Options is now a pane list (General, Display, Proofing, Save,
Language, Accessibility, Advanced, Agents), one function per pane in
crates/ui-egui/src/options.rs. Every setting shown is wired:
- General: theme, interface size (#475), mini toolbar on selection, dark page, community
button, user name and initials (comments use the initials).
- Display: tab characters, spaces, paragraph marks and hidden text shown
with Show/Hide ¶ off; rulers; print the page colour; update fields
before printing.
- Proofing: ignore words in capitals / with numbers / web and file
addresses (proof::Options, passed through layout), check spelling and
mark grammar as you type, custom dictionary add/remove (kept in ui.json).
- Save: AutoSave. Language: interface language. Accessibility: Read Aloud
speed and citations. Advanced: typing replaces selection, Insert key
overtype, overtype (text.overtype), recent documents count, measurement
units (dialogs, Layout ribbon indents and rulers).
file.options reads and sets the engine-side settings headlessly and only
opens the page when given no setting. ui.json loads leniently: a bad value
falls back to its default instead of dropping every setting.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Right-clicking a picture, shape, text box or chart selects it (as a
click would) and its context menu offers Wrap Text: In Line with Text,
Square, Tight, Through, Top and Bottom, Behind Text, In Front of Text
(current one checked), then Move with Text / Fix Position on Page.
The latter two are a new engine command, arrange.moveWithText, which
re-anchors a floating object's vertical position to its paragraph or
the page without moving it. The menu items live in one shared function
(ribbon::wrap_text_items, WRAP_STYLES) for the ribbon to reuse.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
- Model: `ParaProps::mirror_indents` (`w:mirrorIndents`, ECMA-376 §17.3.1.18), read and
written in DOCX; resolved `mirror_indents` and `suppress_line_numbers` (so styles count).
- Layout: a paragraph with mirrored indents moves over by its indents' difference on even
(left-hand) pages, so its left (inside) indent is on the right there.
- Paragraph dialog: Mirror indents (Left/Right become Inside/Outside), Don't add space between
paragraphs of the same style, and Formatting exceptions (Suppress line numbers, Don't
hyphenate) on Line and Page Breaks; OK sends only the options that changed, in the same
`para.set` as the indents and spacing.
- Strings in all nine catalogs; Reveal Formatting names mirrorIndents.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The canvas is now the AccessKit document node: body paragraphs become
paragraph, heading (with level) or list item (with level, grouped into
lists, list numbers as list markers) nodes holding one text run per laid-out
line with character lengths, positions, widths and word starts; tables are
table/row/cell nodes, pictures and charts image nodes named by alt text,
hyperlinks link nodes around their runs. The caret and selection are an
AccessKit text selection on the document node.
Only pages on screen and one either side are described, capped at 6000
nodes; nothing is built unless AccessKit is active, and the tree is rebuilt
only when the layout, selection, described pages or their screen place change.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Compare, Restrict Editing and AutoCorrect Options dialogs (#412)
- Compare / Combine Documents dialog: original (this document or a file) and
revised document, label changes with, More >> comparison settings, word or
character level, show changes in. review.compare honours case changes,
white space, formatting (tracked formatting changes), tables (compared and
kept, no longer dropped) and character level; the LCS no longer drops text
past its size cap. Unsupported settings are accepted and reported.
- Restrict Editing pane: formatting restrictions (locked styles,
w:formatting), editing restrictions, exceptions for everyone
(w:permStart/permEnd), Start Enforcing Protection with an optional password
(SHA-512, salt, spin count 100000; ECMA-376 §17.15.1.29) and Stop
Protection. Enforced in Session::run: outside the exceptions nothing may
change; undo can't lift protection; passwords never land in Repeat/macros.
DOCX round trip of protection, hash attributes, locked styles and ranges.
- AutoCorrect Options dialog: replacement list (add/replace/delete, built-in
entries too), capitalization options with exceptions, AutoFormat As You
Type switches, Math AutoCorrect list. Saved per user in ui.json; damaged
values read as defaults. Names of days are capitalised.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Restrict Editing: verify Word's legacy protection hash; refuse unsupported forms (#412)
- Stop Protection checks Word's legacy w:documentProtection hash
(w:cryptAlgorithmSid/w:hash/w:salt/w:cryptSpinCount) like Word: the password
becomes the legacy 32-bit key (ECMA-376 Part 4 §14.8.1 initial code array and
encryption matrix), its reversed bytes as an upper-case hex string are hashed
with the salt prepended and the iteration number appended. A wrong password
is rejected; the form no longer unlocks with any password.
- Unsupported or damaged hashes (MD2/MD4/MD5 and other SIDs, w:algIdExt,
unknown algorithmName, spin counts above 10,000,000, bad base64) refuse with
"This password type isn't supported" and the protection stays on. A
protection without a hash still stops without a password.
- Known-answer tests: the specification's "Example" key 0x64CEED7E and an
independently computed SHA-1 hash; pane message in all 9 catalogs.
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>
* Dialogs: Line Numbers, Hyphenation, Language, Envelopes and Labels (#407)
- Line Numbers (Line Numbering Options…): add numbering, start at, from
text (auto or a distance), count by, restart each page/section or
continuous; `layout.lineNumbers` takes start/countBy/distance.
- Hyphenation Options: automatic, words in CAPS, hyphenation zone,
consecutive-hyphen limit; kept in DOCX (w:hyphenationZone,
w:doNotHyphenateCaps, w:consecutiveHyphenLimit) and honoured by
layout. Manual…: `layout.manualHyphenation` finds words at line starts
that would fit hyphenated on the line above; Yes inserts an optional
hyphen, No skips.
- Language: localized language list, do not check spelling or grammar,
detect language automatically (per-user preference), Set As Default
(document default language).
- Envelopes: delivery/return address, common sizes (#10, #9, Monarch,
DL, C4, C5, C6, B5); Add to Document inserts an envelope section
before the letter. Labels: common sheets (Letter and A4), full page or
a single label at row/column, one per recipient; New Document builds
the sheet with gap columns.
- Ribbon: Line Numbering Options…, Hyphenation Options…, Manual…; the
Language, Envelopes and Labels buttons open their dialogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Remove duplicate Label: keys in ru and tr (#407)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
- Model: Float carries AltText (title, decorative; the description of shapes and groups);
InlineObject::alt_text/set_alt_text/is_decorative; TableProps.description. Capped input.
- DOCX: wp:docPr / pic:cNvPr / wps:cNvPr descr, title and the [MS-ODRAWXML] adec:decorative
extension read and written for pictures, shapes, text boxes, groups and members, charts;
OLE objects get their alt text in the kept XML; w:tblCaption + w:tblDescription.
- Commands: object.altText {text?, title?, decorative?} (picture.altText kept as alias),
table.altText {title?, text?}, view.accessibilityPane, view.altTextPane.
- Checker (cmd/accessibility.rs): alt text on every object and table (decorative passes),
heading order, header rows, blank table rows/columns, merged cells, WCAG contrast,
floating objects, blank paragraphs; issues carry pos/select and fix actions.
- UI: Accessibility pane (grouped, jump-to, fix buttons), Alt Text pane, Picture/Shape
Format > Accessibility > Alt Text, right-click > View Alt Text...; strings in all catalogs.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* References dialogs: Caption, Index, Table of Contents, Create Source (#402)
- Caption: labels (built-in plus New/Delete Label, kept with the interface
settings), above/below the item, leave the label out, Numbering (format,
chapter number from a heading level with a separator) -> SEQ \* \s and
STYLEREF n \s fields; Update Field renumbers SEQ with \s restarts, \r, \c, \h.
- Index: indented/run-in, columns, right-aligned page numbers with tab leader,
letter headings -> INDEX \h \c \e \r; Mark Index Entry (subentry,
cross-reference, page-range bookmark, bold/italic page numbers, Mark All)
-> XE \t \r \b \i. The index is rebuilt honouring them; columns become a
continuous section of their own. Index commands move to cmd/index.rs.
- Custom Table of Contents: page numbers, right align, tab leader,
hyperlinks, levels, Options (heading styles, other styles with levels,
outline levels, TC fields), Modify for TOC 1-9 -> TOC \o \h \z \u \t \f
\l \n \p; updating honours them (hyperlinks to _Toc bookmarks).
- Source Manager and Create/Edit Source: source types with their fields,
show every bibliography field (new Source fields); Insert Citation menu
lists the sources and Add New Source.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fmt (#402)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Draw: Lasso Select, Add Pen, Ink to Shape and Ink Replay (#395)
- draw.lasso: with no points picks up the lasso; with a loop on a page it
selects the strokes mostly inside it and drawings whose centre is inside.
draw.lassoMove / draw.lassoColor / draw.lassoDelete move, recolour and
delete the selection (one undo step each); it lasts until the document
changes otherwise. In the app: drag a loop, drag the selection to move it,
Delete deletes, Esc lets go, the Color menu recolours it.
- draw.addPen / draw.removePen: a pen gallery (kind, colour, width; capped,
validated) shown after the built-in pens, kept in ui.json; damaged entries
are dropped on reading. Right-click a gallery pen to remove it.
- draw.inkToShape: toggles Ink to Shape (pen and pencil strokes that look
like a line, rectangle/square, ellipse/circle, triangle or polygon of up to
eight corners become that shape, in the same undo step); with ink
lasso-selected it converts the selection (draw.convertInk). Our own
recogniser (cmd/ink_shape.rs): chord test for lines, closure test,
ellipse RMS fit, closed RDP simplification with corner merging.
- draw.replay: Ink Replay plays the strokes back in drawing order with a
Rewind / Play-Pause / Forward / Close bar; pages are drawn without ink
meanwhile. Each new stroke records its drawing order (Freeform::order),
written to DOCX in the drawing name (`Ink Pen 3 #12`) and read back.
Catalog 402/434 -> 406/434 (93.5%). New strings in all nine catalogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Regenerate parity checklist after rebase (#395)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Adds wordcraft_render::icon_lib, 61 monochrome icons in ten categories
(Arrows, Communication, Office, People, Nature, Weather, Shapes, Symbols,
Technology, Transport). They are original WordCraft artwork: simple
geometric line drawings designed on a 24x24 grid as path data in code, with
no icon files and nothing copied from another icon set.
insert.icon {"id", "color"?, "size"?} renders the chosen icon with vello_cpu
to a transparent PNG at about 300 ppi and inserts it at the caret through the
ordinary picture path, so it saves to .docx and every other format. Without
an id it lists the library (narrowed by query and category) and asks the
front end for the picker. picture.iconColor repaints a one-colour picture
in a new colour and keeps its transparency.
Insert > Illustrations > Icons opens a dialog with a search box, category
chips and a grid of icons drawn live by the egui painter from the same path
data; Insert or a double click places the selection. Picture Format > Adjust
gains an Icon Color menu. New strings are translated in every catalog.
Catalog parity 393/434 -> 394/434 (90.8%).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Home › Font › Enclose Characters: circle, square, triangle or diamond around a character (#297)
New command format.enclose {"shape": "circle|square|triangle|diamond" | null,
"style"?: "shrink|enlarge", "text"?: string} puts one full-width or up to two
half-width characters in an enclosure, changes its shape, or (null) takes the
text back out. It is stored the way ECMA-376 describes it: an EQ field with the
overstrike switch, eq \o\ac(○,字), with the text as its plain-text result.
Layout now draws EQ \o fields: the arguments overstruck as one cluster
(centred, \al or \ar). An enclosure symbol among them is drawn as a vector
shape around the text in the text's colour, so it never depends on the font
having ○ □ △ ◇; "shrink text" scales the text into a one-em shape, "enlarge
symbol" grows the shape around text at its own size. Screen and PDF both get
it through the display list. Imported Word documents with enclosed characters
used to show nothing (the field has no result); they now render.
.docx: written as Word stores enclosed characters (begin, the code split so the
symbol and the text have their own run sizes, end, no result); the reader
recovers the style from those sizes. The style lives in the model as a trailing
\* enlarge on the code, never written to a file.
Home › Font gets an Enclose Characters button that opens a dialog (Style:
None / Shrink text / Enlarge symbol, Text, Enclosure list, a drawn preview);
scripts with a shape never see it. Strings in every interface catalog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Merge fix: keep both sides (enclosures and ruby; tests); regenerate parity checklist
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Customize Keyboard dialog (#368)
Tools > Customize Keyboard (tools.customizeKeyboard): pick a category and
a command, see its keys, press a combination to see which command uses it,
then Assign / Remove / Reset All. Also reachable from File > Options
(Keyboard shortcuts: Customize...) and command search.
- engine: KeyMap (assigned key -> command, removed built-in keys), consulted
before the registry's built-in shortcuts; keys compared in one canonical
form. Saved maps read leniently: junk entries and unknown commands are
dropped, never failing the rest of the preferences.
- tools.customizeKeyboard: with params (assign/remove/reset/command/key) it
only changes or reports the map; without params it opens the dialog.
- ui: key dispatch and menu/tooltip shortcut text use the user map; the map
persists in ui.json; more keys (Q, F1-F12, 4/6/7, Insert) can be bound.
- i18n strings in every catalog; docs/ui-parity, gaps #24, ROADMAP log.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Customize Ribbon and Quick Access Toolbar (#379)
Tools > Customize Ribbon (tools.customizeRibbon): one dialog with two
pages. Ribbon: commands by category next to the ribbon's tabs; show or
hide and reorder tabs, add custom tabs and custom groups (on built-in or
custom tabs) holding any commands, rename, remove, reset. Quick Access
Toolbar: add, remove and reorder its commands, reset.
- engine: RibbonLayout (tab order and visibility, custom groups, the
toolbar's commands). Saved layouts read leniently: junk, duplicates,
reserved names and unknown commands are dropped.
- tools.customizeRibbon: with params (hide/show, newTab, newGroup, add,
remove, rename, move, qatAdd/qatRemove/qatMove, reset, layout) it only
changes or reports the layout; without params it opens the dialog.
- ui: the tab strip and ribbon follow the layout (custom groups draw the
commands' own icons, else a lettered tile); the Quick Access Toolbar
shows the user's commands; right-click any ribbon button to add it to
(or remove it from) the toolbar or open the dialog; the toolbar's ...
button opens a menu of common commands and More Commands... (#376);
File > Options has a second Customize button; layout persists in
ui.json under `ribbon`.
- Open and Print icons drawn in code; i18n strings in every catalog;
docs/ui-parity, gaps #24, ROADMAP log, parity checklist.
Fixes#376.
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>
Tools > Customize Keyboard (tools.customizeKeyboard): pick a category and
a command, see its keys, press a combination to see which command uses it,
then Assign / Remove / Reset All. Also reachable from File > Options
(Keyboard shortcuts: Customize...) and command search.
- engine: KeyMap (assigned key -> command, removed built-in keys), consulted
before the registry's built-in shortcuts; keys compared in one canonical
form. Saved maps read leniently: junk entries and unknown commands are
dropped, never failing the rest of the preferences.
- tools.customizeKeyboard: with params (assign/remove/reset/command/key) it
only changes or reports the map; without params it opens the dialog.
- ui: key dispatch and menu/tooltip shortcut text use the user map; the map
persists in ui.json; more keys (Q, F1-F12, 4/6/7, Insert) can be bound.
- i18n strings in every catalog; docs/ui-parity, gaps #24, ROADMAP log.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
- para.moveUp / para.moveDown (Alt+Shift+Up/Down, Ctrl+Shift+Up/Down on macOS): the
paragraphs the selection touches swap with their neighbour in the same story or cell,
keeping style and list membership; selection follows; one undo step; refused at the
container edges, next to a table and across a section break.
- para.promote / para.demote (Alt+Shift+Left/Right, Ctrl+Shift+Left/Right on macOS):
heading level up/down (Heading 1..9), promoted body text takes the level of the heading
before it, list items change level like Shift+Tab/Tab.
- edit.goBack (Shift+F5): cycles the caret through the last four edit places.
- text.toggleUnicode (Alt+X; unbound on macOS): hex code <-> character, undoable. The
canvas no longer types the key's letter after an Alt shortcut ran.
- Catalog rows, translations in every interface catalog, typing/UI parity docs, tests.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Show field codes instead of results with View > Field Codes (Alt+F9) or
per field with Toggle Field Codes (Shift+F9); a field showing its code is
drawn as { CODE } on light grey. Edit a field's code in a small popup
(double-click a field showing its code, right-click > Edit Field..., or
fields.setCode), and Ctrl+F9 asks for a new field's code. F9 updates
fields and shows the selected ones' results again.
New commands: fields.unlink (Ctrl+Shift+F9) replaces fields with their
text, fields.lock / fields.unlock (Ctrl+F11 / Ctrl+Shift+F11) keep a
result through updates (already saved as w:fldLock), fields.next /
fields.previous (F11 / Shift+F11) select the next or previous field, and
fields.selected reports the fields in the selection. Range fields
(citations) can be locked, unlinked, edited and stepped through too.
The right-click menu gains Update Field, Edit Field... and Toggle Field
Codes when the selection holds a field. Strings are translated in every
catalog; parity catalog, ui-parity, gaps and roadmap updated.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Bind Word's documented default shortcuts that were missing to existing
commands, plus a few small new commands: Reset Paragraph Formatting
(Ctrl+Q), Reset Character Formatting (Ctrl+Space, which used to clear all
formatting), em dash, copyright, registered, trademark and ellipsis, DATE /
TIME / PAGE fields (Alt+Shift+D/T/P), Print Layout / Outline / Draft /
Split views, Word Count (Ctrl+Shift+G), Bookmark, Alt+F7, Ctrl+Page Up/Down
find browsing, Shift+F12 / Ctrl+F12 / Ctrl+Shift+F12 / Ctrl+F2 / Ctrl+F4,
Mark Index Entry, Mark Citation and the mail merge keys.
Where macOS claims Word's key (Cmd+Q quits, Cmd+Space is Spotlight, Cmd+Shift+G
is Find Previous, Option+letter types symbols), the binding follows Word for
Mac or is left off there. F4 Repeat, Q and F2 now reach the registry from the
keyboard, and an Alt shortcut no longer also types its key's character.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Every dialog was anchored CENTER_CENTER, which egui re-applies each frame,
so a tab of a different height split the change between top and bottom and
the tab strip jumped. Dialogs are now centred on the frame they open, then
anchored CENTER_TOP at that top edge until they close; only the bottom edge
moves. egui still keeps the window on screen.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
File › Options › General gets an Interface size choice (80–200 %), kept in
ui.json and applied as egui's zoom factor, so it multiplies the display
scale the system reports instead of replacing it. Junk saved values read
as 100 % and out-of-range ones are clamped. Agents use ui.interfaceSize
{"percent"}. Document zoom (view.zoom) is untouched; egui's keyboard zoom
stays off, so Word shortcuts never change the interface size.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Typing in the ribbon's or the mini toolbar's font box now lists the
installed families that match, under the field: exact name first, then
names starting with the text, then names containing it, ignoring case
("roman" finds Times New Roman). Up/Down move the highlight, Enter or a
click applies it ("arial" applies Arial), Escape restores the current
font. Text that matches no family is still applied as typed, as Word
allows fonts that aren't installed.
The search is opt-in (widgets::font_combo); the font size box keeps
literal entry. Matches are computed only when the text changes and the
list stays virtualised (#121).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Draw Table and Eraser: draw tables and split or merge cells with a pen (#303)
Insert › Tables › Draw Table and Table Layout › Draw › Draw Table / Eraser.
table.draw takes a pen stroke in page points: a rectangle outside a table
inserts a one-cell table of that size at the paragraph under its start; a
straight vertical or horizontal line inside a table splits the cells it
crosses (new grid column or row; untouched cells span it). table.eraser
merges the two cells beside the border nearest a point. Each stroke is one
undo step. In the app the buttons toggle pen and eraser modes with a live
preview, a crosshair with the tool's icon, and Esc to stop; canvas clicks
don't move the caret while a tool is on. Splitting through part of a
vertically merged cell and erasing beside one are refused with a clear
error. Catalog 384/430 -> 387/430.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Draw tab: pen, pencil and highlighter ink, stroke eraser and Hide Ink (#307)
Ink strokes are floating freeform shapes: a new Freeform geometry (paths in
shape-local coordinates, opacity, and the pen that drew it) on
InlineObject::Shape with ShapeKind::Freeform. draw.stroke inserts one
anchored to the paragraph under the first point (one undo step; points
capped per stroke, strokes capped per page, NaN dropped); draw.erase /
draw.eraser with a point deletes the stroke under it; draw.pen, draw.pencil,
draw.highlighter, draw.eraser and draw.select set the Draw tab's tool in the
session view state; review.hideInk toggles showing ink on screen.
The canvas draws a live stroke while a pen is dragged and commits it on
release; the eraser deletes strokes it passes over (one undo step per drag);
Esc or Select goes back to selecting. Draw tab buttons show the tool in use,
with colour and thickness menus; Review gets an Ink group with Hide Ink.
Ink renders with round caps and joins on screen and in PDF; highlighters are
translucent and behind the text. DOCX writes freeforms as wps:wsp with
a:custGeom (moveTo/lnTo) in a wp:anchor and reads them back, along with
simple custom-geometry freeforms from other files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #315 review fixes: eraser undo test, sanitized tap dots, custGeom path cap, ink docs
- draw.eraser with a point erases through draw.erase: test that it is one
undo step (undo/redo) and marks the document dirty.
- Render and PDF: a single-point stroke's dot is drawn at the sanitized
point, not the raw first one.
- DOCX custGeom reading stops once MAX_PATHS paths (subpaths included) are
collected, across a:path elements.
- docs/file-format-parity.md: ink is saved as custom geometry, not InkML, so
Word shows it as a freeform.
- widgets test: theme::apply takes an Appearance (#317).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Draw tab: pen, pencil and highlighter ink, stroke eraser and Hide Ink (#307)
Ink strokes are floating freeform shapes: a new Freeform geometry (paths in
shape-local coordinates, opacity, and the pen that drew it) on
InlineObject::Shape with ShapeKind::Freeform. draw.stroke inserts one
anchored to the paragraph under the first point (one undo step; points
capped per stroke, strokes capped per page, NaN dropped); draw.erase /
draw.eraser with a point deletes the stroke under it; draw.pen, draw.pencil,
draw.highlighter, draw.eraser and draw.select set the Draw tab's tool in the
session view state; review.hideInk toggles showing ink on screen.
The canvas draws a live stroke while a pen is dragged and commits it on
release; the eraser deletes strokes it passes over (one undo step per drag);
Esc or Select goes back to selecting. Draw tab buttons show the tool in use,
with colour and thickness menus; Review gets an Ink group with Hide Ink.
Ink renders with round caps and joins on screen and in PDF; highlighters are
translucent and behind the text. DOCX writes freeforms as wps:wsp with
a:custGeom (moveTo/lnTo) in a wp:anchor and reads them back, along with
simple custom-geometry freeforms from other files.
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>
* Add Tabs and Borders and Shading dialogs (#320)
Tabs: lists the paragraph's tab stops with position, alignment and leader,
Set / Clear / Clear All and the default interval. Opens from the Paragraph
dialog's Tabs… button, by double-clicking a tab marker on the ruler, or
para.tabs without params. OK is one para.tabs call, which now takes
set / clear / clearAll / default (clearing a style's stop records a clear
stop over it).
Borders and Shading: Borders (None / Box / Shadow / 3-D / Custom, style,
colour, width, paragraph or text, preview with clickable sides), Page
Border (same, whole document or this section) and Shading (fill, paragraph
or text) tabs. Opens from Home › Paragraph › Borders › Borders and
Shading… and Design › Page Borders. OK runs only the changed parts as one
undo step. para.borders and design.pageBorders take exact "sides" (and
design.pageBorders a style and applyTo). Shadow and 3-D are drawn as
heavier sides because the model has no border shadow flag; shading
patterns are omitted because the model keeps a solid fill only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Keep charts, SmartArt and OLE objects when saving DOCX (#319)
Charts and SmartArt diagrams were drawn since #292 but the writer skipped
them, and OLE objects kept only their picture, so opening and saving a
document silently dropped them.
The reader now keeps each object's own markup (the a:graphic of a chart or
diagram, the whole w:object of an OLE object) on the model object, and every
part it reaches through relationships (chart part, embedded workbook, colour
and style parts; diagram data, layout, quick style, colours and drawing; OLE
embedding and picture) as opaque bytes in Document::passthrough, with their
content types and relationships in a manifest, as the VBA project already is.
The writer emits the object with fresh relationship ids pointing at the
re-added parts, keeping each part's own relationship ids and content types.
A chart or diagram gets its frame from the object's current size and
position; an OLE object gets its new size written into its VML shape. A part
whose name the writer uses itself is renamed, and an object pasted more than
once gets its own copies of every part (sharing past a size budget). Part
counts, relationship counts, markup size and copy bytes are all capped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Columns, Symbol and Field dialogs (#321)
Layout > Columns > More Columns... opens a Columns dialog: One/Two/Three/
Left/Right presets, number of columns, per-column width and spacing,
Equal column width, Line between, a page preview and Apply to (whole
document, this section, this point forward). OK is one layout.columns
call, which now takes unequal widths and an apply target (forward adds a
continuous section break first).
Insert > Symbol > More Symbols... opens a Symbol dialog: font picker,
Unicode subset list that jumps through a grid of only the characters the
font has (drawn in that font), recently used symbols, a hex character
code, and a Special Characters tab. insert.symbol takes a font for the
symbol alone; typing after it goes on in the text's font. The ribbon's
Symbol gallery shows recently used symbols first.
Insert > Quick Parts > Field... opens a Field dialog: categories and the
fields WordCraft computes, date pictures, number formats, bookmark /
sequence / merge field names, preserve formatting, a live field code and
a Field Codes view to type the code directly; OK runs insert.field.
Also: date pictures read correctly when switches follow them, and am/pm
and seconds work in date and time pictures. Strings in every catalog;
gaps #7 and ui-parity updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #321: Field dialog descriptions in our own words (clean room)
Reword the one-line field descriptions and the empty-selection prompt so
none reads like Word's; translations keep their meaning under the new keys.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #320: test that a default-tab-only para.tabs change undoes and redoes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Keep charts, SmartArt and OLE objects when saving DOCX (#319)
Charts and SmartArt diagrams were drawn since #292 but the writer skipped
them, and OLE objects kept only their picture, so opening and saving a
document silently dropped them.
The reader now keeps each object's own markup (the a:graphic of a chart or
diagram, the whole w:object of an OLE object) on the model object, and every
part it reaches through relationships (chart part, embedded workbook, colour
and style parts; diagram data, layout, quick style, colours and drawing; OLE
embedding and picture) as opaque bytes in Document::passthrough, with their
content types and relationships in a manifest, as the VBA project already is.
The writer emits the object with fresh relationship ids pointing at the
re-added parts, keeping each part's own relationship ids and content types.
A chart or diagram gets its frame from the object's current size and
position; an OLE object gets its new size written into its VML shape. A part
whose name the writer uses itself is renamed, and an object pasted more than
once gets its own copies of every part (sharing past a size budget). Part
counts, relationship counts, markup size and copy bytes are all capped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Columns, Symbol and Field dialogs (#321)
Layout > Columns > More Columns... opens a Columns dialog: One/Two/Three/
Left/Right presets, number of columns, per-column width and spacing,
Equal column width, Line between, a page preview and Apply to (whole
document, this section, this point forward). OK is one layout.columns
call, which now takes unequal widths and an apply target (forward adds a
continuous section break first).
Insert > Symbol > More Symbols... opens a Symbol dialog: font picker,
Unicode subset list that jumps through a grid of only the characters the
font has (drawn in that font), recently used symbols, a hex character
code, and a Special Characters tab. insert.symbol takes a font for the
symbol alone; typing after it goes on in the text's font. The ribbon's
Symbol gallery shows recently used symbols first.
Insert > Quick Parts > Field... opens a Field dialog: categories and the
fields WordCraft computes, date pictures, number formats, bookmark /
sequence / merge field names, preserve formatting, a live field code and
a Field Codes view to type the code directly; OK runs insert.field.
Also: date pictures read correctly when switches follow them, and am/pm
and seconds work in date and time pictures. Strings in every catalog;
gaps #7 and ui-parity updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Merge fix: #340's rotation round-trip test builds an Image with #333's ole field
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Add Tabs and Borders and Shading dialogs (#320)
Tabs: lists the paragraph's tab stops with position, alignment and leader,
Set / Clear / Clear All and the default interval. Opens from the Paragraph
dialog's Tabs… button, by double-clicking a tab marker on the ruler, or
para.tabs without params. OK is one para.tabs call, which now takes
set / clear / clearAll / default (clearing a style's stop records a clear
stop over it).
Borders and Shading: Borders (None / Box / Shadow / 3-D / Custom, style,
colour, width, paragraph or text, preview with clickable sides), Page
Border (same, whole document or this section) and Shading (fill, paragraph
or text) tabs. Opens from Home › Paragraph › Borders › Borders and
Shading… and Design › Page Borders. OK runs only the changed parts as one
undo step. para.borders and design.pageBorders take exact "sides" (and
design.pageBorders a style and applyTo). Shadow and 3-D are drawn as
heavier sides because the model has no border shadow flag; shading
patterns are omitted because the model keeps a solid fill only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* cargo fmt after merging main
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Keep charts, SmartArt and OLE objects when saving DOCX (#319)
Charts and SmartArt diagrams were drawn since #292 but the writer skipped
them, and OLE objects kept only their picture, so opening and saving a
document silently dropped them.
The reader now keeps each object's own markup (the a:graphic of a chart or
diagram, the whole w:object of an OLE object) on the model object, and every
part it reaches through relationships (chart part, embedded workbook, colour
and style parts; diagram data, layout, quick style, colours and drawing; OLE
embedding and picture) as opaque bytes in Document::passthrough, with their
content types and relationships in a manifest, as the VBA project already is.
The writer emits the object with fresh relationship ids pointing at the
re-added parts, keeping each part's own relationship ids and content types.
A chart or diagram gets its frame from the object's current size and
position; an OLE object gets its new size written into its VML shape. A part
whose name the writer uses itself is renamed, and an object pasted more than
once gets its own copies of every part (sharing past a size budget). Part
counts, relationship counts, markup size and copy bytes are all capped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Columns, Symbol and Field dialogs (#321)
Layout > Columns > More Columns... opens a Columns dialog: One/Two/Three/
Left/Right presets, number of columns, per-column width and spacing,
Equal column width, Line between, a page preview and Apply to (whole
document, this section, this point forward). OK is one layout.columns
call, which now takes unequal widths and an apply target (forward adds a
continuous section break first).
Insert > Symbol > More Symbols... opens a Symbol dialog: font picker,
Unicode subset list that jumps through a grid of only the characters the
font has (drawn in that font), recently used symbols, a hex character
code, and a Special Characters tab. insert.symbol takes a font for the
symbol alone; typing after it goes on in the text's font. The ribbon's
Symbol gallery shows recently used symbols first.
Insert > Quick Parts > Field... opens a Field dialog: categories and the
fields WordCraft computes, date pictures, number formats, bookmark /
sequence / merge field names, preserve formatting, a live field code and
a Field Codes view to type the code directly; OK runs insert.field.
Also: date pictures read correctly when switches follow them, and am/pm
and seconds work in date and time pictures. Strings in every catalog;
gaps #7 and ui-parity updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #321: Field dialog descriptions in our own words (clean room)
Reword the one-line field descriptions and the empty-selection prompt so
none reads like Word's; translations keep their meaning under the new keys.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Keep charts, SmartArt and OLE objects when saving DOCX (#319)
Charts and SmartArt diagrams were drawn since #292 but the writer skipped
them, and OLE objects kept only their picture, so opening and saving a
document silently dropped them.
The reader now keeps each object's own markup (the a:graphic of a chart or
diagram, the whole w:object of an OLE object) on the model object, and every
part it reaches through relationships (chart part, embedded workbook, colour
and style parts; diagram data, layout, quick style, colours and drawing; OLE
embedding and picture) as opaque bytes in Document::passthrough, with their
content types and relationships in a manifest, as the VBA project already is.
The writer emits the object with fresh relationship ids pointing at the
re-added parts, keeping each part's own relationship ids and content types.
A chart or diagram gets its frame from the object's current size and
position; an OLE object gets its new size written into its VML shape. A part
whose name the writer uses itself is renamed, and an object pasted more than
once gets its own copies of every part (sharing past a size budget). Part
counts, relationship counts, markup size and copy bytes are all capped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Columns, Symbol and Field dialogs (#321)
Layout > Columns > More Columns... opens a Columns dialog: One/Two/Three/
Left/Right presets, number of columns, per-column width and spacing,
Equal column width, Line between, a page preview and Apply to (whole
document, this section, this point forward). OK is one layout.columns
call, which now takes unequal widths and an apply target (forward adds a
continuous section break first).
Insert > Symbol > More Symbols... opens a Symbol dialog: font picker,
Unicode subset list that jumps through a grid of only the characters the
font has (drawn in that font), recently used symbols, a hex character
code, and a Special Characters tab. insert.symbol takes a font for the
symbol alone; typing after it goes on in the text's font. The ribbon's
Symbol gallery shows recently used symbols first.
Insert > Quick Parts > Field... opens a Field dialog: categories and the
fields WordCraft computes, date pictures, number formats, bookmark /
sequence / merge field names, preserve formatting, a live field code and
a Field Codes view to type the code directly; OK runs insert.field.
Also: date pictures read correctly when switches follow them, and am/pm
and seconds work in date and time pictures. Strings in every catalog;
gaps #7 and ui-parity updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Merge fix: #340's rotation round-trip test builds an Image with #333's ole field
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>
* Define New Multilevel List and Track Changes Options dialogs (#328)
Home > Paragraph > Multilevel List > Define New Multilevel List edits all nine
levels with a live preview: number text and style, start, restart after a
level, linked paragraph style, alignment, aligned-at and text indent,
what follows the number with an optional tab stop, and the number's font. OK
runs the new list.define command (list.get reads a definition back). List
levels gain a restart-after level and a tab stop, which the counters and
layout honour and .docx reads and writes (lvlRestart, num tab); number
alignment now places the label.
Review > Tracking's launcher opens Track Changes Options: show comments, ink,
insertions and deletions, formatting; balloon mode; insertion and deletion
marks and colours; changed-line bars and their colour; track formatting. They
go through review.trackingOptions, are saved per user with the interface
preferences, and the page drawing honours the marks, colours, hidden
deletions, change bars and inline mode.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Parity checklist regenerated after merging main into #342
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>
Drawings carry an angle and horizontal/vertical flips on their placement
(Float::rot, flip_h, flip_v), read and written as DrawingML a:xfrm rot,
flipH and flipV for pictures, shapes, text boxes and groups; the written
effect extent covers the rotated bounds and reading takes that overhang
back out. Layout draws them turned about their centre (screen and PDF),
square wrap and inline lines keep clear of the rotated bounds, and hit
testing follows the turned shape. arrange.rotate now turns the frame
(90 degrees, flips) instead of rewriting pixels; arrange.rotation sets an
exact angle. The canvas frame has a rotation handle (Shift snaps to 15
degrees) and turned frames resize along their own axes; Picture Format
and Shape Format get a Rotation box and a Rotate menu. Text inside a
rotated text box stays upright for now.
Groups turn and flip as a whole (wpg:grpSpPr a:xfrm): members keep their
own turn inside the group's, are carried round its centre, and a group's
text boxes move with it. Shape effects are drawn inside the turn; a
shadow's direction follows DrawingML rotWithShape (kept on the model,
true when absent; Word's presets write 0, so their shadows keep their
page direction).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* System interface theme keeps following the OS; Dark page leaves the interface alone (#311, #312)
With Interface theme set to System, every frame resolved the OS appearance
to Light or Dark and passed that to egui's set_theme. egui then sent
SetTheme(Light/Dark) to the window, which on macOS sets an explicit
NSAppearance; winit ignores appearance changes while one is set, so later
OS switches never reached the app.
Both palettes are now installed as egui's light and dark styles (with the
same spacing and text styles), and System leaves egui's preference on
System with a light fallback, so egui picks the matching style from the
OS appearance it reports and the window keeps following the OS. Manual
Light and Dark still pin the window.
Dark page (View > Switch Modes, Options > Dark page) also forced the
interface dark; it now only changes how pages are drawn and the interface
follows the Interface theme setting alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Theme: update #314's test to the new theme::apply(ctx, Appearance) signature
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>
AutoSave writes only a file saved in this session, in a format that keeps
everything (.docx, .docm, .dotx, .dotm, .odt, .rtf, .json), and never in the
browser, where saving is a download. The title-bar switch still showed on for
a saved .md file (#196) and AutoSave looked available in the web app (#176),
so edits stayed unsaved with no explanation.
WordApp::autosave_block() now says why AutoSave can't cover the document
(browser, never saved, a format that can't keep everything, opened but not
saved here). The switch is greyed out with that reason as its tooltip, and
clicking it offers Save As (desktop) so saving as .docx turns AutoSave on.
In the browser, File > Options' AutoSave checkbox is disabled with the same
explanation. file.autosave (scripts, MCP, the switch) now sets the app's
AutoSave and returns the reason as an error when asked to turn it on where it
can't save; turning it off always works. New strings in every catalog.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Table sizing: Height/Width boxes, real AutoFit Contents, Table Properties dialog (#44)
- Table Layout > Cell Size gets Height and Width boxes (inches) for the caret cell's row
and column; each edit runs table.rowHeight / table.columnWidth, a drag is one Undo.
- AutoFit Contents now measures every column's narrowest (longest word) and widest
(unwrapped text) content with the layout's text shaping, shares the text width the way
Word does, and writes the widths into the table grid and cell widths.
- Table Properties opens a dialog (table width, alignment, indent; row height and rule,
break across pages, header row; column width; cell width and vertical alignment). OK
applies only what changed as one table.properties step; with settings the command still
works headlessly, and without them it reports the current values.
- table.columnWidth clears the table's preferred width so the column keeps the new size;
table.rowHeight takes an optional exact rule.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Progress docs: Table Properties dialog and AutoFit Contents landed (#44)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Table sizing: saturating column-span sum in sync_cell_widths
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Merge fix: one region_text (main's) after #287 met the landed table-style code
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>
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>