Severity: medium
Location: src/lib/components/SceneNavigator.svelte:56-108
scenes is a $derived.by(...) reading documentStore.activeContent. Since the editor calls setContent on every transaction, this re-walks the full document and re-parses every scene heading (regex for INT/EXT, time-of-day, page estimate) on every keystroke — even when nothing scene-structural changed.
For a 120-scene screenplay this is 120 scene-objects rebuilt per keystroke. Compounded with SceneCardsView, StatisticsModal, and the character-list plugin doing similar walks, the cumulative cost is hundreds of full-document traversals per second during active typing.
Fix: Memoize the walk by document version. One option:
- Track
documentStore.contentVersion: number (incremented in setContent).
- Compute scenes once per version and cache, or debounce the derive on a 500ms idle timer (Svelte action / setTimeout in
$effect).
Alternatively, only rebuild the entry for the scene whose index changed — ProseMirror's transaction mapping can identify the affected node range.
Severity: medium
Location:
src/lib/components/SceneNavigator.svelte:56-108scenesis a$derived.by(...)readingdocumentStore.activeContent. Since the editor callssetContenton every transaction, this re-walks the full document and re-parses every scene heading (regex for INT/EXT, time-of-day, page estimate) on every keystroke — even when nothing scene-structural changed.For a 120-scene screenplay this is 120 scene-objects rebuilt per keystroke. Compounded with
SceneCardsView,StatisticsModal, and the character-list plugin doing similar walks, the cumulative cost is hundreds of full-document traversals per second during active typing.Fix: Memoize the walk by document version. One option:
documentStore.contentVersion: number(incremented insetContent).$effect).Alternatively, only rebuild the entry for the scene whose
indexchanged — ProseMirror's transactionmappingcan identify the affected node range.