From 3b58d8f8a9667e9578deab17b4bcd96e05515ad8 Mon Sep 17 00:00:00 2001 From: clover caruso Date: Tue, 29 Sep 2026 16:20:50 -0700 Subject: [PATCH] feat: change-driven sync with offline copies, Outlook task tags, OneNote-exact tag art, and pixel-snapped icons - Closed sections sync from change notifications instead of a timer: one recursive CHANGE_NOTIFY per notebook on the embedded SMB client and the OS watcher for local folders, with staggered checks, a retry for failing sections, and an hourly backstop. Every section of a notebook on a share gets an offline copy, as OneNote 2010 keeps. - Outlook task tags keep their shape and property status, never write a definition reference, stay read-only, and are removable; undo of a page delete restores them. - Tag art matches OneNote's rendering pixel for pixel on white: symbol 54 is green, check boxes and badges are redrawn from OneNote's pixels, with translucent edges for dark pages. - Icons land on whole device pixels. - The macOS and Linux toolbar rows use equal leading and trailing insets. Assisted-by: claude-opus-5.5 --- arc/sync.md | 36 +- corpus/custom-tags/README.md | 4 +- corpus/custom-tags/native/shapes-1-72.png | Bin 0 -> 67681 bytes corpus/custom-tags/native/shapes-73-143.png | Bin 0 -> 71553 bytes .../native-ui/notebook/Open Notebook.onetoc2 | Bin 0 -> 3748 bytes corpus/ink-tools/native-ui/notebook/ink.one | Bin 0 -> 75888 bytes corpus/task-tags/README.md | 20 + .../task-tags/candidate/Open Notebook.onetoc2 | Bin 0 -> 1604 bytes corpus/task-tags/candidate/Tasks.one | Bin 0 -> 20912 bytes corpus/task-tags/cold/commands.jsonl | 3 + corpus/task-tags/cold/machine.json | 1 + .../cold/notebook/Open Notebook.onetoc2 | Bin 0 -> 3764 bytes corpus/task-tags/cold/notebook/Tasks.one | Bin 0 -> 20912 bytes corpus/task-tags/cold/read/environment.json | 7 + corpus/task-tags/cold/read/hierarchy.xml | 2 + .../cold/read/page-000.navigation.json | 13 + corpus/task-tags/cold/read/page-000.png | Bin 0 -> 44008 bytes corpus/task-tags/cold/read/page-000.xml | 2 + .../cold/read/page-001.navigation.json | 13 + corpus/task-tags/cold/read/page-001.png | Bin 0 -> 43269 bytes corpus/task-tags/cold/read/page-001.xml | 2 + corpus/task-tags/cold/read/payloads.json | 3 + corpus/task-tags/cold/run.json | 18 + corpus/task-tags/cold/scripts/cold.ps1 | 27 + corpus/task-tags/cold/scripts/read.ps1 | 144 ++++ corpus/task-tags/cold/source.json | 14 + corpus/task-tags/cold/teardown.json | 1 + corpus/task-tags/native/commands.jsonl | 4 + corpus/task-tags/native/machine.json | 1 + .../native/notebook/Open Notebook.onetoc2 | Bin 0 -> 3900 bytes corpus/task-tags/native/notebook/Tasks.one | Bin 0 -> 16320 bytes corpus/task-tags/native/notebook/fixture.json | 15 + .../task-tags/native/notebook/synthetic.one | Bin 0 -> 3348 bytes corpus/task-tags/native/read/environment.json | 7 + corpus/task-tags/native/read/hierarchy.xml | 2 + .../native/read/page-000.navigation.json | 13 + corpus/task-tags/native/read/page-000.png | Bin 0 -> 41346 bytes corpus/task-tags/native/read/page-000.xml | 2 + .../native/read/page-001.navigation.json | 13 + corpus/task-tags/native/read/page-001.png | Bin 0 -> 44880 bytes corpus/task-tags/native/read/page-001.xml | 2 + .../native/read/page-002.navigation.json | 13 + corpus/task-tags/native/read/page-002.png | Bin 0 -> 44884 bytes corpus/task-tags/native/read/page-002.xml | 2 + corpus/task-tags/native/read/payloads.json | 3 + corpus/task-tags/native/run.json | 19 + corpus/task-tags/native/scripts/author.ps1 | 42 ++ corpus/task-tags/native/scripts/cold.ps1 | 27 + corpus/task-tags/native/scripts/read.ps1 | 144 ++++ corpus/task-tags/native/source.json | 20 + corpus/task-tags/native/teardown.json | 1 + crates/canvas/assets/tags/box-small.svg | 13 +- crates/canvas/assets/tags/checkbox-green.svg | 28 +- crates/canvas/assets/tags/checkbox-yellow.svg | 28 +- crates/canvas/assets/tags/checkbox.svg | 29 +- crates/canvas/assets/tags/flag.svg | 10 - crates/canvas/assets/tags/mark-arrow-blue.svg | 44 +- .../canvas/assets/tags/mark-arrow-green.svg | 38 +- .../canvas/assets/tags/mark-arrow-yellow.svg | 43 +- .../assets/tags/mark-exclamation-blue.svg | 49 +- .../assets/tags/mark-exclamation-green.svg | 50 +- .../assets/tags/mark-exclamation-yellow.svg | 49 +- crates/canvas/assets/tags/mark-flag-blue.svg | 58 +- crates/canvas/assets/tags/mark-flag-green.svg | 61 +- .../canvas/assets/tags/mark-flag-yellow.svg | 60 +- crates/canvas/assets/tags/mark-one-blue.svg | 9 +- crates/canvas/assets/tags/mark-one-green.svg | 9 +- crates/canvas/assets/tags/mark-one-yellow.svg | 9 +- .../canvas/assets/tags/mark-person-blue.svg | 69 +- .../canvas/assets/tags/mark-person-green.svg | 74 +- .../canvas/assets/tags/mark-person-yellow.svg | 76 +- crates/canvas/assets/tags/mark-star-blue.svg | 62 +- crates/canvas/assets/tags/mark-star-green.svg | 62 +- .../canvas/assets/tags/mark-star-yellow.svg | 60 +- crates/canvas/assets/tags/mark-three-blue.svg | 9 +- .../canvas/assets/tags/mark-three-green.svg | 9 +- .../canvas/assets/tags/mark-three-yellow.svg | 9 +- crates/canvas/assets/tags/mark-two-blue.svg | 9 +- crates/canvas/assets/tags/mark-two-green.svg | 9 +- crates/canvas/assets/tags/mark-two-yellow.svg | 9 +- crates/canvas/assets/tags/star8-green.svg | 4 + crates/canvas/src/document.rs | 2 + crates/canvas/src/editor/evidence.rs | 2 + crates/canvas/src/editor/format.rs | 6 +- crates/canvas/src/gpu/mod.rs | 90 ++- crates/canvas/src/gpu/page.rs | 2 + .../canvas/src/interaction/accessibility.rs | 4 +- crates/canvas/src/interaction/mod.rs | 31 + crates/canvas/src/outline.rs | 29 +- crates/canvas/tests/task_tags.rs | 145 ++++ crates/draw/src/render.rs | 29 +- crates/draw/src/render/icon.rs | 83 +-- crates/mobile/src/library.rs | 16 +- crates/notebook/README.md | 29 +- crates/notebook/examples/launch_probe.rs | 228 ++++++ crates/notebook/src/background.rs | 671 ++++++++++++++---- crates/notebook/src/discover.rs | 4 + crates/notebook/src/lib.rs | 29 +- crates/notebook/src/merge.rs | 2 + crates/notebook/src/session.rs | 23 +- crates/notebook/src/smb/directory.rs | 67 ++ crates/notebook/src/smb/tests.rs | 38 + crates/notebook/tests/session.rs | 78 +- crates/onestore/examples/ink_lab.rs | 25 + crates/onestore/examples/scratch_dump.rs | 29 + crates/onestore/src/document.rs | 40 +- crates/onestore/src/op/apply.rs | 99 ++- crates/onestore/src/op/content.rs | 164 +++-- crates/onestore/src/op/lower.rs | 40 +- crates/onestore/src/op/mod.rs | 10 +- crates/onestore/src/op/model.rs | 16 + crates/onestore/src/op/properties.rs | 137 ++-- crates/onestore/src/op/tests.rs | 30 +- crates/onestore/src/page/ink.rs | 200 ++++++ crates/onestore/src/page/mod.rs | 32 +- crates/onestore/tests/page_ink.rs | 371 ++++++++++ crates/onestore/tests/page_media.rs | 35 +- crates/onestore/tests/page_tags.rs | 48 ++ crates/onestore/tests/support/page_edits.rs | 2 + crates/snowbound/src/library.rs | 68 +- crates/snowbound/src/linux.rs | 2 + crates/snowbound/src/macos.rs | 2 + crates/snowbound/src/main.rs | 37 +- crates/snowbound/src/manage.rs | 6 +- crates/snowbound/src/meeting.rs | 2 + crates/snowbound/src/sidebar.rs | 2 +- crates/snowbound/src/tags.rs | 12 +- crates/snowbound/src/watch.rs | 441 ++++++++++++ crates/ui/src/popup.rs | 18 +- tools/test_task_tags.py | 52 ++ 130 files changed, 4577 insertions(+), 639 deletions(-) create mode 100644 corpus/custom-tags/native/shapes-1-72.png create mode 100644 corpus/custom-tags/native/shapes-73-143.png create mode 100644 corpus/ink-tools/native-ui/notebook/Open Notebook.onetoc2 create mode 100644 corpus/ink-tools/native-ui/notebook/ink.one create mode 100644 corpus/task-tags/README.md create mode 100644 corpus/task-tags/candidate/Open Notebook.onetoc2 create mode 100644 corpus/task-tags/candidate/Tasks.one create mode 100644 corpus/task-tags/cold/commands.jsonl create mode 100644 corpus/task-tags/cold/machine.json create mode 100644 corpus/task-tags/cold/notebook/Open Notebook.onetoc2 create mode 100644 corpus/task-tags/cold/notebook/Tasks.one create mode 100644 corpus/task-tags/cold/read/environment.json create mode 100644 corpus/task-tags/cold/read/hierarchy.xml create mode 100644 corpus/task-tags/cold/read/page-000.navigation.json create mode 100644 corpus/task-tags/cold/read/page-000.png create mode 100644 corpus/task-tags/cold/read/page-000.xml create mode 100644 corpus/task-tags/cold/read/page-001.navigation.json create mode 100644 corpus/task-tags/cold/read/page-001.png create mode 100644 corpus/task-tags/cold/read/page-001.xml create mode 100644 corpus/task-tags/cold/read/payloads.json create mode 100644 corpus/task-tags/cold/run.json create mode 100644 corpus/task-tags/cold/scripts/cold.ps1 create mode 100644 corpus/task-tags/cold/scripts/read.ps1 create mode 100644 corpus/task-tags/cold/source.json create mode 100644 corpus/task-tags/cold/teardown.json create mode 100644 corpus/task-tags/native/commands.jsonl create mode 100644 corpus/task-tags/native/machine.json create mode 100644 corpus/task-tags/native/notebook/Open Notebook.onetoc2 create mode 100644 corpus/task-tags/native/notebook/Tasks.one create mode 100644 corpus/task-tags/native/notebook/fixture.json create mode 100644 corpus/task-tags/native/notebook/synthetic.one create mode 100644 corpus/task-tags/native/read/environment.json create mode 100644 corpus/task-tags/native/read/hierarchy.xml create mode 100644 corpus/task-tags/native/read/page-000.navigation.json create mode 100644 corpus/task-tags/native/read/page-000.png create mode 100644 corpus/task-tags/native/read/page-000.xml create mode 100644 corpus/task-tags/native/read/page-001.navigation.json create mode 100644 corpus/task-tags/native/read/page-001.png create mode 100644 corpus/task-tags/native/read/page-001.xml create mode 100644 corpus/task-tags/native/read/page-002.navigation.json create mode 100644 corpus/task-tags/native/read/page-002.png create mode 100644 corpus/task-tags/native/read/page-002.xml create mode 100644 corpus/task-tags/native/read/payloads.json create mode 100644 corpus/task-tags/native/run.json create mode 100644 corpus/task-tags/native/scripts/author.ps1 create mode 100644 corpus/task-tags/native/scripts/cold.ps1 create mode 100644 corpus/task-tags/native/scripts/read.ps1 create mode 100644 corpus/task-tags/native/source.json create mode 100644 corpus/task-tags/native/teardown.json delete mode 100644 crates/canvas/assets/tags/flag.svg create mode 100644 crates/canvas/assets/tags/star8-green.svg create mode 100644 crates/canvas/tests/task_tags.rs create mode 100644 crates/notebook/examples/launch_probe.rs create mode 100644 crates/onestore/examples/ink_lab.rs create mode 100644 crates/onestore/examples/scratch_dump.rs create mode 100644 crates/snowbound/src/watch.rs create mode 100644 tools/test_task_tags.py diff --git a/arc/sync.md b/arc/sync.md index 426a30ff8fd5f147df3c0223c536ec0f475935f0..b3676e680cbc3b2a1db55e0cd82f7918be8b9835 100644 --- a/arc/sync.md +++ b/arc/sync.md @@ -176,13 +176,35 @@ online again. ## Sections that aren't open -OneNote keeps every section of an open notebook in sync, not just the one on screen: it -reads a section another client changed within seconds and publishes a closed section's -offline edits the moment the share is back. Snowbound's `session::Background` does the -same with one thread per notebook. Each round reads every section file's stamp. Only a -section whose replica has edits waiting, or whose file moved past the replica's base, -has its replica opened for the usual sync steps, and it is closed again afterwards. The -open section is left to its own session. +OneNote keeps every section of an open notebook in sync and on this computer, not just +the one on screen. Watched in the lab with a notebook of 200 sections on Samba, OneNote +2010: + +- reads every section file once when the notebook opens (all 200 within 15 seconds), so a + section never shown before opens with the share gone; +- arms one CHANGE_NOTIFY on the notebook's folder, with WATCH_TREE and a filter of names, + attributes, size and last write, and otherwise sends nothing: 22 idle minutes put no SMB + request on the wire, not even an echo; +- lists the folder once when the server reports a change, and reads only the section that + changed, some seconds later; +- tries a section it could not reach again about every 31 seconds. + +Snowbound's `session::Background` does the same, with one thread per notebook: + +```text +share ─ CHANGE_NOTIFY (tree) ─► the sections it names are due in 1 s ─► stamp ─► moved: rebase +first pass: one section every 100 ms; a section without a replica gets its offline copy +failing: again in 31 s otherwise: again in an hour, against a lost notification +``` + +A check costs one stamp read. Only a section whose replica has edits waiting, or whose file +moved past the replica's base, has its replica opened for the usual sync steps, and it is +closed again afterwards; the open section is left to its own session. A connection that +drops takes its watch with it, so the next one checks every section again. A notebook in a +folder on this computer has no copies and learns of changes from FSEvents or inotify; one on +a network volume the system mounted, whose server's changes those never report, keeps copies +and is checked every 15 seconds. Closing a notebook deletes its copies, except any with +edits still waiting. ## Notebook structure diff --git a/corpus/custom-tags/README.md b/corpus/custom-tags/README.md index 8710cd7036300fc69bfa957e33a63997f74bc96f..d3ceaf57089254172c25ceaf6a35056b6ea6b412 100644 --- a/corpus/custom-tags/README.md +++ b/corpus/custom-tags/README.md @@ -18,7 +18,9 @@ absolute directory while running the test, then cold-open it with `native/shapes.one` is OneNote 2010's own work in a lab clone: - "Shapes 1-72" and "Shapes 73-143" tag a paragraph with every NoteTagShape, set through - COM (its definitions store NoteTagPropertyStatus 0). + COM (its definitions store NoteTagPropertyStatus 0). `shapes-1-72.png` and + `shapes-73-143.png` are OneNote's render of them at 100%, which + `crates/canvas/src/gpu/mod.rs` pins each shape's art to. - "Custom tags" holds a tag made with New Tag (Snow check, as above), applied at the top of the list (action type 0), then after moving it below To Do (a second definition, action type 1), with To Do applied from both places. The UI stores status 15 and 9. diff --git a/corpus/custom-tags/native/shapes-1-72.png b/corpus/custom-tags/native/shapes-1-72.png new file mode 100644 index 0000000000000000000000000000000000000000..30cd457617deb485161954030de7cb684e434724 GIT binary patch literal 67681 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 literal 0 HcmV?d00001 diff --git a/corpus/custom-tags/native/shapes-73-143.png b/corpus/custom-tags/native/shapes-73-143.png new file mode 100644 index 0000000000000000000000000000000000000000..d04feff39f0c50ba9ce5fc882c22694707208bbf GIT binary patch literal 71553 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 literal 0 HcmV?d00001 diff --git a/corpus/ink-tools/native-ui/notebook/ink.one b/corpus/ink-tools/native-ui/notebook/ink.one new file mode 100644 index 0000000000000000000000000000000000000000..01939d1854e461748502a18f68771afbe4cbf65b GIT binary patch literal 75888 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 literal 0 HcmV?d00001 diff --git a/corpus/task-tags/README.md b/corpus/task-tags/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a1081b7b31d980fd1cb3d62a8ee9917e7715b3ff --- /dev/null +++ b/corpus/task-tags/README.md @@ -0,0 +1,20 @@ +# Outlook task tags through the editor + +`native/` is OneNote 2010 authoring `tools/native/pages.ps1` input (two identical +pages in `Tasks.one`: three paragraphs carrying disabled Outlook tasks, the third +also a To Do tag, and an untagged one). OneNote stores each task as a note tag +without a definition, with ActionItemType 100, NoteTagShape 89 and +NoteTagPropertyStatus 0. + +`candidate/` is the output of `task_tags_survive_tag_removal_and_page_delete_undo` +in `crates/canvas/tests/task_tags.rs`: on "Remove a task" the editor's Remove Tag +clears the first paragraph's task and toggling To Do off the third keeps its +task; "Delete and undo" has all its content deleted and the deletion undone, +each published as its own revision. + +`cold/` is a fresh OneNote 2010 read: the first paragraph has no task, the other +tasks keep their identities, dates and disabled state, and the restored page +reads as OneNote authored it. `tools/test_task_tags.py` checks this without a +VM. Regenerate with `SNOWBOUND_TASK_TAGS_EXPORT` set to a new absolute directory +while running the test, then cold-open it with +`tools/native_runner.py OUTPUT COLD --expected-pages 2 --collect-notebook`. diff --git a/corpus/task-tags/candidate/Open Notebook.onetoc2 b/corpus/task-tags/candidate/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..a79db3efc09ce7ecf9a4e8849013d578d6dfe117 GIT binary patch literal 1604 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 literal 0 HcmV?d00001 diff --git a/corpus/task-tags/cold/commands.jsonl b/corpus/task-tags/cold/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..276dfefb877170ca7f35ce210d77985ef98ae3ba --- /dev/null +++ b/corpus/task-tags/cold/commands.jsonl @@ -0,0 +1,3 @@ +{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-7A22B21E -ExpectedPages 2 -KeepOpen", "exit": 0, "stdout": "Read 1 sections and 2 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} diff --git a/corpus/task-tags/cold/machine.json b/corpus/task-tags/cold/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..9af02ba40b3bad254d3314ecd4b0f30143b7da03 --- /dev/null +++ b/corpus/task-tags/cold/machine.json @@ -0,0 +1 @@ +{"name": "m6-7a22b21e", "hostname": "ONE-M6-7A22B21E"} diff --git a/corpus/task-tags/cold/notebook/Open Notebook.onetoc2 b/corpus/task-tags/cold/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..d9d55ade712677c224c1d2cf4f897cf3ae5d2ad2 GIT binary patch literal 3764 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 literal 0 HcmV?d00001 diff --git a/corpus/task-tags/cold/read/environment.json b/corpus/task-tags/cold/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..00ece83ec92016040f0174d6c20f04629f96af5c --- /dev/null +++ b/corpus/task-tags/cold/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-7A22B21E", + "cold": true, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/task-tags/cold/read/hierarchy.xml b/corpus/task-tags/cold/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..852fecc0cbf0a42e28d0b2370192d137baa5b10e --- /dev/null +++ b/corpus/task-tags/cold/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/task-tags/cold/read/page-000.navigation.json b/corpus/task-tags/cold/read/page-000.navigation.json new file mode 100644 index 0000000000000000000000000000000000000000..f96bf51a7b355a67f40c9bb4d1d7f1cf04129a47 --- /dev/null +++ b/corpus/task-tags/cold/read/page-000.navigation.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Delete and undo - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/task-tags/cold/read/page-000.png b/corpus/task-tags/cold/read/page-000.png new file mode 100644 index 0000000000000000000000000000000000000000..064305ae57a8239de918456eb34bcc60e7e68f80 GIT binary patch literal 44008 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 O0000 + diff --git a/corpus/task-tags/cold/read/page-001.navigation.json b/corpus/task-tags/cold/read/page-001.navigation.json new file mode 100644 index 0000000000000000000000000000000000000000..e5a5078a1abd65bb9ba64a5a6d5603d374cfc401 --- /dev/null +++ b/corpus/task-tags/cold/read/page-001.navigation.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Remove a task - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/task-tags/cold/read/page-001.png b/corpus/task-tags/cold/read/page-001.png new file mode 100644 index 0000000000000000000000000000000000000000..05d3d4e4a616fc3de6837067a64a860720402a4d GIT binary patch literal 43269 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�lCEJt`X~KCK(QVRm0C7 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 + diff --git a/corpus/task-tags/cold/read/payloads.json b/corpus/task-tags/cold/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843 --- /dev/null +++ b/corpus/task-tags/cold/read/payloads.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/corpus/task-tags/cold/run.json b/corpus/task-tags/cold/run.json new file mode 100644 index 0000000000000000000000000000000000000000..8e9ec1b77f472065cbd23c61869969409c18f975 --- /dev/null +++ b/corpus/task-tags/cold/run.json @@ -0,0 +1,18 @@ +{ + "notebook": "/private/tmp/tasktags/candidate", + "expected_pages": 2, + "author": null, + "author_timeout_seconds": 600, + "inspect": false, + "collect_notebook": true, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "22bc760f955ef59bb5f0ccfd55271f8d868674084127fbe219e1048bd3777f35" + } +} diff --git a/corpus/task-tags/cold/scripts/cold.ps1 b/corpus/task-tags/cold/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/task-tags/cold/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/task-tags/cold/scripts/read.ps1 b/corpus/task-tags/cold/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a42366b32e8baaf13dae92e9472cae7a071220cc --- /dev/null +++ b/corpus/task-tags/cold/scripts/read.ps1 @@ -0,0 +1,144 @@ +param( + [Parameter(Mandatory=$true)][string]$Root, + [int]$ExpectedPages = -1, + [switch]$UseCurrentCache, + [switch]$Pdf, + [switch]$KeepOpen, + [string]$CloneHost = '' +) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +$notebook = Join-Path $root 'notebook' +$output = Join-Path $root 'read' +if (Test-Path $output) { throw 'Choose a new read destination.' } +if ($UseCurrentCache) { + if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') { + throw 'Park the personal OneNote profile before reading test notebooks.' + } +} else { + & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost +} +New-Item -ItemType Directory -Path $output | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +$failure = $null +try { + $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0) + $process = Get-Process ONENOTE + @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion; + powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } | + ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8 + $sections = @() + foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) { + $id = '' + $app.OpenHierarchy($file.FullName, '', [ref]$id, 0) + $sections += $id + } + $deadline = [DateTime]::UtcNow.AddSeconds(300) + $previous = '' + $lastChange = '' + $stableSince = [DateTime]::UtcNow + $settled = $false + do { + $pages = @{} + foreach ($section in $sections) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$xml = $hierarchy + foreach ($node in $xml.SelectNodes('//*[@path]')) { + if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) { + throw 'OneNote opened a section outside the copied notebook.' + } + } + foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) { + $id = $node.GetAttribute('ID') + $content = '' + $app.GetPageContent($id, [ref]$content, 1, 1) + $pages[$id] = $content + } + } + $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] })) + if ($signature -ne $previous) { + $lastChange = $previous + $previous = $signature + $stableSince = [DateTime]::UtcNow + } + if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or + ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and + ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $deadline) + if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) { + [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8) + $index = 0 + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + $index++ + } + $hierarchy = '' + $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8) + throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled." + } + $index = 0 + $payloads = @() + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + if ($Pdf) { + $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index) + $app.NavigateTo($id, '', $false) + $app.Publish($id, $pdfPath, 3, '') + if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' } + } + [xml]$page = $pages[$id] + foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) { + $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache')) + $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant() + [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes) + # A paragraph's file is named by its paragraph, one on the page by itself. + $owner = if ($file.ParentNode.LocalName -eq 'Page') { $file } else { $file.ParentNode } + $payloads += @{ page = $id; object = $owner.GetAttribute('objectID'); + kind = $file.LocalName; name = $file.GetAttribute('preferredName'); + sha256 = $hash; bytes = $bytes.Length } + } + $index++ + } + [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8) + $all = '' + $app.GetHierarchy($notebookId, 4, [ref]$all, 1) + [xml]$finalTree = $all + $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique) + if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) { + throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.' + } + [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8) + Write-Output "Read $($sections.Count) sections and $($pages.Count) pages." +} catch { + $failure = $_ + [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + throw +} finally { + try { + try { + if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) } + if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) { + $app.CloseNotebook($notebookId, $false) + } + } catch { + if ($null -eq $failure) { throw } + [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + } + } finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + } + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + if (-not $UseCurrentCache -and -not $CloneHost) { + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + } +} diff --git a/corpus/task-tags/cold/source.json b/corpus/task-tags/cold/source.json new file mode 100644 index 0000000000000000000000000000000000000000..ac722a07015446c753a4481e06ac17245e3c3451 --- /dev/null +++ b/corpus/task-tags/cold/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Open Notebook.onetoc2", + "bytes": 1604, + "sha256": "a1609ca18a7e5a25f51bd75d7f7eaeb815b339406cb4c60c7cc14cd8422e6272", + "mtime_ns": 1790725054434635462 + }, + { + "path": "Tasks.one", + "bytes": 20912, + "sha256": "a40370dd47c7591f67162db6d1448bdd306705cccbbc575076daa99080fda3ac", + "mtime_ns": 1790725054434461086 + } +] diff --git a/corpus/task-tags/cold/teardown.json b/corpus/task-tags/cold/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/task-tags/cold/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/task-tags/native/commands.jsonl b/corpus/task-tags/native/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..d8eb267daaceb2d15beb16e2e90f59141eadda4c --- /dev/null +++ b/corpus/task-tags/native/commands.jsonl @@ -0,0 +1,4 @@ +{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\author.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-7E19D637", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-7E19D637 -ExpectedPages 3 -UseCurrentCache -KeepOpen", "exit": 0, "stdout": "Read 2 sections and 3 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} diff --git a/corpus/task-tags/native/machine.json b/corpus/task-tags/native/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..5694c3afccb4b8c2eb135352bcfd70833a0a00a3 --- /dev/null +++ b/corpus/task-tags/native/machine.json @@ -0,0 +1 @@ +{"name": "m6-7e19d637", "hostname": "ONE-M6-7E19D637"} diff --git a/corpus/task-tags/native/notebook/Open Notebook.onetoc2 b/corpus/task-tags/native/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..472d6e7550e214918ba659757f43e019b2cf823d GIT binary patch literal 3900 zcmeGeTS!zQ_`=B>eXcZ=e>mcX?uybGSc=F^(aNrQ1}i;QX8@mB2?(PjbFqV#+@~$0C z{7?~SYvJ?W9giulAB6>ns2#!j*diDkZ$J|Mp=e!AT&cOW0w#{ z8HAiU*Cy!T`bkhOEj$_lTy}!5GIUFs91$IF70-EiH-~qcI@ks{lUu&*9?#`j@MJ>K z7R1d=^tnCt&3%7rdyfV5aG8)RE)yiRG_9vgo6Bn4^hfB0>ej%Ttkjs?YgJKXLwS6( z#KxekNPr_{szI5t8Co0vHY=@xVK7wSFDkI~SK-b;`tPj~sS}+FK1C5PXZbIso}@B> zp#p{q{HGPb3rG9#qQ@HDNQ-X-y2x~I1UB_wyifKPb_^;M!hqVb{@EtWbQN8K62wqA MhVR{IM7}rfPk}$0ssI20 literal 0 HcmV?d00001 diff --git a/corpus/task-tags/native/notebook/Tasks.one b/corpus/task-tags/native/notebook/Tasks.one new file mode 100644 index 0000000000000000000000000000000000000000..08e153510b758c1c4246131099a0606e8cd5e7d0 GIT binary patch literal 16320 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 G{{IVk+cLBO literal 0 HcmV?d00001 diff --git a/corpus/task-tags/native/notebook/fixture.json b/corpus/task-tags/native/notebook/fixture.json new file mode 100644 index 0000000000000000000000000000000000000000..744a1a27b2a4bf593cd939baec710332fc024232 --- /dev/null +++ b/corpus/task-tags/native/notebook/fixture.json @@ -0,0 +1,15 @@ +{ + "sections": [ + "Tasks.one" + ], + "pages": [ + { + "section": "Tasks.one", + "xml": "Remove a taskFirst taskSecond taskTagged taskUntagged" + }, + { + "section": "Tasks.one", + "xml": "Delete and undoFirst taskSecond taskTagged taskUntagged" + } + ] +} \ No newline at end of file diff --git a/corpus/task-tags/native/notebook/synthetic.one b/corpus/task-tags/native/notebook/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..1ed24299e20474a4366648beba61b958cc609d9d GIT binary patch literal 3348 zcmeHKeN2^Q6hBuGK~QgSxu9I=4rmjzM#~mX4FN@gt})7@=ns=<(1$vaiPqX1NnOR} zWuTZA5vJLk3kb=bjTW4JCSqL z8&f;l-*RCQaDO(`uY)+Z7y^AX z^G7iEhHG-SHu%1_IlienS#57!)Kgr(x_kfbp(FN|?Lj)MoQ-(sgWUs}HQU{HdOMSH zC-0iN>PUHap21VmTasH5`2R-$?^EFu3zAfaI7%E6>vTx66_U(~E0-tEf4jEpq9x7s z>a$ky{Mk2i#U+dHMJqv46)(1$C1UGwh;1SEkoO!P#+O7*s^73(950J$yE-bb^YGBe z8SN*s5<9;fST)7A^o{>E(YEu^_oz+k*dR(--Qlm##{QJud_+D?CO$(Y96s_*+&tv@-Exe$AfZWFfzbC*aOcs6*J zgsA=s!89Fo0$|^{M`ma@B3P({D*-Tiq1ZWtM|H!ru*Z?Gmb{Pdt@K?JO_23ya3H2wQGyGHNP~^ z3>()*pQ(Ss#2ZG0*3B>m1mO?|uY1$`Mna1efn1o~*SbNrl2Tv3`aEsiI z`kWLEB}A>=Us1nCJzhs$vX)sr%bFT~EN-}xRo`{2s{QJ@51H)@ampsyEZd|+He%c? z&p}cwTd{shl|GGi8FZXdAQe&$+1=P@)VKw6qh>i(Bc;S(43?cZn{TKhUL4E8O$)wy zjQR=SY}l-XtqS!dC;X_usubsM1v(wiaDoyG8==1QgB%hOiI~{%Hlx9DUpRuMUADpM@3QxWr!DF1yS9cwn@-<39-owc(d0V!bm z8}7feUkCF6uoNTw!B}P+#0>Dq{iif|q$Y`d((JzMlODEY(3m!u_DSP+MyqD@1KhY!FDVv=Sr4X;qW^789kcU; z;g{K7x^lBTvJgqV%H&E4EFR+i#S>UBv|)CR6>=t&zpJc&l$#oHzTdfEW`}Yk6O0T0 RAD~~%3^O-+pUduV`)^j%je-CG literal 0 HcmV?d00001 diff --git a/corpus/task-tags/native/read/environment.json b/corpus/task-tags/native/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..8e54223dba9fed8f8acde2a6ccc065fddcec0607 --- /dev/null +++ b/corpus/task-tags/native/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-7E19D637", + "cold": false, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/task-tags/native/read/hierarchy.xml b/corpus/task-tags/native/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..96653181b75da23827728de2327b2175641f5ec8 --- /dev/null +++ b/corpus/task-tags/native/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/task-tags/native/read/page-000.navigation.json b/corpus/task-tags/native/read/page-000.navigation.json new file mode 100644 index 0000000000000000000000000000000000000000..002a1c45a5ba0121a28f35c11466c61b8a2aac13 --- /dev/null +++ b/corpus/task-tags/native/read/page-000.navigation.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "A notebook created in Rust. - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/task-tags/native/read/page-000.png b/corpus/task-tags/native/read/page-000.png new file mode 100644 index 0000000000000000000000000000000000000000..1d05020740f0a612f768176deb75dd49f24a7188 GIT binary patch literal 41346 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 ^F)JTz^Z2`5Z`&|KtR&5Ma~3J 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 LfrWmBE;RCgg$s-s literal 0 HcmV?d00001 diff --git a/corpus/task-tags/native/read/page-000.xml b/corpus/task-tags/native/read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..60f94b3d923c8f5a7dafc5da13daa2b3e9f31eac --- /dev/null +++ b/corpus/task-tags/native/read/page-000.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/task-tags/native/read/page-001.navigation.json b/corpus/task-tags/native/read/page-001.navigation.json new file mode 100644 index 0000000000000000000000000000000000000000..f96bf51a7b355a67f40c9bb4d1d7f1cf04129a47 --- /dev/null +++ b/corpus/task-tags/native/read/page-001.navigation.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Delete and undo - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/task-tags/native/read/page-001.png b/corpus/task-tags/native/read/page-001.png new file mode 100644 index 0000000000000000000000000000000000000000..2fd9dc14761352197d5a577db5c87b6552193ac3 GIT binary patch literal 44880 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 + diff --git a/corpus/task-tags/native/read/page-002.navigation.json b/corpus/task-tags/native/read/page-002.navigation.json new file mode 100644 index 0000000000000000000000000000000000000000..e5a5078a1abd65bb9ba64a5a6d5603d374cfc401 --- /dev/null +++ b/corpus/task-tags/native/read/page-002.navigation.json @@ -0,0 +1,13 @@ +{ + "exit": 0, + "stdout": "", + "stderr": "", + "w": 800, + "h": 600, + "error": null, + "win": { + "title": "Remove a task - Microsoft OneNote", + "class": "Framework::CFrame", + "dialog": false + } +} \ No newline at end of file diff --git a/corpus/task-tags/native/read/page-002.png b/corpus/task-tags/native/read/page-002.png new file mode 100644 index 0000000000000000000000000000000000000000..91e9eae3c8ca4e6a29fa14b1dd69c8ce16b243f8 GIT binary patch literal 44884 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 literal 0 HcmV?d00001 diff --git a/corpus/task-tags/native/read/page-002.xml b/corpus/task-tags/native/read/page-002.xml new file mode 100644 index 0000000000000000000000000000000000000000..9ce6cd2ec2b05abcbd5ac71a37ed2cbda9fbc1cb --- /dev/null +++ b/corpus/task-tags/native/read/page-002.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/task-tags/native/read/payloads.json b/corpus/task-tags/native/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843 --- /dev/null +++ b/corpus/task-tags/native/read/payloads.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/corpus/task-tags/native/run.json b/corpus/task-tags/native/run.json new file mode 100644 index 0000000000000000000000000000000000000000..368201b132e9cc2d3ce44f8b52a844e2ad9d1974 --- /dev/null +++ b/corpus/task-tags/native/run.json @@ -0,0 +1,19 @@ +{ + "notebook": "/private/tmp/tasktags/input/notebook", + "expected_pages": 3, + "author": "tools/native/pages.ps1", + "author_timeout_seconds": 600, + "inspect": false, + "collect_notebook": true, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "author.ps1": "dfbf0e016aa4c8486ecea9f39768c633de05ac0db8214139052ca2e9c43bbd02", + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "22bc760f955ef59bb5f0ccfd55271f8d868674084127fbe219e1048bd3777f35" + } +} diff --git a/corpus/task-tags/native/scripts/author.ps1 b/corpus/task-tags/native/scripts/author.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..8e428618e3452caa35da88687293b520c273b6c6 --- /dev/null +++ b/corpus/task-tags/native/scripts/author.ps1 @@ -0,0 +1,42 @@ +param([Parameter(Mandatory=$true)][string]$Root, [Parameter(Mandatory=$true)][string]$CloneHost) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +& "$PSScriptRoot\cold-current.ps1" -Root $Root -CloneHost $CloneHost +$fixture = Get-Content (Join-Path $Root 'notebook\fixture.json') -Raw -Encoding UTF8 | ConvertFrom-Json +$inputs = Join-Path $Root 'inputs' +New-Item -ItemType Directory -Path $inputs | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +try { + $app.OpenHierarchy((Join-Path $Root 'notebook'), '', [ref]$notebookId, 0) + $sections = @{} + foreach ($path in $fixture.sections) { + $parts = $path.Split('/') + if (@($parts | Where-Object { $_ -in @('', '.', '..') -or $_.Contains('\') -or $_.Contains(':') }).Count) { throw 'Invalid fixture section path.' } + $parent = $notebookId + for ($index = 0; $index -lt $parts.Count; $index++) { + $id = '' + $app.OpenHierarchy($parts[$index], $parent, [ref]$id, $(if ($index -eq $parts.Count - 1) {3} else {2})) + $parent = $id + } + $sections[$path] = $id + } + $index = 0 + foreach ($item in $fixture.pages) { + $pageId = '' + $app.CreateNewPage($sections[$item.section], [ref]$pageId, 0) + [xml]$xml = $item.xml.Replace('__ASSETS__', [Security.SecurityElement]::Escape((Join-Path $Root 'notebook\fixture-data'))) + $xml.DocumentElement.SetAttribute('ID', $pageId) + [IO.File]::WriteAllText((Join-Path $inputs ('page-{0:d3}.xml' -f $index)), $xml.OuterXml, [Text.Encoding]::UTF8) + $app.UpdatePageContent($xml.OuterXml, [DateTime]::MinValue, 1, $true) + $index++ + } + $app.SyncHierarchy($notebookId) + $app.CloseNotebook($notebookId, $false) + $notebookId = '' +} finally { + if ($notebookId) { $app.CloseNotebook($notebookId, $false) } + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + [GC]::Collect() + [GC]::WaitForPendingFinalizers() +} diff --git a/corpus/task-tags/native/scripts/cold.ps1 b/corpus/task-tags/native/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/task-tags/native/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/task-tags/native/scripts/read.ps1 b/corpus/task-tags/native/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a42366b32e8baaf13dae92e9472cae7a071220cc --- /dev/null +++ b/corpus/task-tags/native/scripts/read.ps1 @@ -0,0 +1,144 @@ +param( + [Parameter(Mandatory=$true)][string]$Root, + [int]$ExpectedPages = -1, + [switch]$UseCurrentCache, + [switch]$Pdf, + [switch]$KeepOpen, + [string]$CloneHost = '' +) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +$notebook = Join-Path $root 'notebook' +$output = Join-Path $root 'read' +if (Test-Path $output) { throw 'Choose a new read destination.' } +if ($UseCurrentCache) { + if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') { + throw 'Park the personal OneNote profile before reading test notebooks.' + } +} else { + & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost +} +New-Item -ItemType Directory -Path $output | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +$failure = $null +try { + $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0) + $process = Get-Process ONENOTE + @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion; + powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } | + ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8 + $sections = @() + foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) { + $id = '' + $app.OpenHierarchy($file.FullName, '', [ref]$id, 0) + $sections += $id + } + $deadline = [DateTime]::UtcNow.AddSeconds(300) + $previous = '' + $lastChange = '' + $stableSince = [DateTime]::UtcNow + $settled = $false + do { + $pages = @{} + foreach ($section in $sections) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$xml = $hierarchy + foreach ($node in $xml.SelectNodes('//*[@path]')) { + if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) { + throw 'OneNote opened a section outside the copied notebook.' + } + } + foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) { + $id = $node.GetAttribute('ID') + $content = '' + $app.GetPageContent($id, [ref]$content, 1, 1) + $pages[$id] = $content + } + } + $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] })) + if ($signature -ne $previous) { + $lastChange = $previous + $previous = $signature + $stableSince = [DateTime]::UtcNow + } + if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or + ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and + ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $deadline) + if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) { + [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8) + $index = 0 + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + $index++ + } + $hierarchy = '' + $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8) + throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled." + } + $index = 0 + $payloads = @() + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + if ($Pdf) { + $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index) + $app.NavigateTo($id, '', $false) + $app.Publish($id, $pdfPath, 3, '') + if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' } + } + [xml]$page = $pages[$id] + foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) { + $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache')) + $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant() + [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes) + # A paragraph's file is named by its paragraph, one on the page by itself. + $owner = if ($file.ParentNode.LocalName -eq 'Page') { $file } else { $file.ParentNode } + $payloads += @{ page = $id; object = $owner.GetAttribute('objectID'); + kind = $file.LocalName; name = $file.GetAttribute('preferredName'); + sha256 = $hash; bytes = $bytes.Length } + } + $index++ + } + [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8) + $all = '' + $app.GetHierarchy($notebookId, 4, [ref]$all, 1) + [xml]$finalTree = $all + $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique) + if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) { + throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.' + } + [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8) + Write-Output "Read $($sections.Count) sections and $($pages.Count) pages." +} catch { + $failure = $_ + [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + throw +} finally { + try { + try { + if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) } + if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) { + $app.CloseNotebook($notebookId, $false) + } + } catch { + if ($null -eq $failure) { throw } + [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + } + } finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + } + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + if (-not $UseCurrentCache -and -not $CloneHost) { + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + } +} diff --git a/corpus/task-tags/native/source.json b/corpus/task-tags/native/source.json new file mode 100644 index 0000000000000000000000000000000000000000..2a633edfa35e5cbd6be7e671e6ad7070024b433f --- /dev/null +++ b/corpus/task-tags/native/source.json @@ -0,0 +1,20 @@ +[ + { + "path": "Open Notebook.onetoc2", + "bytes": 1612, + "sha256": "fafa5dcb3cd827c7edd30c8ea2fbc957a59fb4d2a8b28b667a3c551c8261ab86", + "mtime_ns": 1790724703557857638 + }, + { + "path": "fixture.json", + "bytes": 2596, + "sha256": "7b243286f1ae5147ae8c97d87ae911e82a6ebfd264fc15450c43176067fbf067", + "mtime_ns": 1790724703583723809 + }, + { + "path": "synthetic.one", + "bytes": 3348, + "sha256": "b0662387c3fdc256d8dbe809e3b8f73b3586559696467fb1fff8f589095fa797", + "mtime_ns": 1790724703558075431 + } +] diff --git a/corpus/task-tags/native/teardown.json b/corpus/task-tags/native/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/task-tags/native/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/crates/canvas/assets/tags/box-small.svg b/crates/canvas/assets/tags/box-small.svg index 17ed0f99c5a134e131cf29b3392fb1940423c000..40989796ea848921c5e00e7994b062800153692a 100644 --- a/crates/canvas/assets/tags/box-small.svg +++ b/crates/canvas/assets/tags/box-small.svg @@ -1,11 +1,10 @@ - - - + - - - - + + + + + diff --git a/crates/canvas/assets/tags/checkbox-green.svg b/crates/canvas/assets/tags/checkbox-green.svg index 26a689bc0d346500fb20cddcb55bb7b8cfda1d45..3ec49601240885b8a1aed61da12bdc5b359f22ab 100644 --- a/crates/canvas/assets/tags/checkbox-green.svg +++ b/crates/canvas/assets/tags/checkbox-green.svg @@ -1,24 +1,18 @@ - - - - - + - - - - - - - - - + - - - + + + + + + + + + diff --git a/crates/canvas/assets/tags/checkbox-yellow.svg b/crates/canvas/assets/tags/checkbox-yellow.svg index 20d79b4f4cb3c19d23e417529533d4693455260a..1ff1a3cebf068760af0b5b93e62ef9d34eb52c67 100644 --- a/crates/canvas/assets/tags/checkbox-yellow.svg +++ b/crates/canvas/assets/tags/checkbox-yellow.svg @@ -1,24 +1,18 @@ - - - - - + - - - - - - - - - + - - - + + + + + + + + + diff --git a/crates/canvas/assets/tags/checkbox.svg b/crates/canvas/assets/tags/checkbox.svg index 17d1680279ef1c19bfdce1024d5f83a5bb86d8ea..31341b84e162e3c92e14379bcd23692d39629207 100644 --- a/crates/canvas/assets/tags/checkbox.svg +++ b/crates/canvas/assets/tags/checkbox.svg @@ -1,24 +1,19 @@ - - - - - + - - - - - - - - - + - - - + + + + + + + + + + diff --git a/crates/canvas/assets/tags/flag.svg b/crates/canvas/assets/tags/flag.svg deleted file mode 100644 index 7219fb33baf9f7dd45400dc26be74ead45ac4751..0000000000000000000000000000000000000000 --- a/crates/canvas/assets/tags/flag.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/crates/canvas/assets/tags/mark-arrow-blue.svg b/crates/canvas/assets/tags/mark-arrow-blue.svg index 97c2c3c44577ef5dad304ca3244098182964baa4..40d398d682e325cdce853ed5e6fc43e82493c533 100644 --- a/crates/canvas/assets/tags/mark-arrow-blue.svg +++ b/crates/canvas/assets/tags/mark-arrow-blue.svg @@ -1,6 +1,42 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/canvas/assets/tags/mark-arrow-green.svg b/crates/canvas/assets/tags/mark-arrow-green.svg index 512ac40f957d091737b171cb1fc8ca4ea9d435d5..c3a3032b80e6ed03ff9df0b201a96d90764529f4 100644 --- a/crates/canvas/assets/tags/mark-arrow-green.svg +++ b/crates/canvas/assets/tags/mark-arrow-green.svg @@ -1,6 +1,36 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/canvas/assets/tags/mark-arrow-yellow.svg b/crates/canvas/assets/tags/mark-arrow-yellow.svg index 34389c3c7493b5cc263937bac07d4e3e4a0372cc..8208759cc5a422d5405a30fc3393292fcc46c275 100644 --- a/crates/canvas/assets/tags/mark-arrow-yellow.svg +++ b/crates/canvas/assets/tags/mark-arrow-yellow.svg @@ -1,6 +1,41 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/canvas/assets/tags/mark-exclamation-blue.svg b/crates/canvas/assets/tags/mark-exclamation-blue.svg index de951416c56df76d3df63727c07414ea48fb5bab..2b2c26355aaaff4494870a3f6d9b35ac0aeabda9 100644 --- a/crates/canvas/assets/tags/mark-exclamation-blue.svg +++ b/crates/canvas/assets/tags/mark-exclamation-blue.svg @@ -1,9 +1,44 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/canvas/assets/tags/mark-exclamation-green.svg b/crates/canvas/assets/tags/mark-exclamation-green.svg index 67a7f3799ecb6aea7aefa3536d92e6e5c9cba7f9..e1d8f1a9745f83fad3cf2b173275e39d8bf7e51e 100644 --- a/crates/canvas/assets/tags/mark-exclamation-green.svg +++ b/crates/canvas/assets/tags/mark-exclamation-green.svg @@ -1,9 +1,45 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/canvas/assets/tags/mark-exclamation-yellow.svg b/crates/canvas/assets/tags/mark-exclamation-yellow.svg index b562e9082ec9bf2d0e6f6dad91a46658025f3f36..ee1fc42c7749fa31ce04c95f50e69cb59116b506 100644 --- a/crates/canvas/assets/tags/mark-exclamation-yellow.svg +++ b/crates/canvas/assets/tags/mark-exclamation-yellow.svg @@ -1,9 +1,44 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/canvas/assets/tags/mark-flag-blue.svg b/crates/canvas/assets/tags/mark-flag-blue.svg index 878943aa2791d75a33791f1ab3d7fe0daf4b7e90..036ed55b31d97694f175e24c1271077ced41a284 100644 --- a/crates/canvas/assets/tags/mark-flag-blue.svg +++ b/crates/canvas/assets/tags/mark-flag-blue.svg @@ -1,7 +1,55 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/canvas/assets/tags/mark-flag-green.svg b/crates/canvas/assets/tags/mark-flag-green.svg index fa9780a1eb2742cfc2cc73232cfbc3dd75f41235..ea2e17c01c3f844945e14d4f999f4b960f157865 100644 --- a/crates/canvas/assets/tags/mark-flag-green.svg +++ b/crates/canvas/assets/tags/mark-flag-green.svg @@ -1,7 +1,58 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/canvas/assets/tags/mark-flag-yellow.svg b/crates/canvas/assets/tags/mark-flag-yellow.svg index 7700e494609f2c94eb96cddbfcaa1dcaf960f9b8..2ecc942fbe74de91305e188828c958a0d4c69bc4 100644 --- a/crates/canvas/assets/tags/mark-flag-yellow.svg +++ b/crates/canvas/assets/tags/mark-flag-yellow.svg @@ -1,7 +1,57 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/canvas/assets/tags/mark-one-blue.svg b/crates/canvas/assets/tags/mark-one-blue.svg index f95d8aa3643bd45b9b71e6c9d4756543526d9c00..a6eb26cdf061ef5170214d25b46cd2d475f512d9 100644 --- a/crates/canvas/assets/tags/mark-one-blue.svg +++ b/crates/canvas/assets/tags/mark-one-blue.svg @@ -1,7 +1,6 @@ - - - - - + + + + diff --git a/crates/canvas/assets/tags/mark-one-green.svg b/crates/canvas/assets/tags/mark-one-green.svg index e0f1a75f255736d27b2f38c20733cb0deabc2997..6a8441d50ce49007d94cd9a19b55eb836ef925e6 100644 --- a/crates/canvas/assets/tags/mark-one-green.svg +++ b/crates/canvas/assets/tags/mark-one-green.svg @@ -1,7 +1,6 @@ - - - - - + + + + diff --git a/crates/canvas/assets/tags/mark-one-yellow.svg b/crates/canvas/assets/tags/mark-one-yellow.svg index 218834e755712a51e2d812bded96a093729f0cbe..07443c8a4bce54b886cd59950624480f791274cc 100644 --- a/crates/canvas/assets/tags/mark-one-yellow.svg +++ b/crates/canvas/assets/tags/mark-one-yellow.svg @@ -1,7 +1,6 @@ - - - - - + + + + diff --git a/crates/canvas/assets/tags/mark-person-blue.svg b/crates/canvas/assets/tags/mark-person-blue.svg index 1f2d6f0affa1495cf724a9825b126c6d175e5a81..e52b0cde393fcf4ff16d845c582c5fb763081d64 100644 --- a/crates/canvas/assets/tags/mark-person-blue.svg +++ b/crates/canvas/assets/tags/mark-person-blue.svg @@ -1,9 +1,64 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/canvas/assets/tags/mark-person-green.svg b/crates/canvas/assets/tags/mark-person-green.svg index e57d0341831f043771649d26b2d00876f0ae7ada..f42ddb12a4725b372547a3dba0dc4d3e5dddd568 100644 --- a/crates/canvas/assets/tags/mark-person-green.svg +++ b/crates/canvas/assets/tags/mark-person-green.svg @@ -1,9 +1,69 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/canvas/assets/tags/mark-person-yellow.svg b/crates/canvas/assets/tags/mark-person-yellow.svg index c7b1b3e74d3f5454151a4bd08edcabb2f54a4dee..15f89ca5d82aac6dce087b592c81f527bf6ecc81 100644 --- a/crates/canvas/assets/tags/mark-person-yellow.svg +++ b/crates/canvas/assets/tags/mark-person-yellow.svg @@ -1,9 +1,71 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/canvas/assets/tags/mark-star-blue.svg b/crates/canvas/assets/tags/mark-star-blue.svg index 64fc936e96b2dca0a40fc90e64909cbccd1b376c..2ea21816e39ffba2157e84beec390025c62cf68d 100644 --- a/crates/canvas/assets/tags/mark-star-blue.svg +++ b/crates/canvas/assets/tags/mark-star-blue.svg @@ -1,7 +1,59 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/canvas/assets/tags/mark-star-green.svg b/crates/canvas/assets/tags/mark-star-green.svg index ab4796db3423d428c90af8be4b51038af88a5119..a8755c7e4d054f4f903a4f88d6f878fac6e26c0c 100644 --- a/crates/canvas/assets/tags/mark-star-green.svg +++ b/crates/canvas/assets/tags/mark-star-green.svg @@ -1,7 +1,59 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/canvas/assets/tags/mark-star-yellow.svg b/crates/canvas/assets/tags/mark-star-yellow.svg index bd1f6c89d96aff646618f66d240d73b103f46cf3..900bbf00365a2f747a675bb305276903f1ae6c35 100644 --- a/crates/canvas/assets/tags/mark-star-yellow.svg +++ b/crates/canvas/assets/tags/mark-star-yellow.svg @@ -1,7 +1,57 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/canvas/assets/tags/mark-three-blue.svg b/crates/canvas/assets/tags/mark-three-blue.svg index 0e8fb6b92842fa4224379bf207198bafbba11154..86ae99bb698e80008b209361982094762296f3a4 100644 --- a/crates/canvas/assets/tags/mark-three-blue.svg +++ b/crates/canvas/assets/tags/mark-three-blue.svg @@ -1,7 +1,6 @@ - - - - - + + + + diff --git a/crates/canvas/assets/tags/mark-three-green.svg b/crates/canvas/assets/tags/mark-three-green.svg index cb2c99e7a2d95c066a73922b046b00f0b6a53861..03df97e3b44f9a8318651c069649bd889654a073 100644 --- a/crates/canvas/assets/tags/mark-three-green.svg +++ b/crates/canvas/assets/tags/mark-three-green.svg @@ -1,7 +1,6 @@ - - - - - + + + + diff --git a/crates/canvas/assets/tags/mark-three-yellow.svg b/crates/canvas/assets/tags/mark-three-yellow.svg index a742086017b7bfab0a90e6fc57b97b065f9e4bab..438143c1514947b8762d99fc747319fe620459a9 100644 --- a/crates/canvas/assets/tags/mark-three-yellow.svg +++ b/crates/canvas/assets/tags/mark-three-yellow.svg @@ -1,7 +1,6 @@ - - - - - + + + + diff --git a/crates/canvas/assets/tags/mark-two-blue.svg b/crates/canvas/assets/tags/mark-two-blue.svg index 40e030cafc219eb29bd7ee96e2208fa36dc01f32..b57326193a2c4b62a47a274c4a62049a8ec21a68 100644 --- a/crates/canvas/assets/tags/mark-two-blue.svg +++ b/crates/canvas/assets/tags/mark-two-blue.svg @@ -1,7 +1,6 @@ - - - - - + + + + diff --git a/crates/canvas/assets/tags/mark-two-green.svg b/crates/canvas/assets/tags/mark-two-green.svg index a7484b31afe5f08b34e55e527546ded2bb5e8620..d9db6b1a87a1705d37b2d26353f3728f9637e8ac 100644 --- a/crates/canvas/assets/tags/mark-two-green.svg +++ b/crates/canvas/assets/tags/mark-two-green.svg @@ -1,7 +1,6 @@ - - - - - + + + + diff --git a/crates/canvas/assets/tags/mark-two-yellow.svg b/crates/canvas/assets/tags/mark-two-yellow.svg index 43c86f79bd98b9127b769d353f8089e1f89d1294..1c620d8c589296a1d40829a30cd15af91a6ebeae 100644 --- a/crates/canvas/assets/tags/mark-two-yellow.svg +++ b/crates/canvas/assets/tags/mark-two-yellow.svg @@ -1,7 +1,6 @@ - - - - - + + + + diff --git a/crates/canvas/assets/tags/star8-green.svg b/crates/canvas/assets/tags/star8-green.svg new file mode 100644 index 0000000000000000000000000000000000000000..7feb0242557aa3451be873965d4037847b2627d2 --- /dev/null +++ b/crates/canvas/assets/tags/star8-green.svg @@ -0,0 +1,4 @@ + + + + diff --git a/crates/canvas/src/document.rs b/crates/canvas/src/document.rs index 1687d88dd6d9b83cc9b77271854c2b65486a8362..b8c418d7570b901ebacc4afed19f0e3825a02955 100644 --- a/crates/canvas/src/document.rs +++ b/crates/canvas/src/document.rs @@ -1578,6 +1578,8 @@ mod tests { nodes[1].tags.push(onestore::document::Tag { definition: Some(new_id().unwrap()), action_type: Some(0), + shape: None, + property_status: None, status: 1, created: Some(123), completed: None, diff --git a/crates/canvas/src/editor/evidence.rs b/crates/canvas/src/editor/evidence.rs index 19e44c28c7f1132dafb9bad94664402c0f54223f..21e92c05ac22102c3a301dcbbde4da240392e514 100644 --- a/crates/canvas/src/editor/evidence.rs +++ b/crates/canvas/src/editor/evidence.rs @@ -97,6 +97,8 @@ fn open(engine: &mut TextEngine, summary: &str) -> CanvasEditor { node.text_mut().unwrap().tags.push(Tag { definition: Some(definition), action_type: None, + shape: None, + property_status: None, status: u16::from(completed), created: Some(1), completed: Some(u32::from(completed)), diff --git a/crates/canvas/src/editor/format.rs b/crates/canvas/src/editor/format.rs index bd5849f029408c8ea71a5bbf31f258ae1ecb0f88..2654ce08cf56997b2bbaea9bbad4b408ccbe006d 100644 --- a/crates/canvas/src/editor/format.rs +++ b/crates/canvas/src/editor/format.rs @@ -769,6 +769,8 @@ impl CanvasEditor { Tag { definition: Some(id), action_type: None, + shape: None, + property_status: None, status: u16::from(!checkable), created, completed: if checkable { Some(0) } else { created }, @@ -1809,6 +1811,8 @@ mod tests { nodes[2].tags.push(Tag { definition: Some(music), action_type: None, + shape: None, + property_status: None, status: 1, created: Some(1), completed: Some(1), @@ -1938,7 +1942,7 @@ mod tests { .iter() .map(|tag| match tag.icon { crate::outline::TagIcon::Symbol { shape, .. } => shape, - crate::outline::TagIcon::Task => unreachable!(), + crate::outline::TagIcon::Task { .. } => unreachable!(), }) .collect::>(); assert_eq!(shapes.len(), defaults.len() - 2); diff --git a/crates/canvas/src/gpu/mod.rs b/crates/canvas/src/gpu/mod.rs index 11694f2fb8d7abeaefe92b7cc1255de7913e54c0..5d73aea37db05a69280742365d21480eabc67bfe 100644 --- a/crates/canvas/src/gpu/mod.rs +++ b/crates/canvas/src/gpu/mod.rs @@ -141,7 +141,6 @@ pub(crate) fn painted_layout(text: &dyn Glyphs) -> &TextLayout { } const CHECKMARK: &str = include_str!("../../assets/tags/checkmark.svg"); -const FLAG: &str = include_str!("../../assets/tags/flag.svg"); const BOX_SMALL: &str = include_str!("../../assets/tags/box-small.svg"); const CHECK_SMALL: &str = include_str!("../../assets/tags/check-small.svg"); const TAG: &str = include_str!("../../assets/tags/tag.svg"); @@ -155,8 +154,7 @@ enum Art { } /// The artwork of symbol `shape` of MS-ONE's NoteTagShape, as OneNote 2010 draws it on the -/// page: it draws 54, Green 8-Point Star, blue there. A shape MS-ONE does not list shows a -/// plain tag. +/// page. A shape MS-ONE does not list shows a plain tag. fn art(shape: u16) -> Art { use Art::{Badged, Box, Plain}; macro_rules! tag { @@ -218,7 +216,7 @@ fn art(shape: u16) -> Art { 51 => Plain(tag!("circle-2-green")), 52 => Badged(tag!("mark-three-green")), 53 => Plain(tag!("circle-3-green")), - 54 => Plain(tag!("star8-blue")), + 54 => Plain(tag!("star8-green")), 55 => Plain(tag!("tick-green")), 56 => Plain(tag!("circle-green")), 57 => Plain(tag!("arrow-down-green")), @@ -318,8 +316,9 @@ const SYMBOLS: u16 = 143; /// The tag's artwork, drawn in order. pub fn tag_sources(icon: TagIcon) -> &'static [&'static str] { static SOURCES: std::sync::OnceLock; 2]>> = std::sync::OnceLock::new(); - let TagIcon::Symbol { shape, checked } = icon else { - return &[FLAG]; + let (shape, checked) = match icon { + TagIcon::Symbol { shape, checked } => (shape, checked), + TagIcon::Task { shape } => (shape, false), }; let sources = SOURCES.get_or_init(|| { (0..=SYMBOLS) @@ -350,6 +349,83 @@ mod tests { use onestore::page::text::Paragraph; use std::time::Duration; + /// OneNote 2010 draws paragraph `shape N` of `corpus/custom-tags/native/shapes.one` with + /// the art `DRAWN`'s Nth word names (its render: `shapes-1-72.png`, `shapes-73-143.png`); + /// each paragraph's tag definition stores the NoteTagShape. + #[test] + fn symbols_draw_the_art_onenote_draws_for_their_number() { + use onestore::page::{PageObject, ParagraphContent}; + use onestore::{RevisionIndex, Store, document::Document, document::Kind, page::Page}; + const DRAWN: &str = "\ + checkbox-green checkbox-yellow checkbox mark-star-green mark-star-yellow \ + mark-star-blue mark-exclamation-green mark-exclamation-yellow mark-exclamation-blue \ + mark-arrow-green mark-arrow-yellow mark-arrow-blue star follow-up question \ + arrow-right-blue exclamation phone calendar clock idea pushpin address blog smiley \ + ribbon key mark-one-blue circle-1-blue mark-two-blue circle-2-blue mark-three-blue \ + circle-3-blue star8-blue tick-blue circle-blue arrow-down-blue arrow-left-blue \ + solid-target-blue star-blue sun-blue target-blue triangle-blue umbrella-blue \ + arrow-up-blue x-dots-blue x-blue mark-one-green circle-1-green mark-two-green \ + circle-2-green mark-three-green circle-3-green star8-green tick-green circle-green \ + arrow-down-green arrow-left-green arrow-right-green solid-target-green star-green \ + sun-green target-green triangle-green umbrella-green arrow-up-green x-dots-green \ + x-green mark-one-yellow circle-1-yellow mark-two-yellow circle-2-yellow \ + mark-three-yellow circle-3-yellow star8-yellow tick-yellow circle-yellow \ + arrow-down-yellow arrow-left-yellow arrow-right-yellow solid-target-yellow \ + sun-yellow target-yellow triangle-yellow umbrella-yellow arrow-up-yellow \ + x-dots-yellow x-yellow flag-today flag-tomorrow flag-this-week flag-next-week \ + flag-no-date mark-person-blue mark-person-yellow mark-person-green mark-flag-blue \ + mark-flag-yellow mark-flag-green red-square yellow-square blue-square green-square \ + orange-square pink-square email envelope envelope-open mobile phone-clock \ + question-balloon paperclip frown im-contact person people bell contact rose date \ + music movie quote globe web laptop plane car binoculars presentation password book \ + notebook paper research highlight dollar coins schedule lightning cloud heart \ + sunflower"; + let bytes = include_bytes!("../../../../corpus/custom-tags/native/shapes.one"); + let store = Store::parse(bytes).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let document = Document::parse(&index).unwrap(); + let files: Vec<&str> = DRAWN.split_whitespace().collect(); + let mut seen = Vec::new(); + for (space, _) in document.pages().unwrap() { + let page = Page::from_space(&document, space).unwrap(); + if !page.title.starts_with("Shapes") { + continue; + } + for object in &page.objects { + let PageObject::Outline(outline) = object else { + continue; + }; + for paragraph in &outline.paragraphs { + let ParagraphContent::Text(text) = ¶graph.content else { + continue; + }; + let label: usize = text.text.text()["shape ".len()..].parse().unwrap(); + let definition = text.tags[0].definition.unwrap(); + let Kind::TagDefinition { + shape: Some(shape), .. + } = page.definitions[&definition].kind + else { + panic!("shape {label} stores no symbol"); + }; + let (Art::Box(drawn) | Art::Badged(drawn) | Art::Plain(drawn)) = art(shape); + let file = files[label - 1]; + let expected = std::fs::read_to_string(format!( + "{}/assets/tags/{file}.svg", + env!("CARGO_MANIFEST_DIR") + )) + .unwrap(); + assert_eq!( + drawn, expected, + "shape {label}, stored {shape}, draws {file}" + ); + seen.push(label); + } + } + } + seen.sort_unstable(); + assert_eq!(seen, (1..=143).collect::>()); + } + /// A page colour paints as OneNote paints it on white paper, and on dark paper as a /// dark paper of its hue; no colour leaves the paper as it was. #[test] @@ -465,7 +541,7 @@ mod tests { ] .into_iter() .map(|(shape, checked)| TagIcon::of(shape, checked).unwrap()) - .chain([TagIcon::Task]) + .chain([TagIcon::Task { shape: 91 }]) .collect(); let text = TextEngine::default() .layout( diff --git a/crates/canvas/src/gpu/page.rs b/crates/canvas/src/gpu/page.rs index 2c4cdd47853e46e6333419516c432bf8d8922d61..2256e577d5f1a9f3c70dfecc4f154d907432ec7e 100644 --- a/crates/canvas/src/gpu/page.rs +++ b/crates/canvas/src/gpu/page.rs @@ -1777,6 +1777,8 @@ mod tests { definition: Some(tag), status: 1, action_type: None, + shape: None, + property_status: None, created: None, completed: None, start: None, diff --git a/crates/canvas/src/interaction/accessibility.rs b/crates/canvas/src/interaction/accessibility.rs index c4b2d33ab987b057760be50f3af52345c5923c28..d96beb8059a2814c3c18484dacaedbc3e2a2eb7c 100644 --- a/crates/canvas/src/interaction/accessibility.rs +++ b/crates/canvas/src/interaction/accessibility.rs @@ -598,7 +598,7 @@ fn runs_of(shaped: &ParagraphLayout, breaks: bool) -> Result let label = match tag.icon { _ if !tag.label.is_empty() => &tag.label, TagIcon::Symbol { shape, .. } => symbol_name(shape).unwrap_or("Tag"), - TagIcon::Task => "Outlook task", + TagIcon::Task { .. } => "Outlook task", }; let state = match tag.icon { TagIcon::Symbol { checked: true, .. } => ", completed", @@ -946,6 +946,8 @@ pub(super) mod tests { paragraphs[index].text_mut().unwrap().tags.push(Tag { definition: Some(id), action_type: None, + shape: None, + property_status: None, status, created: None, completed: None, diff --git a/crates/canvas/src/interaction/mod.rs b/crates/canvas/src/interaction/mod.rs index 3710fef7a6b230d0376a9a22f97a5ed71799f09d..839af8f9900da95d25927e21fc3b43d140004764 100644 --- a/crates/canvas/src/interaction/mod.rs +++ b/crates/canvas/src/interaction/mod.rs @@ -504,6 +504,37 @@ impl PageView { self.hit_test(self.viewport.document_point(position)) } + /// The Outlook task icon under the pointer, in view device pixels, for the host to name. + pub fn task_under_pointer(&self) -> Option<[f32; 4]> { + if !self.pointer_inside || self.drag.is_some() { + return None; + } + let [x, y] = self.viewport.document_point(self.pointer); + let view = |v: f32, axis: usize| v * self.viewport.scale + self.viewport.origin[axis]; + self.editor.visible_outlines().find_map(|outline| { + let offset = match &self.scene { + Some((_, offset)) if self.editor.has_page_outline(outline.id) => *offset, + _ => [0.0; 2], + }; + let shaped = outline.shaped(); + let left = offset[0] + outline.origin()[0] + shaped.tag_column_offset(); + let top = offset[1] + outline.origin()[1]; + shaped.paragraphs.iter().find_map(|paragraph| { + paragraph.tags.iter().find_map(|tag| { + let x0 = left + tag.origin[0]; + let y0 = top + paragraph.origin[1] + tag.origin[1]; + (matches!(tag.icon, crate::outline::TagIcon::Task { .. }) + && (x0..=x0 + tag.size).contains(&x) + && (y0..=y0 + tag.size).contains(&y)) + .then(|| { + let [x1, y1] = [x0 + tag.size, y0 + tag.size]; + [view(x0, 0), view(y0, 1), view(x1, 0), view(y1, 1)] + }) + }) + }) + }) + } + fn set_object_focus(&mut self, focus: Option) { if self.object_focus != focus { self.editor.finish_composition(); diff --git a/crates/canvas/src/outline.rs b/crates/canvas/src/outline.rs index eb80186eabebc1c959cce5a20d6043a1347cc699..f4791b37f49dcbc84876013108dd74bb2146a2c2 100644 --- a/crates/canvas/src/outline.rs +++ b/crates/canvas/src/outline.rs @@ -227,8 +227,8 @@ pub struct ParagraphLayout { pub enum TagIcon { /// Symbol `shape` of MS-ONE's NoteTagShape, `checked` only where it is a check box. Symbol { shape: u16, checked: bool }, - /// An Outlook task, which OneNote 2010 flags. - Task, + /// An Outlook task, drawn with its stored follow-up flag and never checked from the page. + Task { shape: u16 }, } impl TagIcon { @@ -887,8 +887,8 @@ impl ParagraphLayout { return Err(LayoutError::InvalidSpacing); } // Stored newest first; OneNote lists and paints tags oldest first. - // An Outlook task tag carries no definition; a tag whose definition is missing shows - // nothing, as OneNote draws no icon for it. + // An Outlook task tag carries no definition but its own icon; a tag whose definition + // is missing shows nothing, as OneNote draws no icon for it. let mut tag_definitions: Vec<_> = paragraph .tags .iter() @@ -1065,7 +1065,9 @@ impl ParagraphLayout { for tag in &tag_definitions { let (tag, shape, label) = (tag.0, tag.1, tag.2); let icon = match shape { - None => TagIcon::Task, + None => TagIcon::Task { + shape: tag.shape.unwrap_or(0), + }, Some(shape) => match TagIcon::of(shape.unwrap_or(0), tag.status & 1 != 0) { Some(icon) => icon, None => continue, @@ -2241,6 +2243,8 @@ mod tests { definition: Some(id), status: 3, action_type: None, + shape: None, + property_status: None, created: None, completed: None, start: None, @@ -2356,8 +2360,13 @@ mod tests { } ); let mut task = node.clone(); - task.text_mut().unwrap().tags[0].status |= 4; - assert_eq!(shaped(&task, &definitions).tags[0].icon, TagIcon::Task); + let stored = &mut task.text_mut().unwrap().tags[0]; + stored.status |= 4; + stored.shape = Some(89); + assert_eq!( + shaped(&task, &definitions).tags[0].icon, + TagIcon::Task { shape: 89 } + ); let mut untagged = definitions.clone(); untagged.remove(&id); assert!(shaped(&node, &untagged).tags.is_empty()); @@ -2403,6 +2412,8 @@ mod tests { let tag = Tag { definition: Some(id(902)), action_type: None, + shape: None, + property_status: None, status: 0, created: None, completed: None, @@ -2499,6 +2510,8 @@ mod tests { let tag = Tag { definition: Some(id(902)), action_type: None, + shape: None, + property_status: None, status: 0, created: None, completed: None, @@ -2665,6 +2678,8 @@ mod tests { definition: Some(id), status: 1, action_type: None, + shape: None, + property_status: None, created: None, completed: None, start: None, diff --git a/crates/canvas/tests/task_tags.rs b/crates/canvas/tests/task_tags.rs new file mode 100644 index 0000000000000000000000000000000000000000..cb7dac61188519a6ee9a7b89809d7cd7e9f34b9a --- /dev/null +++ b/crates/canvas/tests/task_tags.rs @@ -0,0 +1,145 @@ +//! Outlook task tags stay read-only through the editor: Remove Tag drops one, removing a +//! normal tag beside one keeps it, and deleting a page and undoing restores them as OneNote +//! 2010 stored them (`corpus/task-tags`). + +use canvas::{ + document::TextPosition, + editor::{CanvasEditor, Formatting, NoteTag, Whole}, + layout::TextEngine, +}; +use onestore::{ + Arena, ExGuid, Section, Store, + document::Tag, + op::{Edit, Op}, + page::{Page, PageObject}, +}; + +const TASKS: &[u8] = include_bytes!("../../../corpus/task-tags/native/notebook/Tasks.one"); + +/// Each body paragraph's text and tags, their arena positions cleared. +fn tagged(page: &Page) -> Vec<(String, Vec)> { + page.objects + .iter() + .find_map(|object| match object { + PageObject::Outline(outline) if !outline.title => Some(&outline.paragraphs), + _ => None, + }) + .unwrap() + .iter() + .map(|paragraph| { + let text = paragraph.text().unwrap(); + let tags = paragraph + .tags + .iter() + .chain(&text.tags) + .map(|tag| Tag { + extra_set: 0, + ..tag.clone() + }) + .collect(); + (text.text.text().to_owned(), tags) + }) + .collect() +} + +fn body(editor: &mut CanvasEditor) { + let body = editor + .outlines() + .iter() + .find(|outline| !outline.title) + .unwrap() + .id; + editor.focus_outline(body).unwrap(); +} + +fn at(paragraph: usize) -> canvas::editor::Selection { + [TextPosition { + paragraph, + offset: 0, + }; 2] + .into() +} + +/// Applies the editor's pending ops to the page in `space` as one edit. +fn publish(section: &mut Section<'_>, space: ExGuid, editor: &mut CanvasEditor, at: u64) { + let ops = editor + .take_ops() + .unwrap() + .into_iter() + .map(|op| Op::Page { space, op }) + .collect(); + section.apply("Author", &Edit { at, ops }).unwrap(); +} + +/// `SNOWBOUND_TASK_TAGS_EXPORT` names a new directory receiving the candidate for a cold +/// reopen. +#[test] +fn task_tags_survive_tag_removal_and_page_delete_undo() { + let arena = Arena::default(); + let mut section = Section::open(&arena, TASKS.to_vec()).unwrap(); + let pages = section.pages().unwrap(); + let space = |title: &str| pages.iter().find(|page| page.1 == title).unwrap().0; + let (remove, restore) = (space("Remove a task"), space("Delete and undo")); + let before = tagged(§ion.page(remove).unwrap()); + let shapes: Vec<_> = before + .iter() + .flat_map(|(_, tags)| tags) + .map(|tag| (tag.definition.is_some(), tag.status & 4 != 0, tag.shape)) + .collect(); + assert_eq!( + shapes, + [ + (false, true, Some(89)), + (false, true, Some(89)), + (false, true, Some(89)), + (true, false, None), + ] + ); + let mut engine = TextEngine::default(); + + let mut editor = CanvasEditor::from_page(section.page(remove).unwrap(), &mut engine).unwrap(); + body(&mut editor); + editor.select(at(0)).unwrap(); + editor.format(&mut engine, Formatting::RemoveTags).unwrap(); + editor.select(at(2)).unwrap(); + let to_do = NoteTag::defaults()[0].clone(); + editor + .format(&mut engine, Formatting::Tag(to_do, 0)) + .unwrap(); + publish(&mut section, remove, &mut editor, 134_000_000_000_000_000); + let mut expected = before.clone(); + expected[0].1.clear(); + expected[2].1.retain(|tag| tag.definition.is_none()); + assert_eq!(tagged(§ion.page(remove).unwrap()), expected); + + let original = section.page(restore).unwrap(); + let kept = tagged(&original); + let mut editor = CanvasEditor::from_page(original, &mut engine).unwrap(); + body(&mut editor); + while editor.whole() != Some(Whole::Page) { + editor.widen_selection().unwrap(); + } + editor.delete(&mut engine, false).unwrap(); + publish(&mut section, restore, &mut editor, 134_000_000_010_000_000); + assert!(editor.undo(&mut engine).unwrap()); + publish(&mut section, restore, &mut editor, 134_000_000_020_000_000); + assert_eq!(tagged(§ion.page(restore).unwrap()), kept); + + section.seal().unwrap(); + let written = section.image(); + let reread = Section::open(&arena, written.clone()).unwrap(); + assert_eq!(tagged(&reread.page(remove).unwrap()), expected); + assert_eq!(tagged(&reread.page(restore).unwrap()), kept); + if let Some(directory) = std::env::var_os("SNOWBOUND_TASK_TAGS_EXPORT") { + let directory = std::path::PathBuf::from(directory); + std::fs::create_dir(&directory).unwrap(); + std::fs::write(directory.join("Tasks.one"), &written).unwrap(); + let file_id = Store::parse(&written).unwrap().header.file_id; + std::fs::write( + directory.join("Open Notebook.onetoc2"), + onestore::create_table_of_contents("Open Notebook.onetoc2", &[("Tasks.one", file_id)]) + .unwrap(), + ) + .unwrap(); + } +} diff --git a/crates/draw/src/render.rs b/crates/draw/src/render.rs index 48cca150a12f8ad15a514a659e2c42f0448e1684..32fc7f447c79f8086da71519b2c7b4b086697e68 100644 --- a/crates/draw/src/render.rs +++ b/crates/draw/src/render.rs @@ -224,7 +224,6 @@ enum AtlasKey { Icon { sources: &'static [&'static str], size: u32, - phase: [u8; 2], /// The bits of the colour `currentColor` paints. ink: [u32; 3], palette: [Option<[u32; 3]>; 3], @@ -1104,27 +1103,24 @@ impl Renderer { tint: [f32; 4], palette: &Palette, ) -> Result<(), RenderError> { - let size = size * space.scale; - let [x, y] = space.origin.map(|v| (v * 4.0).round() * 0.25); + // Whole device pixels keep a sprite's pixel-art edges crisp at every scale. + let size = (size * space.scale).round().max(1.0); + let [x, y] = space.origin.map(f32::round); if !size.is_finite() || [x, y].iter().any(|v| !v.is_finite()) { return Err(RenderError::InvalidPrimitive); } - if x + size + 1.0 < 0.0 - || y + size + 1.0 < 0.0 - || x - 1.0 > space.size[0] as f32 - || y - 1.0 > space.size[1] as f32 + if x + size < 0.0 || y + size < 0.0 || x > space.size[0] as f32 || y > space.size[1] as f32 { return Ok(()); } - if size + 3.0 > self.atlas_side() as f32 { + if size + 2.0 > self.atlas_side() as f32 { return Err(RenderError::AtlasFull); } - let phase = [((x - x.floor()) * 4.0) as u8, ((y - y.floor()) * 4.0) as u8]; + let size = size as u32; let ink = [tint[0], tint[1], tint[2]]; let key = AtlasKey::Icon { sources, - size: size.to_bits(), - phase, + size, ink: ink.map(f32::to_bits), palette: palette.bits(), }; @@ -1134,20 +1130,13 @@ impl Renderer { if self.glyphs.len() >= self.glyph_limit() { return Err(RenderError::AtlasFull); } - let glyph = self.upload(icon::rasterize(sources, size, phase, ink, palette))?; + let glyph = self.upload(icon::rasterize(sources, size, ink, palette))?; self.glyphs.insert(key, Some(glyph)); glyph }; - let left = x.floor(); - let top = y.floor(); self.quad( space, - [ - left, - top, - left + glyph.width as f32, - top + glyph.height as f32, - ], + [x, y, x + glyph.width as f32, y + glyph.height as f32], self.uv(glyph), [1.0, 1.0, 1.0, tint[3]], ) diff --git a/crates/draw/src/render/icon.rs b/crates/draw/src/render/icon.rs index 77b71307eaa064fb42ca867fd11e1ae57cb44c58..b9a97c18d0bfab5511beb37060b12e6ab2b602ad 100644 --- a/crates/draw/src/render/icon.rs +++ b/crates/draw/src/render/icon.rs @@ -1,9 +1,6 @@ use swash::{ scale::image::{Content, Image}, - zeno::{ - self, Cap, Fill, Join, Mask, PathBuilder, Placement, Point, Stroke, Style, Transform, - Vector, - }, + zeno::{self, Cap, Fill, Join, Mask, PathBuilder, Placement, Point, Stroke, Style, Transform}, }; /// Colours for an icon's slots, linear RGB: paths of `class="accent"`, `"highlight"` or @@ -32,24 +29,17 @@ impl Palette { } } -/// Rasterizes 16×16 SVG sources over each other at `size` device pixels, offset by -/// `phase` quarter pixels. Paths are filled and stroked with `#rrggbb`, `currentColor`, +/// Rasterizes 16×16 SVG sources over each other at a whole `size` of device pixels. Paths are filled and stroked with `#rrggbb`, `currentColor`, /// which paints the linear `ink`, or two-stop `linearGradient`s with SVG's defaults /// (`objectBoundingBox` unless `userSpaceOnUse`, `x1`..`y2`, stop offsets, pad spread; no /// `gradientTransform`). `fill-opacity` and `stroke-opacity` apply, and strokes have round /// caps and joins. A slot's paths keep the shading among their colours: the hue turn, /// saturation scale and lightness shift that carry the mean of the slot's colours in a /// source onto the palette's colour apply to each of them. -pub(crate) fn rasterize( - sources: &[&str], - size: f32, - phase: [u8; 2], - ink: [f32; 3], - palette: &Palette, -) -> Image { - let side = (size + 1.0).ceil() as u32; +pub(crate) fn rasterize(sources: &[&str], size: u32, ink: [f32; 3], palette: &Palette) -> Image { + let side = size; + let size = size as f32; let mut pixels = vec![[0.0_f32; 4]; (side * side) as usize]; - let offset = phase.map(|quarters| f32::from(quarters) * 0.25); for source in sources { let svg = roxmltree::Document::parse(source).expect("Bundled icon SVG must be valid"); assert_eq!(svg.root_element().attribute("viewBox"), Some("0 0 16 16")); @@ -135,7 +125,6 @@ pub(crate) fn rasterize( let (mask, _) = Mask::new(data) .style(style) .transform(Some(Transform::scale(size / 16.0, size / 16.0))) - .render_offset(Vector::new(offset[0], offset[1])) .size(side, side) .render(); for (index, (pixel, coverage)) in pixels.iter_mut().zip(mask).enumerate() { @@ -147,7 +136,7 @@ pub(crate) fn rasterize( let [x, y] = [index % side as usize, index / side as usize] .map(|device| device as f32 + 0.5) .map(|device| device * 16.0 / size); - let color = color([x - offset[0] * 16.0 / size, y - offset[1] * 16.0 / size]); + let color = color([x, y]); for channel in 0..3 { pixel[channel] = color[channel] * alpha + pixel[channel] * (1.0 - alpha); } @@ -468,7 +457,7 @@ mod tests { /// The grey painted at a pixel of `source` rasterized at 16 pixels, and the grey a /// black-to-white ramp paints at fraction `t`. fn grey(source: &str, x: usize, y: usize) -> u8 { - rasterize(&[source], 16.0, [0, 0], [1.0; 3], &Palette::default()).data[(y * 17 + x) * 4] + rasterize(&[source], 16, [1.0; 3], &Palette::default()).data[(y * 16 + x) * 4] } fn ramp(t: f32) -> u8 { @@ -516,8 +505,8 @@ mod tests { // Two blues of one hue in the accent slot, a red outside it. const ART: &str = r##""##; let pixel = |palette: &Palette, y: usize| { - let image = rasterize(&[ART], 16.0, [0, 0], [0.0; 3], palette); - let at = (y * 17 + 8) * 4; + let image = rasterize(&[ART], 16, [0.0; 3], palette); + let at = (y * 16 + 8) * 4; let [r, g, b] = [0, 1, 2].map(|channel| f32::from(image.data[at + channel]) / 255.0); hsl([r, g, b]) }; @@ -542,14 +531,8 @@ mod tests { fn current_color_paints_the_ink_and_fixed_colours_keep_theirs() { const LINE: &str = r##""##; let ink = super::super::srgb(0x20, 0x60, 0xa0); - let image = rasterize( - &[LINE], - 16.0, - [0, 0], - [ink[0], ink[1], ink[2]], - &Palette::default(), - ); - let pixel = |x: usize, y: usize| &image.data[(y * 17 + x) * 4..][..4]; + let image = rasterize(&[LINE], 16, [ink[0], ink[1], ink[2]], &Palette::default()); + let pixel = |x: usize, y: usize| &image.data[(y * 16 + x) * 4..][..4]; assert_eq!(pixel(8, 7), [0x20, 0x60, 0xa0, 255]); assert_eq!(pixel(8, 11), [255, 0, 0, 255]); assert_eq!(pixel(8, 2)[3], 0); @@ -560,8 +543,8 @@ mod tests { #[test] fn opacity_scales_each_paint_on_its_own() { const HALF: &str = r##""##; - let image = rasterize(&[HALF], 16.0, [0, 0], [1.0; 3], &Palette::default()); - let alpha = |x: usize, y: usize| image.data[(y * 17 + x) * 4 + 3]; + let image = rasterize(&[HALF], 16, [1.0; 3], &Palette::default()); + let alpha = |x: usize, y: usize| image.data[(y * 16 + x) * 4 + 3]; assert_eq!(alpha(8, 8), 128); // The stroke's outer half covers only what the fill leaves. assert_eq!(alpha(1, 8), 64); @@ -569,28 +552,26 @@ mod tests { #[test] fn icons_keep_transparency_and_overlays_across_scales() { - for size in [8.0, 16.0, 32.0, 57.5] { - for phase in [[0, 0], [1, 2], [3, 3]] { - let plain = rasterize(&[BOX], size, phase, [1.0; 3], &Palette::default()); - let marked = rasterize(&[BOX, MARK], size, phase, [1.0; 3], &Palette::default()); - for image in [&plain, &marked] { - assert_eq!( - image.data.len(), - (image.placement.width * image.placement.height * 4) as usize - ); - assert!(image.data.chunks_exact(4).any(|p| p[3] > 0)); - assert!(image.data.chunks_exact(4).any(|p| p[3] < 255)); - assert!( - image - .data - .chunks_exact(4) - .filter(|p| p[3] == 0) - .all(|p| p[..3] == [0; 3]) - ); - } - assert_ne!(plain.data, marked.data); - assert!(marked.data.chunks_exact(4).any(|p| p[0] > p[2] && p[3] > 0)); + for size in [8, 16, 32, 57] { + let plain = rasterize(&[BOX], size, [1.0; 3], &Palette::default()); + let marked = rasterize(&[BOX, MARK], size, [1.0; 3], &Palette::default()); + for image in [&plain, &marked] { + assert_eq!( + image.data.len(), + (image.placement.width * image.placement.height * 4) as usize + ); + assert!(image.data.chunks_exact(4).any(|p| p[3] > 0)); + assert!(image.data.chunks_exact(4).any(|p| p[3] < 255)); + assert!( + image + .data + .chunks_exact(4) + .filter(|p| p[3] == 0) + .all(|p| p[..3] == [0; 3]) + ); } + assert_ne!(plain.data, marked.data); + assert!(marked.data.chunks_exact(4).any(|p| p[0] > p[2] && p[3] > 0)); } } } diff --git a/crates/mobile/src/library.rs b/crates/mobile/src/library.rs index e38232478794070acad6ca71e0db2c6be1a8df50..c641d7c063f7e1f2e791cf4aba6e465eddbd10b4 100644 --- a/crates/mobile/src/library.rs +++ b/crates/mobile/src/library.rs @@ -33,8 +33,6 @@ const TIMEOUT: Duration = Duration::from_secs(10); const LIMIT: usize = 256 * 1024 * 1024; /// The tab colour OneNote gives a section that stores none, as the desktop shows it. const SECTION_COLOR: u32 = 0x00e4_a88a; -/// How often sections no page shows are polled, as the desktop polls them. -const BACKGROUND: Duration = Duration::from_secs(15); /// Called on a background thread when a notebook's sync status may have changed. static SYNC_WAKE: OnceLock = OnceLock::new(); @@ -239,7 +237,7 @@ fn stem(path: &str) -> String { fn tabs(folder: &Folder, tabs: &mut Vec) { for section in &folder.sections { let (name, color, readable) = match §ion.state { - SectionState::Readable { name, color } => (name.clone(), *color, true), + SectionState::Readable { name, color, .. } => (name.clone(), *color, true), SectionState::Locked | SectionState::Unreadable(_) => (None, None, false), }; tabs.push(Tab { @@ -272,8 +270,10 @@ impl Library { (None, Place::File(path.to_owned()), None) } else { let notebook = Notebook::open(path, cache)?; + // Nothing here tells a file provider's folder on this device from one elsewhere. let background = notebook.background_with( - BACKGROUND, + Background::UNWATCHED, + false, |file| Coordinated(file.to_owned()), sync_woken, )?; @@ -300,13 +300,7 @@ impl Library { pub(crate) fn server(server: Server, root: &str, cache: &Path) -> Result { let server = Arc::new(server); let connect = Arc::clone(&server); - let background = Background::smb( - root, - LIMIT, - BACKGROUND, - move || connect.connect(), - sync_woken, - )?; + let background = Background::smb(root, LIMIT, move || connect.connect(), sync_woken)?; let library = Self { notebook: Mutex::default(), place: Place::Share { diff --git a/crates/notebook/README.md b/crates/notebook/README.md index 61ba64a1660588da42bd6f3846b4771a472355b0..0b03d4ebee38b91c5fdc5230984ca04c63218230 100644 --- a/crates/notebook/README.md +++ b/crates/notebook/README.md @@ -65,14 +65,21 @@ and how many edits wait for it. `set_offline(true)` works offline as OneNote doe worker stops connecting and edits queue until `wake()` (Sync Now) or `set_offline(false)`. `session::Background` keeps the sections no session holds in sync, as OneNote 2010 keeps -every section of an open notebook: `Notebook::background(interval, notify)` for a mounted -notebook, `Background::smb(root, limit, interval, connect, notify)` on a share, then -`watch(notebook.replicas())`. Each round reads every watched file's stamp; a section whose -replica has edits waiting, or whose file moved past the replica's base, has the replica -opened for the synchronization steps that publish or rebase it and closed again. A replica -a session holds is skipped (`Error::busy`), so opening a section may wait out one step. -`status()` gives each section's `SyncStatus`, `changed()` the sections another client -changed, and `wake` and `set_offline` follow Sync Now and Work Offline. +every section of an open notebook: `Background::smb(root, limit, connect, notify)` on a +share, `Notebook::background(interval, copies, notify)` for a mounted notebook, then +`watch(notebook.replicas())`. On a share, one CHANGE_NOTIFY on the notebook's folder +(`smb::Client::watch`) reports what changed; a host watching a mounted folder passes the +changed paths to `touched`. A reported section is checked a second later, a failing one +every 31 seconds, and any other every `interval` (`Background::BACKSTOP` while a watch +reports). Newly watched sections, and every section after a reconnect, are checked one at a +time 100 ms apart. A check reads the file's stamp; a section whose replica has edits waiting, +or whose file moved past the replica's base, has the replica opened for the synchronization +steps that publish or rebase it and closed again, and a section without a replica gets one +from the file, its offline copy, on a share or with `copies`. A replica a session holds is +skipped (`Error::busy`), so opening a section may wait out one step. `status()` gives each +section's `SyncStatus`, `changed()` the sections another client changed, `wake` and +`set_offline` follow Sync Now and Work Offline, and `discard` stops the thread and deletes the +replicas holding nothing unpublished, as when the notebook closes. `Notebook::replica_path(path)` names a section's replica for either kind of notebook. `Section::resume(file, replica, notify)` starts from an owned `Replica` without @@ -373,6 +380,12 @@ are rejected with `ResourceBusy`. Notebook identities come from the files, not directory names or sizes. Missing paths, denied access and non-directory paths have distinct I/O error kinds. +`Client::watch(path, changed)` arms one CHANGE_NOTIFY with WATCH_TREE on a directory, as +OneNote 2010 watches a notebook's folder, and keeps it armed on the client's runtime without +probing the connection, so an idle watch sends nothing. `changed` hears each batch of changed +paths relative to the directory, `""` when the server lost count, and an error when the watch +ends with its connection. + `Client::read_asset(path, byte_limit)` reads an external payload under a read-only share handle that excludes writes and deletion. Empty files succeed; limits, interrupted reads and failed close never return partial bytes. This payload read diff --git a/crates/notebook/examples/launch_probe.rs b/crates/notebook/examples/launch_probe.rs new file mode 100644 index 0000000000000000000000000000000000000000..6fea1a6b45e5e4e86f3a835f7b864d53452733e7 --- /dev/null +++ b/crates/notebook/examples/launch_probe.rs @@ -0,0 +1,228 @@ +//! Temporary: measures a notebook's launch (catalog and background first pass). +//! `generate DIR N` | `local DIR CACHE` | `upload ADDRESS SHARE DIR ROOT` | +//! `smb ADDRESS SHARE ROOT CACHE [SECONDS]` + +use notebook::{ + discover::{Entry, Limits, Local, Source, discover}, + session::{Background, Notebook}, +}; +use onestore::{ + document::{Format, Layout}, + op::{Edit, Op, SectionOp}, + page::{ + Outline, Page, PageObject, PageParagraph, Paragraph, ParagraphContent, TextObject, + text::new_id, + }, +}; +use std::{ + io, + path::Path, + time::{Duration, Instant}, +}; + +type Result = std::result::Result>; + +struct Counting(S, u64, u64); + +impl Source for Counting { + fn entries(&mut self, path: &str, limit: usize) -> io::Result> { + self.2 += 1; + self.0.entries(path, limit) + } + fn read(&mut self, path: &str, limit: usize) -> io::Result> { + let bytes = self.0.read(path, limit)?; + self.1 += bytes.len() as u64; + Ok(bytes) + } +} + +const LIMITS: Limits = Limits { + entries: 100_000, + bytes_per_file: 256 * 1024 * 1024, + depth: 64, +}; + +fn page(count: usize) -> Result { + let paragraph = |i: usize| PageParagraph { + id: new_id().unwrap(), + parent: None, + level: 1, + style: None, + format: Format::default(), + content: ParagraphContent::Text(TextObject { + id: new_id().unwrap(), + date_field: None, + text: Paragraph::new( + format!("Paragraph {i} of the launch probe, with some words."), + Format::default(), + ), + tags: Vec::new(), + }), + lists: Vec::new(), + tags: Vec::new(), + media: Default::default(), + collapsed: false, + }; + Ok(Page { + title: "Probe".into(), + identity: None, + created: None, + margin_origin: [0.0; 2], + color: None, + rule_lines: None, + objects: vec![PageObject::Outline(Outline { + id: new_id()?, + title: false, + min_width: None, + layout: Layout { + x: Some(36.0), + y: Some(86.4), + ..Layout::default() + }, + indents: Vec::new(), + paragraphs: (0..count).map(paragraph).collect(), + unsupported: Vec::new(), + })], + definitions: Default::default(), + }) +} + +fn generate(root: &Path, count: usize) -> Result<()> { + let cache = tempfile::tempdir()?; + let creation = onestore::PageCreation::new(None, Some("First"), "Probe")?; + let mut notebook = Notebook::create(root, cache.path(), 0x00e4a88a, &creation)?; + notebook.create_group("", "Group A")?; + notebook.create_group("", "Group B")?; + for n in 0..count { + let folder = ["", "Group A", "Group B"][n % 3]; + let path = notebook.create_section(folder, &format!("Section {n:03}"), &creation)?; + let file = root.join(&path); + let arena = onestore::Arena::default(); + let mut section = onestore::Section::open(&arena, std::fs::read(&file)?)?; + let creation = onestore::PageCreation::new(None, Some("Probe"), "Probe")?; + section.apply( + "Probe", + &Edit { + at: 133_000_000_000_000_000, + ops: vec![Op::Section(SectionOp::Import { + creation, + page: page(1200)?, + })], + }, + )?; + let transaction = section.seal()?.ok_or("nothing sealed")?; + transaction.commit_file(&file)?; + } + Ok(()) +} + +fn size(root: &Path) -> u64 { + std::fs::read_dir(root) + .into_iter() + .flatten() + .flatten() + .map(|entry| match entry.file_type() { + Ok(kind) if kind.is_dir() => size(&entry.path()), + _ => entry.metadata().map_or(0, |metadata| metadata.len()), + }) + .sum() +} + +#[cfg(feature = "smb")] +fn client(address: &str, share: &str) -> Result { + Ok(notebook::smb::Client::connect( + address, + share, + notebook::smb::Credentials { + username: "", + password: "", + domain: "", + }, + Duration::from_secs(10), + )?) +} + +#[cfg(feature = "smb")] +fn upload(client: ¬ebook::smb::Client, local: &Path, remote: &str) -> Result<()> { + client.create_directory(remote)?; + for entry in std::fs::read_dir(local)? { + let entry = entry?; + let name = entry.file_name().into_string().map_err(|_| "name")?; + let target = format!("{remote}/{name}"); + if entry.file_type()?.is_dir() { + upload(client, &entry.path(), &target)?; + } else { + client.create(&target, &std::fs::read(entry.path())?)?; + } + } + Ok(()) +} + +fn main() -> Result<()> { + let args: Vec = std::env::args().skip(1).collect(); + match args.iter().map(String::as_str).collect::>().as_slice() { + ["generate", root, count] => { + generate(Path::new(root), count.parse()?)?; + println!("{} bytes", size(Path::new(root))); + } + ["local", root, cache] => { + let start = Instant::now(); + let notebook = Notebook::open(root, cache)?; + let replicas = notebook.replicas(); + println!( + "open: {:?}, {} sections", + start.elapsed(), + replicas.len() + ); + let mut counting = Counting(Local::open(root)?, 0, 0); + discover(&mut counting, LIMITS)?; + println!("discovery reads {} bytes, {} listings", counting.1, counting.2); + } + #[cfg(feature = "smb")] + ["upload", address, share, local, root] => { + upload(&client(address, share)?, Path::new(local), root)?; + } + #[cfg(feature = "smb")] + ["smb", address, share, root, cache, rest @ ..] => { + let seconds: u64 = rest.first().map_or(Ok(0), |s| s.parse())?; + let start = Instant::now(); + let connected = client(address, share)?; + let notebook = Notebook::open_smb(std::sync::Arc::new(connected), root, cache)?; + let replicas = notebook.replicas(); + println!( + "open: {:?}, {} sections", + start.elapsed(), + replicas.len() + ); + if seconds > 0 { + let (address, share) = (address.to_string(), share.to_string()); + let background = Background::smb( + root, + 256 * 1024 * 1024, + move || { + client(&address, &share) + .map_err(|error| io::Error::other(error.to_string())) + }, + || {}, + )?; + background.watch(replicas); + let start = Instant::now(); + loop { + let status = background.status(); + if status.iter().all(|(_, status)| status.synced.is_some()) { + println!("background first pass: {:?}", start.elapsed()); + break; + } + if start.elapsed() > Duration::from_secs(seconds) { + println!("background still going after {seconds} s"); + break; + } + std::thread::sleep(Duration::from_millis(50)); + } + std::thread::sleep(Duration::from_secs(2)); + } + } + _ => return Err("usage".into()), + } + Ok(()) +} diff --git a/crates/notebook/src/background.rs b/crates/notebook/src/background.rs index 1c327727876c1099208f13396682bca462be5fa8..db8647f83115bb7fcfdff5bd4b3908fd4c653830 100644 --- a/crates/notebook/src/background.rs +++ b/crates/notebook/src/background.rs @@ -11,19 +11,45 @@ use onestore::Stamp; use std::{ io, path::{Path, PathBuf}, - sync::{Arc, Mutex, atomic::Ordering}, + sync::{ + Arc, Mutex, Weak, + atomic::{AtomicBool, Ordering}, + }, thread, - time::Duration, + time::{Duration, Instant}, }; -/// Polls each watched section file's stamp every interval. A section whose replica has -/// edits waiting, or whose file moved past the replica's base, has its replica opened for -/// the synchronization steps that publish or rebase it, then closed again; any other costs -/// one stamp read. A replica a session holds is left to that session's worker. Dropping -/// requests cancellation without waiting for the step in flight. -pub struct Background { +/// How long a section that could not be reached waits to be tried again; OneNote 2010 +/// retries a failing section about every 31 seconds. +const RETRY: Duration = Duration::from_secs(31); +/// Between one section's first check and the next's, so opening a notebook reads its files +/// one after another rather than in a burst. +const STAGGER: Duration = Duration::from_millis(100); +/// How long a reported change settles before its section is checked, so the writes of one +/// commit cost one check. +const SETTLE: Duration = Duration::from_secs(1); + +/// Keeps each watched section in sync: a section is checked when a watch on the notebook's +/// folder reports it changed, when it could not be reached, and otherwise every interval. +/// A check costs one stamp read; a section whose replica has edits waiting, or whose file +/// moved past the replica's base, has its replica opened for the synchronization steps that +/// publish or rebase it, then closed again. A section without a replica gets one, its offline +/// copy, where the notebook keeps them. A replica a session holds is left to that session's +/// worker. Dropping requests cancellation without waiting for the step in flight. +pub struct Background(Arc); + +/// What the background thread and whatever reports changes to it share. +pub(crate) struct Shared { signal: Arc, - watched: Arc>, + watched: Mutex, + /// Asked for by `discard`, once the thread stops. + discard: AtomicBool, +} + +/// A connection's report of its notebook folder's changes, from a watch armed on connecting. +pub(crate) struct Reports { + shared: Weak, + connection: u64, } #[derive(Default)] @@ -31,6 +57,10 @@ struct Watched { sections: Vec, /// Sections whose file changed since `changed` was last asked. changed: Vec, + /// Counts connections, so a watch that ended with an earlier one is not this one's. + connection: u64, + /// The current connection's watch ended: changes since went unreported. + lost: bool, } struct Watch { @@ -39,12 +69,24 @@ struct Watch { /// The file's stamp when last reached. stamp: Option, status: SyncStatus, + /// When the section is next checked. + due: Instant, } impl Background { + /// How often each section is checked while a watch reports its folder's changes, against + /// a report that went missing; OneNote 2010, watching, checks nothing on a timer. + pub const BACKSTOP: Duration = Duration::from_secs(60 * 60); + /// How often each section is checked where nothing reports its folder's changes. + pub const UNWATCHED: Duration = Duration::from_secs(15); + + /// Starts the thread. `connect` binds each catalog path to its file, arming any watch + /// that reports the folder's changes through `Reports`, and answers how long a section + /// may go unchecked while nothing reports it; it runs again after a transport failure. + /// `copies` keeps an offline copy of every section. pub(crate) fn start( - interval: Duration, - mut connect: impl FnMut() -> io::Result + Send + 'static, + copies: bool, + mut connect: impl FnMut(Reports) -> io::Result<(B, Duration)> + Send + 'static, notify: impl Fn() + Send + 'static, ) -> Result where @@ -52,165 +94,201 @@ impl Background { B: FnMut(&str) -> R, { let (signal, receiver) = Signal::new(); - let watched = Arc::new(Mutex::new(Watched::default())); - let (shared, sections) = (Arc::clone(&signal), Arc::clone(&watched)); + let shared = Arc::new(Shared { + signal, + watched: Mutex::default(), + discard: AtomicBool::new(false), + }); + let weak = Arc::downgrade(&shared); + let owner = Arc::clone(&shared); thread::Builder::new() .name("onestore-background".into()) .spawn(move || { - let mut bound: Option = None; - while !shared.stopped.load(Ordering::Acquire) { - if shared.offline.load(Ordering::Acquire) - && !shared.requested.load(Ordering::Acquire) + let signal = &owner.signal; + let mut bound: Option<(B, Duration)> = None; + let mut news = false; + while !signal.stopped.load(Ordering::Acquire) { + if signal.offline.load(Ordering::Acquire) + && !signal.requested.load(Ordering::Acquire) { bound = None; let _ = receiver.recv(); continue; } - shared.requested.store(false, Ordering::Release); - let round: Vec<_> = match sections.lock() { - Ok(watched) => watched - .sections - .iter() - .map(|watch| { - ( - watch.path.clone(), - watch.replica.clone(), - watch.stamp.clone(), - ) - }) - .collect(), - Err(_) => return, - }; - let mut news = false; - for (path, replica, seen) in round { - if shared.stopped.load(Ordering::Acquire) { + let now = Instant::now(); + let (path, replica, seen, connection) = { + let Ok(mut watched) = owner.watched.lock() else { return; + }; + if std::mem::take(&mut watched.lost) { + bound = None; + watched.rescan(now); } - let bind = match &mut bound { - Some(bind) => bind, - None => match connect() { - Ok(bind) => bound.insert(bind), + let due = watched.next().map(|index| &watched.sections[index]); + match due.filter(|watch| watch.due <= now) { + Some(watch) => ( + watch.path.clone(), + watch.replica.clone(), + watch.stamp.clone(), + watched.connection, + ), + None => { + let wait = due.map(|watch| watch.due - now); + drop(watched); + signal.requested.store(false, Ordering::Release); + if std::mem::take(&mut news) { + notify(); + } + let _ = match wait { + Some(wait) => receiver.recv_timeout(wait).ok(), + None => receiver.recv().ok(), + }; + continue; + } + } + }; + let (bind, interval) = match &mut bound { + Some(bound) => bound, + None => { + let reports = Reports { + shared: weak.clone(), + connection: connection + 1, + }; + let connected = connect(reports); + let Ok(mut watched) = owner.watched.lock() else { + return; + }; + // Whatever ended before now ended with the last connection. + watched.connection += 1; + watched.lost = false; + match connected { + // Changes while unwatched went unreported. + Ok(connected) => { + watched.rescan(now); + bound = Some(connected); + } Err(error) => { - // Nothing on the share is reachable this round. - let Ok(mut watched) = sections.lock() else { - return; - }; + let error = Error::RemoteIo(error); for watch in &mut watched.sections { - news |= watch.fail( - &Error::RemoteIo(io::Error::new( - error.kind(), - error.to_string(), - )), - None, - ); + news |= watch.fail(&error, None); + watch.due = now + RETRY; } - break; } - }, - }; - let (queued, outcome) = - step(&mut bind(&path), replica.as_deref(), seen.as_ref()); - if matches!(outcome, Err(Error::RemoteIo(_) | Error::Remote(_))) { - bound = None; - } - let Ok(mut watched) = sections.lock() else { - return; - }; - let Watched { sections, changed } = &mut *watched; - let Some(watch) = sections.iter_mut().find(|watch| watch.path == path) - else { + } continue; - }; - news |= match outcome { - Ok((stamp, moved)) => { - watch.stamp = Some(stamp); - if moved { - changed.push(path); - } - let before = summary(&watch.status); - if let Some(queued) = queued { - watch.status = SyncStatus { - synced: Some(crate::now()), - error: None, - queued, - }; - } - moved || before != summary(&watch.status) - } - Err(error) => watch.fail(&error, queued), - }; + } + }; + let (queued, outcome) = + step(&mut bind(&path), replica.as_deref(), seen.as_ref(), copies); + let interval = *interval; + if outcome.as_ref().is_err_and(disconnected) { + bound = None; } - if news { - notify(); + let Ok(mut watched) = owner.watched.lock() else { + return; + }; + let Watched { + sections, changed, .. + } = &mut *watched; + let Some(watch) = sections.iter_mut().find(|watch| watch.path == path) else { + continue; + }; + let now = Instant::now(); + news |= match outcome { + Ok((stamp, moved)) => { + watch.stamp = Some(stamp); + watch.due = now + interval; + if moved { + changed.push(path); + } + let before = summary(&watch.status); + if let Some(queued) = queued { + watch.status = SyncStatus { + synced: Some(crate::now()), + error: None, + queued, + }; + } + moved || before != summary(&watch.status) + } + Err(error) => { + watch.due = now + RETRY; + watch.fail(&error, queued) + } + }; + } + if owner.discard.load(Ordering::Acquire) + && let Ok(watched) = owner.watched.lock() + { + for replica in watched + .sections + .iter() + .filter_map(|watch| watch.replica.as_ref()) + { + discard(replica); } - let _ = receiver.recv_timeout(interval); } })?; - Ok(Self { signal, watched }) + Ok(Self(shared)) } - /// Keeps the sections of a notebook on a share in sync while they are not open; - /// `connect` runs again after a transport failure. Paths are relative to `root`. + /// Keeps the sections of a notebook on a share in sync while they are not open, with an + /// offline copy of each, and one watch on the notebook's folder reporting what changed, + /// as OneNote 2010 watches it; `connect` runs again after a transport failure. Paths are + /// relative to `root`. #[cfg(feature = "smb")] pub fn smb( root: &str, limit: usize, - interval: Duration, mut connect: impl FnMut() -> io::Result + Send + 'static, notify: impl Fn() + Send + 'static, ) -> Result { let root = root.replace('\\', "/"); Self::start( - interval, - move || { + true, + move |reports| { let client = Arc::new(connect()?); + let interval = match client.watch(&root, move |changed| match changed { + Ok(paths) => reports.touched(&paths), + Err(_) => reports.lost(), + }) { + Ok(()) => Self::BACKSTOP, + Err(error) if error.kind() == io::ErrorKind::Unsupported => Self::UNWATCHED, + Err(error) => return Err(error), + }; let root = root.clone(); - Ok(move |path: &str| { + let bind = move |path: &str| { let file = match root.as_str() { "" => path.to_owned(), root => format!("{root}/{path}"), }; crate::SmbRemote::new(Arc::clone(&client), file, limit) - }) + }; + Ok((bind, interval)) }, notify, ) } - /// Watches these sections, by catalog path and replica (`Notebook::replicas`), from - /// the next round, which starts now. + /// Watches these sections, by catalog path and replica (`Notebook::replicas`); a section + /// not watched before is first checked soon after, the next one a little later. pub fn watch(&self, sections: Vec<(String, Option)>) { - if let Ok(mut watched) = self.watched.lock() { - let mut previous = std::mem::take(&mut watched.sections); - watched.sections = sections - .into_iter() - .map( - |(path, replica)| match previous.iter().position(|watch| watch.path == path) { - Some(index) => Watch { - replica, - ..previous.swap_remove(index) - }, - None => Watch { - path, - replica, - stamp: None, - status: SyncStatus { - synced: None, - error: None, - queued: 0, - }, - }, - }, - ) - .collect(); + if let Ok(mut watched) = self.0.watched.lock() { + watched.watch(sections, Instant::now()); } - self.signal.wake(); + self.0.signal.wake(); } - /// Each watched section's status as its last round left it, in watch order. A section a + /// Checks the sections at or below these catalog paths soon, as a watch on the + /// notebook's folder reports them changed; `""` names every section. + pub fn touched(&self, paths: &[String]) { + self.0.touched(paths); + } + + /// Each watched section's status as its last check left it, in watch order. A section a /// session holds keeps the status it had before. pub fn status(&self) -> Vec<(String, SyncStatus)> { - self.watched.lock().map_or_else( + self.0.watched.lock().map_or_else( |_| Vec::new(), |watched| { watched @@ -237,32 +315,165 @@ impl Background { /// Sections whose file changed since the last call, by catalog path. pub fn changed(&self) -> Vec { - self.watched + self.0 + .watched .lock() .map(|mut watched| std::mem::take(&mut watched.changed)) .unwrap_or_default() } - /// Runs a round now, working offline included (Sync Now). + /// Checks every section now, working offline included (Sync Now). pub fn wake(&self) { - self.signal.requested.store(true, Ordering::Release); - self.signal.wake(); + if let Ok(mut watched) = self.0.watched.lock() { + let now = Instant::now(); + for watch in &mut watched.sections { + watch.due = now; + } + } + self.0.signal.requested.store(true, Ordering::Release); + self.0.signal.wake(); } - /// Working offline, no round runs until `wake`, or until working online again. + /// Stops for good, then deletes each section's replica that holds nothing unpublished, as + /// OneNote lets go of a notebook it closes; one with edits waiting, or that a session + /// holds, stays. + pub fn discard(&self) { + self.0.discard.store(true, Ordering::Release); + self.0.signal.stopped.store(true, Ordering::Release); + self.0.signal.wake(); + } + + /// Working offline, nothing is checked until `wake`, or until working online again. pub fn set_offline(&self, offline: bool) { - self.signal.offline.store(offline, Ordering::Release); - self.signal.wake(); + self.0.signal.offline.store(offline, Ordering::Release); + self.0.signal.wake(); } } impl Drop for Background { fn drop(&mut self) { - self.signal.stopped.store(true, Ordering::Release); + self.0.signal.stopped.store(true, Ordering::Release); + self.0.signal.wake(); + } +} + +impl Shared { + fn touched(&self, paths: &[String]) { + if let Ok(mut watched) = self.watched.lock() { + watched.touched(paths, Instant::now()); + } self.signal.wake(); } } +impl Reports { + /// The sections at or below these paths changed. + pub(crate) fn touched(&self, paths: &[String]) { + if let Some(shared) = self.shared.upgrade() { + shared.touched(paths); + } + } + + /// The watch ended, and with it, as far as anyone can tell, the connection. + pub(crate) fn lost(&self) { + if let Some(shared) = self.shared.upgrade() { + if let Ok(mut watched) = shared.watched.lock() + && watched.connection == self.connection + { + watched.lost = true; + } + shared.signal.wake(); + } + } +} + +impl Watched { + fn watch(&mut self, sections: Vec<(String, Option)>, now: Instant) { + let mut previous = std::mem::take(&mut self.sections); + let mut new = 0; + self.sections = sections + .into_iter() + .map( + |(path, replica)| match previous.iter().position(|watch| watch.path == path) { + Some(index) => Watch { + replica, + ..previous.swap_remove(index) + }, + None => { + new += 1; + Watch { + path, + replica, + stamp: None, + status: SyncStatus { + synced: None, + error: None, + queued: 0, + }, + due: now + STAGGER * (new - 1), + } + } + }, + ) + .collect(); + } + + /// Checks every section from `now`, one `STAGGER` after another. + fn rescan(&mut self, now: Instant) { + for (index, watch) in (0..).zip(&mut self.sections) { + watch.due = now + STAGGER * index; + } + } + + fn touched(&mut self, paths: &[String], now: Instant) { + for watch in &mut self.sections { + if paths.iter().any(|path| within(&watch.path, path)) { + watch.due = watch.due.min(now + SETTLE); + } + } + } + + /// The section checked next. + fn next(&self) -> Option { + (0..self.sections.len()).min_by_key(|&index| self.sections[index].due) + } +} + +/// Deletes the replica at `replica` if it holds nothing unpublished and no one holds it. +fn discard(replica: &Path) { + if matches!(crate::peek(replica), Ok((_, 0))) { + for suffix in ["-wal", "-shm", ""] { + let mut file = replica.as_os_str().to_owned(); + file.push(suffix); + let _ = std::fs::remove_file(file); + } + } +} + +/// Whether the catalog path `section` is `path` or lies below it, as a share compares names. +fn within(section: &str, path: &str) -> bool { + let path = path.trim_matches('/'); + path.is_empty() + || section.len() >= path.len() + && section.is_char_boundary(path.len()) + && section[..path.len()].eq_ignore_ascii_case(path) + && matches!(section.as_bytes().get(path.len()), None | Some(b'/')) +} + +/// Whether a failed step lost the connection, rather than failing for its one file. +fn disconnected(error: &Error) -> bool { + use io::ErrorKind::*; + let kind = match error { + Error::RemoteIo(error) => error.kind(), + Error::Remote(error) => error.error.kind(), + _ => return false, + }; + matches!( + kind, + NotConnected | TimedOut | ConnectionReset | ConnectionAborted | BrokenPipe + ) +} + impl Watch { /// Records a failed step and what it found waiting, answering whether the status shown /// changes. @@ -284,20 +495,43 @@ fn summary(status: &SyncStatus) -> (bool, Option, u64) { } /// One section's step: how many of its edits wait (`None` while unknown, as while a session -/// holds its replica), then the file's stamp now and whether it changed since `seen`. +/// holds its replica), then the file's stamp now and whether it changed since `seen`. With +/// `copies`, a section without a replica gets one from the file as it is now. fn step( remote: &mut R, replica: Option<&Path>, seen: Option<&Stamp>, + copies: bool, ) -> (Option, Result<(Stamp, bool)>) { let stamp = match remote.stamp() { Ok(stamp) => stamp, Err(error) => return (None, Err(Error::RemoteIo(error))), }; let moved = seen.is_some_and(|seen| *seen != stamp); - let Some(replica) = replica.filter(|replica| replica.exists()) else { + let Some(replica) = replica else { return (Some(0), Ok((stamp, moved))); }; + if !replica.exists() { + if !copies { + return (Some(0), Ok((stamp, moved))); + } + let copied = (|| { + let image = remote.read().map_err(Error::RemoteIo)?; + if let Some(folder) = replica.parent() { + std::fs::create_dir_all(folder)?; + } + Replica::seed(replica, &image)?; + Ok(Stamp::of(&image)?) + })(); + return match copied { + Ok(stamp) => (Some(0), Ok((stamp, moved))), + // A session made it first. + Err(Error::Io(error)) if error.kind() == io::ErrorKind::AlreadyExists => { + (None, Ok((stamp, false))) + } + Err(error) => (None, Err(error)), + }; + } match crate::peek(replica) { Ok((base, 0)) if base == stamp => return (Some(0), Ok((stamp, moved))), Err(error) if error.busy() => return (None, Ok((stamp, false))), @@ -324,3 +558,176 @@ fn step( .map(|summary| summary.queued_edits); (queued, synced) } + +#[cfg(test)] +mod tests { + use super::*; + const BACKSTOP: Duration = Background::BACKSTOP; + use onestore::{CommitError, Transaction}; + use std::{ + collections::HashMap, + sync::mpsc::{self, Receiver}, + }; + + fn sections(count: usize) -> Vec<(String, Option)> { + (0..count) + .map(|n| { + let folder = if n % 2 == 0 { "" } else { "Group/" }; + (format!("{folder}Section {n:03}.one"), None) + }) + .collect() + } + + /// Runs the schedule as the thread would with every check succeeding, up to `end`, + /// answering which sections were checked when. + fn run(watched: &mut Watched, end: Instant, interval: Duration) -> Vec<(Instant, String)> { + let mut checks = Vec::new(); + while let Some(index) = watched.next() { + let watch = &mut watched.sections[index]; + if watch.due > end { + break; + } + checks.push((watch.due, watch.path.clone())); + watch.due += interval; + } + checks + } + + #[test] + fn an_idle_notebook_is_read_once_staggered_then_left_alone() { + let start = Instant::now(); + let mut watched = Watched::default(); + watched.watch(sections(200), start); + let checks = run(&mut watched, start + BACKSTOP - SETTLE, BACKSTOP); + assert_eq!(checks.len(), 200, "one first check each, then none"); + for pair in checks.windows(2) { + assert!(pair[1].0 - pair[0].0 >= STAGGER, "never a burst"); + } + // Then each section costs one backstop check an interval: over a day, 24 stamp reads + // where a 15-second poll made 5,760. + let day = run( + &mut watched, + start + Duration::from_secs(86_400) - SETTLE, + BACKSTOP, + ); + assert_eq!(day.len(), 200 * (86_400 / BACKSTOP.as_secs() as usize - 1)); + } + + #[test] + fn a_reported_change_checks_exactly_the_sections_it_names() { + let start = Instant::now(); + let mut watched = Watched::default(); + watched.watch(sections(200), start); + let now = start + Duration::from_secs(60); + run(&mut watched, now, BACKSTOP); + // A commit's several writes are reported apiece, and settle into one check. + for _ in 0..3 { + watched.touched(&["section 004.ONE".into()], now); + } + watched.touched( + &["Section 004.one".into()], + now + Duration::from_millis(300), + ); + let checks = run(&mut watched, now + Duration::from_secs(60), BACKSTOP); + assert_eq!(checks, vec![(now + SETTLE, "Section 004.one".to_owned())]); + // A group's folder names the sections in it, and "" every section. + let now = now + Duration::from_secs(60); + watched.touched(&["Group".into()], now); + let group = run(&mut watched, now + SETTLE, BACKSTOP); + assert_eq!(group.len(), 100); + assert!(group.iter().all(|(_, path)| path.starts_with("Group/"))); + watched.touched(&["Grou".into(), String::new()], now); + assert_eq!(run(&mut watched, now + SETTLE, BACKSTOP).len(), 200); + } + + /// Section files in memory, each stamp read reported as it happens. + #[derive(Clone)] + struct Files { + images: Arc>>>, + stamps: mpsc::Sender, + } + + struct File(Files, String); + + impl Remote for File { + fn read(&mut self) -> io::Result> { + let images = self.0.images.lock().unwrap(); + Ok(images[&self.1].clone()) + } + + fn stamp(&mut self) -> io::Result { + let _ = self.0.stamps.send(self.1.clone()); + Stamp::of(&self.read()?).map_err(io::Error::other) + } + + fn publish(&mut self, _: &Transaction) -> std::result::Result<(), CommitError> { + unreachable!("nothing is queued") + } + + fn confirm(&mut self, _: &Stamp) -> std::result::Result<(), CommitError> { + unreachable!("nothing is queued") + } + } + + fn quiet(stamps: &Receiver, wait: Duration) -> Vec { + let mut read = Vec::new(); + while let Ok(path) = stamps.recv_timeout(wait) { + read.push(path); + } + read + } + + #[test] + fn a_watch_wakes_the_section_it_reports_and_a_lost_one_rescans() { + let (sender, stamps) = mpsc::channel(); + let files = Files { + images: Arc::default(), + stamps: sender, + }; + let mut paths: Vec = sections(6).into_iter().map(|(path, _)| path).collect(); + paths.sort(); + for path in &paths { + let image = onestore::create_section("s.one", path, "Author").unwrap(); + files.images.lock().unwrap().insert(path.clone(), image); + } + let (armed, watches) = mpsc::channel(); + let remote = files.clone(); + let background = Background::start( + false, + move |reports| { + armed.send(reports).unwrap(); + let files = remote.clone(); + Ok(( + move |path: &str| File(files.clone(), path.to_owned()), + Duration::from_secs(3600), + )) + }, + || {}, + ) + .unwrap(); + background.watch(sections(6)); + let mut first = quiet(&stamps, STAGGER * 4); + first.sort(); + assert_eq!(first, paths, "each section's first check"); + let watch = watches.recv().unwrap(); + assert!(quiet(&stamps, SETTLE).is_empty(), "idle reads nothing"); + + let changed = &paths[3]; + files.images.lock().unwrap().insert( + changed.clone(), + onestore::create_section("s.one", "Changed", "Author").unwrap(), + ); + watch.touched(std::slice::from_ref(changed)); + assert_eq!(quiet(&stamps, SETTLE * 2), vec![changed.clone()]); + assert_eq!(background.changed(), vec![changed.clone()]); + + // A watch that ended leaves changes unreported, so everything is read again. + watch.lost(); + let rewatch = watches.recv_timeout(SETTLE).unwrap(); + assert_eq!(quiet(&stamps, STAGGER * 4).len(), 6); + // The ended watch's connection is gone; only the current one's loss counts. + watch.lost(); + assert!(quiet(&stamps, SETTLE).is_empty()); + drop(rewatch); + } +} diff --git a/crates/notebook/src/discover.rs b/crates/notebook/src/discover.rs index 60a4e136f50d9c3ad2abc4dcc3e23a5ab8449756..ea9ca8427a4f8a92ef8a1875f5ffd9f32798df7d 100644 --- a/crates/notebook/src/discover.rs +++ b/crates/notebook/src/discover.rs @@ -30,6 +30,9 @@ pub enum SectionState { name: Option, /// The tab colour as a COLORREF; OneNote assigns one when absent. color: Option, + /// The root object space's GUID, the document identity that names the section's + /// replica in a mounted notebook. + document: [u8; 16], }, Locked, Unreadable(onestore::Error), @@ -310,6 +313,7 @@ fn scan( state: SectionState::Readable { name, color: color.filter(|color| *color != 0xffff_ffff), + document: index.root.guid, }, }); } else { diff --git a/crates/notebook/src/lib.rs b/crates/notebook/src/lib.rs index 288a7e32ffcd8203755d8dff6f0bd7a669c10262..983f04abc300140f32e08b2b79eaa40686051863 100644 --- a/crates/notebook/src/lib.rs +++ b/crates/notebook/src/lib.rs @@ -105,23 +105,28 @@ impl Replica { /// Seeds a new cache from a validated section image, refusing any existing path. /// An initialization error preserves the created file for inspection. pub fn create(path: impl AsRef, source: &[u8]) -> Result { + Self::seed(path.as_ref(), source)?; + Self::start(cache_connection(path.as_ref())?) + } + + /// `create` without opening the cache it made, as for an offline copy. + pub(crate) fn seed(path: &Path, source: &[u8]) -> Result<()> { validate(source)?; - let path = path.as_ref(); - // Built under another name and linked into place whole, so that a cache that exists - // is complete: a background step or a failed creation never leaves one half made. + // Built under a name of its own and linked into place whole, so that a cache that + // exists is complete, and two threads making the same one never share a build. + static BUILDS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0); + let build = BUILDS.fetch_add(1, std::sync::atomic::Ordering::Relaxed); let mut building = path.as_os_str().to_owned(); - building.push(".creating"); + building.push(format!(".creating-{}-{build}", std::process::id())); let building = PathBuf::from(building); - for stale in ["", "-wal", "-shm"] { - let mut file = building.as_os_str().to_owned(); - file.push(stale); - let _ = std::fs::remove_file(file); - } let built = Self::build(&building, source); let linked = built.and_then(|()| Ok(std::fs::hard_link(&building, path)?)); - let _ = std::fs::remove_file(&building); - linked?; - Self::start(cache_connection(path)?) + for suffix in ["", "-wal", "-shm"] { + let mut file = building.as_os_str().to_owned(); + file.push(suffix); + let _ = std::fs::remove_file(file); + } + linked } fn build(path: &Path, source: &[u8]) -> Result<()> { diff --git a/crates/notebook/src/merge.rs b/crates/notebook/src/merge.rs index de83c4e40c61e46fec5d67449a064e81a8192d23..907fa6c694e1cd75804d27ea204ed7e8108557e2 100644 --- a/crates/notebook/src/merge.rs +++ b/crates/notebook/src/merge.rs @@ -164,6 +164,7 @@ fn named(op: &PageOp) -> Vec { PageOp::Level { paragraph, .. } | PageOp::Paragraph { paragraph, .. } | PageOp::Style { paragraph, .. } + | PageOp::Media { paragraph, .. } | PageOp::List { paragraph, .. } => vec![*paragraph], PageOp::Tags { target, .. } => vec![*target], PageOp::Add { object, .. } => vec![object.id()], @@ -736,6 +737,7 @@ impl Diff { } PageOp::Paragraph { paragraph, .. } | PageOp::Style { paragraph, .. } + | PageOp::Media { paragraph, .. } | PageOp::List { paragraph, .. } => { require(self.kept(*paragraph) && self.new_has(*paragraph))? } diff --git a/crates/notebook/src/session.rs b/crates/notebook/src/session.rs index d76676e865ef5052deed1d2e9b499d8344429edd..680c2b3c4a22c2b3d1087f305f9b60b101f9dabb 100644 --- a/crates/notebook/src/session.rs +++ b/crates/notebook/src/session.rs @@ -887,13 +887,16 @@ impl Notebook { /// `smb` on a share. It exists once the section has been opened. pub fn replica_path(&self, path: &str) -> Result { let section = self.section_path(path)?; - Ok(match &self.root { - Some(_) => { + Ok(match (&self.root, §ion.state) { + (Some(_), discover::SectionState::Readable { document, .. }) => { + replica_file(&self.cache, document) + } + (Some(_), _) => { let image = self.storage.read(§ion.path)?; let root = RevisionIndex::parse(&Store::parse(&image)?)?.root; replica_file(&self.cache, &root.guid) } - None => replica_file(&self.cache.join("smb"), §ion.file_id), + (None, _) => replica_file(&self.cache.join("smb"), §ion.file_id), }) } @@ -914,13 +917,16 @@ impl Notebook { } /// Keeps the sections of a mounted notebook in sync while they are not open - /// (`Background`), polling each file every `interval` once watched. + /// (`Background`): each file is checked every `interval`, and sooner when + /// `Background::touched` reports it changed. `copies` keeps an offline copy of every + /// section, for a folder that is not on this computer. pub fn background( &self, interval: Duration, + copies: bool, notify: impl Fn() + Send + 'static, ) -> Result { - self.background_with(interval, |file| FileRemote(file.to_owned()), notify) + self.background_with(interval, copies, |file| FileRemote(file.to_owned()), notify) } /// `background`, reaching each section file through the remote `remote` makes for it @@ -928,6 +934,7 @@ impl Notebook { pub fn background_with( &self, interval: Duration, + copies: bool, remote: impl Fn(&Path) -> R + Clone + Send + 'static, notify: impl Fn() + Send + 'static, ) -> Result { @@ -939,10 +946,10 @@ impl Notebook { .into()); }; Background::start( - interval, - move || { + copies, + move |_| { let (root, remote) = (root.clone(), remote.clone()); - Ok(move |path: &str| remote(&root.join(path))) + Ok((move |path: &str| remote(&root.join(path)), interval)) }, notify, ) diff --git a/crates/notebook/src/smb/directory.rs b/crates/notebook/src/smb/directory.rs index 1649cc9e07b18c39b8a8c2d0899c51d5e90b4a5b..6a0cc5ff34bd5d5e07c0a545c4666366dfc3fe27 100644 --- a/crates/notebook/src/smb/directory.rs +++ b/crates/notebook/src/smb/directory.rs @@ -96,6 +96,73 @@ impl Client { }) .collect()) } + + /// Watches the directory tree at `path` as OneNote 2010 watches a notebook's folder: one + /// CHANGE_NOTIFY with WATCH_TREE, kept armed on the server, so an idle watch sends + /// nothing. `changed` runs on the client's runtime with each batch of changed paths, + /// relative to `path` and `/`-separated, or `""` when the server lost count of them, then + /// `Err(_)` once the watch ends with its connection or the client. + pub fn watch( + &self, + path: &str, + changed: impl FnMut(io::Result>) + Send + 'static, + ) -> io::Result<()> { + if Handle::try_current().is_ok() { + return Err(io::ErrorKind::InvalidInput.into()); + } + let mut connection = self + .connection + .lock() + .map_err(|_| io::ErrorKind::Other)? + .clone() + .ok_or(io::ErrorKind::NotConnected)?; + // Probing a connection whose only request is the watch would end OneNote's silence. + connection.set_keepalive(None); + let runtime = self.runtime.lock().map_err(|_| io::ErrorKind::Other)?; + let runtime = runtime.as_ref().ok_or(io::ErrorKind::NotConnected)?; + let path = path.replace('\\', "/"); + let mut watcher = runtime + .block_on(async { + tokio::time::timeout(self.timeout, self.tree.watch(&mut connection, &path, true)) + .await + }) + .map_err(|_| io::Error::from(io::ErrorKind::TimedOut))? + .map_err(|error| match &error { + smb2::Error::Protocol { status, .. } if status.0 == 0xc0000034 => { + io::Error::new(io::ErrorKind::NotFound, error) + } + // STATUS_NOT_SUPPORTED, STATUS_INVALID_DEVICE_REQUEST + smb2::Error::Protocol { status, .. } + if matches!(status.0, 0xc00000bb | 0xc0000010) => + { + io::Error::new(io::ErrorKind::Unsupported, error) + } + _ => io::Error::other(error), + })?; + // However the task ends, the connection failing or the client retiring its runtime, + // `changed` hears that the watch did. + struct Ends>)>(F); + impl>)> Drop for Ends { + fn drop(&mut self) { + (self.0)(Err(io::ErrorKind::NotConnected.into())); + } + } + let mut ends = Ends(changed); + runtime.spawn(async move { + loop { + match watcher.next_events().await { + Ok(events) => { + (ends.0)(Ok(events.into_iter().map(|event| event.filename).collect())) + } + Err(smb2::Error::Protocol { status, .. }) if status.0 == 0x0000010c => { + (ends.0)(Ok(vec![String::new()])) + } + Err(_) => return, + } + } + }); + Ok(()) + } } fn decode(mut bytes: &[u8]) -> io::Result> { diff --git a/crates/notebook/src/smb/tests.rs b/crates/notebook/src/smb/tests.rs index 74340bd1f7b7fa7a5ce4b7de2c78e69834b3b12d..32fc3ec9e204498cba8f0a2b4513abbbfe17de3a 100644 --- a/crates/notebook/src/smb/tests.rs +++ b/crates/notebook/src/smb/tests.rs @@ -421,6 +421,44 @@ fn live_stamp_and_transaction() { poller.delete(&path).unwrap(); } +/// A watch names the file another client commits to in the watched folder. +#[test] +#[ignore = "requires ONESTORE_SMB_LAB pointing to disposable Samba"] +fn live_watch() { + let watcher = client(); + let writer = client(); + let folder = format!( + "watch-{}", + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() + ); + writer.create_directory(&folder).unwrap(); + let path = format!("{folder}/Watched.one"); + let source = onestore::create_section("Watched.one", "Watched", "Author").unwrap(); + create(&writer, &path, &source); + let (sender, changes) = std::sync::mpsc::channel(); + watcher + .watch(&folder, move |changed| { + let _ = sender.send(changed.map_err(|error| error.kind())); + }) + .unwrap(); + std::thread::sleep(Duration::from_secs(1)); + writer + .commit_transaction(&path, &replaced(&source, 0..0, "Changed ")) + .unwrap(); + let changed = changes.recv_timeout(Duration::from_secs(10)).unwrap(); + assert!(changed.unwrap().contains(&"Watched.one".to_owned())); + // An idle watch hears nothing more. + while let Ok(changed) = changes.recv_timeout(Duration::from_secs(2)) { + assert!(changed.unwrap().iter().all(|path| path == "Watched.one")); + } + drop(watcher); + writer.delete(&path).unwrap(); + writer.delete(&folder).unwrap(); +} + #[test] #[ignore = "requires ONESTORE_SMB_LAB pointing to disposable Samba"] fn live_storage_inspection() { diff --git a/crates/notebook/tests/session.rs b/crates/notebook/tests/session.rs index 0620ce0a6d3aa30a5a657104612649b32bcbc4eb..e041320398dc31de45b9ee16b9043301d8b559b0 100644 --- a/crates/notebook/tests/session.rs +++ b/crates/notebook/tests/session.rs @@ -1029,7 +1029,7 @@ fn a_closed_sections_queued_edits_publish_in_the_background_when_online() { let notebook = two_sections(directory.path()); let file = directory.path().join("Shared/Second.one"); let background = notebook - .background(Duration::from_millis(20), || {}) + .background(Duration::from_millis(20), false, || {}) .unwrap(); background.set_offline(true); background.watch(notebook.replicas()); @@ -1083,7 +1083,7 @@ fn a_remote_change_to_a_closed_section_is_noticed_and_rebases_its_replica() { let notified = Arc::new(AtomicUsize::new(0)); let counter = Arc::clone(¬ified); let background = notebook - .background(Duration::from_millis(20), move || { + .background(Duration::from_millis(20), false, move || { counter.fetch_add(1, Ordering::SeqCst); }) .unwrap(); @@ -1125,3 +1125,77 @@ fn a_remote_change_to_a_closed_section_is_noticed_and_rebases_its_replica() { }); assert_same(opened.unwrap().page(*space).unwrap(), native); } + +#[test] +fn a_reported_change_wakes_its_section_without_polling() { + let directory = tempfile::tempdir().unwrap(); + let notebook = two_sections(directory.path()); + let background = notebook + .background(Duration::from_secs(3600), false, || {}) + .unwrap(); + background.watch(notebook.replicas()); + until("both sections were reached", || { + background + .status() + .iter() + .all(|(_, status)| status.synced.is_some()) + }); + let file = directory.path().join("Shared/Second.one"); + let bytes = onestore::read_file(&file).unwrap(); + let space = notebook::session::stored_pages(&bytes).unwrap()[0].space; + let native = edited(&model_ops::page_of(&bytes, space), "Native "); + ops::save(&bytes, space, &native) + .unwrap() + .commit_file(&file) + .unwrap(); + std::thread::sleep(Duration::from_secs(2)); + assert!( + background.changed().is_empty(), + "nothing polls an idle notebook" + ); + background.touched(&["Second.one".to_owned()]); + let mut noticed = Vec::new(); + until("the reported change was noticed", || { + noticed.extend(background.changed()); + !noticed.is_empty() + }); + assert_eq!(noticed, ["Second.one"]); +} + +#[test] +fn every_section_gets_an_offline_copy_that_closing_the_notebook_discards() { + let directory = tempfile::tempdir().unwrap(); + let notebook = two_sections(directory.path()); + let replicas: Vec<_> = notebook + .replicas() + .into_iter() + .map(|(_, replica)| replica.unwrap()) + .collect(); + assert!(replicas.iter().all(|replica| !replica.exists())); + let background = notebook + .background(Duration::from_secs(3600), true, || {}) + .unwrap(); + background.watch(notebook.replicas()); + until("both sections have offline copies", || { + replicas.iter().all(|replica| replica.exists()) + && background + .status() + .iter() + .all(|(_, status)| status.synced.is_some()) + }); + // Opening a section starts from its copy, and edits made offline wait in it. + let section = notebook.section("Second.one", || {}).unwrap(); + section.set_offline(true); + let space = section.pages().unwrap()[0].0; + let before = section.page(space).unwrap(); + typed(§ion, space, &before, 0..0, "Waiting "); + section.close().unwrap(); + background.discard(); + let second = notebook.replica_path("Second.one").unwrap(); + until("the copy with nothing waiting was discarded", || { + !notebook.replica_path("First.one").unwrap().exists() + }); + assert!(second.exists(), "a copy with edits waiting stays"); + let kept = notebook::Replica::open(&second).unwrap(); + assert_eq!(kept.pending().unwrap().len(), 1); +} diff --git a/crates/onestore/examples/ink_lab.rs b/crates/onestore/examples/ink_lab.rs new file mode 100644 index 0000000000000000000000000000000000000000..7a92fcfda39d0711b662a6d02d3aeadb0f8913d9 --- /dev/null +++ b/crates/onestore/examples/ink_lab.rs @@ -0,0 +1,25 @@ +use onestore::{RevisionIndex, Store, document::Document}; +fn main() { + let path = std::env::args().nth(1).unwrap(); + let needle = std::env::args().nth(2).unwrap(); + let bytes = std::fs::read(path).unwrap(); + let store = Store::parse(&bytes).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let document = Document::parse(&index).unwrap(); + for (space, _) in document.pages().unwrap() { + let page = onestore::page::Page::from_space(&document, space).unwrap(); + if page.title != needle { + continue; + } + if std::env::var_os("INK_MODEL").is_some() { + for object in &page.objects { + if let onestore::page::PageObject::Ink(ink) = object { + println!("{}", serde_json::to_string(ink).unwrap()); + } + } + continue; + } + let revision = document.spaces[&space].active().unwrap(); + println!("{}", serde_json::to_string_pretty(revision).unwrap()); + } +} diff --git a/crates/onestore/examples/scratch_dump.rs b/crates/onestore/examples/scratch_dump.rs new file mode 100644 index 0000000000000000000000000000000000000000..db7d79089617abe0c8d20fe30b2c53eaaf76d8ff --- /dev/null +++ b/crates/onestore/examples/scratch_dump.rs @@ -0,0 +1,29 @@ +use onestore::{RevisionIndex, Store, document::Document}; +fn main() { + let path = std::env::args().nth(1).unwrap(); + let needle = std::env::args().nth(2).unwrap(); + let bytes = std::fs::read(path).unwrap(); + let store = Store::parse(&bytes).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let document = Document::parse(&index).unwrap(); + for (space, _) in document.pages().unwrap() { + let page = onestore::page::Page::from_space(&document, space).unwrap(); + if page.title != needle { continue; } + let revision = document.spaces[&space].active().unwrap(); + println!("{}", serde_json::to_string_pretty(revision).unwrap()); + if let Ok(out) = std::env::var("PAYLOAD_OUT") { + for object in &page.objects { + if let onestore::page::PageObject::Outline(o) = object { + for p in &o.paragraphs { + if let onestore::page::ParagraphContent::Attachment(a) = &p.content { + std::fs::write(format!("{out}/{}", a.filename), a.bytes.as_deref().unwrap()).unwrap(); + if let Some(i) = &a.preview { std::fs::write(format!("{out}/{}.png", a.filename), i).unwrap(); } + } + } + } + } + } + } +} +#[allow(dead_code)] +fn unused() {} diff --git a/crates/onestore/src/document.rs b/crates/onestore/src/document.rs index 4556ed6da34ba0aa5beabd308c6c5a06d34bae60..069fa5b5638cf5a02615c7833bacaa42df2e10a6 100644 --- a/crates/onestore/src/document.rs +++ b/crates/onestore/src/document.rs @@ -405,9 +405,12 @@ impl Revision<'_> { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] /// Tag dates count seconds since 1980-01-01 UTC; status retains the ActionItemStatus bits. +/// A task tag (status bit 4) has no definition and carries its own icon and property status. pub struct Tag { pub definition: Option, pub action_type: Option, + pub shape: Option, + pub property_status: Option, pub status: u16, pub created: Option, pub completed: Option, @@ -446,6 +449,8 @@ pub enum Kind<'a> { margin_origin_x: Option, margin_origin_y: Option, rtl: Option, + /// Every audio and video recording on the page (AudioRecordingGuids). + recordings: Vec<[u8; 16]>, }, Metadata { title: Option, @@ -475,11 +480,16 @@ pub enum Kind<'a> { }, OutlineGroup, /// An ink drawing or handwriting container: strokes hang off `data`, nested containers off - /// the element's content list. Scaling multiplies stroke coordinates. + /// the element's content list. Scaling multiplies stroke coordinates. A drawn shape adds + /// its kind (11 a line, 12 a closed shape) and geometry: a line's ends, or a closed + /// shape's transform and anchors, as `page::InkShape` reads them. Ink { data: Option, scale_x: Option, scale_y: Option, + shape_kind: Option, + line: Option>, + anchors: Option>, }, InkData { strokes: Vec, @@ -555,6 +565,8 @@ pub enum Kind<'a> { source_path: Option, recording_id: Option<[u8; 16]>, recording_type: Option, + /// AudioRecordingDuration, in milliseconds. + recording_duration: Option, /// Displayed icon width and height in points. icon_width: Option, icon_height: Option, @@ -675,6 +687,16 @@ impl<'a, 'o> Fields<'a, 'o> { fn u32(&mut self, id: u32) -> Result> { Ok(self.fixed(id)?.map(u32::from_le_bytes)) } + fn guids(&mut self, id: u32) -> Result> { + let bytes = self.bytes(id)?.unwrap_or_default(); + if !bytes.len().is_multiple_of(16) { + return Err(invalid("Media identifiers have an invalid length")); + } + Ok(bytes + .chunks_exact(16) + .map(|id| id.try_into().unwrap()) + .collect()) + } fn u16(&mut self, id: u32) -> Result> { Ok(self.fixed(id)?.map(u16::from_le_bytes)) } @@ -1058,6 +1080,8 @@ impl<'a> Element<'a> { tags.push(Tag { definition: tag.one(0x20003488)?, action_type: tag.u16(0x10003463)?, + shape: tag.u16(0x10003464)?, + property_status: tag.u32(0x14003467)?, status: tag .u16(0x10003470)? .ok_or_else(|| invalid("Note tag has no status"))?, @@ -1114,6 +1138,7 @@ impl<'a> Element<'a> { margin_origin_x: f.float(0x14001d0f, 36.0)?, margin_origin_y: f.float(0x14001d10, 36.0)?, rtl: f.boolean(0x08001c92)?, + recordings: f.guids(0x1c001ca3)?, }, 0x20030 => Kind::Metadata { title: f.text(0x1c001cf3)?, @@ -1128,6 +1153,9 @@ impl<'a> Element<'a> { data: f.one(0x20003415)?, scale_x: f.float(0x14001c46, 1.0)?, scale_y: f.float(0x14001c47, 1.0)?, + shape_kind: f.bytes(0x0c001d4f)?.and_then(|b| b.first().copied()), + line: f.bytes(0x1c001dac)?.map(<[u8]>::to_vec), + anchors: f.bytes(0x1c001daa)?.map(<[u8]>::to_vec), }, 0x2003b => Kind::InkData { strokes: f.refs(0x24003416, IdStream::Objects)?, @@ -1327,6 +1355,7 @@ impl<'a> Element<'a> { source_path: f.text(0x1c001d9d)?, recording_id: f.fixed(0x1c001c97)?, recording_type: f.u32(0x14001d24)?, + recording_duration: f.u32(0x14001cfd)?, icon_width: f.float(0x140034cd, 36.0)?, icon_height: f.float(0x140034ce, 36.0)?, }, @@ -1349,14 +1378,7 @@ impl<'a> Element<'a> { }, _ => Kind::Unknown, }; - let media_ids = f.bytes(0x1c001c98)?.unwrap_or_default(); - if !media_ids.len().is_multiple_of(16) { - return Err(invalid("Media identifiers have an invalid length")); - } - let media_ids = media_ids - .chunks_exact(16) - .map(|id| id.try_into().unwrap()) - .collect(); + let media_ids = f.guids(0x1c001c98)?; Ok(Self { jcid: object.jcid, children, diff --git a/crates/onestore/src/op/apply.rs b/crates/onestore/src/op/apply.rs index 3fcab72678b98e557e839720dbe6786b0d6a4b57..e66c662e72a90eebefcda9f8203312ee67bac13a 100644 --- a/crates/onestore/src/op/apply.rs +++ b/crates/onestore/src/op/apply.rs @@ -63,9 +63,16 @@ impl<'a> Section<'a> { .collect(); for space in spaces { // A page the edit went on to delete has no cells left to check. - let Ok(page) = section.active(space) else { + if section.active(space).is_err() { continue; - }; + } + Writer { + section: &mut *section, + space, + author, + } + .playlist()?; + let page = section.active(space)?; if page.view.nodes.values().any(|node| { matches!(node.kind, Kind::Cell { .. }) && node.children.is_empty() }) { @@ -474,6 +481,41 @@ impl<'a> Writer<'_, 'a> { Ok(()) } + /// Lists the page's recordings in its AudioRecordingGuids as OneNote keeps them: in + /// the order they arrived, without those since removed, absent when there are none. + fn playlist(&mut self) -> Result<(), Failure> { + let page = self.page()?; + let [id] = page.pages[..] else { + return Ok(()); + }; + let Kind::Page { recordings, .. } = &page.view.nodes[&id].kind else { + return Ok(()); + }; + let present: Vec<[u8; 16]> = page + .view + .nodes + .values() + .filter_map(|node| match node.kind { + Kind::Attachment { recording_id, .. } => recording_id, + _ => None, + }) + .collect(); + let mut listed: Vec<[u8; 16]> = recordings + .iter() + .filter(|recording| present.contains(recording)) + .copied() + .collect(); + for recording in present { + if !listed.contains(&recording) { + listed.push(recording); + } + } + if listed == *recordings { + return Ok(()); + } + self.write(|page| content::playlist_changes(page, id, &listed)) + } + /// Requires `id` reachable on the page. fn target(&mut self, id: ExGuid) -> Result<(), Failure> { let page = self.page()?; @@ -746,6 +788,33 @@ impl<'a> Writer<'_, 'a> { let text = self.text_of(*paragraph)?; self.write(|page| properties::style_changes(page, text, *style, Some(definition))) } + PageOp::Media { paragraph, media } => { + self.target(*paragraph)?; + let page = self.page()?; + let content = match page.view.nodes[paragraph].content.as_slice() { + [content] + if matches!( + page.view.nodes[content].kind, + Kind::RichText { .. } | Kind::Attachment { .. } + ) => + { + *content + } + _ => { + return Err(OpError::Unsupported( + "Link a paragraph of text or a file to a recording", + ) + .into()); + } + }; + if media.recordings.is_empty() != media.time_ms.is_none() { + return Err(OpError::Unsupported( + "A recording link names recordings and a moment in them", + ) + .into()); + } + self.write(|page| properties::media_changes(page, content, media)) + } PageOp::List { paragraph, lists } => { self.target(*paragraph)?; let page = self.page()?; @@ -772,17 +841,16 @@ impl<'a> Writer<'_, 'a> { definitions, } => { self.target(*target)?; - let mut entries = Vec::new(); - for tag in tags { - let definition = tag - .definition - .ok_or(OpError::Unsupported("A note tag names its definition"))?; - let model = definitions - .iter() - .find(|(id, _)| *id == definition) - .map(|(_, definition)| definition); - entries.push((definition, tag, model)); - } + let entries: Vec<_> = tags + .iter() + .map(|tag| { + let model = definitions + .iter() + .find(|(id, _)| Some(*id) == tag.definition) + .map(|(_, definition)| definition); + (tag, model) + }) + .collect(); self.write(|page| properties::tag_changes(page, *target, &entries)) } PageOp::Add { object, before } => self.add(object, *before), @@ -1449,7 +1517,7 @@ fn bare(paragraph: &PageParagraph) -> Result<(), OpError> { } if paragraph.media != Default::default() { return Err(OpError::Unsupported( - "Recording annotations are made by OneNote while it records", + "Link a paragraph to a recording with its own op", )); } match ¶graph.content { @@ -1470,9 +1538,6 @@ fn bare(paragraph: &PageParagraph) -> Result<(), OpError> { } Ok(()) } - ParagraphContent::Attachment(attachment) if attachment.recording.is_some() => Err( - OpError::Unsupported("Recordings are captured by OneNote, not inserted"), - ), _ => Ok(()), } } diff --git a/crates/onestore/src/op/content.rs b/crates/onestore/src/op/content.rs index cd897b9107d76050272e315f5556969367e073ba..3f9cfb43f198f7014f7156192b69301e2c9ae66a 100644 --- a/crates/onestore/src/op/content.rs +++ b/crates/onestore/src/op/content.rs @@ -6,7 +6,7 @@ use crate::{ Error, ExGuid, active::{ActivePage, Changes}, document::{Format, Layout}, - page::{Attachment, Image, Ink, InkStroke, Paragraph}, + page::{Attachment, Image, Ink, InkStroke, Paragraph, Recording}, write::PropertyObject, }; use std::collections::BTreeMap; @@ -319,8 +319,10 @@ pub(crate) fn attachment_changes( "An attachment needs a file name without path separators", )); } - if attachment.recording.is_some() { - return Err(invalid("Recordings are captured by OneNote, not inserted")); + if Recording::kind_of(name).is_some() != attachment.recording.is_some() { + return Err(invalid( + "Audio and video files are recordings, and only they are", + )); } if attachment.bytes.is_none() { return Err(invalid("A new attachment needs its payload")); @@ -374,6 +376,19 @@ pub(crate) fn attachment_changes( if let Some(path) = &attachment.source_path { values.push((0x1c001d9d, crate::create::string(path))); } + if let Some(recording) = attachment.recording { + if recording.id == [0; 16] || !matches!(recording.kind, 1 | 2) { + return Err(invalid( + "A recording needs an identity and is audio or video", + )); + } + // AudioRecordingGuid, IRecordMedia and AudioRecordingDuration. + values.push((0x1c001c97, recording.id.to_vec())); + values.push((0x14001d24, recording.kind.to_le_bytes().to_vec())); + if let Some(duration) = recording.duration_ms { + values.push((0x14001cfd, duration.to_le_bytes().to_vec())); + } + } values.extend(layout_values(&Default::default(), &attachment.layout)?.0); let mut changed = BTreeMap::new(); let mut file = PropertyObject::file(ids.file, &payload_reference(ids.payload), extension)?; @@ -400,6 +415,21 @@ pub(crate) fn attachment_changes( Ok(changed) } +/// Page `page` listing `recordings` in its AudioRecordingGuids, or none. +pub(crate) fn playlist_changes( + active: &ActivePage<'_>, + page: ExGuid, + recordings: &[[u8; 16]], +) -> Result { + let mut node = PropertyObject::from_object(&active.live.revision.objects[&page])?; + if recordings.is_empty() { + node.remove(&[0x1c001ca3])?; + } else { + node.set(&[(0x1c001ca3, recordings.concat().as_slice())])?; + } + Ok(BTreeMap::from([(page, node)])) +} + /// A stored attachment keeps its payload and preview; its shown name, recorded source /// path and icon size change in place, as OneNote's rename does. pub(crate) fn attachment_edit_changes( @@ -415,7 +445,7 @@ pub(crate) fn attachment_edit_changes( .to_vec(), )]; if attachment.recording != stored.recording { - return Err(invalid("A recording stays the recording OneNote captured")); + return Err(invalid("A file keeps its recording")); } let name = attachment.filename.as_str(); if name.is_empty() || name.contains(['\0', '/', '\\']) { @@ -425,6 +455,11 @@ pub(crate) fn attachment_edit_changes( } let mut removed = Vec::new(); if attachment.filename != stored.filename { + if Recording::kind_of(name).is_some() != attachment.recording.is_some() { + return Err(invalid( + "Audio and video files are recordings, and only they are", + )); + } let name = crate::create::string(&attachment.filename); values.push((0x1c001c22, name.clone())); values.push((0x1c001d9c, name)); @@ -457,19 +492,31 @@ pub(crate) fn attachment_edit_changes( Ok(BTreeMap::from([(object, node)])) } -/// Creates stroke objects (numbered from `first`) and one drawing-attribute object per -/// distinct pen; returns the compact references for the data node's stroke list. +/// Creates stroke objects (numbered on from the page's last stroke) and one +/// drawing-attribute object per distinct pen; returns the compact references for the data +/// node's stroke list. A shape's strokes carry only their path and pen, as OneNote's do. fn write_strokes( + active: &ActivePage<'_>, changed: &mut Changes, data_object: &mut PropertyObject, strokes: &[(ExGuid, &InkStroke)], - first: usize, + shape: bool, filetime: u64, ) -> Result, Error> { + let first = active + .view + .nodes + .values() + .filter_map(|node| match node.kind { + crate::document::Kind::InkStroke { index, .. } => index, + _ => None, + }) + .max() + .unwrap_or(0); let mut styles: Vec<(InkPen, ExGuid)> = Vec::new(); let mut references = Vec::new(); for (offset, (stroke_id, stroke)) in strokes.iter().enumerate() { - let pen = InkPen::of(stroke); + let pen = InkPen::of(stroke, shape); let style = match styles.iter().find(|(known, _)| *known == pen) { Some((_, id)) => *id, None => { @@ -488,13 +535,14 @@ fn write_strokes( id } }; + let values = if shape { + stroke_path(stroke)? + } else { + stroke_values(stroke, first + offset as u32 + 1, filetime)? + }; let mut object = PropertyObject { jcid: 0x20047, - bytes: crate::create::properties(&stroke_values( - stroke, - (first + offset) as u32 + 1, - filetime, - )?)?, + bytes: crate::create::properties(&values)?, global_ids: std::sync::Arc::new(BTreeMap::from([(0, stroke_id.guid)])), }; object.reference(*stroke_id)?; @@ -506,7 +554,8 @@ fn write_strokes( Ok(references) } -/// The drawing attributes OneNote shares between strokes drawn with the same pen. +/// The drawing attributes OneNote shares between strokes drawn with the same pen. A pen +/// drawn with the mouse ignores pressure; a shape's pen spans every coordinate instead. #[derive(PartialEq)] struct InkPen { width: u32, @@ -514,22 +563,31 @@ struct InkPen { color: Option, transparency: Option, pen_tip: Option, + raster_operation: Option, + shape: bool, } impl InkPen { - fn of(stroke: &InkStroke) -> Self { + fn of(stroke: &InkStroke, shape: bool) -> Self { Self { width: (stroke.width * 2540.0 / 72.0).to_bits(), height: (stroke.height * 2540.0 / 72.0).to_bits(), color: stroke.color, transparency: stroke.transparency, pen_tip: stroke.pen_tip, + raster_operation: stroke.raster_operation, + shape, } } fn values(&self) -> Values { + let dimensions = if self.shape { + crate::page::ink::SHAPE_DIMENSIONS + } else { + crate::page::ink::DIMENSIONS + }; let mut values: Values = vec![ - (0x1c00340a, crate::page::ink::DIMENSIONS.to_vec()), + (0x1c00340a, dimensions.to_vec()), (0x1400340c, self.height.to_le_bytes().to_vec()), (0x1400340d, self.width.to_le_bytes().to_vec()), ]; @@ -542,11 +600,17 @@ impl InkPen { if let Some(tip) = self.pen_tip { values.push((0x0c003412, vec![tip])); } + if let Some(operation) = self.raster_operation { + values.push((0x0c003413, vec![operation])); + } + if !self.shape { + values.push((0x88003411, Vec::new())); + } values } } -fn stroke_values(stroke: &InkStroke, index: u32, filetime: u64) -> Result { +fn stroke_path(stroke: &InkStroke) -> Result { if stroke.points.is_empty() { return Err(invalid("A stroke needs at least one point")); } @@ -562,27 +626,32 @@ fn stroke_values(stroke: &InkStroke, index: u32, filetime: u64) -> Result Result { + let mut values = stroke_path(stroke)?; + let [left, top] = [0, 1].map(|axis| { + stroke + .points + .iter() + .map(|p| p[axis]) + .fold(f32::INFINITY, f32::min) + }); let mut origin = ((left - stroke.width / 2.0) / 36.0).to_le_bytes().to_vec(); origin.extend_from_slice(&((top - stroke.height / 2.0) / 36.0).to_le_bytes()); - Ok(vec![ - (0x1c00340b, stroke.packet()), + let bias = if stroke.raster_operation == Some(9) { 2 } else { 0 }; + values.extend([ (0x14003419, index.to_le_bytes().to_vec()), (0x1000341b, 0x409_u16.to_le_bytes().to_vec()), - (0x0c00341c, vec![0]), + (0x0c00341c, vec![bias]), (0x1c00341a, crate::write::fresh_guid()?.to_vec()), (0x1c00341d, filetime.to_le_bytes().to_vec()), (0x1c00345b, origin), - ]) + ]); + Ok(values) } /// New ink as OneNote 2010 stores a drawing: container `id` that the page lists as a child @@ -614,15 +683,27 @@ pub(crate) fn ink_changes( global_ids: std::sync::Arc::new(BTreeMap::from([(0, data.guid)])), }; data_object.reference(data)?; - let references = write_strokes(&mut changed, &mut data_object, strokes, 0, filetime)?; + let references = write_strokes( + active, + &mut changed, + &mut data_object, + strokes, + ink.shape.is_some(), + filetime, + )?; data_object.set(&[(0x24003416, &references)])?; changed.insert(data, data_object); + // A shape keeps its kind and geometry beside the strokes, and counts as a second kind of + // drawing. + let mut values: Values = vec![(0x14001d7a, modified.to_vec())]; + if let Some(shape) = &ink.shape { + let (kind, property, bytes) = shape.stored(); + values.extend([(0x0c001d4f, vec![kind]), (property, bytes)]); + } + values.push((0x14001d4e, (1 + u32::from(ink.shape.is_some())).to_le_bytes().to_vec())); let mut object = PropertyObject { jcid: 0x60014, - bytes: crate::create::properties(&[ - (0x14001d7a, modified.to_vec()), - (0x14001d4e, 1u32.to_le_bytes().to_vec()), - ])?, + bytes: crate::create::properties(&values)?, global_ids: std::sync::Arc::new(BTreeMap::from([(0, id.guid)])), }; object.reference(id)?; @@ -641,10 +722,12 @@ pub(crate) fn strokes_changes( kept: &[ExGuid], added: &[(ExGuid, &InkStroke)], ) -> Result { - let data = match active.view.nodes.get(&container).map(|node| &node.kind) { + let (data, shape) = match active.view.nodes.get(&container).map(|node| &node.kind) { Some(crate::document::Kind::Ink { - data: Some(data), .. - }) => *data, + data: Some(data), + shape_kind, + .. + }) => (*data, shape_kind.is_some()), _ => return Err(invalid("Stored ink has no stroke data to rewrite")), }; let (modified, filetime) = crate::create::current_timestamps()?; @@ -657,10 +740,11 @@ pub(crate) fn strokes_changes( references.extend(data_object.reference(*id)?); } references.extend(write_strokes( + active, &mut changed, &mut data_object, added, - kept.len(), + shape, filetime, )?); data_object.set(&[(0x24003416, &references)])?; diff --git a/crates/onestore/src/op/lower.rs b/crates/onestore/src/op/lower.rs index 9b8c0f043e15eb97480707642a07ef84364a8d2c..9110ef3e27dc1c72dad65128145eea70af1f6dee 100644 --- a/crates/onestore/src/op/lower.rs +++ b/crates/onestore/src/op/lower.rs @@ -225,7 +225,7 @@ pub(crate) fn collect_containers(list: &[PageParagraph], out: &mut Vec) /// What the writers keep as stored, checked before any op: titles, unsupported objects, /// definitions other than lists and tags, outline roles, paragraph styles and formats, -/// recording links, fields and content types. +/// fields and content types. pub(crate) fn validate( before: &Page, after: &Page, @@ -309,17 +309,13 @@ pub(crate) fn validate( let Some(previous) = old.paragraphs.get(id) else { continue; }; - // A join moves a text object, with its style and recording link, into an emptied - // paragraph; one new to the page starts as a copy without the link. + // A join moves a text object, with its style, into an emptied paragraph. let owner = paragraph.text().map(|text| owners.get(&text.id)); - let (style, media) = match owner { - Some(Some(owner)) => (owner.style, owner.media.clone()), - Some(None) => (paragraph.style, Default::default()), - None => (previous.style, previous.media.clone()), + let style = match owner { + Some(Some(owner)) => owner.style, + Some(None) => paragraph.style, + None => previous.style, }; - if paragraph.media != media { - return Err(invalid("Recording annotations cannot be edited")); - } if paragraph.style != style || paragraph.format != previous.format { return Err(invalid( "Paragraph styles and paragraph formatting cannot be edited", @@ -695,6 +691,8 @@ pub(crate) fn same_tags(a: &[Tag], b: &[Tag]) -> bool { ( t.definition, t.action_type, + t.shape, + t.property_status, t.status, t.created, t.completed, @@ -745,6 +743,7 @@ fn bare(paragraph: &PageParagraph) -> PageParagraph { stripped.lists.clear(); stripped.tags.clear(); stripped.collapsed = false; + stripped.media = Default::default(); match &mut stripped.content { ParagraphContent::Text(text) => text.tags.clear(), ParagraphContent::Table(table) => { @@ -820,6 +819,7 @@ impl Lowering { self.styles(after, new)?; self.lists(after, new)?; self.tags(after, new)?; + self.media(new)?; self.paragraph_formatting(new)?; self.formatting(old, new)?; self.layout(old, new)?; @@ -1775,9 +1775,9 @@ impl Lowering { } let mut definitions = Vec::new(); for tag in tags { - let definition = tag - .definition - .ok_or_else(|| invalid("A note tag names its definition"))?; + let Some(definition) = tag.definition else { + continue; + }; if let Some(model) = after.definitions.get(&definition) && !definitions.iter().any(|(id, _)| *id == definition) { @@ -1794,6 +1794,20 @@ impl Lowering { Ok(()) } + fn media(&mut self, new: &View<'_>) -> Result<(), Error> { + for (id, paragraph) in &new.paragraphs { + let current = model::paragraph(&self.current, *id) + .ok_or_else(|| invalid("A paragraph is missing after text edits"))?; + if current.media != paragraph.media { + self.emit(PageOp::Media { + paragraph: *id, + media: paragraph.media.clone(), + })?; + } + } + Ok(()) + } + fn paragraph_formatting(&mut self, new: &View<'_>) -> Result<(), Error> { for (id, paragraph) in &new.paragraphs { let Some(text) = paragraph.text() else { diff --git a/crates/onestore/src/op/mod.rs b/crates/onestore/src/op/mod.rs index 3c91882a2766cb0a9ae75ee8ffd63979af2e3ac8..4de89028000f8a4b1bc8ac8bdd4e2291aab9d105 100644 --- a/crates/onestore/src/op/mod.rs +++ b/crates/onestore/src/op/mod.rs @@ -6,8 +6,8 @@ use crate::{ ExGuid, OutlineEdit, PageCreation, PageEdit, TextAttribute, document::{Layout, Tag}, page::{ - Definition, InkStroke, Page, PageObject, PageParagraph, Paragraph, TableCell, TableColumn, - TableRow, + Definition, InkStroke, MediaIndex, Page, PageObject, PageParagraph, Paragraph, TableCell, + TableColumn, TableRow, }, }; use serde::{Deserialize, Serialize}; @@ -151,6 +151,12 @@ pub enum PageOp { style: ExGuid, definition: Definition, }, + /// Links a paragraph's text or file to a moment in recordings on the page, or with + /// empty `media` unlinks it. + Media { + paragraph: ExGuid, + media: MediaIndex, + }, /// Replaces a paragraph's list nodes; a node another paragraph owns is copied. List { paragraph: ExGuid, diff --git a/crates/onestore/src/op/model.rs b/crates/onestore/src/op/model.rs index c03231f41104c61b5297c58b2de9dfdcf00cfab1..e7b320617de5fa7793cc25a84698601400166849 100644 --- a/crates/onestore/src/op/model.rs +++ b/crates/onestore/src/op/model.rs @@ -468,6 +468,7 @@ fn stroked(stroke: &crate::page::InkStroke) -> crate::page::InkStroke { fn inked(ink: &crate::page::Ink) -> crate::page::Ink { crate::page::Ink { strokes: ink.strokes.iter().map(stroked).collect(), + shape: ink.shape.as_ref().map(crate::page::ink::InkShape::snapped), ..ink.clone() } } @@ -1042,6 +1043,21 @@ fn interpret(page: &mut Page, op: &PageOp) -> Result<(), Error> { text.text = Paragraph::from_runs(runs); } } + PageOp::Media { paragraph, media } => { + let paragraph = paragraph_mut(page, *paragraph)?; + if !matches!( + paragraph.content, + ParagraphContent::Text(_) | ParagraphContent::Attachment(_) + ) { + return Err(invalid("Link a paragraph of text or a file to a recording")); + } + if media.recordings.is_empty() != media.time_ms.is_none() { + return Err(invalid( + "A recording link names recordings and a moment in them", + )); + } + paragraph.media = media.clone(); + } PageOp::List { paragraph, lists } => { paragraph_mut(page, *paragraph)?.lists = lists.iter().map(|(id, _)| *id).collect(); for (id, definition) in lists { diff --git a/crates/onestore/src/op/properties.rs b/crates/onestore/src/op/properties.rs index fd0e7c8f8c71714f3f8f3119643d428c382c5171..4d7aadd7f94dc035e14cc157128f5ee21ab2e1df 100644 --- a/crates/onestore/src/op/properties.rs +++ b/crates/onestore/src/op/properties.rs @@ -5,7 +5,7 @@ use crate::{ Error, ExGuid, PropertySets, Value, active::{ActivePage, Changes}, document::{Kind, Tag}, - page::Definition, + page::{Definition, MediaIndex}, write::PropertyObject, }; use std::collections::{BTreeMap, BTreeSet}; @@ -165,54 +165,78 @@ fn tag_definition_values(definition: &Definition) -> Result { Ok(values) } -/// Replaces the note tags of `object`. Each entry names the stored identity of its tag's -/// definition and, where the tag does not carry its action type or the page lacks the -/// definition, the definition itself. +/// Replaces the note tags of `object`. A normal tag comes with its definition where the page +/// lacks it; a task tag has none and stores its own icon, in the order OneNote writes it. pub(crate) fn tag_changes( active: &ActivePage<'_>, object: ExGuid, - tags: &[(ExGuid, &Tag, Option<&Definition>)], + tags: &[(&Tag, Option<&Definition>)], ) -> Result { let raw = &active.live.revision; let mut definitions: Vec<(ExGuid, Option)> = Vec::new(); - let mut sets: Vec<(usize, Values)> = Vec::new(); + let mut sets: Vec<(Option, Values)> = Vec::new(); let mut action_types = BTreeSet::new(); - for (written, tag, definition) in tags { - let action_type = if tag.status & 4 != 0 { - tag.action_type - } else { - match definition.map(|d| &d.kind) { - Some(Kind::TagDefinition { action_type, .. }) => *action_type, - _ => return Err(invalid("A note tag must reference a tag definition")), - } - }; - if !action_types.insert(action_type.unwrap_or(0)) { - return Err(invalid("An element holds one note tag per action type")); - } - let index = match definitions.iter().position(|(id, _)| id == written) { - Some(index) => index, - None => { - let values = if raw.objects.contains_key(written) { - None - } else { - let definition = definition - .ok_or_else(|| invalid("A note tag references a missing tag definition"))?; - Some(tag_definition_values(definition)?) - }; - definitions.push((*written, values)); - definitions.len() - 1 - } - }; - let mut fields: Values = Vec::new(); - if let Some(action_type) = tag.action_type { - fields.push((0x10003463, action_type.to_le_bytes().to_vec())); - } - for (id, value) in [ + for (tag, definition) in tags { + let dates = [ (0x1400346e, tag.created), (0x1400346f, tag.completed), (0x1400346a, tag.start), (0x1400346b, tag.due), - ] { + ]; + let mut fields: Values = Vec::new(); + if tag.status & 4 != 0 { + let (Some(action_type), Some(shape), Some(property_status)) = + (tag.action_type, tag.shape, tag.property_status) + else { + return Err(invalid( + "A task tag stores its action type, icon and property status", + )); + }; + if !action_types.insert(action_type) { + return Err(invalid("An element holds one note tag per action type")); + } + fields.extend([ + (0x0c003473, vec![0]), + (0x10003463, action_type.to_le_bytes().to_vec()), + (0x10003464, shape.to_le_bytes().to_vec()), + (0x14003467, property_status.to_le_bytes().to_vec()), + ]); + if let Some(task) = tag.task_id { + fields.push((0x1c003469, task.to_vec())); + } + for (id, value) in [dates[2], dates[3], dates[0], dates[1]] { + if let Some(value) = value { + fields.push((id, value.to_le_bytes().to_vec())); + } + } + fields.push((0x10003470, tag.status.to_le_bytes().to_vec())); + sets.push((None, fields)); + continue; + } + let written = tag + .definition + .ok_or_else(|| invalid("A note tag must reference a tag definition"))?; + let action_type = match definition.map(|d| &d.kind) { + Some(Kind::TagDefinition { action_type, .. }) => *action_type, + _ => return Err(invalid("A note tag must reference a tag definition")), + }; + if !action_types.insert(action_type.unwrap_or(0)) { + return Err(invalid("An element holds one note tag per action type")); + } + if !definitions.iter().any(|(id, _)| *id == written) { + let values = if raw.objects.contains_key(&written) { + None + } else { + let definition = definition + .ok_or_else(|| invalid("A note tag references a missing tag definition"))?; + Some(tag_definition_values(definition)?) + }; + definitions.push((written, values)); + } + if let Some(action_type) = tag.action_type { + fields.push((0x10003463, action_type.to_le_bytes().to_vec())); + } + for (id, value) in dates { if let Some(value) = value { fields.push((id, value.to_le_bytes().to_vec())); } @@ -221,7 +245,7 @@ pub(crate) fn tag_changes( if let Some(task) = tag.task_id { fields.push((0x1c003469, task.to_vec())); } - sets.push((index, fields)); + sets.push((Some(written), fields)); } let parents = active.editable_parents(object)?; let modified = crate::create::current_timestamps()?.0.to_le_bytes(); @@ -240,9 +264,12 @@ pub(crate) fn tag_changes( } let mut target = PropertyObject::from_object(&raw.objects[&object])?; let mut encoded = Vec::new(); - for (index, fields) in &sets { - let reference = target.reference(definitions[*index].0)?; - let mut set = vec![(0x20003488, reference.to_vec())]; + for (definition, fields) in &sets { + let mut set = Vec::new(); + if let Some(definition) = definition { + let reference = target.reference(*definition)?; + set.push((0x20003488, reference.to_vec())); + } set.extend(fields.iter().cloned()); encoded.push(set); } @@ -380,3 +407,29 @@ pub(crate) fn paragraph_format_changes( crate::formatting::touch_ancestors(raw, parents, object, &modified, &mut changed)?; Ok(changed) } + +/// Links content `object` to a moment in recordings (MediaIndex), or unlinks it. +pub(crate) fn media_changes( + active: &ActivePage<'_>, + object: ExGuid, + media: &MediaIndex, +) -> Result { + let parents = active.editable_parents(object)?; + let modified = crate::create::current_timestamps()?.0.to_le_bytes(); + let raw = &active.live.revision; + let mut target = PropertyObject::from_object(&raw.objects[&object])?; + match media.time_ms { + Some(time) => { + let recordings = media.recordings.concat(); + target.set(&[ + (0x1c001c98, recordings.as_slice()), + (0x14001c99, &time.to_le_bytes()), + ])?; + } + None => target.remove(&[0x1c001c98, 0x14001c99])?, + } + target.set(&[(0x14001d7a, &modified)])?; + let mut changed = BTreeMap::from([(object, target)]); + crate::formatting::touch_ancestors(raw, parents, object, &modified, &mut changed)?; + Ok(changed) +} diff --git a/crates/onestore/src/op/tests.rs b/crates/onestore/src/op/tests.rs index d88b3eb776b3e5cf1616609583b735b9293bfe98..0fda9207f9db8c27833a6beb61ae7ec6b7994d5a 100644 --- a/crates/onestore/src/op/tests.rs +++ b/crates/onestore/src/op/tests.rs @@ -834,6 +834,8 @@ pub(super) fn mutate(page: &mut Page, rng: &mut Rng, family: Family) -> bool { text.tags.push(crate::document::Tag { definition: Some(definition), action_type: None, + shape: None, + property_status: None, status, created: Some(1_262_401_445), completed: (status == 1).then_some(1_262_402_000), @@ -1159,6 +1161,7 @@ pub(super) fn mutate(page: &mut Page, rng: &mut Rng, family: Family) -> bool { layout: Layout::default(), strokes: vec![stroke(rng)], groups: Vec::new(), + shape: None, }) }; page.objects.insert(at, object); @@ -1251,6 +1254,8 @@ fn stroke(rng: &mut Rng) -> InkStroke { color: Some(0x0000ff), transparency: None, pen_tip: None, + // Every other stroke a highlighter's. + raster_operation: (x as u32).is_multiple_of(2).then_some(9), } } @@ -1595,6 +1600,8 @@ fn random_op(page: &Page, rng: &mut Rng) -> Option { tags: vec![crate::document::Tag { definition: Some(definition), action_type: None, + shape: None, + property_status: None, status: 0, created: Some(1_262_401_445), completed: None, @@ -1639,15 +1646,20 @@ fn random_op(page: &Page, rng: &mut Rng) -> Option { }), before: None, }, - _ => PageOp::Add { - object: PageObject::Ink(Ink { - id: id(), - layout: Layout::default(), - strokes: vec![stroke(rng)], - groups: Vec::new(), - }), - before: None, - }, + _ => { + let stroke = stroke(rng); + let ends = [stroke.points[0], stroke.points[stroke.points.len() - 1]]; + PageOp::Add { + object: PageObject::Ink(Ink { + id: id(), + layout: Layout::default(), + strokes: vec![stroke], + groups: Vec::new(), + shape: Some(crate::page::ink::InkShape::Line(ends)), + }), + before: None, + } + } }) } diff --git a/crates/onestore/src/page/ink.rs b/crates/onestore/src/page/ink.rs index 881a2222ddac97a8fdccae1d17a4a85596bfc483..2e3da7394a66d91130d5b4e14500a09dd529c4df 100644 --- a/crates/onestore/src/page/ink.rs +++ b/crates/onestore/src/page/ink.rs @@ -32,6 +32,119 @@ pub struct Ink { pub strokes: Vec, /// Nested ink containers, as newer OneNote versions group handwriting. pub groups: Vec, + /// The shape drawn, for a drawing made from Draw's Insert Shapes. + #[serde(default)] + pub shape: Option, +} + +/// The shapes of Draw's Insert Shapes that Snowbound draws. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ShapeKind { + Line, + Arrow, + Rectangle, + Ellipse, +} + +impl Ink { + /// A shape dragged from `from` to `to` in page points, drawn with `pen`'s width and + /// colour as OneNote 2010 draws it (`corpus/ink-tools`): a rectangle clockwise from its + /// top left corner, an ellipse in 50 steps clockwise from its right end, and an arrow's + /// head as a second stroke of two barbs at atan(1/2) to the line, 8 points plus twice + /// the pen's width long. The result reads back unchanged once stored. + pub fn drawn( + kind: ShapeKind, + from: [f32; 2], + to: [f32; 2], + pen: &InkStroke, + ) -> Result { + let [x0, y0] = [from[0].min(to[0]), from[1].min(to[1])]; + let [x1, y1] = [from[0].max(to[0]), from[1].max(to[1])]; + let (shape, paths) = match kind { + ShapeKind::Line | ShapeKind::Arrow => { + let mut paths = vec![vec![from, to]]; + let [dx, dy] = [to[0] - from[0], to[1] - from[1]]; + if kind == ShapeKind::Arrow && dx.hypot(dy) > 0.0 { + let back = (-dy).atan2(-dx); + let length = 8.0 + 2.0 * pen.width; + let barb = |turn: f32| { + let angle = back + turn; + [to[0] + length * angle.cos(), to[1] + length * angle.sin()] + }; + let spread = 0.5f32.atan(); + paths.push(vec![barb(spread), to, barb(-spread)]); + } + (InkShape::Line([from, to]), paths) + } + ShapeKind::Rectangle | ShapeKind::Ellipse => { + let [w, h] = [x1 - x0, y1 - y0]; + let transform = [w, 0.0, 0.0, h, x0, y0]; + let (anchors, path): (&[[f32; 2]], Vec<[f32; 2]>) = if kind == ShapeKind::Rectangle + { + ( + &[ + [0.0, 0.0], + [0.5, 0.0], + [1.0, 0.0], + [1.0, 0.5], + [1.0, 1.0], + [0.5, 1.0], + [0.0, 1.0], + [0.0, 0.5], + ], + vec![[x0, y0], [x1, y0], [x1, y1], [x0, y1], [x0, y0]], + ) + } else { + let [cx, cy, rx, ry] = [x0 + w / 2.0, y0 + h / 2.0, w / 2.0, h / 2.0]; + ( + &[[0.5, 0.0], [1.0, 0.5], [0.5, 1.0], [0.0, 0.5]], + (0..=50) + .map(|step| { + let angle = step as f32 * std::f32::consts::TAU / 50.0; + [cx + rx * angle.cos(), cy + ry * angle.sin()] + }) + .collect(), + ) + }; + ( + InkShape::Closed { + transform, + anchors: anchors.to_vec(), + }, + vec![path], + ) + } + }; + let mut strokes = Vec::new(); + for path in paths { + strokes.push(InkStroke { + id: super::text::new_id()?, + points: path.into_iter().map(|p| p.map(snap)).collect(), + ..pen.clone() + }); + } + Ok(Self { + id: super::text::new_id()?, + layout: Layout::default(), + strokes, + groups: Vec::new(), + shape: Some(shape.snapped()), + }) + } +} + +/// What OneNote keeps beside a drawn shape's strokes to edit it by (`corpus/ink-tools`), in +/// page points before the drawing's offset. +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +pub enum InkShape { + /// A line or arrow from one end to the other. + Line([[f32; 2]; 2]), + /// A closed shape: `transform` (`[a, b, c, d, x, y]`) takes its anchors, in the unit + /// square, onto the page. + Closed { + transform: [f32; 6], + anchors: Vec<[f32; 2]>, + }, } #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] @@ -48,6 +161,10 @@ pub struct InkStroke { pub transparency: Option, /// 0 is a round (ball) tip, 1 a rectangle. pub pen_tip: Option, + /// The ISF raster operation: 9 (MaskPen) for a highlighter, whose colour multiplies + /// what lies beneath. + #[serde(default)] + pub raster_operation: Option, } impl Ink { @@ -61,6 +178,9 @@ impl Ink { data, scale_x, scale_y, + shape_kind, + line, + anchors, } = &node.kind else { unreachable!() @@ -98,6 +218,7 @@ impl Ink { layout: node.layout.clone(), strokes, groups, + shape: InkShape::read(*shape_kind, line.as_deref(), anchors.as_deref()), }) } @@ -151,6 +272,7 @@ impl InkStroke { color, transparency, pen_tip, + raster_operation, .. } = &style.kind else { @@ -190,10 +312,78 @@ impl InkStroke { color: *color, transparency: *transparency, pen_tip: *pen_tip, + raster_operation: *raster_operation, }) } } +impl InkShape { + /// Any geometry this does not recognise reads as no shape: the strokes still draw it. + fn read(kind: Option, line: Option<&[u8]>, anchors: Option<&[u8]>) -> Option { + let floats = |bytes: &[u8]| -> Vec { + bytes + .chunks_exact(4) + .map(|b| f32::from_le_bytes(b.try_into().unwrap())) + .collect() + }; + match (kind?, line, anchors) { + (11, Some(line), _) if line.len() == 16 => { + let v = floats(line); + Some(Self::Line([[v[0], v[1]], [v[2], v[3]]].map(|end| end.map(|v| v * 36.0)))) + .filter(Self::finite) + } + (12, _, Some(bytes)) if bytes.len() >= 24 && (bytes.len() - 24) % 8 == 0 => { + let v = floats(bytes); + Some(Self::Closed { + transform: std::array::from_fn(|i| v[i] * 36.0), + anchors: v[6..].chunks_exact(2).map(|p| [p[0], p[1]]).collect(), + }) + .filter(Self::finite) + } + _ => None, + } + } + + fn finite(&self) -> bool { + match self { + Self::Line(ends) => ends.iter().flatten().all(|v| v.is_finite()), + Self::Closed { transform, anchors } => transform + .iter() + .chain(anchors.iter().flatten()) + .all(|v| v.is_finite()), + } + } + + /// The kind, property and bytes OneNote stores the shape as. + pub(crate) fn stored(&self) -> (u8, u32, Vec) { + let half = |v: &f32| (v / 36.0).to_le_bytes(); + match self { + Self::Line(ends) => (11, 0x1c001dac, ends.iter().flatten().flat_map(half).collect()), + Self::Closed { transform, anchors } => ( + 12, + 0x1c001daa, + transform + .iter() + .flat_map(half) + .chain(anchors.iter().flatten().flat_map(|v| v.to_le_bytes())) + .collect(), + ), + } + } + + /// This shape as it reads back once stored. + pub fn snapped(&self) -> Self { + let half = |v: f32| (v / 36.0) * 36.0; + match self { + Self::Line(ends) => Self::Line(ends.map(|end| end.map(half))), + Self::Closed { transform, anchors } => Self::Closed { + transform: transform.map(half), + anchors: anchors.clone(), + }, + } + } +} + /// ISF multi-byte decoding: a count, then that many 7-bit little-endian varints whose low bit /// is the sign. fn multi_byte(bytes: &[u8]) -> Option> { @@ -268,6 +458,15 @@ pub(crate) const DIMENSIONS: [u8; 64] = [ 0x00, 0x00, 0x00, 0x00, 0x38, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x8b, 0xc5, 0xe2, 0x41, ]; +/// The dimension table OneNote 2010 writes for a drawn shape's pen: X and Y in HIMETRIC over +/// the whole 32-bit range, at a resolution of 1000. +pub(crate) const SHAPE_DIMENSIONS: [u8; 64] = [ + 0x8f, 0x6a, 0x8a, 0x59, 0xc0, 0x52, 0xa0, 0x4b, 0x93, 0xaf, 0xaf, 0x35, 0x74, 0x11, 0xa5, 0x61, + 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x7f, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x44, + 0x75, 0x9f, 0x3f, 0xb5, 0xe0, 0x04, 0x98, 0x44, 0xa7, 0xee, 0xc3, 0x0d, 0xbb, 0x5a, 0x90, 0x11, + 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x7f, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x44, +]; + #[cfg(test)] mod tests { use super::*; @@ -282,6 +481,7 @@ mod tests { color: None, transparency: None, pen_tip: None, + raster_operation: None, }; let values = multi_byte(&stroke.packet()).unwrap(); assert_eq!(values.len(), 6); diff --git a/crates/onestore/src/page/mod.rs b/crates/onestore/src/page/mod.rs index 54e6a9a648bd982a20eca80f23a3a5f643714b08..0dabb489978a4275ed06e2119b81d61524adfc95 100644 --- a/crates/onestore/src/page/mod.rs +++ b/crates/onestore/src/page/mod.rs @@ -282,21 +282,34 @@ pub struct Attachment { pub bytes: Option>, #[serde(with = "payload")] pub preview: Option>, - /// Set when the file is an audio or video recording OneNote captured; annotations on - /// the page refer to it by identity (`PageParagraph::media`). + /// Set when the file is audio or video OneNote plays; notes on the page refer to it by + /// identity (`PageParagraph::media`). pub recording: Option, } -/// A recording's identity and OneNote's type code for it. +/// A recording's identity, whether it is audio (1) or video (2), and its length. #[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct Recording { pub id: [u8; 16], pub kind: u32, + pub duration_ms: Option, +} + +impl Recording { + /// The kind of recording MS-ONE requires a file named `filename` to be, if any: audio + /// for .wma, .mp3 and .wav, video for .wmv, .avi and .mpg. + pub fn kind_of(filename: &str) -> Option { + let extension = filename.rsplit_once('.')?.1.to_ascii_lowercase(); + match extension.as_str() { + "wma" | "mp3" | "wav" => Some(1), + "wmv" | "avi" | "mpg" => Some(2), + _ => None, + } + } } /// A paragraph's link to a moment in recordings on the page: OneNote plays from -/// `time_ms` when the paragraph is chosen. Read from native pages and preserved through -/// edits; OneNote alone creates them while recording. +/// `time_ms` when the paragraph is chosen, and links what is written while it records. #[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct MediaIndex { pub recordings: Vec<[u8; 16]>, @@ -328,6 +341,7 @@ impl Attachment { source_path, recording_id, recording_type, + recording_duration, icon_width, icon_height, } = &node.kind @@ -357,7 +371,10 @@ impl Attachment { preview: payload(preview)?, recording: recording_id.map(|id| Recording { id, - kind: recording_type.unwrap_or(0), + kind: recording_type + .or_else(|| Recording::kind_of(filename.as_deref().unwrap_or_default())) + .unwrap_or(1), + duration_ms: *recording_duration, }), }) } @@ -1369,6 +1386,7 @@ mod tests { margin_origin_x: Some(36.0), margin_origin_y: Some(12.0), rtl: None, + recordings: Vec::new(), }); page.children.push(id(2)); let mut outline = element(Kind::Outline { @@ -1621,6 +1639,8 @@ mod tests { source.nodes.get_mut(&id(4)).unwrap().tags.push(Tag { definition: Some(id(7)), action_type: None, + shape: None, + property_status: None, status: 0, created: Some(123), completed: None, diff --git a/crates/onestore/tests/page_ink.rs b/crates/onestore/tests/page_ink.rs index 329e50b6662427da2472c06b69b0668b86b71b28..3d8faa34d709aad5d8c40148b6c9d5b48a9faedc 100644 --- a/crates/onestore/tests/page_ink.rs +++ b/crates/onestore/tests/page_ink.rs @@ -88,6 +88,7 @@ fn stroke(points: &[[f32; 2]], color: Option) -> InkStroke { color, transparency: None, pen_tip: None, + raster_operation: None, } } @@ -109,6 +110,7 @@ fn drawing() -> Ink { stroke(&[[300.0, 120.0], [360.0, 180.0]], Some(0x0000ff)), ], groups: Vec::new(), + shape: None, } } @@ -232,3 +234,372 @@ fn stored_strokes_keep_their_paths() { } assert!(ops::saved(NATIVE, space, &moved).is_err()); } + +/// OneNote 2010's Draw tab used with the mouse (`corpus/ink-tools/native-ui`): every favourite +/// pen and highlighter, shapes, and ink erased, moved and deleted. +const TOOLS: &[u8] = include_bytes!("../../../corpus/ink-tools/native-ui/notebook/ink.one"); + +fn titled(bytes: &[u8], title: &str) -> (ExGuid, Page) { + let store = Store::parse(bytes).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let document = Document::parse(&index).unwrap(); + document + .pages() + .unwrap() + .into_iter() + .map(|(space, _)| (space, Page::from_space(&document, space).unwrap())) + .find(|(_, page)| page.title == title) + .unwrap() +} + +fn himetric(value: f32) -> f32 { + value * 72.0 / 2540.0 +} + +use onestore::page::ink::{InkShape, ShapeKind}; + +#[test] +fn native_pens_highlighters_and_shapes_read_as_drawn() { + let (_, gallery) = titled(TOOLS, "Gallery"); + let pens: Vec<_> = drawings(&gallery) + .iter() + .map(|ink| { + let [stroke] = ink.strokes.as_slice() else { + panic!("OneNote keeps each stroke a drawing of its own") + }; + ( + (stroke.width / himetric(1.0)).round(), + (stroke.height / himetric(1.0)).round(), + stroke.color, + stroke.raster_operation, + ) + }) + .collect(); + let (red, blue, green, grey) = (0x241ced, 0xbb6531, 0x367d17, 0x808080); + let pen = |width, color| (width, width, color, None); + let marker = |color| (70.0, 400.0, Some(color), Some(9)); + assert_eq!( + pens, + [ + pen(35.0, None), + pen(35.0, Some(red)), + pen(35.0, Some(blue)), + pen(35.0, Some(green)), + pen(35.0, Some(grey)), + marker(0x00ffff), + marker(0xffff00), + pen(50.0, None), + pen(50.0, Some(red)), + pen(50.0, Some(blue)), + pen(50.0, Some(green)), + pen(50.0, Some(grey)), + marker(0x00ff00), + marker(0xff00ff), + ] + ); + let highlighter = &drawings(&gallery)[5].strokes[0]; + assert_eq!( + (highlighter.transparency, highlighter.pen_tip), + (Some(127), Some(1)) + ); + + let (_, pens) = titled(TOOLS, "Pens"); + let shapes: Vec<&Ink> = drawings(&pens) + .into_iter() + .filter(|ink| ink.shape.is_some()) + .collect(); + let [rectangle, line, arrow, ellipse] = shapes.as_slice() else { + panic!("{}", shapes.len()) + }; + let pen = InkStroke { + points: Vec::new(), + ..rectangle.strokes[0].clone() + }; + assert!((pen.width - himetric(50.0)).abs() < 1e-5); + let close_points = |a: &[[f32; 2]], b: &[[f32; 2]]| { + a.len() == b.len() + && a.iter() + .zip(b) + .all(|(a, b)| (a[0] - b[0]).abs() < 0.05 && (a[1] - b[1]).abs() < 0.05) + }; + for (native, kind, from, to) in [ + (rectangle, ShapeKind::Rectangle, [252.0, 86.4], [324.0, 122.4]), + (line, ShapeKind::Line, [342.0, 86.4], [414.0, 122.4]), + (arrow, ShapeKind::Arrow, [252.0, 158.4], [324.0, 194.4]), + (ellipse, ShapeKind::Ellipse, [342.0, 158.4], [414.0, 194.4]), + ] { + let drawn = Ink::drawn(kind, from, to, &pen).unwrap(); + assert_eq!(drawn.strokes.len(), native.strokes.len(), "{kind:?}"); + for (drawn, native) in drawn.strokes.iter().zip(&native.strokes) { + assert!( + close_points(&drawn.points, &native.points), + "{kind:?}: {:?} against {:?}", + drawn.points, + native.points + ); + } + match (drawn.shape.unwrap(), native.shape.clone().unwrap()) { + (InkShape::Line(a), InkShape::Line(b)) => assert!(close_points(&a, &b)), + ( + InkShape::Closed { transform, anchors }, + InkShape::Closed { + transform: native_transform, + anchors: native_anchors, + }, + ) => { + assert_eq!(anchors, native_anchors); + assert!( + transform + .iter() + .zip(native_transform) + .all(|(a, b)| (a - b).abs() < 0.05) + ); + } + shapes => panic!("{shapes:?}"), + } + } + + // Erased and deleted drawings leave the page; a moved one keeps its strokes and takes + // an offset. + let (_, edits) = titled(TOOLS, "Edits"); + let moved: Vec<_> = drawings(&edits) + .into_iter() + .filter(|ink| ink.shape.is_none()) + .map(|ink| (ink.layout.x, ink.layout.y)) + .collect(); + assert_eq!(moved.len(), 2); + assert!(moved.iter().any(|&(x, y)| x == Some(45.0) + && y.is_some_and(|y| (y - 15.0).abs() < 1e-4))); +} + +fn tool_page(source: &[u8]) -> (ExGuid, Page, Vec) { + let (space, page) = first_page(source); + let accent = InkStroke { + id: new_id().unwrap(), + points: Vec::new(), + width: snap(himetric(35.0)), + height: snap(himetric(35.0)), + color: Some(0x7a9a1f), + transparency: None, + pen_tip: None, + raster_operation: None, + }; + let marker = InkStroke { + width: himetric(70.0), + height: himetric(400.0), + color: Some(0x00ffff), + transparency: Some(127), + pen_tip: Some(1), + raster_operation: Some(9), + ..accent.clone() + }; + let shape_pen = InkStroke { + width: himetric(50.0), + height: himetric(50.0), + ..accent.clone() + }; + let free = |pen: &InkStroke, points: Vec<[f32; 2]>| Ink { + id: new_id().unwrap(), + layout: Default::default(), + strokes: vec![InkStroke { + id: new_id().unwrap(), + points: points.into_iter().map(|p| p.map(snap)).collect(), + ..pen.clone() + }], + groups: Vec::new(), + shape: None, + }; + let wave: Vec<[f32; 2]> = (0..=40) + .map(|step| { + let x = 60.0 + step as f32 * 4.0; + [x, 150.0 + 12.0 * (x / 18.0).sin()] + }) + .collect(); + let inks = vec![ + free(&marker, vec![[60.0, 190.0], [220.0, 190.0]]), + free(&accent, wave), + Ink::drawn(ShapeKind::Rectangle, [252.0, 140.4], [324.0, 194.4], &shape_pen).unwrap(), + Ink::drawn(ShapeKind::Ellipse, [342.0, 140.4], [432.0, 194.4], &shape_pen).unwrap(), + Ink::drawn(ShapeKind::Arrow, [252.0, 230.4], [360.0, 266.4], &shape_pen).unwrap(), + Ink::drawn(ShapeKind::Line, [378.0, 230.4], [450.0, 230.4], &shape_pen).unwrap(), + ]; + (space, page, inks) +} + +fn added(source: &[u8], space: ExGuid, inks: &[Ink]) -> Vec { + let mut bytes = source.to_vec(); + for ink in inks { + bytes = ops::page_edited( + &bytes, + space, + vec![onestore::op::PageOp::Add { + object: PageObject::Ink(ink.clone()), + before: None, + }], + ) + .unwrap(); + } + bytes +} + +/// `ONESTORE_INK_TOOLS_EXPORT` names a new directory receiving the candidate for a cold +/// reopen. +#[test] +fn pens_highlighters_and_shapes_are_written_as_onenote_draws_them() { + use onestore::document::{FieldValue, Kind}; + let source = onestore::create_section("ink.one", "Drawn in Snowbound", "Author").unwrap(); + let (space, before, inks) = tool_page(&source); + // Each stroke is one edit, as the canvas stores it. + let written = added(&source, space, &inks); + let stored = page_in(&written, space); + let mut expected = before.clone(); + expected + .objects + .extend(inks.iter().cloned().map(PageObject::Ink)); + expected.title = stored.title.clone(); + assert_eq!(stored, expected); + export("ONESTORE_INK_TOOLS_EXPORT", "ink.one", &written); + + let store = Store::parse(&written).unwrap(); + let index = RevisionIndex::parse(&store).unwrap(); + let document = Document::parse(&index).unwrap(); + let revision = document.active(space).unwrap(); + let node = |id: ExGuid| &revision.nodes[&id]; + let extra = |id: ExGuid, property: u32| { + node(id) + .extra + .iter() + .flatten() + .find(|field| field.id == property) + .map(|field| match field.value { + FieldValue::Bytes(bytes) => bytes.to_vec(), + _ => Vec::new(), + }) + }; + let style = |stroke: ExGuid| match node(stroke).kind { + Kind::InkStroke { + style: Some(style), + bias, + index, + .. + } => (node(style).kind.clone(), bias, index), + _ => panic!(), + }; + let [marker, wave, rectangle, _, arrow, _] = inks.as_slice() else { + unreachable!() + }; + match style(marker.strokes[0].id) { + ( + Kind::InkStyle { + raster_operation: Some(9), + pen_tip: Some(1), + transparency: Some(127), + ignore_pressure: Some(true), + .. + }, + Some(2), + Some(1), + ) => {} + other => panic!("{other:?}"), + } + match style(wave.strokes[0].id) { + ( + Kind::InkStyle { + raster_operation: None, + ignore_pressure: Some(true), + color: Some(0x7a9a1f), + .. + }, + Some(0), + Some(2), + ) => {} + other => panic!("{other:?}"), + } + for stroke in &arrow.strokes { + match style(stroke.id) { + ( + Kind::InkStyle { + ignore_pressure: None, + .. + }, + None, + None, + ) => {} + other => panic!("{other:?}"), + } + } + assert_eq!(extra(wave.id, 0x14001d4e), Some(1u32.to_le_bytes().to_vec())); + assert_eq!(extra(rectangle.id, 0x14001d4e), Some(2u32.to_le_bytes().to_vec())); + match &node(rectangle.id).kind { + Kind::Ink { + shape_kind: Some(12), + anchors: Some(anchors), + .. + } => assert_eq!(anchors.len(), 24 + 8 * 8), + other => panic!("{other:?}"), + } +} + +/// `ONESTORE_INK_TOOLS_EDIT_EXPORT` names a new directory receiving the candidate for a cold +/// reopen: OneNote's own drawings after Snowbound erases one, moves another and draws more. +#[test] +fn onenote_drawings_take_erasing_moving_and_more_strokes() { + use onestore::{OutlineEdit, op::PageOp}; + let (space, page) = titled(TOOLS, "Pens"); + let native = drawings(&page); + let zigzag = native + .iter() + .find(|ink| ink.strokes[0].color == Some(0x241ced)) + .unwrap() + .id; + let line = native[1].id; + let edited = ops::page_edited( + TOOLS, + space, + vec![ + PageOp::Delete { object: zigzag }, + PageOp::Outline { + object: line, + edit: OutlineEdit::Position { x: 36.0, y: 18.0 }, + }, + ], + ) + .unwrap(); + let (_, _, inks) = tool_page(&onestore::create_section("x.one", "x", "x").unwrap()); + let moved: Vec = inks + .into_iter() + .map(|mut ink| { + for stroke in &mut ink.strokes { + for point in &mut stroke.points { + point[1] = snap(point[1] + 200.0); + } + } + if let Some(shape) = &mut ink.shape { + *shape = match shape { + InkShape::Line([a, b]) => InkShape::Line([[a[0], a[1] + 200.0], [b[0], b[1] + 200.0]]), + InkShape::Closed { transform, anchors } => InkShape::Closed { + transform: { + let mut t = *transform; + t[5] += 200.0; + t + }, + anchors: anchors.clone(), + }, + } + .snapped(); + } + ink + }) + .collect(); + let written = added(&edited, space, &moved); + let stored = page_in(&written, space); + let found = drawings(&stored); + assert!(!found.iter().any(|ink| ink.id == zigzag)); + let line = found.iter().find(|ink| ink.id == line).unwrap(); + assert_eq!((line.layout.x, line.layout.y), (Some(36.0), Some(18.0))); + assert_eq!(line.strokes, native[1].strokes); + for ink in &moved { + assert_eq!(found.iter().find(|found| found.id == ink.id), Some(&ink)); + } + export("ONESTORE_INK_TOOLS_EDIT_EXPORT", "ink.one", &written); +} diff --git a/crates/onestore/tests/page_media.rs b/crates/onestore/tests/page_media.rs index c18be4ca6372b03407a962d7dce9f053837ab5dc..e465e939296c5691b9a8b9f96e20112bf0b0f93a 100644 --- a/crates/onestore/tests/page_media.rs +++ b/crates/onestore/tests/page_media.rs @@ -103,18 +103,29 @@ fn editing_a_recorded_page_keeps_the_recording_and_its_annotation() { assert_eq!(recording(&stored), recording(&before)); assert_eq!(stored.objects.len(), before.objects.len()); - let mut annotated = after.clone(); - let outline = annotated - .objects - .iter_mut() - .find_map(|object| match object { - PageObject::Outline(outline) if !outline.title => Some(outline), - _ => None, - }) - .unwrap(); - let last = outline.paragraphs.last_mut().unwrap(); - last.media = recording(&before).1; - assert!(ops::saved(written.as_slice(), space, &annotated).is_err()); + // A note linked to the recording, then unlinked, as Snowbound links notes taken while + // it records. + let annotate = |page: &Page, media: MediaIndex| { + let mut page = page.clone(); + for object in &mut page.objects { + if let PageObject::Outline(outline) = object + && !outline.title + { + outline.paragraphs.last_mut().unwrap().media = media.clone(); + } + } + page + }; + let linked = ops::saved( + written.as_slice(), + space, + &annotate(&stored, recording(&before).1), + ) + .unwrap(); + let (_, relinked) = page(linked.as_slice()); + assert_eq!(relinked, annotate(&stored, recording(&before).1)); + let unlinked = ops::saved(linked.as_slice(), space, &stored).unwrap(); + assert_eq!(page(unlinked.as_slice()).1, stored); let mut recaptured = after.clone(); for object in &mut recaptured.objects { if let PageObject::Outline(outline) = object { diff --git a/crates/onestore/tests/page_tags.rs b/crates/onestore/tests/page_tags.rs index eec322c51ca55e55bd0f5e1c8199fb83bda499e6..1f2c2d8d638bdbcec471a1308aae9f1c410778b8 100644 --- a/crates/onestore/tests/page_tags.rs +++ b/crates/onestore/tests/page_tags.rs @@ -113,6 +113,8 @@ fn tag(definition: ExGuid, completed: bool) -> Tag { Tag { definition: Some(definition), action_type: None, + shape: None, + property_status: None, status: u16::from(completed), created: Some(1_262_401_445), completed: completed.then_some(1_262_405_106), @@ -305,3 +307,49 @@ fn new_definitions_and_tags_publish_on_a_fresh_page() { .unwrap(); } } + +/// An Outlook task tag names no definition: rewritten beside a normal tag it keeps its icon, +/// property status and dates, and one missing its icon is refused. +#[test] +fn a_task_tag_rewrites_without_a_definition() { + let features = include_bytes!("../../../corpus/m6/native-features-01/notebook/Features.one"); + let (space, before) = page_by_title(features, "Task controls"); + let at = tagged(&before); + let task = body_paragraphs(&mut before.clone())[at] + .text() + .unwrap() + .tags[0] + .clone(); + assert_eq!( + ( + task.definition, + task.shape, + task.property_status, + task.status + ), + (None, Some(89), Some(0), 6) + ); + let mut after = before.clone(); + let id = new_id().unwrap(); + after.definitions.insert(id, definition("To Do", 3)); + body_paragraphs(&mut after)[at] + .text_mut() + .unwrap() + .tags + .insert(0, tag(id, false)); + let written = ops::saved(features, space, &after).unwrap(); + let stored = assert_same(written.as_slice(), space, &after); + let mut untagged = stored.clone(); + body_paragraphs(&mut untagged)[at] + .text_mut() + .unwrap() + .tags + .remove(0); + untagged.definitions.remove(&id); + let again = ops::saved(&written, space, &untagged).unwrap(); + assert_same(again.as_slice(), space, &before); + let mut shapeless = before.clone(); + body_paragraphs(&mut shapeless)[at].text_mut().unwrap().tags[0].shape = None; + body_paragraphs(&mut shapeless)[at].text_mut().unwrap().tags[0].created = Some(1); + assert!(ops::saved(features, space, &shapeless).is_err()); +} diff --git a/crates/onestore/tests/support/page_edits.rs b/crates/onestore/tests/support/page_edits.rs index 53718a2e02fe072b70ffe31f93b7dcffe64c86f6..dd9aa7507cfa66de67cc9725f840754b739265cc 100644 --- a/crates/onestore/tests/support/page_edits.rs +++ b/crates/onestore/tests/support/page_edits.rs @@ -256,6 +256,8 @@ fn mutate(page: &mut Page, bytes: &mut Bytes<'_>) { text.tags.push(onestore::document::Tag { definition: Some(definition), action_type: None, + shape: None, + property_status: None, status: u16::from(bytes.next().unwrap_or(0) % 2), created: Some(1_262_401_445), completed: None, diff --git a/crates/snowbound/src/library.rs b/crates/snowbound/src/library.rs index d5ffe0f22f05b120971698491cc4ef16139f6f1d..15dc47f8d7bfddc3548dce0c46fd3754cbeb08ef 100644 --- a/crates/snowbound/src/library.rs +++ b/crates/snowbound/src/library.rs @@ -33,10 +33,6 @@ pub fn on_background(notify: impl Fn() + Send + Sync + 'static) { let _ = NOTIFY.set(Box::new(notify)); } -/// How often the sections no tab shows are polled; OneNote 2010 read a closed section -/// fifteen seconds after another client changed it. -const BACKGROUND: Duration = Duration::from_secs(15); - fn notify_background() { if let Some(notify) = NOTIFY.get() { notify(); @@ -171,6 +167,8 @@ pub struct Library { /// Syncs the notebook's sections while no tab shows them, as OneNote syncs every /// section of an open notebook. pub background: Option>, + /// Reports the changes to a folder on this computer to `background`. + watch: Option>, } impl Library { @@ -192,10 +190,15 @@ impl Library { } } let notebook = Notebook::open(location, cache); + let (background, watch) = match ¬ebook { + Ok(notebook) => local_background(notebook, location), + Err(_) => (None, None), + }; Self { location: location.to_owned(), name: file_name(Path::new(location)), - background: notebook.as_ref().ok().and_then(local_background), + background, + watch, notebook: notebook.map(Some).map_err(|error| error.to_string()), server: None, notice, @@ -219,7 +222,6 @@ impl Library { let background = Background::smb( &server.mount.root, LIMIT, - BACKGROUND, move || connect.connect(), notify_background, ) @@ -234,6 +236,7 @@ impl Library { notice: None, cache: cache.to_owned(), background: Some(Arc::new(background)), + watch: None, }) } @@ -260,15 +263,18 @@ impl Library { notice: self.notice.clone(), cache: self.cache.clone(), background: self.background.clone(), + watch: self.watch.clone(), } } /// A notebook `Notebook::create` just made in the folder at `location`. pub fn created(location: &str, notebook: Notebook, cache: &Path) -> Self { + let (background, watch) = local_background(¬ebook, location); Self { location: location.to_owned(), name: file_name(Path::new(location)), - background: local_background(¬ebook), + background, + watch, notebook: Ok(Some(notebook)), server: None, notice: None, @@ -286,6 +292,7 @@ impl Library { notice: None, cache: cache.to_owned(), background: None, + watch: None, } } @@ -296,7 +303,8 @@ impl Library { path: &str, notify: impl Fn() + Send + 'static, ) -> Result> { - // The background may hold the replica for a step, which takes a network round trip. + // The background may hold the replica for a step, which takes a network round trip, + // or be making it as an offline copy. let deadline = Instant::now() + TIMEOUT * 3; let notify = Arc::new(Mutex::new(notify)); let notifier = || { @@ -335,7 +343,12 @@ impl Library { (Err(error), _) => return Err(error.clone().into()), }; match opened { - Err(error) if error.busy() && Instant::now() < deadline => { + Err(error) + if (error.busy() + || matches!(&error, notebook::Error::Io(error) + if error.kind() == io::ErrorKind::AlreadyExists)) + && Instant::now() < deadline => + { std::thread::sleep(Duration::from_millis(50)); } opened => break opened?, @@ -446,15 +459,37 @@ pub fn recycle_bin(path: &str) -> bool { path.rsplit('/').next() == Some("OneNote_RecycleBin") } -/// A mounted notebook's background sync, following Work Offline. -fn local_background(notebook: &Notebook) -> Option> { - let background = notebook - .background(BACKGROUND, notify_background) +/// A mounted notebook's background sync, following Work Offline, and the watch that reports +/// the folder's changes to it. Only a folder on this computer reports every change; one on a +/// network volume is checked more often and kept in offline copies, as on a share. +fn local_background( + notebook: &Notebook, + location: &str, +) -> (Option>, Option>) { + let reports = Arc::new(OnceLock::>::new()); + let report = Arc::clone(&reports); + let watch = crate::watch::watch(Path::new(location), move |paths| { + if let Some(background) = report.get().and_then(std::sync::Weak::upgrade) { + background.touched(&paths); + } + }); + let remote = !crate::watch::on_this_computer(Path::new(location)); + let interval = match watch { + Some(_) if !remote => Background::BACKSTOP, + _ => Background::UNWATCHED, + }; + let Some(background) = notebook + .background(interval, remote, notify_background) .inspect_err(|error| eprintln!("Background sync did not start: {error}")) - .ok()?; + .ok() + else { + return (None, None); + }; background.set_offline(offline()); background.watch(notebook.replicas()); - Some(Arc::new(background)) + let background = Arc::new(background); + let _ = reports.set(Arc::downgrade(&background)); + (Some(background), watch.map(Arc::new)) } pub fn file_name(path: &Path) -> String { @@ -483,7 +518,7 @@ fn tabs(catalog: &Folder) -> Vec { .sections .iter() .filter_map(|section| match §ion.state { - SectionState::Readable { name, color } => Some(Tab { + SectionState::Readable { name, color, .. } => Some(Tab { name: section_name(§ion.path, name), path: section.path.clone(), color: *color, @@ -668,6 +703,7 @@ mod tests { notice: None, cache: PathBuf::new(), background: None, + watch: None, }; let shown = |root, file| library(root).local(Path::new(file)); let under = Some(PathBuf::from("/Volumes/agent/lab/Group/New Section 1.one")); diff --git a/crates/snowbound/src/linux.rs b/crates/snowbound/src/linux.rs index a700797a0f7b2cb98b94398d14cac4f70b633c47..396c5b0e28a9b8d4bd37ef31947e5af0998a8484 100644 --- a/crates/snowbound/src/linux.rs +++ b/crates/snowbound/src/linux.rs @@ -25,6 +25,8 @@ use winit::{ const APP_ID: &str = "snowbound"; /// The title bar's leading margin; the window controls sit at its trailing end. pub const LEADING: f32 = 8.0; +/// The margin past the window controls. +pub const TRAILING: f32 = 8.0; /// How far inside the window's edges a press resizes it, and how far along them a corner /// reaches, in logical pixels. const EDGE: f32 = 5.0; diff --git a/crates/snowbound/src/macos.rs b/crates/snowbound/src/macos.rs index d4fccd8adaf5efa67099198b7b6e2675c6756482..29464bf36949dba8d816b49dc439045a3f3a7171 100644 --- a/crates/snowbound/src/macos.rs +++ b/crates/snowbound/src/macos.rs @@ -86,6 +86,8 @@ fn ns_window(window: &Window) -> Retained { /// Room the traffic lights take at the title bar's leading edge. pub const LEADING: f32 = 78.0; +/// The title bar's trailing margin, the gap the traffic lights leave before the toolbar. +pub const TRAILING: f32 = 12.0; /// How far AppKit rounds a window's corners. pub const CORNER_RADIUS: f32 = 10.0; diff --git a/crates/snowbound/src/main.rs b/crates/snowbound/src/main.rs index 21289acf0965534b1da6def0f7372e5f022a7b95..bea24a9a40cf6a7735f348210cf34fa8ebfae38c 100644 --- a/crates/snowbound/src/main.rs +++ b/crates/snowbound/src/main.rs @@ -31,6 +31,7 @@ mod surface; mod sync; mod tags; mod templates; +mod watch; use canvas::gpu::page::PageScene; use canvas::interaction::{Cursor, PageView, Place, Request, Response, TextColors, accessibility}; @@ -1200,6 +1201,13 @@ impl State { if self.session.is_some() { self.page_tools(&theme); } + self.ui.leaf( + "trail", + Spec { + size: [px(self.trailing() - FRAME), px(1.0)], + ..Spec::default() + }, + ); self.ui.close(); self.ui.open_as( @@ -1268,6 +1276,23 @@ impl State { } } self.ui.close(); + if let Some(task) = self.view.task_under_pointer() { + let [left, top, ..] = self.ui.rect(page()).unwrap_or_default(); + let scale = self.ui.scale(); + let [x0, y0, x1, y1] = task; + let part = [ + left + x0 / scale, + top + y0 / scale, + left + x1 / scale, + top + y1 / scale, + ]; + ui::popup::tooltip_over( + &mut self.ui, + part, + "Outlook task", + Some("Edit it in OneNote with Outlook"), + ); + } let signal = self.ui.signal(page()); let focused = self.ui.window_focused && self.ui.focused() == Some(page()); if focused != self.page_focused { @@ -1411,6 +1436,16 @@ impl State { ); } + /// The toolbar and tab row's margin at the window's trailing side, which matches the + /// leading gap where the row is the title bar. + fn trailing(&self) -> f32 { + if platform::system_titlebar(&self.window) { + FRAME + } else { + platform::TRAILING + } + } + /// Whether the chrome's empty space drags the window: where it is the title bar, or /// over the title's gradient. fn chrome_drags(&self) -> bool { @@ -1468,7 +1503,7 @@ impl State { self.ui.leaf( "trail", Spec { - size: [px(FRAME - GAP), px(1.0)], + size: [px(self.trailing() - GAP), px(1.0)], ..Spec::default() }, ); diff --git a/crates/snowbound/src/manage.rs b/crates/snowbound/src/manage.rs index 48fcc4edfc83221b32b995d19c257721598b5dc7..6df5eb3400a46d1b973e624a7aa84217d9388847 100644 --- a/crates/snowbound/src/manage.rs +++ b/crates/snowbound/src/manage.rs @@ -189,9 +189,13 @@ impl State { Ok(()) } - /// Closes `library`: its files stay, and the sidebar and the next launch leave it out. + /// Closes `library`: its files stay, its offline copies go unless edits wait in them, + /// and the sidebar and the next launch leave it out. pub(crate) fn close_notebook(&mut self, library: &Arc) { self.notebooks.retain(|open| !Arc::ptr_eq(open, library)); + if let Some(background) = &library.background { + background.discard(); + } let shown = |shown: &Arc| shown.location == library.location; if self.sectionless.as_ref().is_some_and(shown) || self diff --git a/crates/snowbound/src/meeting.rs b/crates/snowbound/src/meeting.rs index f289ef2cb784afc795a94e92106c6cb3593db422..e2c3eb2d816872860f97450788a2e95cf38d02b2 100644 --- a/crates/snowbound/src/meeting.rs +++ b/crates/snowbound/src/meeting.rs @@ -164,6 +164,8 @@ pub fn content(page: &Page) -> Result> { vec![Tag { definition: Some(to_do), action_type: None, + shape: None, + property_status: None, status: 0, created: Some(created), completed: Some(0), diff --git a/crates/snowbound/src/sidebar.rs b/crates/snowbound/src/sidebar.rs index 939124e4bf13141c52146d4c54ade3c7f4da925f..424fba2f1cd2c9a6875fe69b4d07b844363631ce 100644 --- a/crates/snowbound/src/sidebar.rs +++ b/crates/snowbound/src/sidebar.rs @@ -260,7 +260,7 @@ fn folder( }; for section in &folder.sections { let (name, color, readable) = match §ion.state { - SectionState::Readable { name, color } => ( + SectionState::Readable { name, color, .. } => ( crate::library::section_name(§ion.path, name), *color, true, diff --git a/crates/snowbound/src/tags.rs b/crates/snowbound/src/tags.rs index 48caad352c92fb43c36cb95572541b638a35d51a..b4a5113514e2788b29e377d2738795216d2e9c85 100644 --- a/crates/snowbound/src/tags.rs +++ b/crates/snowbound/src/tags.rs @@ -103,13 +103,13 @@ fn label(ui: &mut Ui, part: &str, text: &str, tag: &NoteTag) { ); } -fn icon(ui: &mut Ui, part: &str, art: Option<&'static [&'static str]>) { +fn icon(ui: &mut Ui, part: &str, art: Option<&'static [&'static str]>, tint: [f32; 4]) { ui.leaf( part, Spec { size: [px(SIDE + 4.0), px(ROW)], icon: art, - color: Some([1.0; 4]), + color: Some(tint), center: true, ..Spec::default() }, @@ -324,7 +324,11 @@ impl State { ..Spec::default() }, ); - icon(ui, "icon", artwork(tag)); + // OneNote's list marks a tag without a symbol with its "None" symbol art. + match artwork(tag) { + Some(art) => icon(ui, "icon", Some(art), [1.0; 4]), + None => icon(ui, "icon", Some(crate::art::FONT_COLOR), theme.text), + } let text = match commands::tag_chord(place) { Some(chord) => format!("{} ({})", tag.label, chord.label(Platform::CURRENT)), None => tag.label.clone(), @@ -585,7 +589,7 @@ fn tag_editor( ..Spec::default() }, ); - icon(ui, "icon", artwork(tag)); + icon(ui, "icon", artwork(tag), [1.0; 4]); label(ui, "label", &tag.label, tag); ui.leaf( "end", diff --git a/crates/snowbound/src/watch.rs b/crates/snowbound/src/watch.rs new file mode 100644 index 0000000000000000000000000000000000000000..d6d95ad4c2e099a3a16d61a22f2a003dd66814fe --- /dev/null +++ b/crates/snowbound/src/watch.rs @@ -0,0 +1,441 @@ +//! Changes below a notebook folder on this computer, as the system reports them: FSEvents on +//! macOS, inotify on Linux. A notebook on a share learns of its changes from the server +//! instead (`notebook::session::Background::smb`). + +use std::path::Path; + +/// Reports until dropped. +pub struct Watch { + _stream: platform::Stream, +} + +/// Watches the folder at `root`: `changed` hears the paths that changed below it, relative to +/// it and `/`-separated, or `""` when the system can only say something did. `None` where the +/// system cannot watch it. +pub fn watch(root: &Path, changed: impl Fn(Vec) + Send + Sync + 'static) -> Option { + let root = root.canonicalize().ok()?; + let folder = root.clone(); + let relative = move |paths: Vec| { + changed( + paths + .iter() + .map(|path| match path.strip_prefix(&folder) { + Ok(path) => path.to_string_lossy().replace('\\', "/"), + Err(_) => String::new(), + }) + .collect(), + ) + }; + platform::Stream::start(&root, Box::new(relative)) + .inspect_err(|error| eprintln!("{}: changes go unwatched: {error}", root.display())) + .ok() + .map(|stream| Watch { _stream: stream }) +} + +/// Whether the folder at `path` is on this computer's own disks, where every change to it is +/// one the system reports, rather than on a network volume. +pub fn on_this_computer(path: &Path) -> bool { + use std::os::unix::ffi::OsStrExt; + let Ok(name) = std::ffi::CString::new(path.as_os_str().as_bytes()) else { + return true; + }; + let mut volume: libc::statfs = unsafe { std::mem::zeroed() }; + if unsafe { libc::statfs(name.as_ptr(), &mut volume) } != 0 { + return true; + } + #[cfg(target_os = "macos")] + return volume.f_flags & libc::MNT_LOCAL as u32 != 0; + // NFS, SMB, CIFS, SMB2, FUSE (sshfs and the like), 9P, Ceph, AFS. + #[cfg(target_os = "linux")] + return ![ + 0x6969, + 0x517b, + 0xff53_4d42, + 0xfe53_4d42, + 0x6573_5546, + 0x0102_1997, + 0x00c3_6400, + 0x5346_414f, + ] + .contains(&(volume.f_type as u64)); +} + +type Changed = Box) + Send + Sync>; + +#[cfg(target_os = "macos")] +mod platform { + use super::Changed; + use std::{ + ffi::{CStr, c_char, c_void}, + io, + os::unix::ffi::OsStrExt, + path::{Path, PathBuf}, + }; + + type Stream_ = *mut c_void; + + #[repr(C)] + struct Context { + version: isize, + info: *mut c_void, + retain: *const c_void, + release: Option, + description: *const c_void, + } + + type Callback = + unsafe extern "C" fn(Stream_, *mut c_void, usize, *mut c_void, *const u32, *const u64); + + #[link(name = "CoreServices", kind = "framework")] + unsafe extern "C" { + fn FSEventStreamCreate( + allocator: *const c_void, + callback: Callback, + context: *const Context, + paths: *const c_void, + since: u64, + latency: f64, + flags: u32, + ) -> Stream_; + fn FSEventStreamSetDispatchQueue(stream: Stream_, queue: *mut c_void); + fn FSEventStreamStart(stream: Stream_) -> u8; + fn FSEventStreamStop(stream: Stream_); + fn FSEventStreamInvalidate(stream: Stream_); + fn FSEventStreamRelease(stream: Stream_); + } + + #[link(name = "CoreFoundation", kind = "framework")] + unsafe extern "C" { + static kCFTypeArrayCallBacks: c_void; + fn CFStringCreateWithBytes( + allocator: *const c_void, + bytes: *const u8, + length: isize, + encoding: u32, + external: u8, + ) -> *const c_void; + fn CFArrayCreate( + allocator: *const c_void, + values: *const *const c_void, + count: isize, + callbacks: *const c_void, + ) -> *const c_void; + fn CFRelease(object: *const c_void); + } + + unsafe extern "C" { + fn dispatch_queue_create(label: *const c_char, attributes: *const c_void) -> *mut c_void; + fn dispatch_sync_f( + queue: *mut c_void, + context: *mut c_void, + work: extern "C" fn(*mut c_void), + ); + fn dispatch_release(object: *mut c_void); + } + + const SINCE_NOW: u64 = u64::MAX; + const NO_DEFER: u32 = 0x2; + const WATCH_ROOT: u32 = 0x4; + /// Mac OS X 10.7 and later; 10.6 reports the folders that changed. + const FILE_EVENTS: u32 = 0x10; + const UTF8: u32 = 0x0800_0100; + + pub struct Stream { + stream: Stream_, + queue: *mut c_void, + } + + // The stream and queue are only started and torn down, each call safe from any thread. + unsafe impl Send for Stream {} + unsafe impl Sync for Stream {} + + unsafe extern "C" fn events( + _: Stream_, + info: *mut c_void, + count: usize, + paths: *mut c_void, + _: *const u32, + _: *const u64, + ) { + let changed = unsafe { &*(info as *const Changed) }; + let paths = unsafe { std::slice::from_raw_parts(paths as *const *const c_char, count) }; + changed( + paths + .iter() + .map(|&path| { + let path = unsafe { CStr::from_ptr(path) }; + PathBuf::from(std::ffi::OsStr::from_bytes(path.to_bytes())) + }) + .collect(), + ); + } + + unsafe extern "C" fn release(info: *const c_void) { + drop(unsafe { Box::from_raw(info as *mut Changed) }); + } + + extern "C" fn drained(_: *mut c_void) {} + + /// Whether this is Mac OS X 10.7 or later (Darwin 11). + fn file_events() -> bool { + let mut name: libc::utsname = unsafe { std::mem::zeroed() }; + (unsafe { libc::uname(&mut name) }) == 0 + && unsafe { CStr::from_ptr(name.release.as_ptr()) } + .to_str() + .ok() + .and_then(|release| release.split('.').next()?.parse::().ok()) + .is_some_and(|major| major >= 11) + } + + impl Stream { + pub fn start(root: &Path, changed: Changed) -> io::Result { + let root = root.as_os_str().as_bytes(); + let flags = NO_DEFER | WATCH_ROOT | if file_events() { FILE_EVENTS } else { 0 }; + unsafe { + let path = CFStringCreateWithBytes( + std::ptr::null(), + root.as_ptr(), + root.len() as isize, + UTF8, + 0, + ); + let paths = CFArrayCreate(std::ptr::null(), &path, 1, &kCFTypeArrayCallBacks); + CFRelease(path); + let context = Context { + version: 0, + info: Box::into_raw(Box::new(changed)).cast(), + retain: std::ptr::null(), + release: Some(release), + description: std::ptr::null(), + }; + let stream = FSEventStreamCreate( + std::ptr::null(), + events, + &context, + paths, + SINCE_NOW, + 0.3, + flags, + ); + CFRelease(paths); + if stream.is_null() { + release(context.info); + return Err(io::Error::other("FSEvents refused the folder")); + } + let queue = dispatch_queue_create(c"snowbound.watch".as_ptr(), std::ptr::null()); + FSEventStreamSetDispatchQueue(stream, queue); + let stream = Self { stream, queue }; + if FSEventStreamStart(stream.stream) == 0 { + return Err(io::Error::other("FSEvents did not start")); + } + Ok(stream) + } + } + } + + impl Drop for Stream { + fn drop(&mut self) { + unsafe { + FSEventStreamStop(self.stream); + FSEventStreamInvalidate(self.stream); + // A callback already on the queue finishes before the closure goes. + dispatch_sync_f(self.queue, std::ptr::null_mut(), drained); + FSEventStreamRelease(self.stream); + dispatch_release(self.queue); + } + } + } +} + +#[cfg(target_os = "linux")] +mod platform { + use super::Changed; + use std::{ + collections::HashMap, + ffi::CString, + io, + os::unix::ffi::OsStrExt, + path::{Path, PathBuf}, + thread::JoinHandle, + }; + + const MASK: u32 = libc::IN_MODIFY + | libc::IN_CLOSE_WRITE + | libc::IN_ATTRIB + | libc::IN_CREATE + | libc::IN_DELETE + | libc::IN_MOVED_FROM + | libc::IN_MOVED_TO + | libc::IN_DELETE_SELF + | libc::IN_MOVE_SELF; + + /// inotify watches one folder at a time, so every folder below the root has its own. + pub struct Stream { + inotify: i32, + stop: i32, + thread: Option>, + } + + fn check(result: i32) -> io::Result { + if result < 0 { + Err(io::Error::last_os_error()) + } else { + Ok(result) + } + } + + fn add(inotify: i32, folder: &Path, folders: &mut HashMap) { + let Ok(name) = CString::new(folder.as_os_str().as_bytes()) else { + return; + }; + if let Ok(watch) = check(unsafe { libc::inotify_add_watch(inotify, name.as_ptr(), MASK) }) { + folders.insert(watch, folder.to_owned()); + } + for entry in std::fs::read_dir(folder).into_iter().flatten().flatten() { + if entry.file_type().is_ok_and(|kind| kind.is_dir()) { + add(inotify, &entry.path(), folders); + } + } + } + + impl Stream { + pub fn start(root: &Path, changed: Changed) -> io::Result { + let inotify = check(unsafe { libc::inotify_init1(libc::IN_CLOEXEC) })?; + let stop = match check(unsafe { libc::eventfd(0, libc::EFD_CLOEXEC) }) { + Ok(stop) => stop, + Err(error) => { + unsafe { libc::close(inotify) }; + return Err(error); + } + }; + let mut folders = HashMap::new(); + add(inotify, root, &mut folders); + let root = root.to_owned(); + let thread = std::thread::Builder::new() + .name("snowbound-watch".into()) + .spawn(move || { + let mut buffer = vec![0u8; 64 * 1024]; + loop { + let mut polled = [ + libc::pollfd { + fd: inotify, + events: libc::POLLIN, + revents: 0, + }, + libc::pollfd { + fd: stop, + events: libc::POLLIN, + revents: 0, + }, + ]; + if unsafe { libc::poll(polled.as_mut_ptr(), 2, -1) } < 0 { + if io::Error::last_os_error().kind() == io::ErrorKind::Interrupted { + continue; + } + return; + } + if polled[1].revents != 0 { + return; + } + let read = unsafe { + libc::read(inotify, buffer.as_mut_ptr().cast(), buffer.len()) + }; + let Ok(read) = usize::try_from(read) else { + return; + }; + let mut paths = Vec::new(); + let mut at = 0; + while at + std::mem::size_of::() <= read { + let event: libc::inotify_event = + unsafe { std::ptr::read_unaligned(buffer[at..].as_ptr().cast()) }; + let start = at + std::mem::size_of::(); + let end = (start + event.len as usize).min(read); + at = end; + if event.mask & libc::IN_Q_OVERFLOW != 0 { + paths.push(root.clone()); + continue; + } + let Some(folder) = folders.get(&event.wd).cloned() else { + continue; + }; + let name = &buffer[start..end]; + let name = + &name[..name.iter().position(|&b| b == 0).unwrap_or(name.len())]; + let path = if name.is_empty() { + folder + } else { + folder.join(std::ffi::OsStr::from_bytes(name)) + }; + if event.mask & libc::IN_ISDIR != 0 + && event.mask & (libc::IN_CREATE | libc::IN_MOVED_TO) != 0 + { + add(inotify, &path, &mut folders); + } + if event.mask & libc::IN_IGNORED != 0 { + folders.remove(&event.wd); + } + paths.push(path); + } + if !paths.is_empty() { + changed(paths); + } + } + })?; + Ok(Self { + inotify, + stop, + thread: Some(thread), + }) + } + } + + impl Drop for Stream { + fn drop(&mut self) { + unsafe { libc::write(self.stop, (&1u64 as *const u64).cast(), 8) }; + if let Some(thread) = self.thread.take() { + let _ = thread.join(); + } + unsafe { + libc::close(self.inotify); + libc::close(self.stop); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::{sync::mpsc, time::Duration}; + + #[test] + fn a_folder_on_this_computer_reports_the_file_that_changed() { + let folder = std::env::temp_dir().join(format!("snowbound-watch-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&folder); + std::fs::create_dir_all(folder.join("Group")).unwrap(); + std::fs::write(folder.join("Group/Section.one"), b"before").unwrap(); + assert!(on_this_computer(&folder)); + let (sender, changes) = mpsc::channel(); + let sender = std::sync::Mutex::new(sender); + let watching = watch(&folder, move |paths| { + let _ = sender.lock().unwrap().send(paths); + }) + .unwrap(); + // FSEvents starts reporting a moment after the stream starts. + std::thread::sleep(Duration::from_millis(500)); + std::fs::write(folder.join("Group/Section.one"), b"after").unwrap(); + let mut reported = Vec::new(); + while let Ok(paths) = changes.recv_timeout(Duration::from_secs(5)) { + reported.extend(paths); + if reported.iter().any(|path| path == "Group/Section.one") { + break; + } + } + drop(watching); + std::fs::remove_dir_all(&folder).unwrap(); + assert!( + reported.iter().any(|path| path == "Group/Section.one"), + "{reported:?}" + ); + } +} diff --git a/crates/ui/src/popup.rs b/crates/ui/src/popup.rs index 10c173f9c128fedcae9fd22afc2cea9bd8cbbfad..577dd9450f4a640d90450c2b9b1c28d2bb8f6553 100644 --- a/crates/ui/src/popup.rs +++ b/crates/ui/src/popup.rs @@ -138,6 +138,22 @@ pub fn menu( /// delay, or at once while another has just shown. A press or the wheel hides it until the /// pointer leaves. pub fn tooltip(ui: &mut Ui, title: &str, keys: &str, description: Option<&str>) { + tooltip_below(ui, None, title, keys, description); +} + +/// A `tooltip` below `part`, a window rectangle within the box built last, as a custom +/// box names what it draws. +pub fn tooltip_over(ui: &mut Ui, part: [f32; 4], title: &str, description: Option<&str>) { + tooltip_below(ui, Some(part), title, "", description); +} + +fn tooltip_below( + ui: &mut Ui, + part: Option<[f32; 4]>, + title: &str, + keys: &str, + description: Option<&str>, +) { let Some(&index) = ui.nodes[*ui.stack.last().unwrap()].children.last() else { return; }; @@ -183,7 +199,7 @@ pub fn tooltip(ui: &mut Ui, title: &str, keys: &str, description: Option<&str>) } else { format!("{title} ({keys})") }; - let [left, top, right, bottom] = ui.rect(id).unwrap_or_default(); + let [left, top, right, bottom] = part.or(ui.rect(id)).unwrap_or_default(); let theme = &ui.theme; let spec = Spec { axis: Axis::Y, diff --git a/tools/test_task_tags.py b/tools/test_task_tags.py new file mode 100644 index 0000000000000000000000000000000000000000..5fc8cc24949ac95e4f4f4f5cbfd786532cf23a2b --- /dev/null +++ b/tools/test_task_tags.py @@ -0,0 +1,52 @@ +from pathlib import Path +import re +import runpy +import shutil +from tempfile import TemporaryDirectory +import unittest +import xml.etree.ElementTree as ET + +from native_xml import ns + +ROOT = Path(__file__).resolve().parent.parent +FIXTURE = ROOT / 'corpus/task-tags' +compare = runpy.run_path(str(ROOT / 'tools/verify-document.py'))['compare'] + + +def tasks(read): + """Each page's paragraphs by title: their text, task identities and normal tag count.""" + result = {} + for path in sorted(read.glob('page-*.xml')): + page = ET.parse(path).getroot() + title = page.find('one:Title/one:OE/one:T', ns) + if title is None: + continue + result[title.text] = [(re.sub(r'<[^>]*>', '', oe.find('one:T', ns).text or ''), + [(t.get('guidTask'), t.get('disabled'), t.get('startDate'), t.get('dueDate')) + for t in oe.findall('one:OutlookTask', ns)], + len(oe.findall('one:Tag', ns))) + for oe in page.find('one:Outline', ns).iter('{%s}OE' % ns['one'])] + return result + + +class TaskTagsTest(unittest.TestCase): + def test_removed_and_restored_task_tags_reopen_natively(self): + with TemporaryDirectory() as temporary: + read = Path(temporary) / 'read' + shutil.copytree(FIXTURE / 'cold/read', read) + compare(FIXTURE / 'candidate', read) + native = tasks(FIXTURE / 'native/read') + cold = tasks(FIXTURE / 'cold/read') + self.assertEqual(cold['Delete and undo'], native['Delete and undo']) + removed = native['Remove a task'] + self.assertEqual([len(t) for _, t, _ in removed], [1, 1, 1, 0]) + self.assertEqual(cold['Remove a task'], [ + (removed[0][0], [], 0), + removed[1], + (removed[2][0], removed[2][1], 0), + removed[3], + ]) + + +if __name__ == '__main__': + unittest.main() -- 2.54.0