desktop: Import and Export work offline; the menu toggle is reachable; errors say why
CI & Build / Build now, or wait for Android? (push) Successful in 4s
Android / Build, or is the channel already serving this? (push) Successful in 4s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Web typecheck and unit tests (push) Successful in 12s
Desktop (Tauri) / Build, or is the channel already serving this? (push) Successful in 2s
CI & Build / Python tests (push) Successful in 13s
CI & Build / integration (push) Successful in 48s
CI & Build / Build & push image (push) Skipped
Desktop (Tauri) / Web tests, clippy, Rust tests and rustfmt (push) Successful in 4m26s
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 3m32s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 4m10s
Desktop (Tauri) / Update manifest (push) Successful in 4s
Android / Kotlin + Rust (APK) (push) Successful in 12m42s
CI & Build / Build now, or wait for Android? (push) Successful in 4s
Android / Build, or is the channel already serving this? (push) Successful in 4s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Web typecheck and unit tests (push) Successful in 12s
Desktop (Tauri) / Build, or is the channel already serving this? (push) Successful in 2s
CI & Build / Python tests (push) Successful in 13s
CI & Build / integration (push) Successful in 48s
CI & Build / Build & push image (push) Skipped
Desktop (Tauri) / Web tests, clippy, Rust tests and rustfmt (push) Successful in 4m26s
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 3m32s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 4m10s
Desktop (Tauri) / Update manifest (push) Successful in 4s
Android / Kotlin + Rust (APK) (push) Successful in 12m42s
Export and Import were a link to the server and a reject("needs a server") on the
desktop. Both now run in the core with no server:
- core/src/local/portable.rs builds the same zip the server writes (notes.json,
a Markdown file per note, each attachment this device holds) and reads either
export marker or a Google Keep Takeout zip, with the server's decompression
budget and an all-or-nothing transaction. Export saves to Downloads (no new
plugin) and the sidebar says where; Import takes the archive as raw IPC bytes.
- core/testdata/portable.json pins the format for both copies: the server runs
its Keep and native readers against it (test_portable_fixture.py) and checks
its real export's keys (test_integration.py); the core runs the same cases.
- Found on the way: both importers skipped a Keep note that is only a photo as
"empty". It now imports, on the server and in the core.
- New dependency, approved: `zip` (deflate only) plus `flate2` on its pure-Rust
backend, both already in the lockfile.
The AppImage applications-menu toggle moves from Account, which the desktop
never shows, to the Sync page; the first-run prompt now says so.
errorMessage (#5236) replaces the hand-rolled `.error ?? …` / `.message ?? e`
reads at the remaining catch sites, so a desktop failure shows its real reason.
Task #5170.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vendored
+146
@@ -0,0 +1,146 @@
|
||||
{
|
||||
"_about": [
|
||||
"The export/import format, pinned for every implementation of it.",
|
||||
"Read by the server (tests/test_portable_fixture.py, and the export check in test_integration.py) and by the core (local/portable.rs). The desktop's offline Export and Import are the core's copy; a backup taken on one surface has to restore on the other.",
|
||||
"`keep` and `native` are notes as they arrive, with the spec each importer must make of them. `export` names the keys an export writes, at the top of notes.json and on each note. A difference between the server and the core here is a backup that loses something on the way across."
|
||||
],
|
||||
"keep": [
|
||||
{
|
||||
"about": "A list note keeps its text as well as its items; annotation URLs are folded into the body; the colour is read past.",
|
||||
"dir": "Takeout/Keep",
|
||||
"note": {
|
||||
"title": "Groceries",
|
||||
"textContent": "for the weekend",
|
||||
"listContent": [
|
||||
{ "text": "Milk", "isChecked": false },
|
||||
{ "text": "Eggs", "isChecked": true }
|
||||
],
|
||||
"annotations": [{ "url": "https://example.com/recipe" }],
|
||||
"labels": [{ "name": "shopping" }],
|
||||
"color": "TEAL",
|
||||
"isPinned": true,
|
||||
"isArchived": false,
|
||||
"isTrashed": false,
|
||||
"createdTimestampUsec": 1600000000000000,
|
||||
"userEditedTimestampUsec": 1600000100000000,
|
||||
"attachments": [
|
||||
{ "filePath": "photo.jpg", "mimetype": "image/jpeg" },
|
||||
{ "filePath": "scan.png" }
|
||||
]
|
||||
},
|
||||
"spec": {
|
||||
"title": "Groceries",
|
||||
"body": "for the weekend\n\nhttps://example.com/recipe",
|
||||
"pinned": true,
|
||||
"archived": false,
|
||||
"trashed": false,
|
||||
"labels": ["shopping"],
|
||||
"items": [
|
||||
{ "text": "Milk", "checked": false },
|
||||
{ "text": "Eggs", "checked": true }
|
||||
],
|
||||
"attachments": [
|
||||
{ "file": "Takeout/Keep/photo.jpg", "mime": "image/jpeg" },
|
||||
{ "file": "Takeout/Keep/scan.png", "mime": "image/png" }
|
||||
],
|
||||
"created_at": "2020-09-13T12:26:40Z",
|
||||
"updated_at": "2020-09-13T12:28:20Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"about": "A trashed, archived text note with no timestamps.",
|
||||
"dir": "",
|
||||
"note": {
|
||||
"textContent": "old idea",
|
||||
"isArchived": true,
|
||||
"isTrashed": true
|
||||
},
|
||||
"spec": {
|
||||
"title": null,
|
||||
"body": "old idea",
|
||||
"pinned": false,
|
||||
"archived": true,
|
||||
"trashed": true,
|
||||
"labels": [],
|
||||
"items": [],
|
||||
"attachments": [],
|
||||
"created_at": null,
|
||||
"updated_at": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"native": [
|
||||
{
|
||||
"about": "A current export: the checklist is already in the body, a reminder and recurrence come across, and the pre-M315 colour is read past.",
|
||||
"note": {
|
||||
"id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
||||
"display_title": "Trip",
|
||||
"body": "Trip\n\n- [ ] passport",
|
||||
"color": "blue",
|
||||
"pinned": true,
|
||||
"archived": false,
|
||||
"remind_at": "2026-11-01T09:00:00+00:00",
|
||||
"recurrence": "weekly",
|
||||
"created_at": "2026-07-19T00:00:00+00:00",
|
||||
"updated_at": "2026-07-20T08:30:00.250000+00:00",
|
||||
"labels": ["travel"],
|
||||
"attachments": [{ "file": "attachments/6ba7b810/0/ticket.pdf", "mime": "application/pdf" }]
|
||||
},
|
||||
"spec": {
|
||||
"title": null,
|
||||
"body": "Trip\n\n- [ ] passport",
|
||||
"pinned": true,
|
||||
"archived": false,
|
||||
"trashed": false,
|
||||
"remind_at": "2026-11-01T09:00:00Z",
|
||||
"recurrence": "weekly",
|
||||
"labels": ["travel"],
|
||||
"items": [],
|
||||
"attachments": [{ "file": "attachments/6ba7b810/0/ticket.pdf", "mime": "application/pdf" }],
|
||||
"created_at": "2026-07-19T00:00:00Z",
|
||||
"updated_at": "2026-07-20T08:30:00.250Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"about": "An export from before M304 carries a title and a separate items array; an unknown recurrence is dropped.",
|
||||
"note": {
|
||||
"title": "shopping",
|
||||
"body": "",
|
||||
"recurrence": "fortnightly",
|
||||
"items": [{ "text": "milk", "checked": true }]
|
||||
},
|
||||
"spec": {
|
||||
"title": "shopping",
|
||||
"body": "",
|
||||
"pinned": false,
|
||||
"archived": false,
|
||||
"trashed": false,
|
||||
"remind_at": null,
|
||||
"recurrence": null,
|
||||
"labels": [],
|
||||
"items": [{ "text": "milk", "checked": true }],
|
||||
"attachments": [],
|
||||
"created_at": null,
|
||||
"updated_at": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"export": {
|
||||
"document": ["app", "version", "exported_at", "labels", "notes"],
|
||||
"note": [
|
||||
"id",
|
||||
"display_title",
|
||||
"body",
|
||||
"pinned",
|
||||
"archived",
|
||||
"remind_at",
|
||||
"recurrence",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"labels",
|
||||
"attachments"
|
||||
],
|
||||
"label": ["name", "color"],
|
||||
"attachment": ["file", "mime"]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user