Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
791da7b
feat(editor): zoom-aware page-fit + metadata label stacking (4a.4)
claude Jul 6, 2026
31ca77b
feat(editor): move caret into the new table's first cell on insert (4…
claude Jul 6, 2026
028bca0
feat(editor): double-Enter exits an empty list item (4b.1 tail)
claude Jul 6, 2026
a1690d7
feat(editor): contextual Table ribbon tab + selected_object signal (4…
claude Jul 6, 2026
a822f06
feat(table): structural row/column mutations + Table-tab controls (4a.2)
claude Jul 6, 2026
68213d4
feat(editor): paragraph alignment controls, path-aware (works in cells)
claude Jul 6, 2026
6ce624d
feat(editor): font-size grow/shrink controls (path-aware)
claude Jul 6, 2026
781a269
feat(editor): text-colour controls with preset swatches (path-aware)
claude Jul 6, 2026
e4ceb05
feat(editor): Layout ribbon tab with page-orientation toggle (4a.2)
claude Jul 6, 2026
6bc6267
feat(editor): Layout tab margin presets (Normal/Narrow/Wide)
claude Jul 6, 2026
82256e6
feat(editor): Layout tab page-size presets (A4 / US Letter)
claude Jul 6, 2026
31a7b4e
feat(editor): Layout tab column presets (one / two / three)
claude Jul 6, 2026
5291367
Add highlight-colour group and caret-relative table row/column inserts
claude Jul 6, 2026
286b92b
Add the width-driven ribbon collapse engine (Spec 04 M3 §7)
claude Jul 6, 2026
3f123bc
Bind the ribbon collapse engine to the viewport + condensed group UI
claude Jul 6, 2026
0602d05
Add the collapse-aware ribbon container + overflow menu (Spec 04 M3)
claude Jul 6, 2026
efa2e3c
Migrate Write + Insert tabs to the collapse cascade; R-13e select-width
claude Jul 6, 2026
b2ebe02
Migrate Publish + Table tabs to the collapse cascade; menu dismiss ro…
claude Jul 6, 2026
a2079a0
Add the character family's Default resolution source (Spec 05 / 4a.3)
claude Jul 6, 2026
1d255ee
Make the character style family editable (Spec 05 M6 / 4a.3)
claude Jul 6, 2026
4ec5f91
Add the Compact-tree breadcrumb + drill-down (Spec 05 M7 / 4a.3)
claude Jul 6, 2026
5e0ee44
Add the table-style provenance resolver + Default source (Spec 05 / 4…
claude Jul 6, 2026
cee2112
Start the page style family: PageStyle model + import-mapping core (4…
claude Jul 6, 2026
8216349
Add the read-only page style panel (Spec 05 M6 / 4a.3)
claude Jul 6, 2026
098d32a
Add the ODF character default-style import (Spec 05 / 4a.3)
claude Jul 6, 2026
577118b
Add the per-page-style geometry edit mutation (Spec 05 / 4a.3)
claude Jul 6, 2026
9c39819
Add the editable per-page-style form (Spec 05 / 4a.3)
claude Jul 6, 2026
6a2c974
Add the stored section→page-style reference (Spec 05 / 4a.3)
claude Jul 6, 2026
b5c1d85
Page family: rename UI + migrate panel to stored refs
claude Jul 7, 2026
0b113ea
Page family: ODT native master-page naming + import population
claude Jul 7, 2026
1075b61
Page family: round-trip the master-page style:display-name
claude Jul 7, 2026
3377ea4
References tab: generate a table of contents (Spec 04 M5, 4a.2)
claude Jul 7, 2026
489bede
Review tab: track-changes model foundation (Spec 04 M5, 4a.2)
claude Jul 7, 2026
0b84f15
Review tab: render tracked changes (Spec 04 M5, 4a.2)
claude Jul 7, 2026
a73fbd2
Review tab: editor records tracked insertions (Spec 04 M5, 4a.2)
claude Jul 7, 2026
f71f1d1
Review tab: durable track-changes toggle (Spec 04 M5, 4a.2)
claude Jul 7, 2026
608d1a9
Review tab: accept/reject all tracked changes (Spec 04 M5, 4a.2)
claude Jul 7, 2026
4e41e91
Review tab: tracked deletions (Spec 04 M5, 4a.2)
claude Jul 7, 2026
4944a21
OOXML w:ins/w:del import/export for tracked changes (Spec 04 M5, 4a.2)
claude Jul 7, 2026
e18188e
ODF text:tracked-changes import/export for tracked changes (Spec 04 M…
claude Jul 7, 2026
7b9cdc5
Tracked selection deletion for track changes (Spec 04 M5, 4a.2)
claude Jul 7, 2026
62a6401
Per-change accept/reject at the caret for track changes (Spec 04 M5, …
claude Jul 8, 2026
93f79c7
Resolve tracked changes nested in table cells and note bodies (Spec 0…
claude Jul 8, 2026
12c5fab
Paragraph-mark tracked deletion (Spec 04 M5, 4a.2)
claude Jul 8, 2026
feb8351
DOCX paragraph-mark tracked deletion round-trip (Spec 04 M5, 4a.2)
claude Jul 8, 2026
5300224
Record paragraph-mark deletions inside table cells / note bodies (Spe…
claude Jul 8, 2026
a4f65f0
Per-change accept/reject of a paragraph-mark deletion (Spec 04 M5, 4a.2)
claude Jul 8, 2026
64f1e8f
Defer struck-¶ rendering (Review tab, 4a.2)
claude Jul 8, 2026
6c02a10
Table-style reference foundation for banding/conditional formatting (…
claude Jul 8, 2026
456cf05
Add table-style banding/conditional model + pure shading resolver
claude Jul 8, 2026
47448c9
Consult table style for cell shading in the flow engine
claude Jul 8, 2026
7dcbcde
Import DOCX table-style conditional formatting (w:tblStylePr)
claude Jul 8, 2026
c8c221d
Import per-table w:tblLook so tables use their own active regions
claude Jul 8, 2026
4b0f776
Export DOCX table-style banding for a full round-trip
claude Jul 8, 2026
70daec1
Export ODT cell shading as per-cell table-cell styles
claude Jul 8, 2026
308d206
Resolve table-style banding into per-cell shading on ODT export
claude Jul 8, 2026
1c618ec
Fix RUSTSEC-2026-0194/0195 (quick-xml DoS) and document the Marvin At…
claude Jul 8, 2026
6e36141
Write ODT table-level table:style-name + round-trip the reference
claude Jul 8, 2026
294b7d5
Merge quick-xml 0.41 security fixes (RUSTSEC-2026-0194/0195)
claude Jul 8, 2026
06abb39
Split flow.rs: extract table-geometry helpers (Phase 7.1)
claude Jul 8, 2026
e4dbcd5
Split flow.rs: extract PAGE/NUMPAGES field cluster (Phase 7.1)
claude Jul 8, 2026
b258374
Split para.rs: extract tab-stop cluster (Phase 7.1)
claude Jul 8, 2026
d56b7ad
Split resolve.rs: extract ParaProps mapping (Phase 7.1)
claude Jul 8, 2026
fa323cd
Split flow.rs: sub-split flow_table into measure + paint (Phase 7.1)
claude Jul 8, 2026
2a1e63e
Split para.rs: extract selection-geometry underlays (Phase 7.1)
claude Jul 8, 2026
4dca430
Split odt/reader/styles.rs: extract inline tests (Phase 7.1)
claude Jul 8, 2026
64f2720
Split odt/reader/styles.rs: extract page/master-page parsers (Phase 7.1)
claude Jul 8, 2026
f08fce3
Split odt/reader/styles.rs: extract list-level parsers (Phase 7.1)
claude Jul 9, 2026
21b2666
Split odt/reader/document.rs: extract inline tests (Phase 7.1)
claude Jul 9, 2026
01b781c
Split odt/reader/document.rs: extract table parsers (Phase 7.1)
claude Jul 9, 2026
ef64e91
Extract inline tests: scene.rs, package.rs, docx mapper document.rs (…
claude Jul 9, 2026
bcffc0f
docs: update Phase 7.1 split-pass progress (15 cuts, -2957 lines)
claude Jul 9, 2026
71a864a
Extract inline tests: docx mapper props.rs, doc-model document.rs (Ph…
claude Jul 9, 2026
068a2fd
Split doc-model document.rs under the ceiling (Phase 7.1)
claude Jul 9, 2026
b9e7a6d
Split docx/mapper/props.rs under the ceiling (Phase 7.1)
claude Jul 9, 2026
15ac41e
Split docx/reader/document.rs: extract table-cell parsers (Phase 7.1)
claude Jul 9, 2026
79361b7
docs: consolidate Phase 7.1 split-pass progress (20 cuts, backlog 31-…
claude Jul 9, 2026
42b7ad9
Split docx/write/document.rs: extract table serialization (Phase 7.1)
claude Jul 9, 2026
bf24067
Split docx/reader/document.rs: extract table-level parsers (Phase 7.1)
claude Jul 9, 2026
bf06c41
Split odt/reader/document.rs: extract frame parsers (Phase 7.1)
claude Jul 9, 2026
c097d9c
Split odt/reader/styles.rs: extract paragraph-property parsers (Phase…
claude Jul 9, 2026
5ab977b
docs: update Phase 7.1 split-pass progress (24 cuts, -4222 lines)
claude Jul 9, 2026
ebdc9cb
Split flow.rs: extract flow_table pass 3a — unblocks 4b.5 (Phase 7.1)
claude Jul 9, 2026
97ac493
Split para.rs: extract ParagraphLayout query methods — unblocks 6.3 (…
claude Jul 9, 2026
72c1835
Implement rotated table-cell editing (4b.5 core)
claude Jul 9, 2026
b57be1b
Render underline / strikethrough variants (Phase 5.2)
claude Jul 9, 2026
20478f3
feat(layout): honour DocumentSettings.default_tab_stop_pt (5.1)
claude Jul 9, 2026
63a3abf
feat(layout): anchor spelling squiggle to the descender (5.3)
claude Jul 9, 2026
3cd3473
feat(pdf): rotate RotatedGroup via content CTM (5.5)
claude Jul 9, 2026
1a8c057
feat(odf): master-page transitions → sections, fix leading empty sect…
claude Jul 9, 2026
7667d7f
feat(layout): honour floating class for wrap-less anchored images (5.6)
claude Jul 10, 2026
7b145d6
feat(layout): point→URL hyperlink hit-testing primitive (5.11)
claude Jul 10, 2026
90b9f69
feat(text): Ctrl/Cmd+click opens hyperlinks in the editor (5.11)
claude Jul 10, 2026
c0f7fa2
feat(layout): render picture bullets as out-of-band images (5.4, mode…
claude Jul 10, 2026
e83aaaa
feat(ooxml): import DOCX picture bullets (5.4, OOXML)
claude Jul 10, 2026
9a1f5a5
feat(odf): import ODT picture bullets (5.4, ODF)
claude Jul 10, 2026
444a60e
docs: mark 5.4 (picture bullets) done in plan + fidelity-status
claude Jul 10, 2026
b226463
feat(layout): even/odd section-break blank filler pages (5.10)
claude Jul 10, 2026
2ebab0f
feat(epub): emit Inline::Math as native MathML (5.10)
claude Jul 10, 2026
2568596
Model and honour unequal column widths (5.10)
claude Jul 10, 2026
7098095
Render EPUB fields and comments (5.10)
claude Jul 10, 2026
42319a4
Subset embedded fonts in PDF export (5.10)
claude Jul 10, 2026
4ab592e
Balance columns of single-page multi-column sections (5.10)
claude Jul 10, 2026
ee278e5
Document and streamline PDF ICC/CMYK OutputIntent (5.10)
claude Jul 10, 2026
0a1e24d
Bundle a CC0 CMYK ICC profile and embed it by default (5.10)
claude Jul 10, 2026
7437bd7
Honour widow/orphan control in pagination (5.9)
claude Jul 10, 2026
743f82e
Unwrap block-level content controls on DOCX import (5.9)
claude Jul 10, 2026
791cb52
Fix clippy useless_borrows_in_formatting in flow footnote mark
claude Jul 10, 2026
27dc87a
Fix CI failures under toolchain 1.97 (clippy lints + test compile)
claude Jul 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
29 changes: 29 additions & 0 deletions .cargo/audit.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# cargo-audit configuration. See `cargo audit --help` / the rustsec/rustsec repo
# for the full schema.

[advisories]
ignore = [
# RUSTSEC-2023-0071 — "Marvin Attack" timing side-channel in `rsa`'s
# PKCS#1 v1.5 decryption. No patched `rsa` release exists upstream (this
# advisory has carried "No fixed upgrade is available!" since 2023).
#
# Root-cause diagnosis (2026-07-08): `rsa` reaches this workspace only as
# an optional transitive dependency of `sqlx-mysql`, itself an optional
# dependency of the `sqlx` meta-crate. This workspace's `sqlx` dependency
# is declared with `default-features = false` and only the `postgres`
# feature enabled (see the root `Cargo.toml` — no crate here requests
# `mysql`). Confirmed with `cargo tree -i rsa` (and `cargo tree --target
# all -i rsa`) returning "nothing to print" even against a from-scratch
# `cargo generate-lockfile` — Cargo.lock lists `sqlx-mysql`/`rsa` because
# modern Cargo pre-resolves every optional dependency for lockfile
# stability, not because either crate is compiled into any workspace
# binary. The vulnerable `rsa` decryption code path is therefore never
# linked or reachable; there is nothing to vendor-patch.
#
# Re-verify this reasoning (`cargo tree -i rsa`, expect no output) before
# removing this ignore, and remove it immediately if any crate ever
# enables `sqlx`'s `mysql` feature — at that point `rsa` would become
# reachable and would need a real fix (vendored patch or an alternate
# MySQL driver).
"RUSTSEC-2023-0071",
]
86 changes: 73 additions & 13 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,22 @@ may not grow, and a file split to ≤300 must be removed from the baseline. So t
backlog can only shrink. When you split a file below the ceiling, drop its line
with `scripts/check-file-ceiling.py --update` (review the diff).

The split pass is **in progress** — current backlog is the **35** entries in the
baseline file. Two techniques:
The split pass is **in progress** — current backlog is the **29** entries in the
baseline file (a 2026-07-08 pass cut ~20 files: −3600 lines across eleven new
production submodules + seven inline-test extractions, driving `doc-model`
`document.rs` and `docx/mapper/props.rs` fully under the ceiling and off the
baseline). Three techniques (the third added 2026-07-08):
1. *Inline-test extraction* (safest, no production-code change): move a file's
`#[cfg(test)] mod tests { … }` into a sibling `<name>_tests.rs` referenced via
`#[cfg(test)] #[path = "<name>_tests.rs"] mod tests;`. Done 2026-06-21 for
`block.rs`, `docx/mapper/{paragraph,numbering,mod,table}.rs`, `odt/import.rs`,
`odt/mapper/lists.rs`, `layout/result.rs`, `renderer/render_layout.rs`, and
2026-06-28 for `editing/hit_test.rs`, `xml_util.rs`, `pdf/src/page.rs` — each
was over the ceiling only because of a large inline test module.
2026-06-28 for `editing/hit_test.rs`, `xml_util.rs`, `pdf/src/page.rs`, and
2026-07-08 for `odt/reader/styles.rs` (1554 → 1298), `odt/reader/document.rs`
(1492 → 1002; ~490-line module), `loki-vello/scene.rs` (948 → 727),
`loki-odf/package.rs` (644 → 410), and `loki-ooxml/docx/mapper/document.rs`
(611 → 448) — each was over the ceiling only because of a large inline
test module (or, as with `styles.rs`, partly so).
2. *Directory split*: convert `foo.rs` → a `foo/` directory with section-cohesive
submodules, re-export the public entry points from `foo/mod.rs`, and move the
tests via the same `#[path]` idiom. Give each submodule its **own explicit
Expand All @@ -167,22 +174,47 @@ baseline file. Two techniques:
`odt/mapper/props/` and `odt/mapper/document.rs` →
`odt/mapper/document/` (`mod`/`inlines`/`frames`/`blocks`/`page`/`meta`; worked
examples).
3. *Cohesive-cluster extraction* (for a monolith whose tests are already
extracted, so technique 1 doesn't apply): move a self-contained group of
functions into a new `<name>_helper.rs` sibling declared via
`#[path = "…"] mod <name>;`, accessing the parent's state through `super::`
(mark the shared items `pub(super)`). The new file must itself be ≤300.
Done 2026-07-08 for `loki-layout/src/flow.rs` (1948 → 1535, two cuts): the
four table-geometry helpers → `flow_table_geom.rs` (`table_geom` submodule),
and the PAGE/NUMPAGES field cluster → `flow_page_fields.rs` (`page_fields`
submodule; a `pub(crate)` item used elsewhere is re-exported from `flow.rs`
so its external path stays stable). Also `loki-layout/src/para.rs`
(1856 → 1698): the tab-stop cluster → `para_tabs.rs` (`tabs` submodule); and
`loki-layout/src/resolve.rs` (978 → 865): the `ParaProps`→`ResolvedParaProps`
mapping → `para_props_map.rs` (`para_map` submodule). A fourth variant is
*function-internal phase extraction* — a single >300-line function split by
moving self-contained phases into helper fns in a sibling module (thread the
captured locals as params; `#[allow(clippy::too_many_arguments)]` at the
narrowest scope is acceptable, per the `flow_cell_blocks` precedent). Done
2026-07-08 for `flow.rs`'s `flow_table` (~420 lines): row-height measurement +
cell-decoration passes → `flow_table_paint.rs` (`table_paint` submodule),
`flow.rs` 1535 → 1362; and for `para.rs`'s `layout_paragraph_uncached`
(~630 lines): the two selection-geometry underlay passes (highlight fills +
spelling squiggles) → `para_underlays.rs` (`underlays` submodule),
`para.rs` 1698 → 1626; and `flow.rs`'s `flow_table` pass 3a (the per-cell
content-flow loop) → `flow_table_cells.rs` (`table_cells` submodule),
`flow.rs` 1362 → 1209 (this landed the `rotated-cell-editing` path in a
sub-ceiling module, unblocking deferred-feature 4b.5).

(Test files are exempt from the production-line count.)

| File | Current lines | Priority |
|---|---|---|
| `loki-layout/src/para.rs` | 1979 | High |
| `loki-layout/src/flow.rs` | 1953 | High |
| `loki-odf/src/odt/reader/styles.rs` | 1554 | High |
| `loki-odf/src/odt/reader/document.rs` | 1494 | High |
| `loki-spreadsheet/src/routes/editor/editor_inner.rs` | 1244 | High |
| `loki-ooxml/src/docx/reader/document.rs` | 1209 | High |
| `loki-layout/src/para.rs` | 1626 | High |
| `loki-layout/src/flow.rs` | 1362 | High |
| `loki-ooxml/src/docx/write/document.rs` | 1073 | High |
| `loki-layout/src/resolve.rs` | 984 | High |
| … 27 more (6 over 600, 21 in 300–600) — see `scripts/file-ceiling-baseline.txt` | | |
| `loki-spreadsheet/src/routes/editor/editor_inner.rs` | 1047 | High |
| `loki-ooxml/src/docx/reader/document.rs` | 1004 | High |
| `loki-odf/src/odt/reader/styles.rs` | 892 | Med |
| `loki-layout/src/resolve.rs` | 865 | Med |
| … 22 more — see `scripts/file-ceiling-baseline.txt` (29 entries after the 2026-07-08 pass) | | |

*(Sizes above are from `scripts/file-ceiling-baseline.txt`, refreshed 2026-07-04;
*(Sizes above are from `scripts/file-ceiling-baseline.txt`, refreshed 2026-07-08;
the earlier numbers were stale — several files grew since first baselined.)*

(`odt/mapper/document.rs` (1094 lines) was split into the `odt/mapper/document/`
Expand All @@ -205,6 +237,34 @@ but are **not perfectly round-tripped through the Loro CRDT**.

---

## Known tech debt — residual vulnerable transitive `quick-xml` copies (2026-07-08)

This workspace's own `quick-xml` usage (`loki-epub`, `loki-odf`, `loki-ooxml`,
`loki-opc`) is on `0.41.0`, patched against RUSTSEC-2026-0194 (quadratic-time
duplicate-attribute check) and RUSTSEC-2026-0195 (unbounded namespace-allocation
DoS). `cargo audit` still reports both advisories against three *other*,
transitively-pulled `quick-xml` copies that this workspace does not control —
each is pinned by an intermediate crate's own manifest to a range that doesn't
yet reach `0.41`:

| Locked version | Pulled in by | Actual exposure |
|---|---|---|
| `0.38.4` | `object_store` (`^0.38.0`, via the `aws` feature — `loki-server-store`'s S3 backend) | Parses XML API responses (list-bucket, multipart) from the configured object-storage endpoint. `object_store` 0.14.0 (latest at audit time) still only requires `quick-xml ^0.40.1`, which is *also* unpatched (fix requires `>=0.41.0`) — no released `object_store` version resolves this yet. |
| `0.39.4` | `wayland-scanner` → `smithay-client-toolkit` → `winit` → `blitz-shell` (`loki-presentation`) | Build-time codegen only: generates Rust bindings from the (trusted, locally-vendored) Wayland protocol XML. Not exposed to untrusted runtime input. |
| `0.30.0` | `zbus_xml` → `zbus-lockstep` → `atspi` → `accesskit_unix` → `accesskit_winit` → `blitz-shell` | Parses AT-SPI/D-Bus introspection XML on the local session bus (Linux accessibility stack) — local IPC, not attacker-controlled document content. |

None of these are fixable by bumping our own `Cargo.toml` requirements — each is
gated behind an upstream crate release that hasn't caught up to `quick-xml`
0.41 yet. `object_store` is the one with real untrusted-network exposure and is
worth re-checking most often. Re-run `cargo audit` (or
`cargo tree -i quick-xml`) periodically and bump `object_store` /
`wayland-scanner`'s dependents the moment a release satisfies `quick-xml
>=0.41` — do not silence these via `.cargo/audit.toml` (unlike the
`rsa`/Marvin-Attack entry there, these three *are* actually compiled and
reachable).

---

## Workspace layout & capabilities

The workspace is a set of focused crates (one responsibility each). Key groups:
Expand Down
53 changes: 40 additions & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading