| 1 | # Native paragraph editing |
| 2 | |
| 3 | OneNote 2010 authored 13 cases, split each through keyboard input, reopened the |
| 4 | result in a fresh disposable VM, and joined each pair through keyboard input. |
| 5 | The four phases retain notebook bytes and independent native XML: `before`, |
| 6 | `split`, `cold-split`, and `joined`. A fourteenth control page remains unchanged. |
| 7 | |
| 8 | Cases cover mixed formatting, style/start/end/empty boundaries, nested children, |
| 9 | bullets, numbering, completed tags, table cells, soft breaks, and splitting plain |
| 10 | text before a hyperlink. The manifest records storage identities and split |
| 11 | positions. Captured UI scripts and screenshots retain the actual native actions; |
| 12 | `joined/first-backspace` records the intermediate list/indentation changes. |
| 13 | |
| 14 | The original paragraph stays on the left and children transfer to the new right |
| 15 | paragraph. Completed tags remain on the left rich-text object. Joining an empty |
| 16 | left paragraph retains its paragraph identity but adopts the right text object. |
| 17 | Bullet, numbered, and nested cases require two Backspaces because the first |
| 18 | changes list or indentation state. The native application also added flag |
| 19 | `0x880034dd` and changed timestamps on some unrelated text objects; the manifest |
| 20 | records those observations without assigning undocumented meaning to the flag. |
| 21 | |
| 22 | `tools/native/paragraphs.ps1` authors the cases. Use `tools/native_runner.py` with |
| 23 | that author, `--expected-pages 14 --inspect --collect-notebook`, then navigate to |
| 24 | each current page/object ID and send its recorded keys. Preserve the authored |
| 25 | notebook before completing inspection. Cold-open the split notebook for the join |
| 26 | pass. Captured COM IDs belong to their originating sessions; discover fresh IDs |
| 27 | when regenerating the corpus. |
| 28 | |
| 29 | `rust-split` retains twelve Rust paragraph-split outputs, their cold native captures, |
| 30 | and a second native session typing into four empty boundary paragraphs. The |
| 31 | hyperlink case has no output: the operation rejected fields when this row was |
| 32 | captured, and the writer now splits before a link as OneNote does. The Rust |
| 33 | test `export_native_paragraph_splits` generates candidate notebooks; its output |
| 34 | directory comes from `ONESTORE_PARAGRAPH_OUTPUT`. Native character/style comparisons |
| 35 | use both the keyboard-generated controls and the Rust-written notebooks. Empty |
| 36 | typing checks extend the preexisting empty run in an independent expected model. |
| 37 | |
| 38 | `tools/test_paragraph_edit.py` verifies these controls without a VM. |
| 39 | Identical captured files link to one canonical copy within this corpus. |
| 40 | |
| 41 | `join-edges`, authored with `tools/native/paragraph-joins.ps1`, adds five native |
| 42 | Backspace cases for differing inherited styles, tags on either side and children |
| 43 | on both sides. A nonempty left paragraph keeps its own tags; right-only tags |
| 44 | disappear from the active result. An empty tagged left paragraph transfers its |
| 45 | tags onto the adopted right text. A left paragraph with children joins through |
| 46 | its last descendant, and the right children become that descendant's peers. |
| 47 | The untouched parent's text gains a timestamp and `0x880034dd`, as in the earlier |
| 48 | native controls; its content and other properties stay unchanged. The test fixes |
| 49 | these observed graph, identity and metadata effects alongside independent native |
| 50 | character-style comparisons. |
| 51 | |
| 52 | `join-tags`, authored with `tools/native/paragraph-tag-joins.ps1`, confirms the |
| 53 | left-tag rule for an empty untagged left paragraph, an empty tagged left paragraph |
| 54 | with a different right tag, and two nonempty paragraphs with different tags. |
| 55 | Right-side tags disappear in all three cases; only the left tag survives. |
| 56 | |
| 57 | `rust-join` retains twenty Rust joins across the original splits, inheritance |
| 58 | edges and additional tag controls. Each group has a cold OneNote capture and |
| 59 | saved notebook. Public tests compare the Rust and native-saved images to native |
| 60 | XML, preserve exact child/content identities across reopening, and compare |
| 61 | the native-rendered result to the corresponding keyboard-generated control. |
| 62 | `export_native_paragraph_joins` regenerates candidates in a new directory specified |
| 63 | by `ONESTORE_PARAGRAPH_JOIN_OUTPUT`. |
| 64 | |
| 65 | `offline` retains reconciled split/join candidates and cold native captures. |
| 66 | The cache queues each recorded split or join as a page-model save before |
| 67 | observing an independent remote `☂` prefix, reopens between acknowledgements, |
| 68 | and confirms modeled lost replies without replaying a publication. The |
| 69 | `inheritance` group has four cases: `Join inherited styles` joins runs whose |
| 70 | language or spacing differ, which the page model cannot express, and is |
| 71 | recorded as refused. Tests compare native text/styles to the original keyboard |
| 72 | controls, account for the remote prefixes, and retain exact graph identities |
| 73 | after reopening. |
| 74 | The ignored offline test `export_native_offline_paragraphs` regenerates candidates |
| 75 | in a new directory specified by `ONESTORE_OFFLINE_PARAGRAPH_OUTPUT`. |