Overview
The frontend codebase has accumulated 64+ $effect blocks, many of which represent anti-patterns that fight against Svelte 5's reactive model rather than embracing it. This epic tracks the systematic refactoring effort to eliminate unnecessary effects and adopt idiomatic Svelte 5 patterns.
Problem Statement
Current State:
- 64
$effect usages across the codebase
- Hotspots:
base-node.svelte (13), base-node-viewer.svelte (9), header-node.svelte (6)
TextareaController class requires manual prop synchronization via effects
- Effects used as event handlers, for derived state, and for lifecycle management
Root Cause:
The TextareaController class is an imperative escape hatch that doesn't participate in Svelte's reactivity system. Every prop that needs to reach the controller requires an $effect bridge for synchronization.
Anti-patterns Identified:
- Effects as event handlers - Watching state to trigger actions instead of calling functions from events
- Effects for derived state - Setting innerHTML/values imperatively instead of using
$derived
- Effects as lifecycle hooks - Creating/destroying objects based on element existence
- Effects for prop synchronization - Manually syncing props to external class instances
Proposed Solution
Refactor in independent, testable pieces:
- Convert imperative controller patterns to reactive
.svelte.ts modules
- Replace effect-based derived state with
$derived
- Move event-triggered logic to actual event handlers
- Eliminate prop sync effects by making controllers reactive
Success Criteria
Child Issues
Phase 1 (Completed ✅)
| Issue |
Description |
Status |
| #624 |
Refactor TextareaController to reactive .svelte.ts module |
✅ Done |
| #625 |
Convert view mode HTML rendering from $effect to $derived |
✅ Done |
| #626 |
Replace autocomplete and slash command effects with event-driven updates |
✅ Done |
| #627 |
Eliminate calendar date selection effect - use onchange handler |
✅ Done |
| #628 |
Audit and simplify base-node-viewer effects |
✅ Done |
| #629 |
Audit and simplify header-node effects |
✅ Done |
Phase 2 (New - Final Cleanup)
| Issue |
Description |
Effects to Eliminate |
| #693 |
Replace internalContent prop sync effects with $derived |
4 |
| #694 |
Eliminate date-node-viewer onTitleChange callback effect |
1 |
| #695 |
Optimize textarea-controller content/config sync effects |
2 |
Phase 2 target: Reduce from 16 effects to 6 legitimate effects
Current Progress (Post-Phase 1 Audit)
Effects reduced: 64 → 16 (75% reduction)
Remaining 16 effects:
- 6 LEGITIMATE (DOM lifecycle, async loading, subscriptions with cleanup)
- 10 ELIMINATE-able (prop sync, callbacks) - tracked in Phase 2 issues
Technical Specifications
Legitimate Effects (Keep These)
base-node.svelte:742 - Controller initialization (DOM lifecycle)
code-block-node.svelte:235 - Click-outside handler (document event subscription)
custom-entity-node.svelte:61 - Async schema loading
pane-content.svelte:65 - Dynamic component loading
schema-property-form.svelte:50 - Store subscription with cleanup
schema-property-form.svelte:86 - Async schema loading
Reference Documentation
- Svelte 5 runes documentation
- Existing
.svelte.ts patterns in codebase
Non-Goals
- Rewriting component logic or features
- Changing user-facing behavior
- Refactoring non-effect-related code
Definition of Done
Overview
The frontend codebase has accumulated 64+
$effectblocks, many of which represent anti-patterns that fight against Svelte 5's reactive model rather than embracing it. This epic tracks the systematic refactoring effort to eliminate unnecessary effects and adopt idiomatic Svelte 5 patterns.Problem Statement
Current State:
$effectusages across the codebasebase-node.svelte(13),base-node-viewer.svelte(9),header-node.svelte(6)TextareaControllerclass requires manual prop synchronization via effectsRoot Cause:
The
TextareaControllerclass is an imperative escape hatch that doesn't participate in Svelte's reactivity system. Every prop that needs to reach the controller requires an$effectbridge for synchronization.Anti-patterns Identified:
$derivedProposed Solution
Refactor in independent, testable pieces:
.svelte.tsmodules$derivedSuccess Criteria
$effectcount by 50%+ (from 64 to <32)Child Issues
Phase 1 (Completed ✅)
.svelte.tsmodule$effectto$derivedPhase 2 (New - Final Cleanup)
Phase 2 target: Reduce from 16 effects to 6 legitimate effects
Current Progress (Post-Phase 1 Audit)
Effects reduced: 64 → 16 (75% reduction)
Remaining 16 effects:
Technical Specifications
Legitimate Effects (Keep These)
base-node.svelte:742- Controller initialization (DOM lifecycle)code-block-node.svelte:235- Click-outside handler (document event subscription)custom-entity-node.svelte:61- Async schema loadingpane-content.svelte:65- Dynamic component loadingschema-property-form.svelte:50- Store subscription with cleanupschema-property-form.svelte:86- Async schema loadingReference Documentation
.svelte.tspatterns in codebaseNon-Goals
Definition of Done