Skip to content

Latest commit

 

History

History
133 lines (92 loc) · 6.41 KB

File metadata and controls

133 lines (92 loc) · 6.41 KB

Core Module

The foundational infrastructure that all features build upon. Core defines how things work; features define what things do.

Navigation

  • Features README - Complete guide to feature slices: architecture patterns, directory structure, naming conventions, and catalog of all features
  • This file - Core module internals: services, store, components

Feature Slices

Core composes Zustand slices from feature modules. For the complete feature catalog, architecture patterns, and how to create new features, see Features README.

Core Principles

1. All Vault Access Through VaultService

Features MUST NOT make direct fs calls. All file system operations go through VaultService:

// WRONG - direct fs in feature code
import fs from 'fs';
fs.readFileSync(path);

// RIGHT - use VaultService
const vaultService = serviceManager.getVaultService();
await vaultService.readFile(relativePath);

VaultService handles vault path resolution and provides a consistent interface. Core owns the vault communication boundary.

2. Store as Communication Bus

Features declare intent via store state; core reacts. The spatialLayout value drives all UI behavior:

spatialLayout: 'constellation' | 'liminal-web' | 'copilot' | 'edit' | 'search' | 'creation'

Features call setSpatialLayout(). They don't need to know about each other.

3. Dependency Direction

Features → Core → Obsidian/React
  • Features import from core
  • Core does NOT import from features (except slice composition)

Quick Reference

Need to... Use...
Read/write files serviceManager.getVaultService()
CRUD DreamNodes serviceManager.getActive()
Open content in leaves serviceManager.getLeafManagerService()
Position nodes in 3D SpatialOrchestrator via context
Show user feedback UIService
Access store state useInterBrainStore()

Directory Map

core/
├── store/          → Zustand store + slice composition + IndexedDB adapter
├── components/     → DreamspaceCanvas, SpatialOrchestrator, EphemeralNodeManager, DreamspaceView
├── services/       → ServiceLifecycleManager, VaultStateService, VaultService, LeafManagerService, UIService, ServiceManager, ephemeral-despawn-queue
├── hooks/          → useEscapeKeyHandler, useOptionKeyHandlers
├── context/        → OrchestratorContext (spatial orchestrator ref)
├── commands/       → camera-commands
├── test-utils/     → Obsidian mocks, Vitest setup
└── types/          → TypeScript declarations

Key Files

Store: store/interbrain-store.ts - Zustand with slice composition. Core owns spatialLayout, camera, layoutTransition. Features own their data (e.g., dreamnode owns dreamNodes via its slice).

Canvas: components/DreamspaceCanvas.tsx (~670 lines) - R3F canvas, user input, overlay mounting. Delegates drop logic to features/drag-and-drop.

Orchestrator: components/SpatialOrchestrator.tsx (~935 lines) - Node positioning, layout animations, interrupt-capable movement. Uses ring layout from liminal-web-layout feature. Manages ephemeral node spawning with staggered timing (40ms per node) to prevent main thread blocking.

EphemeralNodeManager: components/EphemeralNodeManager.tsx - React hooks for ephemeral node lifecycle:

  • useEphemeralSpawner() — spawns unmounted nodes that enter a liminal web layout
  • useEphemeralGarbageCollector() — cleans up stale ephemeral nodes after layout transitions

Services

ServiceLifecycleManager (services/service-lifecycle-manager.ts) - Coordinates async service initialization through 5 phases:

  1. BOOTSTRAP: Set vault context, load settings
  2. HYDRATE: Read IndexedDB, validate persisted data
  3. SCAN: Scan vault (only if needed based on vault state)
  4. READY: UI can interact, services available
  5. BACKGROUND: Heavy operations (indexing, sync)

Each phase must complete before the next begins. Features subscribe to phase completion events to coordinate initialization. Includes graceful shutdown with pending operation tracking.

VaultStateService (services/vault-state-service.ts) - Tracks vault state for incremental scanning:

  • Persists vault-state.json in plugin directory
  • Stores last scan timestamp, node count, vault ID
  • Enables skipping full vault scans when data is fresh
  • Uses .obsidian mtime for quick change detection

ServiceManager (services/service-manager.ts) - Central registry. Initialize with plugin, then access all services.

VaultService (services/vault-service.ts) - Thin fs wrapper with path resolution. All vault file operations go here.

LeafManagerService (services/leaf-manager-service.ts) - Obsidian workspace leaves, 50/50 splits, tab stacking.

UIService (services/ui-service.ts) - Notices, modals, user prompts.

Ephemeral Despawn Queue (services/ephemeral-despawn-queue.ts) - Staggers ephemeral node unmounts to prevent main thread blocking. When multiple exit animations complete in the same frame, each would trigger a Zustand set() + React unmount. With 15+ nodes finishing simultaneously, that blocks the main thread for 50-100ms. The queue collects despawn requests and drains them one-by-one:

  • 500ms initial delay: Lets spawn animations settle before any despawns begin
  • 40ms interval: Between individual despawn operations
  • Cancellation: cancelEphemeralDespawn(nodeId) rescues a node from the queue when a new layout reclaims it (e.g., rapid navigation: InterBrain → person → back to InterBrain)
  • Flush: flushDespawnQueue() drains everything immediately (used when returning to constellation with no animations running)

Notes

When working in features:

  1. Never import fs directly - use VaultService
  2. Never manipulate node positions directly - use SpatialOrchestrator API
  3. Declare intent via store - don't reach into other features
  4. Extract feature logic from core - if it's feature-specific, it belongs in features/

When working in core:

  1. Keep it minimal - resist feature-specific code
  2. Layout algorithms live in feature slices (e.g., liminal-web-layout, constellation-layout)

Lifecycle Architecture

For detailed analysis of async operations, race conditions, and the lifecycle coordination solution, see LIFECYCLE-AUDIT.md.