Notechondria
Version: 0.1.38 Build Date: 2026-04-18T16:00
What's Changed
.nchron v1 local-data archive (shared spec + editor wiring)
Replaces the minimal .env-style config-file download with a
full-state versioned ZIP export/import pair. This is commit 1 of 3
in the plan from 0.1.37 \u2014 spec + shared helpers + editor
end-to-end; planner + portal wire-through remains in docs/TODO.md.
Spec
New docs/export_format_v1.md documents the entire package layout,
manifest schema, attachments/ promotion rules, cross-app
portability policy, importer version gating, and the legacy .env
migration shim. Current package format version is 1.
Shared helpers
New frontend/notechondria_shared/lib/src/utils/local_archive.dart
provides:
writeLocalArchive(LocalArchiveInput)\u2192Uint8ListZIP bytes.readLocalArchive(Uint8List)\u2192LocalArchiveOutputwith a non-nullerrorMessageon failure. The output rehydratesqueued_attachmentspaths back intobytes_base64so the host's existing sync promotion code works without special-casing imports.tryReadLegacyEnvConfig(Uint8List)\u2192Map<String, String>?for the pre-0.1.38.envmigration shim.LocalArchiveAppenum +tag/fromTagextension for exporter attribution across editor / planner / portal.kLocalArchivePackageVersionconstant (1).
Promotes every draft's metadata_json['queued_attachments'] binary
payload out to real archive entries under attachments/<draft-id>/
and rewrites the queue with path pointers. On read, the pointers
rehydrate back to base64 so the existing
Editor.Sync.Notes/attachment.promote pass uploads them normally
the next time the draft syncs.
archive: ^3.4.10 added to notechondria_shared/pubspec.yaml;
exports wired through the barrel (notechondria_shared.dart).
Shared tests
New frontend/notechondria_shared/test/local_archive_test.dart
exercises six scenarios:
- Editor bucket round-trip.
- Attachment promotion on write + rehydration on read.
- Future-version rejection.
- Legacy
.envdetection. - Legacy sniff correctly ignores plain ZIPs.
- Empty-payload returns a \u00a71.7-shaped
Shared.LocalArchive/readerror.
All six pass (flutter test -r compact in
frontend/notechondria_shared).
Editor wiring
frontend/editor_app/lib/app_shell.dart:
- Removed
_buildConfigFileContent+_downloadConfigFile(the legacy.envdownload path). - Added
_exportLocalArchive()\u2014 builds aLocalArchiveInputfrom current session state (profile stripped of token + API key prefix per the v1 spec), writes the ZIP, and usesgetSaveLocation/XFile.fromDatato drop it to disk with a.nchronextension. Success/fail messages follow \u00a71.7 asEditor.LocalStore/export_zip. - Added
_restoreFromLocalImport()\u2014 picks a file viaopenFile, sniffs for legacy.envfirst (runs the migration shim on hit), else parses viareadLocalArchive. On successful parse, shows a 5-secondConfirmWithDelayDialogsummarizing the archive counts + exporter app, then replaces every_LocalAppStorebucket in one transaction, rebinds the debug log controller, and re-runs_loadInitialDatato refresh UI. Source:Editor.LocalStore/restore_from_import.
frontend/editor_app/lib/modules/settings.dart:
- Dropped
onDownloadConfigparameter from_SettingsPage. - Added
onExportLocalData+onRestoreFromLocalImportoptional callbacks. - Configuration section now shows "Download local user data" and "Restore from local imports" buttons when the callbacks are present.
Editor SettingsPage call site in app_shell.dart now passes the
new two callbacks instead of onDownloadConfig.
Backward compatibility
- The legacy
.envdownloads users may already have on disk still import cleanly via the sniff path:API_BASE_URLis applied through_applyLocalAppSettingsand the other local state stays untouched. - Archive format bumps (e.g. adding a new
planner_events.jsonbucket) can happen without bumping theVERSIONfile as long as the new file stays optional; the importer already treats missing optional files as empty defaults.
Files Changed
New
docs/export_format_v1.md\u2014 spec.docs/versions/0.1.38.md(this file).frontend/notechondria_shared/lib/src/utils/local_archive.dart\u2014 helpers.frontend/notechondria_shared/test/local_archive_test.dart\u2014 6 passing unit tests.
Modified
VERSION: 0.1.37 \u2192 0.1.38.docs/TODO.md: export/import task marked partially done; planner + portal replication and cross-app test matrix deferred.frontend/notechondria_shared/pubspec.yaml: addsarchive: ^3.4.10.frontend/notechondria_shared/lib/notechondria_shared.dart: exports the newlocal_archivesymbols.frontend/editor_app/lib/app_shell.dart: removes legacy download code, adds_exportLocalArchive+_restoreFromLocalImport, updates_SettingsPagecall site.frontend/editor_app/lib/modules/settings.dart: replacesonDownloadConfigwithonExportLocalData+onRestoreFromLocalImport; Configuration buttons updated.
Verification
notechondria_shared:flutter analyze\u2014 2 pre-existingsurfaceVariantdeprecation infos; no new errors.flutter test -r compact\u2014 6 tests pass.editor_app:flutter analyze\u2014 52 issues (same as 0.1.37; no new lint or errors).flutter test test/smoke_test.dart -r compact\u2014 passes.planner_app/portal_app: still analyze + smoke-test clean with the updated shared dependency.
Notes / follow-ups
- Planner + portal wiring \u2014 their Settings pages do not yet
surface the new two buttons, and their app_shells still lack
_exportLocalArchive/_restoreFromLocalImport. Replicate the editor pattern and add the app-specific buckets (plannerEvents,calendarFeeds,activityWeekfor planner;frontPagefor portal) to theirLocalArchiveInput. - Cross-app import coverage \u2014 the shared parser already
returns empty defaults for missing optional files, so an editor
export imports cleanly into planner / portal (and vice versa).
Add cross-app round-trip tests in
notechondria_shared/test/local_archive_test.dartwhen the planner + portal wiring lands so the matrix stays green. - UI copy pass \u2014 "Restore from local imports" reads a bit awkwardly; consider "Restore from backup" in a later round.