Skip to content

Extract editor-session lifecycle from App.vue#95

Merged
Renakoni merged 1 commit into
mainfrom
refactor/extract-editor-session
Jul 10, 2026
Merged

Extract editor-session lifecycle from App.vue#95
Renakoni merged 1 commit into
mainfrom
refactor/extract-editor-session

Conversation

@Renakoni

Copy link
Copy Markdown
Owner

Summary

Final proactive decomposition PR: the Muya editor instance and its runtime lifecycle move out of App.vue into a dedicated editor-session boundary, createEditorSession in src/features/editor/editorSession.ts. App.vue drops from 1,659 to 1,457 lines and no longer owns any low-level editor machinery — it composes screens and coordinates workflows, reaching the editor only through session accessors.

Ownership boundary

Before: App.vue held the module-scope editor variable, the init token, the host-element ref, initEditor / openEditor / destroyEditor, Markdown snapshot + document-state sync, post-open focus release, and the input/selection diagnostic hooks — ~250 lines of editor internals interleaved with page composition.

After — owned by the session:

  • the Muya instance, host element, and editorReady state
  • initialization with stale-init token protection, including the failure path
  • the destroy → (remount through home) → init cycle behind openEditor
  • getEditorMarkdownSnapshot / syncDocumentFromEditor (and the shared normalizeEditorMarkdown)
  • post-open focus release
  • input/selection diagnostics install/teardown and describeEditorInputState
  • cleanup on destroy (timers, diagnostics, native tap listener, suppression reset)

Intentionally still in App.vue: navigation and screen composition, syncMarkdown (status text + autosave scheduling policy), focus/blur status handlers, Android document open/save/share workflows, persistence orchestration, home actions, back-button/exit decisions, toolbar and link/image business actions, settings maintenance, notices.

Why this is a session boundary, not a second app controller

The session receives App policy as injected collaborators — syncMarkdown, focus/blur handlers, the selection-lifecycle functions, autosave timer handles, closeEditorToolbar — and never decides status wording, autosave timing, or navigation policy itself. This is the same factory-with-injected-options pattern as createEditorSelectionLifecycle, createAutosaveScheduler, and createCurrentDocumentPersistence; the mutual references between session, scheduler, and selection lifecycle use the deferred-closure convention already established in App.vue.

Behavior equivalence

Every moved function is verbatim except two documented equivalence rewrites:

  • syncMarkdown now calls the extracted syncDocumentFromEditor(markDirty) — same editor guard, same normalization, same updateDocumentMarkdown call it previously inlined.
  • The init-complete log reads documentState.value.stats.* directly; the App computeds it previously used resolve to exactly those fields.

Initialization ordering, suppression reasons (editor-init, editor-init-stale, editor-init-failed, editor-destroy), token-based race protection, remount semantics (screen flip through home with suppression update skipped), unmount cleanup, and all logging are unchanged. editorRuntime gains only an export on CreateMuyaEditorOptions so the session can type the injected factory. No UI, persistence, navigation, or Android bridge changes; third_party/muya untouched.

Testing

  • New editorSession.test.ts (14 tests, happy-dom): open/ready wiring, missing host element, stale-init supersession (a superseded init resolving late must not replace the live editor), remount-through-home cycle, snapshot/sync incl. flush and single-newline normalization, destroy/teardown, suppression skip on remount destroy, repeated open/close cycles, init failure, diagnostics install/teardown + late enabling via the settings watcher path, focus release, input-state description.
  • Full unit suite: 326/326 (54 files). ESLint clean. pnpm build (includes vue-tsc -b) clean.
  • Mobile WebView e2e contract specs covering editor startup/shutdown/navigation: mobile-home-navigation, mobile-local-draft-lifecycle, mobile-android-document-lifecycle, mobile-markdown-editing23/23 passed.

Per the decomposition plan, this is the last size-driven extraction; further refactoring should be driven by product changes, bugs, or ownership conflicts.

App.vue owned the Muya editor instance and all of its low-level runtime
machinery directly: the module-scope editor variable and init token, the
host-element ref, initialization with stale-init protection, the
destroy/remount cycle behind opening a document, Markdown snapshots and
document-state sync, post-open focus release, and the input/selection
diagnostic hooks. That machinery now lives in a dedicated editor-session
factory (createEditorSession in src/features/editor/editorSession.ts),
and App.vue reaches the editor only through the session accessors.

The session deliberately does not become a second application
controller: status text policy, autosave scheduling, toolbar and
link/image business actions, navigation, persistence, and Android
workflows all stay in App.vue and are reached through injected
collaborators (syncMarkdown, focus/blur handlers, selection-lifecycle
functions, autosave timer handles), following the same
factory-with-injected-options pattern as the existing selection
lifecycle, autosave scheduler, and persistence modules.

Everything moved verbatim; the only equivalence rewrites are
syncMarkdown reusing the extracted syncDocumentFromEditor (same guard,
normalization, and update call) and the init-complete log reading
documentState stats directly instead of through App computeds that
resolve to the same fields. Initialization ordering, token-based stale
protection, cleanup on destroy and unmount, suppression reasons, and
diagnostics semantics are unchanged. editorRuntime now exports
CreateMuyaEditorOptions so the session can type the injected factory.

New editorSession tests cover open/ready wiring, missing host element,
stale-init supersession, the remount-through-home cycle, snapshot and
sync behavior, destroy/teardown, repeated open/close cycles, init
failure, input-diagnostics install/teardown and late enabling, focus
release, and input-state description.
@Renakoni Renakoni merged commit 0701767 into main Jul 10, 2026
5 checks passed
@Renakoni Renakoni deleted the refactor/extract-editor-session branch July 10, 2026 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant