* #1002: equation pictures share one texture, keeping theme colours on Windows
The Equation tab's gallery tiles, structure buttons and symbols were a
texture each: hundreds of small textures, created as galleries opened and
all freed at once when the preview cache filled. On Windows (DirectX 12)
tiles then showed other textures' memory (inverted pictures, red and teal
fills, damaged interface letters). They are now packed into one atlas
texture written in place, the way egui keeps its glyphs; it starts over
when full (at most once a frame).
Tests: every equation picture composited in both themes is only the
theme's greys; all pictures share one texture, none freed while browsing,
none overlapping; the atlas packs rows and starts over when full.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1002: the theme test looks for paper anywhere, not in the corner (Windows fonts reach it)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1002: equation atlas uses up to four pages and never wipes a page drawn from this frame
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>
* #816: copied equations reach other programs as MathML
A copy or cut whose selection holds an equation now also puts HTML on the
system clipboard, with each equation as Presentation MathML (wrapped in
<semantics> with the linear format as an application/x-unicodemath
annotation), so Word, LibreOffice, Google Docs and browsers paste a
formatted equation instead of `y=2x_s`.
- formats: new mathml module, a writer from the equation tree (runs,
fractions, scripts and pre-scripts, radicals, n-ary operators, delimiters,
functions, limits, accents, bars, border boxes, group characters, equation
arrays, matrices, phantoms) and a bounded reader (our annotation, else the
presentation elements, else a TeX annotation). HTML export writes
equations as MathML; HTML import (open and paste) reads <math> back.
- engine: edit.copy/edit.cut (and column copy) return `html` and keep
Session::clipboard_html when the fragment holds an equation.
- ui-egui: Services::set_clipboard_html; copy sites put HTML with the text
through it, falling back to egui's text copy.
- wordcraft: arboard set().html() with the text alternative; on Wayland
without data-control the text-only path is kept.
OMML on the clipboard (Word's "Office Open XML") needs custom clipboard
formats arboard can't set; left as a follow-up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #816: honest clipboard docs; MathML reader fixes (token cap, function names, column vectors)
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>
* #822: a chosen interface size is absolute; Auto keeps the display scale
On a display the system reports at 200 % (a bare window manager on X11,
where winit guesses the scale from the monitor's DPI), "100%" drew at
200 %. As in PhotoCraft and PdfCraft (pdfcraft#1005), a chosen size now
divides the display scale back out, so 100% is one screen pixel per
point. The new default, Auto, keeps the display scale and its menu entry
shows the size it draws at. Sizes run 75-300 %; ui.json moves to
uiScale and migrates the old interfaceSize (100 -> Auto).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #822: an old interface size becomes the size it drew at; a valid uiScale wins
A pre-#822 interfaceSize was on top of the display scale, so it is kept
until the first frame knows that scale and becomes old x native
(125% on a 2x display stays at 250%). Note that on the web a chosen
size cancels browser zoom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #822: rustfmt
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>
* #1116: keep a paragraph's space after clear of the footnote separator
On a page with footnotes (already there or brought by the line), a
paragraph's last line only fits if its space after fits above the notes
area too; otherwise it goes to the next page with its note (and, with
widow control, one more line). Contextual spacing and automatic spacing
between list items drop the requirement; pages without notes are
unchanged (space after may still run past the bottom margin).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1116: space after vs footnote separator uses the spacing code's tests, only on pages with notes
Contextual spacing drops a paragraph's space after only when that paragraph
asks for it, and the next paragraph counts as a list item only when it gets
a label (Numbering::shows_label), as in the spacing between paragraphs.
The kept space after is worked out only for a last line on a page with notes.
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>
* #884: a dragged shape anchors in the column it lands in, so that column's text wraps around it
arrange.bounds (the canvas drag) re-anchored a moved object to the paragraph
under its top edge but ignored x: on a multi-column page it took the last
paragraph starting above that y in any column, usually one in the last column.
Layout places a float when it lays out its anchor, so text in an earlier column
(already laid out) ran through a shape dropped there. anchor_paragraph now picks
the paragraph in the column under the object's centre (the ink placement uses
the first stroke point).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #884: a dropped object finds its column from the section's column geometry
Columns came from the distinct left edges of the page's text, so a
paragraph shifted by mirrored indents on an even page made a column of its
own and the drop anchored to the wrong paragraph. The columns now come from
the section's column boxes on the page. Notes in the layout parity doc that
lines a paragraph continues into the next column don't wrap yet.
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>
egui drops the page's keyboard focus on any click elsewhere. Command
buttons handed it back (#686), but gallery tiles, tab names, empty ribbon
space, View check boxes and the status bar's items did not, so the caret
vanished and typing went nowhere. After a click on background chrome that
leaves nothing focused (no text field, dialog, popup, Backstage or comment
balloon), the canvas takes focus back, as in Word. Tabs, style tiles and
status-bar items now report their name and state to screen readers.
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>
* #299: caret contrasts with the paper, page colour, shading or highlight under it
The caret was black (white only in Switch Modes' dark page), so it vanished
on a dark page colour, dark shading or a dark highlight. It now takes black
or white by WCAG contrast with the background at the caret
(wordcraft_render::background_at), cached per layout/view/position.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #299: shapes colour the caret's background only inside their outline; look up only when drawn
background_at hit-tests a filled shape's real outline (shape_path), so an ellipse's corner
shows what lies beneath. The caret's colour is worked out only when the caret (or IME text)
is drawn, and the unused Tokens::caret is gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #299: rustfmt
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>
* #1129: no document behind the Start screen; Back shows an empty window
Launched with nothing to open, the Start screen no longer has a blank
document behind it. Esc or Back shows an empty window (no page, ribbon
greyed out but for Help, Info/Save/Save As/Print/Export off in File,
title "WordCraft") until Blank document, a template or a file is
opened; closing asks nothing. Commands that need a document say so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1129: explicit no-document allow-list; control inspect/render need a document; fonts can be added
works_without_document lists the ui.* commands that make sense with no document instead of
letting every ui.* command through; font.add/list/remove work so a font file dropped on the
empty window is added. The control channel's document.inspect and ui.render answer that no
document is open instead of reading the hidden placeholder.
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>
* #1062: honour Remove personal information / Remove date and time on save
w:removePersonalInformation and w:removeDateAndTime (ECMA-376 §17.15.1.69-70)
are read into Settings, written back in CT_Settings order, and honoured by the
DOCX writer: comment and revision authors saved as "Author" (initials "A"), no
dc:creator / cp:lastModifiedBy, no w:date on comments and tracked changes. The
open document keeps its real values. New file.privacyOptions command sets both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1062: reading privacy options has no side effects; File › Options › Save check boxes
file.privacy reads the settings (a pure command: no undo step, no unsaved changes);
file.privacyOptions only changes them and refuses an empty call. File › Options › Save gets
a "Privacy for this document" section with the two check boxes, translated in every catalog.
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>
* Text boxes: VML text boxes keep their internal margins, fit to their text and alignment
A VML text box's inset ("l,t,r,b", ECMA-376 Part 4 §19.1.2.22) was ignored,
so its text sat Word's default 7.2 pt in and 3.6 pt down even where the file
says 0,0,0,0, which is how letterheads and form headers place address blocks
exactly. It's now read like DrawingML's lIns/tIns/rIns/bIns: each value left
out keeps the default, a bare number is in EMUs, o:insetmode="auto" leaves
the defaults. mso-fit-shape-to-text (VML's "Resize shape to fit text") is read
as auto-fit, like a:spAutoFit, and the shape's v-text-anchor as the text's
vertical alignment. In a group, a member's margins go with that member's
text.
The reader feeds layout, so the editor, PDF and page images all change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drawings: hidden shapes and pictures are kept but not shown
A drawing marked hidden (VML visibility:hidden, floating or inline;
DrawingML wp:docPr/@hidden, or a group member's cNvPr/@hidden) was drawn,
took room and wrapped text. Word for Windows shows it in no view; it's now
kept and saved hidden, but left out of layout: not placed, no wrap area, no
room in its line, nothing drawn, its text box's notes not placed. Exports to
other formats and copies leave it out, the accessibility checker no longer
asks for its alt text, and the selection pane says it's hidden.
Word for Mac draws hidden VML shapes; Windows follows the markup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Naxvog <4963183+naxvog@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
ui.wheel injects a mouse-wheel / trackpad scroll at a window point (the
control channel had no scroll input). perf_probe runs the real UI
headless, opens a document and times the frames that handle typing,
scrolling, zooming, ribbon tabs and resizes (numbers only).
Co-authored-by: przemek-arch <263159043+przemek-arch@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Italian proofing: it-IT spelling, suggestions and grammar (#951)
* Italian proofing: Review Language menu and editing language
* Italian proofing: interface test for editing language
* Italian proofing: find the Swiss it_CH dictionary too
* Italian proofing: TeX hyphenation patterns (MIT option)
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Write WordprocessingML 2003 by rewriting the DOCX writer's package into the 2003 vocabulary; file.save/saveAs take format wordml2003, File > Export lists the format, CLI convert --format wordml2003, a plain save over a 2003 file keeps it.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Rgb now reads "RRGGBB" / "#RRGGBB" as well as [r, g, b], so every colour
in CharProps and ParaProps takes the hex that format.color, para.borders
and table.shading take, and {"Rgb": "1B1611"} works for color. It is
still written as [r, g, b], so saved documents and settings are unchanged.
Fixes#865
Co-authored-by: Fabio Lussi <198260595+itrycodinganditsbad@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Track Changes: emptying a paragraph no longer turns its mark into the text's revision (#864)
Paragraph::delete copies the formatting of what it deletes onto the paragraph
mark when it empties the paragraph. That copy included the revision ids, so
accepting a tracked deletion of a whole paragraph text made the mark a deleted
mark too, and Accept All joined the following paragraph. Rejecting typed text
in an empty paragraph likewise rejected the paragraph mark. The mark now keeps
its own ins, del and fmt_change.
* Credit Silvertree2010 in the About window
* Docs: update the progress docs for this change
* Docs: Last reviewed stays as it was
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Docs: list the CharProps and ParaProps fields (#865)
* docs/mcp.md: color also takes RRGGBB and #RRGGBB (#865, #881)
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Adds the 374 strings the Polish catalog still lacked, mostly from #750 by
@olekkut, with a few corrections, and drops the Russian exception from the
Polish coverage test now that Polish has every Russian key.
Co-authored-by: Aleksander Kutycki <74899889+olekkut@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
RTF import read every 8-bit byte as Windows-1252, so Cyrillic (and any
other non-Western) RTF came out as mojibake. Bytes now decode in the
current font's code page (\fcharset, \cpg), else the default font's,
else the document's (\ansicpg), for the single-byte Windows code pages
874 and 1250-1258.
A .doc, .docx or .rtf whose content is another of these formats now opens
by its signature, as Word does: RTF saved as .doc (hh.ru résumés and
accounting exports are commonly saved that way), a .docx renamed .doc, a
Word 97-2003 file renamed .docx.
Co-authored-by: Магда М.Г. <mail@mgmagda.ru>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Word's symbol fonts are proprietary and never bundled, so text set in them
showed the substitute's letters. New wordcraft_doc::symbol_fonts maps every
code (stored as U+F020-U+F0FF, e.g. w:sym, or as a plain 8-bit code) to its
Unicode equivalent: Symbol from Unicode's Adobe symbol.txt, the Wingdings
and Webdings sets from Unicode L2/12-368 (WG2 N4384), checked against UCD.
Layout shapes the mapped characters (fallback faces draw them); PDF text,
copy as text and TXT export read as the symbols; the stored text and docx
round trips are unchanged.
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>
WMF and EMF pictures already drew (crates/metafile) when they came inside a
document, but Insert › Picture refused them ("not a supported image"), and the
file dialog, drag-and-drop and paste lists left them out. A metafile is now
placed at its own frame size (points for placeable WMF and EMF; very large
frames scaled to 22 inches on the longer side) and stored as .wmf or .emf, so a
save writes it as such. render gains metafile_size().
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>
* #848: font list previews no longer load every family they show
The Font box drew each entry in its own face through the full layout
pipeline, which loaded the whole family into the font database for good
(faces are never unloaded). Every trip through the list kept tens of
megabytes. Entries are now drawn with FontDb::preview_face: a loaded
family's face is shared, an installed one is read into a face the
database doesn't keep and is freed when dropped, and render_text_line
draws it without the shared outline cache.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Font preview test tolerates other tests loading fallbacks; gaps rows 1–3 de-duplicated, #848 noted in row 3
---------
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>
A click in the left margin beside body text selects that line (trailing
space, and the paragraph mark on a paragraph's last line), a double-click
the paragraph, a triple-click or Ctrl/Cmd+click the document; dragging
there selects whole lines and Shift+click extends by lines. The pointer
shows the arrow there. Layout gains selection_bar_at, body_line_at and
line_span; select.line takes an optional pos; select.dragUnit takes the
units line and document.
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>
Layout › Breaks (next page, continuous, even, odd) deleted the selection
before splitting, so with "one" selected the first section came out empty.
The break now goes just before the selection, which keeps its text and
direction, as in Word.
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>
Closing an edited document asked Save / Don't Save / Cancel with no button
focused, so the arrow keys did nothing and only the mouse could pick Don't
Save or Cancel. Save now has focus when the prompt opens, as in Word: Enter
saves, and the arrow keys (egui's focus navigation) or Tab move between the
buttons.
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>
Every frame fingerprinted every item of every page for the page texture
cache (thousands of cell borders a page, formatted to strings), and the
markup-area check walked every paragraph twice a frame. Page content keys
are now taken once per layout and only for pages on screen or cached; the
document facts behind the markup area are cached per revision; textures of
pages far from the view are dropped. Layout and rendering are unchanged.
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>
Builds with craft-fonts (releases, the FreeBSD job) put only its Japanese
faces in the document font database; Noto Sans CJK SC served the interface
alone. Simplified-only ideographs in a document then had no face unless the
system had one, and the run-level CJK pick from #567 found no face covering a
mixed run, which the FreeBSD job's
east_asian_run_uses_one_face_covering_the_whole_run caught. The Chinese craft
faces now follow the Japanese ones (Mincho stays first for Japanese text).
fallback_reads_each_installed_font_file_once_however_many_characters_miss
used U+30001.., part of which Noto Sans CJK SC maps (CJK Extension G); it now
uses Supplementary Private Use Area-A, which no font here maps.
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>
* Find, Replace and Go To work like Word (#712)
Before: Ctrl+F opened both the Navigation pane and a Find window,
Advanced Find opened the same window, Ctrl+H failed with nothing to
replace, and Go To was a separate little window that only took a page.
After, as in Word:
- Ctrl+F and Home > Find open only the Navigation pane, with its search
box focused so typing searches; selected text is searched for.
- Find, Replace and Go To are one Find and Replace dialog with three
tabs: Advanced Find opens Find, Ctrl+H (edit.findReplace, which never
marks the document edited) opens Replace, Ctrl+G / F5 open Go To.
- Go To moves to a page (a number, or +2 / -1 from the page the caret
is on; empty, Next and Previous step a page) or picks a bookmark.
edit.goto takes pageBy for scripts and agents.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Go To: list bookmarks only while Bookmark is chosen (#712)
Finding a document's bookmarks walks every paragraph; the Go To tab did it every frame even
while it showed pages.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Rulers: table column and row markers; indents in a cell measured from it (#700)
With the caret in a table, the rulers show the table, as Word's do:
- The horizontal ruler marks the column borders of the caret's row (past
the table's left edge); the vertical ruler marks the bottom of each of
the table's rows on the page. Each marker has the resize pointer, a
tooltip and a name for screen readers.
- Dragging a marker drags that border exactly like a drag on the page
(#600): a dashed guide, Escape cancels, one undo step on release
through `table.resizeDrag`. On the horizontal ruler only the column
left of the border changes and the columns right of it move along, so
the table's width changes (Word's ruler); a row marker sets the row's
height. The markers follow any drag of the table's borders, on the
ruler or on the page.
- In a cell, the indent markers and tabs are measured from the cell's
text area (its box less its margins); they were placed as if the
paragraph started at the page's margin.
- Layout: `cell_margins` (a cell's own margins, else the table's, else
Word's), so the ruler uses what the layout does.
- UI: crates/ui-egui/src/table_ruler.rs; table_resize.rs starts a drag
from a ruler marker (`grab`) and tells the ruler where a dragged border
is (`moved`), sharing that computation with the dashed guide.
- Tests: layout (cell_margins matches where each cell's text starts);
kittest with real pointer events: a column marker drag (the marker
follows, one undo step, the columns right of it keep their widths), a
row marker drag, and an 18 pt drag of the left-indent marker in a cell
indenting the paragraph 18 pt.
Progress docs updated: ui-parity (rulers 70% -> 85%), gaps (the ruler's
column markers are off the list), target-app-parity, ROADMAP. No new
commands or labels (the tooltips reuse "Table Column Width" and "Table
Row Height"). `cargo xtask ci` passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Rulers: Shift works the other way round on the ruler; row markers beside a vertical merge
Checked against Word (2019, Windows): on the ruler a column marker drag
changes only the column on its left and the columns right of it move
along (as before), and with Shift the neighbour makes up the difference,
so the table keeps its width; on the page Shift does the opposite. The
ruler now follows that: `ResizeDrag::plan` takes the Shift key and flips
it for a drag started on a ruler marker.
Since #669 a vertically merged cell's box spans all its rows, so a row
marker took the merge's bottom when the merge was the row's leftmost cell.
The marker now sits on the row's shortest box. (`Placed::Cell` patterns
take `..` for #669's new field.)
- Test (kittest, real pointer events): a Shift drag of a column marker
on the ruler gives the neighbour's width to the column on the left and
keeps the table's width.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: Ctrl, Alt, left-edge and selected-cells border drags (#711)
Word's border drags that #600 left out:
- Ctrl while dragging a column border: the columns right of it share the
change in proportion to their widths, so the table keeps its width
(none narrower than the minimum). Ctrl wins over Shift; on the table's
right edge only the last column changes.
- The table's left edge can be dragged, from just outside it: the
table's indent moves and the first column makes up the difference, so
the other borders stay. A centred or right-aligned table becomes
left-aligned where the edge was dropped; a floating table moves its
horizontal offset. The horizontal ruler has a marker on it too.
- Alt while dragging a column border: the horizontal ruler shows each
column's width as it is going to be.
- With the selection across two or more cells, dragging one of their
column borders (in one of their rows) moves it only in their rows; the
grid gets a line for the new border and the cells' spans follow. A
border of a cell merged down past the selection moves in the whole
column. Text selected within one cell isn't a cell selection (it can't
be told from one selected cell yet), so it moves the whole column.
- Engine: `Share` (who makes up a column border's move),
`Edge::Column(0)`, `ResizeTarget::limit_to_selection`/`only`/
`planned_x`, `Plan::indent_by`/`cells`; `table.resizeDrag` takes
`"ctrl"`. `plan` still takes a bool for Shift. Each drag is one command
and one undo step.
- UI: table_resize.rs (keys, a guide down the rows that move,
`measuring`), table_ruler.rs (left-edge marker, `widths`).
- Tests: engine (Ctrl in proportion and at the minimum; the left edge,
also of a centred table; selected cells' rows only, with the grid and
spans; a merged cell past the selection); kittest with real pointer
events (a Ctrl drag, the left edge and its ruler marker, Alt's widths
mid-drag).
Progress docs updated: ui-parity (tables on the page 80% -> 90%),
target-app-parity, ROADMAP. No new commands or labels. `cargo xtask ci`
passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: a centred or right-aligned table keeps its alignment when its left edge is dragged
Checked against Word (2019, Windows): dragging the left edge of a centred
table widens the first column by the whole drag and the table stays
centred (each side moves half); a right-aligned table keeps its right edge
and grows to the left. Only a left-aligned table's indent moves. #711 made
such a table left-aligned where the edge was dropped; now `move_left_edge`
leaves a centred or right-aligned table's alignment and indent alone and
says whether it moved anything, and the drop-point correction runs only
then.
The ruler's Shift (from #710) now goes through `share`: on a ruler marker
Shift makes the neighbour take up the change, without it only the column
on the left changes.
- Tests: the left-edge test checks the centred table (first column +20 pt,
the others unchanged, still centred) and a right-aligned one (only the
left edge moves); a kittest Shift drag on the ruler.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
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>
* Search: Alt+Q opens it from anywhere, like Word (#819)
- Alt+Q (and Alt, then Q on the keytips) opens the Search box with its
text field focused, from the page or anywhere else; the key's own "q"
is never typed into the box or the document. The title-bar box shows
its Q keytip and its shortcut in the tooltip.
- An empty box suggests common commands; every word of a query has to
match (so "insert table" finds Insert > Tables > Table, as the box's
own hint promises); Enter runs the top match; "Find ... in the
document" opens the Navigation pane on the results. Matching is
about 2x faster than before.
- tools.search lists matching commands for scripts and agents.
Observed in Word for Windows (Microsoft 365): Alt+Q and Alt, Q focus
Search with nothing typed; multi-word queries match commands; the list
also offers finding the words in the document.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Search: Bulgarian, Czech, German, French, Hungarian, Italian and Polish strings
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Rulers: table column and row markers; indents in a cell measured from it (#700)
With the caret in a table, the rulers show the table, as Word's do:
- The horizontal ruler marks the column borders of the caret's row (past
the table's left edge); the vertical ruler marks the bottom of each of
the table's rows on the page. Each marker has the resize pointer, a
tooltip and a name for screen readers.
- Dragging a marker drags that border exactly like a drag on the page
(#600): a dashed guide, Escape cancels, one undo step on release
through `table.resizeDrag`. On the horizontal ruler only the column
left of the border changes and the columns right of it move along, so
the table's width changes (Word's ruler); a row marker sets the row's
height. The markers follow any drag of the table's borders, on the
ruler or on the page.
- In a cell, the indent markers and tabs are measured from the cell's
text area (its box less its margins); they were placed as if the
paragraph started at the page's margin.
- Layout: `cell_margins` (a cell's own margins, else the table's, else
Word's), so the ruler uses what the layout does.
- UI: crates/ui-egui/src/table_ruler.rs; table_resize.rs starts a drag
from a ruler marker (`grab`) and tells the ruler where a dragged border
is (`moved`), sharing that computation with the dashed guide.
- Tests: layout (cell_margins matches where each cell's text starts);
kittest with real pointer events: a column marker drag (the marker
follows, one undo step, the columns right of it keep their widths), a
row marker drag, and an 18 pt drag of the left-indent marker in a cell
indenting the paragraph 18 pt.
Progress docs updated: ui-parity (rulers 70% -> 85%), gaps (the ruler's
column markers are off the list), target-app-parity, ROADMAP. No new
commands or labels (the tooltips reuse "Table Column Width" and "Table
Row Height"). `cargo xtask ci` passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Rulers: Shift works the other way round on the ruler; row markers beside a vertical merge
Checked against Word (2019, Windows): on the ruler a column marker drag
changes only the column on its left and the columns right of it move
along (as before), and with Shift the neighbour makes up the difference,
so the table keeps its width; on the page Shift does the opposite. The
ruler now follows that: `ResizeDrag::plan` takes the Shift key and flips
it for a drag started on a ruler marker.
Since #669 a vertically merged cell's box spans all its rows, so a row
marker took the merge's bottom when the merge was the row's leftmost cell.
The marker now sits on the row's shortest box. (`Placed::Cell` patterns
take `..` for #669's new field.)
- Test (kittest, real pointer events): a Shift drag of a column marker
on the ruler gives the neighbour's width to the column on the left and
keeps the table's width.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
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>
* Tables: select, insert and move with the mouse; double-click a border to AutoFit
Like Word, on top of the border resizing:
- Selection areas: just above a column the pointer becomes a down arrow,
left of a row the row arrow, along a cell's left edge (inside it) the
cell arrow. A click selects the column, row or cell; a drag selects every
one up to the pointer. Selected cells are shaded as cells, empty ones too.
- Insert buttons: left of a row border and above a column border a "+"
shows with the border marked; a click adds a row or column there and puts
the caret in it.
- Move handle past the table's top-left corner: a click selects the table;
a drag shows a dashed outline and, on release, the table floats where it
is dropped with the text wrapping around it (Word's behaviour). One undo
step. Nested tables and tables outside the main text are only selected.
- A double-click on a column border fits the column left of it to its
contents (the right neighbour gives or takes the room; on the right edge
the table's width changes, up to the right margin).
- Table Layout > Select > Select Column (catalog 418/449).
Whole-cell selection: a column (or a rectangle of cells over several rows)
is not one run of the text, so selecting it makes a block of its cells'
paragraphs (`CellRange` on the existing `ColumnBlock`). Bold, alignment,
Delete, typing, copy and numbering act on those cells only (a numbered
column is one list); table commands (shading, merge, delete columns) take
the rectangle; Tab moves on without clearing it; Undo brings the selection
back. Cells of one row, or whole rows, stay an ordinary stream selection.
The format painter no longer clears a block's text (edit.pasteFormat was
handled like a paste). The mini toolbar stays hidden over whole-cell
selections and during these drags: pinned above the selection it covered
the column areas, the "+" buttons and the move handle.
Engine: crates/engine/src/cmd/table_mouse.rs - table.selectAt,
table.insertAt, table.moveDrag, table.fitColumn (page coordinates, so
agents and the control channel can do the same) and table.selectColumn;
hit testing reuses table_resize's drawn geometry, and borders keep priority
for resizing. 10 engine tests (areas, a column edited cell by cell,
numbering, rows/cells/drags, insert buttons, move, fit, format painter,
hostile params).
UI: crates/ui-egui/src/table_mouse.rs, wired into the existing canvas mouse
handler (no extra ui.interact areas, so clicks in a cell's text still place
the caret); kittest tests with real pointer events: column click, row drag,
"+" button, move-handle drag in one undo step, double-click fit.
New labels translated in all eleven catalogs. Progress docs updated: gaps,
ui-parity, target-app-parity, ROADMAP, parity checklist.
`cargo xtask ci` passes (fmt, clippy, test, assets, layers, wasm).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: a selected column's word count is its cells'; merged cells shade once
With whole cells selected (a column, or a rectangle that isn't one run of
the text) the status bar said "12 of 23 words": `Session::selected_text`
took everything between the selection's ends. It now takes each piece's
own text, a line each, for a column block too, so the status bar, Word
Count and the other callers see what is selected.
Since #669 a vertically merged cell is one box as tall as its rows, with a
`covered` box for each continuation row; shading selected cells skips
those, so a merged cell isn't shaded twice. `selected_paths` follows
main's `para_paths_in` (#418): a selection ending at the start of the next
paragraph doesn't take it.
- Test: a selected column's text is its three cells' and Word Count says
three words.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: double-click AutoFit and the "+" buttons work as in Word
Checked against Word (2019, Windows):
- A double-click on a column border fits the column left of it to its
contents and the columns right of it keep their widths, moving with the
border, so the table narrows or widens (up to the right margin). The
right neighbour used to give or take the difference.
- A "+" insert button selects the new row or column (the caret used to
go into it); typing then goes into its first cell. If the table can't
take another row or column, nothing changes.
- A double-click along a cell's left edge (the cell selection area)
selects its row.
- Tests: engine (fit keeps the columns on the right; insert selects the
new row or column, typing lands in its first cell) and kittest (real
double-click on a border; "+" selects the row; double-click by a cell's
edge selects the row).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: drags and Shift+arrows over cells select whole cells
An ordinary drag, Shift+click or Shift+arrow from one table cell into
another now selects the rectangle of whole cells between them, as Word
does, instead of the run of text between the two points: bold, alignment
and Delete leave the cells outside the rectangle alone (they already did
for Select Column and the table's selection areas). Between a cell and
text outside its table the selection takes whole rows, from the cell's
row out to the table's edge, and the text beyond. Extending again goes on
from where the selection really started and ends: dragging back into the
first cell selects its text again, Shift+Up after Shift+Down shrinks the
rectangle, and from a nested table's cell into another cell of the table
around it the outer table's cells are taken.
- Engine: `table::extend_selection` (the rule, down through nested
tables) and `Session::sel_ends` (where a widened selection really
runs); every caret command run with `extend` goes through it in
`column::dispatch`, so agents get the same with `caret.*`.
- UI: a text drag extends through `caret.set` (still straight to the
session, so nothing scrolls on every frame); the view follows the real
caret.
- Tests: engine (drag across cells and back, nested tables, Shift+Down
and Shift+Up through and past a table, Shift+click, from text into a
table) and a kittest with a real mouse drag.
Progress docs updated: gaps (the known gap from #637 is gone),
ui-parity, target-app-parity, ROADMAP. `cargo xtask ci` passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
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>
* Tables: select, insert and move with the mouse; double-click a border to AutoFit
Like Word, on top of the border resizing:
- Selection areas: just above a column the pointer becomes a down arrow,
left of a row the row arrow, along a cell's left edge (inside it) the
cell arrow. A click selects the column, row or cell; a drag selects every
one up to the pointer. Selected cells are shaded as cells, empty ones too.
- Insert buttons: left of a row border and above a column border a "+"
shows with the border marked; a click adds a row or column there and puts
the caret in it.
- Move handle past the table's top-left corner: a click selects the table;
a drag shows a dashed outline and, on release, the table floats where it
is dropped with the text wrapping around it (Word's behaviour). One undo
step. Nested tables and tables outside the main text are only selected.
- A double-click on a column border fits the column left of it to its
contents (the right neighbour gives or takes the room; on the right edge
the table's width changes, up to the right margin).
- Table Layout > Select > Select Column (catalog 418/449).
Whole-cell selection: a column (or a rectangle of cells over several rows)
is not one run of the text, so selecting it makes a block of its cells'
paragraphs (`CellRange` on the existing `ColumnBlock`). Bold, alignment,
Delete, typing, copy and numbering act on those cells only (a numbered
column is one list); table commands (shading, merge, delete columns) take
the rectangle; Tab moves on without clearing it; Undo brings the selection
back. Cells of one row, or whole rows, stay an ordinary stream selection.
The format painter no longer clears a block's text (edit.pasteFormat was
handled like a paste). The mini toolbar stays hidden over whole-cell
selections and during these drags: pinned above the selection it covered
the column areas, the "+" buttons and the move handle.
Engine: crates/engine/src/cmd/table_mouse.rs - table.selectAt,
table.insertAt, table.moveDrag, table.fitColumn (page coordinates, so
agents and the control channel can do the same) and table.selectColumn;
hit testing reuses table_resize's drawn geometry, and borders keep priority
for resizing. 10 engine tests (areas, a column edited cell by cell,
numbering, rows/cells/drags, insert buttons, move, fit, format painter,
hostile params).
UI: crates/ui-egui/src/table_mouse.rs, wired into the existing canvas mouse
handler (no extra ui.interact areas, so clicks in a cell's text still place
the caret); kittest tests with real pointer events: column click, row drag,
"+" button, move-handle drag in one undo step, double-click fit.
New labels translated in all eleven catalogs. Progress docs updated: gaps,
ui-parity, target-app-parity, ROADMAP, parity checklist.
`cargo xtask ci` passes (fmt, clippy, test, assets, layers, wasm).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: a selected column's word count is its cells'; merged cells shade once
With whole cells selected (a column, or a rectangle that isn't one run of
the text) the status bar said "12 of 23 words": `Session::selected_text`
took everything between the selection's ends. It now takes each piece's
own text, a line each, for a column block too, so the status bar, Word
Count and the other callers see what is selected.
Since #669 a vertically merged cell is one box as tall as its rows, with a
`covered` box for each continuation row; shading selected cells skips
those, so a merged cell isn't shaded twice. `selected_paths` follows
main's `para_paths_in` (#418): a selection ending at the start of the next
paragraph doesn't take it.
- Test: a selected column's text is its three cells' and Word Count says
three words.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: double-click AutoFit and the "+" buttons work as in Word
Checked against Word (2019, Windows):
- A double-click on a column border fits the column left of it to its
contents and the columns right of it keep their widths, moving with the
border, so the table narrows or widens (up to the right margin). The
right neighbour used to give or take the difference.
- A "+" insert button selects the new row or column (the caret used to
go into it); typing then goes into its first cell. If the table can't
take another row or column, nothing changes.
- A double-click along a cell's left edge (the cell selection area)
selects its row.
- Tests: engine (fit keeps the columns on the right; insert selects the
new row or column, typing lands in its first cell) and kittest (real
double-click on a border; "+" selects the row; double-click by a cell's
edge selects the row).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: the right-click menu offers the table's commands
Right-clicking a table cell now offers what Word's table menu does, all
through existing commands: Insert (rows above/below, columns left/right),
Delete (cells, rows, columns, table), Select (cell, column, row, table),
Merge Cells, Split Cells, Distribute Rows, Distribute Columns, AutoFit
(contents, window, fixed column width), Text Direction, Cell Alignment
(the nine alignments) and Table Properties… (opens the dialog, as the
ribbon's button does).
A right-click beside selected cells now moves the caret there, as in
Word: with a column selected, the cells between its first and last cell
in the text (the other columns of those rows) counted as "inside the
selection", so the selection stayed and the menu acted on the column
instead of the cell clicked. A right-click on a selected cell keeps the
selection, so Cell Alignment, Distribute and the rest act on it.
- UI only (crates/ui-egui/src/canvas.rs): the table part of
`context_menu()` and a small check before the right-click caret logic
(separate from it, so it doesn't overlap #521's change to that block).
Table Layout's nine cell alignments are one shared list (`CELL_ALIGN`
in ribbon.rs) for the ribbon grid and the menu.
- Two new labels, "Cell Alignment" and "Table Properties…", translated
in all eleven catalogs; the rest reuse the ribbon's labels.
- Tests: kittest with real pointer events: right-click in a cell, the
menu's table entries, Select > Select Column; with a column selected,
a right-click on it keeps it and Cell Alignment > Align Bottom Right
aligns just the column (one undo step), and a right-click beside it
drops it.
Progress docs updated: ui-parity (context menus 55% -> 60%), gaps,
target-app-parity, ROADMAP. No new commands, so the catalog is unchanged.
`cargo xtask ci` passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: the right-click menu's table items follow the selection, as in Word
Checked against Word (2019, Windows): Word's table items in the context
menu depend on what is selected, and there is no Select or Cell
Alignment submenu among them.
- Caret in a cell: Insert, Delete Cells, Split Cells, Text Direction,
Table Properties…
- Several cells: Insert, Delete Cells, Merge Cells, Text Direction,
Table Properties…
- Whole columns: Insert, Delete Columns, Merge Cells, Distribute Rows,
Distribute Columns, Text Direction, Table Properties… (whole rows the
same with Delete Rows; not checked in Word).
- The whole table: Insert, Delete Table, Distribute Rows and Columns,
AutoFit, Text Direction, Insert Caption…, Table Properties…
Insert lists the columns first, as Word does. Select and Cell Alignment
are gone, and with them the shared CELL_ALIGN constant in the ribbon;
"Insert Caption…" replaces "Cell Alignment" in all fifteen catalogs.
Word's Border Styles and Insert Cells… aren't here yet.
- Tests (kittest, real right-clicks): the menu with the caret in a cell
and with the whole table; a selected column keeps its column menu and
Text Direction turns only it; beside it the menu is a cell's again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
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>
The right-click menu could only be opened with the mouse. Shift+F10,
Windows' keyboard way to the context menu (the Menu key's equivalent),
now opens the same menu just below the caret, with the spelling
suggestions and synonyms for the word there; Escape closes it.
ui.contextMenu opens it for scripts and agents.
The Menu key itself still does nothing: egui-winit drops that key
before the app sees it, so it needs an upstream change (or a winit
hook) to reach the same path.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* R17: no automatic paragraph spacing between list items
Word drops automatic (HTML-style) space before and after between two list
items, of one list or two, at any level; the first item keeps its space
before and the last its space after, and measured spacing stays. Measured in
Word for Windows: 16 synthetic cases. NIH's bulleted IND report items now
touch as in Word.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ROADMAP: keep the progress log separator under its header
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Found by rendering 12 US government Word documents (NIH, EPA, MCC) in
Word for Windows and WordCraft and comparing the pages side by side,
then pinning each rule down with synthetic documents measured in Word.
- Kerning: the shaper kerned every run, so WordCraft's lines held more
text than Word's and broke differently. Word kerns only a run with
Kerning for fonts (w:kern) at or above that size; no w:kern and
w:kern="0" are unkerned. New documents keep kerning from 1 pt, like
Word's Normal template.
- Automatic paragraph spacing (w:beforeAutospacing/w:afterAutospacing,
common in text pasted from web pages) was ignored. It is 14 pt at
any size, none before the document's or a cell's first paragraph,
none after a cell's last, and the larger of two neighbouring spaces
counts. Read, written back, and settable through para.spacing
(beforeAuto/afterAuto); a number, Ctrl+0 and Remove Space After
replace it as in Word.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Tables: select, insert and move with the mouse; double-click a border to AutoFit
Like Word, on top of the border resizing:
- Selection areas: just above a column the pointer becomes a down arrow,
left of a row the row arrow, along a cell's left edge (inside it) the
cell arrow. A click selects the column, row or cell; a drag selects every
one up to the pointer. Selected cells are shaded as cells, empty ones too.
- Insert buttons: left of a row border and above a column border a "+"
shows with the border marked; a click adds a row or column there and puts
the caret in it.
- Move handle past the table's top-left corner: a click selects the table;
a drag shows a dashed outline and, on release, the table floats where it
is dropped with the text wrapping around it (Word's behaviour). One undo
step. Nested tables and tables outside the main text are only selected.
- A double-click on a column border fits the column left of it to its
contents (the right neighbour gives or takes the room; on the right edge
the table's width changes, up to the right margin).
- Table Layout > Select > Select Column (catalog 418/449).
Whole-cell selection: a column (or a rectangle of cells over several rows)
is not one run of the text, so selecting it makes a block of its cells'
paragraphs (`CellRange` on the existing `ColumnBlock`). Bold, alignment,
Delete, typing, copy and numbering act on those cells only (a numbered
column is one list); table commands (shading, merge, delete columns) take
the rectangle; Tab moves on without clearing it; Undo brings the selection
back. Cells of one row, or whole rows, stay an ordinary stream selection.
The format painter no longer clears a block's text (edit.pasteFormat was
handled like a paste). The mini toolbar stays hidden over whole-cell
selections and during these drags: pinned above the selection it covered
the column areas, the "+" buttons and the move handle.
Engine: crates/engine/src/cmd/table_mouse.rs - table.selectAt,
table.insertAt, table.moveDrag, table.fitColumn (page coordinates, so
agents and the control channel can do the same) and table.selectColumn;
hit testing reuses table_resize's drawn geometry, and borders keep priority
for resizing. 10 engine tests (areas, a column edited cell by cell,
numbering, rows/cells/drags, insert buttons, move, fit, format painter,
hostile params).
UI: crates/ui-egui/src/table_mouse.rs, wired into the existing canvas mouse
handler (no extra ui.interact areas, so clicks in a cell's text still place
the caret); kittest tests with real pointer events: column click, row drag,
"+" button, move-handle drag in one undo step, double-click fit.
New labels translated in all eleven catalogs. Progress docs updated: gaps,
ui-parity, target-app-parity, ROADMAP, parity checklist.
`cargo xtask ci` passes (fmt, clippy, test, assets, layers, wasm).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: a selected column's word count is its cells'; merged cells shade once
With whole cells selected (a column, or a rectangle that isn't one run of
the text) the status bar said "12 of 23 words": `Session::selected_text`
took everything between the selection's ends. It now takes each piece's
own text, a line each, for a column block too, so the status bar, Word
Count and the other callers see what is selected.
Since #669 a vertically merged cell is one box as tall as its rows, with a
`covered` box for each continuation row; shading selected cells skips
those, so a merged cell isn't shaded twice. `selected_paths` follows
main's `para_paths_in` (#418): a selection ending at the start of the next
paragraph doesn't take it.
- Test: a selected column's text is its three cells' and Word Count says
three words.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: double-click AutoFit and the "+" buttons work as in Word
Checked against Word (2019, Windows):
- A double-click on a column border fits the column left of it to its
contents and the columns right of it keep their widths, moving with the
border, so the table narrows or widens (up to the right margin). The
right neighbour used to give or take the difference.
- A "+" insert button selects the new row or column (the caret used to
go into it); typing then goes into its first cell. If the table can't
take another row or column, nothing changes.
- A double-click along a cell's left edge (the cell selection area)
selects its row.
- Tests: engine (fit keeps the columns on the right; insert selects the
new row or column, typing lands in its first cell) and kittest (real
double-click on a border; "+" selects the row; double-click by a cell's
edge selects the row).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
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>
* Layout: space before at the top of a page after page breaks, as in Word
Found comparing Microsoft's specification documents with Word for
Windows, then measured with 32 synthetic documents in compatibility
modes 12 (Word 2007) and 15 (Word 2013+):
- A paragraph that ends with a page break keeps its mark with the
break: WordCraft put an empty line at the top of the next page.
- The paragraph after it, first on the new page, gets no space before.
- A paragraph that starts with a page break starts on the new page
with its space before there (WordCraft put the space on the old page).
- A page break before keeps the space before in modes up to 14 and
drops it in mode 15.
- Natural breaks, section breaks and the first page were already right.
All 32 cases now land where Word puts them (within 0.3 pt), and the
caret still reaches a paragraph mark kept with its break.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Layout: after a page break before or a section break, the space after above counts
Measured in Word for Windows (10 more cases): at the top of a page after a page break before
(modes up to 14) or a next-page section break, the previous paragraph's space after comes off the
space before (24 pt before after 10 pt after leaves 14 pt; after 30 pt leaves none). This put the
numbered Heading 1s of Microsoft's specification documents 9 pt too low. All 42 page-top cases now
match Word.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Found in NIH's SARP form compared with Word for Windows: the form's
second section has a narrow label column (67 pt) and a wide text column
(438 pt), and each answer starts with a column break into the wide one.
Before: the paragraph's lines were broken once, for the column it
started in, so the text after the break ran a word or two wide down the
wide column. After: when a column break lands the rest of a paragraph
in a column of another width, that rest is broken again for it, as in
Word. Equal columns are unaffected (nothing is laid out twice).
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Word positions a floating object "relative to paragraph" (and to its line)
from the paragraph's start above its space before, below the previous
paragraph's space after. WordCraft measured from the first line, so floats
in spaced paragraphs sat lower by the space before. Measured in Word for
Windows: 10 synthetic cases.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #701: Undo history list beside the Quick Access Toolbar's Undo
A drop-down arrow next to Undo lists recent steps, newest first; hovering a
row highlights it and every newer step, the footer reads "Undo N Actions"
(Cancel when none is highlighted), and a click undoes them all at once.
Engine: Session::undo_history (newest first, capped at UNDO_LIST_MAX = 100)
and Session::undo_steps; edit.undoTo {"count": n} (0, negative and non-number
counts rejected, counts past the depth clamped, stops at protection) and the
read-only edit.undoHistory for CLI and MCP. Catalog: Undo List.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n: Hungarian and French strings for this change
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #701: Bulgarian for the Undo history strings; drop a duplicate Turkish entry
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>
* R21: an empty continuous section mark takes no room
An empty paragraph that only carries a section break before a continuous
section takes no room in Word: the text after it follows right on, and
columns balanced above it don't count it. A break on a paragraph with text
keeps its line. Measured in Word for Windows: 10 synthetic cases. The SARP
form's fields drifted a line lower at each of its continuous breaks.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ROADMAP: keep the progress log separator under its header
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #766: start on File › Home (Start screen) when launched with nothing to open
Desktop and web start on Backstage Home (Blank document, templates, Recent)
when no file, --sample or --control is given; Esc/back leaves the empty blank
document. File › Options › General › Start-up checkbox (ui.startScreen, kept
between runs, default on). WordApp::new is unchanged; hosts opt in via
WordApp::start_screen. ui_shot: {"empty":true,"startScreen":true}.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n: Hungarian and French strings for this change
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #766: Bulgarian for the Start screen setting
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>
Word moves a "keep with next" paragraph to the next page only when it would
fit on this one by itself; one that doesn't fit anyway breaks as usual, its
last lines going over with the next paragraph (with keep lines together it
moves whole). A chain of kept paragraphs moves together. Measured in Word
for Windows: 20 synthetic cases. NIH's five-line kept paragraph no longer
leaves a third of a page empty.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #835: AutoCorrect's English replacement list applies only to English text
Built-in word entries (teh, i, dont…) are replaced only when the word's
language (run w:lang, style, document default) is English or unset;
symbols, fractions and user entries apply in every language. Polish,
Croatian, Bosnian and Serbian "i" ("and") is no longer turned into "I".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #835 review: untagged text follows the editing language; English as proofing decides it
- Text with no language at all (a .docx without w:lang) is in the editing language, so with a
Polish interface `Ala i kot ` keeps its `i` there too.
- `wordcraft_proof::ProofLang::from_tag` decides what is English (it also takes `eng`), so
AutoCorrect and proofing agree; the local `is_english` is gone.
- Tests through `set_editing_language` (the document-default path) and with no language at all.
- docs/typing-parity.md: the Croatian/Bosnian/Serbian case needs the text tagged, as those
interfaces set no editing language yet.
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>
A numbered paragraph style's own indents (and those of styles based on it)
now beat the list level's, its base styles' don't, and the paragraph's own
list beats every style. A paragraph that switches its list off (numId 0)
drops the styles' indents, as Word does; the first Backspace in a List
Paragraph item makes the item's indent the paragraph's own so the text stays
put. Measured in Word for Windows: 27 synthetic cases, all match. NIH's
Heading 3s sit at their 0.9" and its "sample text" paragraphs at the margin.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>