Editor app (frontend/editor_app/)
Offline-first markdown note editor. One of three standalone Flutter apps that share the same Notechondria backend.
Related: planner_app, portal_app, server/backend.md.
Role
- Offline-capable authoring surface: notes, folders, tags, attachments.
- Local persistence via
shared_preferences+ filesystem underSharedPreferences-managed keys, so the app boots without network. - Optional cloud sync to Django when the user is signed in and the API base URL passes the handshake check.
- Settings surface handles auth (login, register, OAuth with Google and GitHub), API base URL, API key rotation, change-email, change-password, identity-code verification, and theme.
Shape
Self-contained Flutter workspace with its own
pubspec.yaml, lib/, web/, windows/, test/, Dockerfile,
docker-compose.yml, and nginx/default.conf.template. Navigation is
constrained to Learner / Settings surfaces.
Library layout
frontend/editor_app/lib/ is a single Dart library (part of notechondria_frontend) split across these files:
| File | Responsibility |
|---|---|
main.dart | Library declaration, top-level main(), launches NotechondriaApp with visibleIndices for this app. |
app_shell.dart | The root widget + state: bootstraps local store, runs the settings-save flow, drives theme + API base URL, owns _localSettings, _localDrafts, _localCourses, _localStats, _localCache. ~2500 LOC — split candidates noted in index.md §6. |
core/client.dart | NotechondriaClient interface and HttpNotechondriaClient HTTP implementation; holds the base URL, verifyHandshake, token auth, debug snapshots. |
core/helpers.dart | _defaultApiBaseUrl, _kDefaultApiUrl (compile-time via --dart-define=DEFAULT_API_URL=...), _slugifyLocalText, date helpers. |
core/local_store.dart | _LocalAppStore.load/save* — every SharedPreferences key the app persists (settings, drafts, courses, stats, cache, UI logs, session token). |
core/url_strategy.dart / url_strategy_web.dart | Path-URL strategy for web; conditional import keeps non-web builds pure Dart. |
components/ | Shared UI: navigation.dart, avatar.dart, debug_widgets.dart, error_state.dart, note_viewer.dart, splash_screen.dart (Krebs-cycle animation). |
modules/ | Feature screens: learner.dart (note list + editor), settings.dart (full account surface), plus stale copies of front.dart, course.dart, activity.dart that aren't on this app's nav but still compile. |
Local store
_LocalAppStore in core/local_store.dart is the one place
SharedPreferences reads/writes happen. Keys:
settings→{api_base_url, theme_preset, theme_mode, log_preferences, updated_at}and related.drafts→ unsynced note drafts.courses→ cached course list.stats→ local counters (settings saves, sync count).cache→{front_page, courses}snapshots used for offline renders.logs→ UI logs surfaced in the debug drawer.session→{token, user}after login, cleared on logout.
API client contract
HttpNotechondriaClient talks to <base>/api/v1/.... See
server/backend.md for the endpoint list and
response shapes.
Key entry points:
updateBaseUrl(String)— mutate the in-memory base URL without re-instantiating the client. Called when Settings persists a URL change or when_loadLocalStateboots a saved value.verifyHandshake(String)— probes<candidate>/handshake/; see handshake. Settings-save refuses to commit a URL that fails this check.login / register / verifyEmail / oauth...— auth surface.getFrontPage / getCourses / getCourseNotes / getNote / updateNote— read/write data.
Build and deploy
- Local dev:
flutter run -d chromefromfrontend/editor_app/. - Smoke test:
flutter test test/smoke_test.dart -r compact. - GitHub Pages: built by
.github/workflows/frontend-pages.ymlwith--base-href "/Notechondria/editor/" --no-web-resources-cdn. - Self-hosted Docker: app-local
Dockerfile+docker-compose.yml, joined to theNOTECHONDRIA_SHARED_NETWORKso it resolves the backend by its compose alias.
Known drift and open work
See index.md §6 "Open work / caution list". In particular:
app_shell.dartis ~2500 LOC andclient.dartis ~812 LOC — both above the 500-LOC target fromAGENTS.md/AGENTS.md§1.5.- Stale modules
front.dart,course.dart,activity.dart,learner.dartthat this app'svisibleIndicesdoesn't use still compile — deleting them needs theapp_shell.dartmixin refactor.