0.1.92 — shared CustomMetaListEditor in all three note-metadata dialogs + docs sync

Round 3 of the multi-app migration started in 0.1.90. Closes the custom-meta UI carryover left after 0.1.91 by lifting the expandable list into notechondria_shared and mounting it in editor / portal / planner.

Shared widget extraction

  • New notechondria_shared/lib/src/components/custom_meta_list_editor.dart exporting CustomMetaController + CustomMetaListEditor. The controller parses the JSON object string from note.custom_meta on construction, exposes serialize() for the parent's save path, and notifies on add / remove / expand toggle. Malformed JSON is preserved in an invalid_json row so the user can fix it without losing data.

editor_app

  • _NoteMetadataDialog switched from its private _CustomMetaRow list to the shared controller. The custom-meta dispose loop, the _loadCustomMeta parser, and the inline expandable widget are all gone; _buildCustomMetaSection now just returns CustomMetaListEditor(controller: _customMetaController).
  • _serializeCustomMeta() removed; the two pop-with-result paths now read _customMetaController.serialize() directly.

portal_app

  • _NoteMetadataDialog (forked from editor for portal's learner flow) gained a CustomMetaController, mounts CustomMetaListEditor between the cover section and version history, and threads custom_meta through both pop maps.
  • learner_note_editor.dart save payload now sends custom_meta out-of-band of metadata_json so the backend's dedicated column receives the user-defined keys without double-storing them.

planner_app

  • Same migration as portal: _NoteMetadataDialog mounts the shared editor; learner_note_editor.dart strips custom_meta from the metadata_json blob and sends it as its own field.

Docs sync (per AGENTS.md §2.6)

  • Root README.md adds two sections:
    • "Per-app OAuth redirect URIs (since 0.1.90)" documenting the new GOOGLE_AUTHORIZED_REDIRECT_URIS / GITHUB_AUTHORIZED_REDIRECT_URIS env-var allow-lists.
    • "Experimental: GitHub data-sync (since 0.1.90)" pointing at docs/integrations/github-sync.md and listing the GITHUB_DATA_SYNC_APP_* env-var contract + the JWT-signing gap.
  • docs/readme.md adds parallel sections for the same two features plus a per-user MCP skill and custom-meta surface explanation.
  • docs/deployment/deploy.md env-block extended with the new vars, with comments explaining the per-app allow-list semantics and the JWT-signing gate.
  • docs/deployment/render_free_tier.md and docs/deployment/northflank.md updated with the same env-var guidance + the data-sync caveat.

Verification

  • flutter analyze runs cleanly across editor_app, portal_app, and planner_app. No new warnings or errors introduced; only pre-existing unused-element / unused-import lints remain.

Carryover (still open)

  • GitHub Sync — actual push pipeline. Add pyjwt + cryptography to backend/requirements.txt, finish _refresh_installation_token (JWT sign → POST /app/installations/<id>/access_tokens), build the repo-picker UI on top of GET /installation/repositories, and ship a CLI restore script in backend/scripts/. Tracked under "Experimental GitHub Sync" in docs/TODO.md. Suggest scheduling this as a separate round once the dependency bumps land.