0.1.99 — Casdoor as the primary auth surface

Reorganises the signed-out auth UI so Casdoor SSO is the front-and- centre call to action whenever the backend reports it as configured. The legacy email/password + per-provider OAuth flow tucks behind a "Use email / password instead" expander. Users with un-migrated accounts still get in; everyone else lands on the SSO button.

This is a UI-only round — no backend changes, no schema changes, no new env vars. Phase 4 of the migration plan (cutover, which deletes the legacy endpoints) stays open; this round narrows the entry point without removing the back door.

Shared (notechondria_shared)

  • AuthHub (the login dialog used by portal + planner) split into a thin stateless AuthHub shell and a new stateful _AuthHubBody widget that owns the _showLegacy toggle. The Card skin and callback prop fan-out stay on AuthHub.
  • When onCasdoorLogin != null, the body renders:
    • A full-width FilledButton.icon "Continue with Casdoor SSO" primary CTA.
    • A TextButton.icon toggle ("Use email / password instead" / "Hide email / password fallback").
    • Behind the toggle: the legacy Sign-up + Login pair plus the Google / GitHub OutlinedButton.icons, exactly as they used to render.
  • When onCasdoorLogin == null (shadow mode), the body falls through to the legacy block with no expander, preserving the pre-0.1.99 surface byte-for-byte.

editor_app

  • _buildSignedOutAccount (in settings_build.dart) split the same way: _OAuthPillButton for "Continue with Casdoor SSO" on top, then the toggle, then the legacy _legacyAuthBlock helper with Sign-up + Login + Casdoor-less Google / GitHub pills.
  • _SettingsPageState gains a _showLegacyAuthFallback bool plus a toggleLegacyAuthFallback method. The build extension can't call setState directly (Dart blocks protected methods on extensions), so the toggle bounces through the State-owned method.
  • Casdoor-less code path is unchanged.

Verification

  • flutter analyze clean across editor_app, portal_app, planner_app. No new errors / warnings.
  • The portal and planner apps automatically pick up the new AuthHub body since they consume it from the shared package.

Carryover

  • Phase 4 cutover: disable LoginApiView / RegisterApiView etc., freeze Session writes. The expander stays as the migration path until the operator decides every active user has a Casdoor account.
  • Phase 5 cleanup: delete the legacy auth surface.
  • 0.1.94 GitHub Sync open items.