Purpose: Transforms Obsidian canvas files into linear DreamSong story flows and manages git submodule relationships between DreamNodes, enabling compositional knowledge weaving through canvas-based storytelling.
dreamweaving/
├── store/
│ └── slice.ts # Zustand slice (cache, scroll position, relationship state)
├── components/
│ ├── DreamSong.tsx # Pure React renderer for DreamSong content (browser-safe)
│ ├── DreamSongWithExtensions.tsx # Wrapper with Obsidian-only extensions (perspectives, conversations)
│ ├── DreamSongFullScreenView.ts # Obsidian ItemView for fullscreen DreamSong display
│ ├── LinkFileView.ts # Custom view for .link files with YouTube thumbnails
│ └── ReadmeSection.tsx # Clickable README link in DreamSong UI
├── hooks/
│ └── useDreamSongData.ts # React hook with hash-based change detection
├── services/
│ ├── audio-streaming-service.ts # Load audio files as base64 data URLs
│ ├── canvas-layout-service.ts # Auto-arrange canvas elements in linear flow
│ ├── canvas-observer-service.ts # MutationObserver for .link file thumbnail replacement
│ ├── canvas-parser-service.ts # Parse canvas JSON, find DreamNode boundaries
│ ├── canvas-parser-service.test.ts
│ ├── dreamsong-parser-service.ts # L1/L2 caching parser (used by relationship extraction)
│ ├── dreamsong-relationship-service.ts # Extract relationship graphs from DreamSong sequences
│ ├── submodule-manager-service.ts # Git submodule operations with bidirectional tracking
│ └── submodule-manager-service.test.ts
├── dreamsong/ # Pure parsing functions (Layer 1 architecture)
│ ├── parser.ts # Canvas → blocks transformation, topological sort
│ ├── hasher.ts # Structure hash generation for change detection
│ ├── media-resolver.ts # Resolve media paths to data URLs
│ └── index.ts # Barrel export with convenience functions
├── types/
│ ├── dreamsong.ts # DreamSong blocks, media, parsing types
│ └── relationship.ts # Relationship graph types (source of truth for constellation-layout)
├── styles/
│ └── dreamsong.module.css # DreamSong component styles
├── assets/
│ └── Separator.png # Visual separator for DreamSong sections
├── commands.ts # All Obsidian commands (canvas, submodule, sync, layout, link files)
├── index.ts # Barrel export
└── README.md
// Store (state management)
export { createDreamweavingSlice, DreamweavingSlice, ... } from './store/slice';
// Commands (includes link file commands)
export { registerDreamweavingCommands, registerLinkFileCommands, enhanceFileSuggestions } from './commands';
// Types
export * from './types/dreamsong';
export * from './types/relationship';
// Services
export { DreamSongRelationshipService } from './services/dreamsong-relationship-service';
export { DreamSongParserService } from './services/dreamsong-parser-service';
export { CanvasParserService } from './services/canvas-parser-service';
export { CanvasLayoutService } from './services/canvas-layout-service';
export { SubmoduleManagerService } from './services/submodule-manager-service';
export { CanvasObserverService } from './services/canvas-observer-service';
export { AudioStreamingServiceImpl, initializeAudioStreamingService, getAudioStreamingService } from './services/audio-streaming-service';
// Components
export { DreamSong } from './components/DreamSong';
export { DreamSongWithExtensions } from './components/DreamSongWithExtensions';
export { ReadmeSection } from './components/ReadmeSection';
// Views
export { DreamSongFullScreenView, DREAMSONG_FULLSCREEN_VIEW_TYPE } from './components/DreamSongFullScreenView';
export { LinkFileView, LINK_FILE_VIEW_TYPE } from './components/LinkFileView';
// Hooks
export { useDreamSongData, useDreamSongExists } from './hooks/useDreamSongData';
// Pure functions (parser, hasher, media-resolver)
export { parseCanvasToBlocks, parseAndResolveCanvas, resolveMediaPaths, ... } from './dreamsong/index';- Canvas Parsing: Extract nodes and edges from Obsidian canvas JSON
- Topological Sort: Order nodes by dependency graph (respecting directed edges)
- Media-Text Pairing: Detect undirected edges linking file nodes to text nodes
- Block Creation: Generate linear DreamSong blocks with flip-flop media-text layout
- Media Resolution: Convert file paths to data URLs for rendering
- Dependency Detection: Parse canvas for external DreamNode references
- Submodule Import: Add referenced DreamNodes as git submodules via Radicle URLs
- Bidirectional Tracking: Update parent's
submodulesand child'ssupermodulesin .udd files - Coherence Beacons: Commit metadata for network-based relationship discovery
Dreamweaving owns the DreamSong relationship data because relationships are created through the act of weaving DreamTalks into DreamSongs on the canvas.
- constellation-layout reads this data for visualization
- Stored in
dreamSongRelationshipsstate in dreamweaving slice - Serialized/deserialized for persistence in localStorage
| Command | Description |
|---|---|
create-dreamsong-canvas |
Create DreamSong.canvas in selected DreamNode |
commit-dreamsong-changes |
Commit pending changes to selected DreamNode |
auto-save-and-commit-dreamsong |
Auto-save with submodule sync |
sync-canvas-submodules |
Import/remove submodules based on canvas dependencies |
auto-layout-dreamsong-canvas |
Arrange canvas elements in linear flow |
scan-dreamsong-relationships |
Scan vault and update relationship graph |
open-dreamsong-fullscreen |
Open DreamSong in fullscreen Obsidian view |
show-link-files |
Fuzzy search all .link files in vault |
add-link-file-to-canvas |
Add .link file to active canvas |
Stateless, easily testable functions:
parser.ts: Canvas → blocks transformationhasher.ts: Structure hash for change detectionmedia-resolver.ts: Path → data URL resolution
Minimal state management with hash-based optimization:
- Memoized parsing
- File change watching
- Songline feature detection
Stateful orchestrators with side effects:
- Canvas parsing and analysis
- Git submodule operations
- Relationship extraction
Zustand state slice for:
- DreamSong cache entries
- Relationship graph state
- Persistence serialization
- dreamnode: Provides UDD service, git operations
- constellation-layout: Consumes relationship graph for visualization
- songline: Provides perspective/conversation components
- social-resonance: Provides RadicleService for submodule URLs
- drag-and-drop: Provides .link file parsing utilities
InterBrain provides custom rendering for .link files (URL bookmarks stored as JSON) across multiple contexts:
{
"url": "https://www.youtube.com/watch?v=VIDEO_ID",
"type": "youtube",
"title": "Video Title",
"videoId": "VIDEO_ID",
"embedUrl": "https://www.youtube.com/embed/VIDEO_ID",
"thumbnail": "https://img.youtube.com/vi/VIDEO_ID/maxresdefault.jpg",
"created": "2025-01-01T00:00:00.000Z"
}| Context | Component | Behavior |
|---|---|---|
| Editor View | LinkFileView.ts |
Full preview with thumbnail, metadata, and action buttons |
| Canvas Card | CanvasObserverService |
Thumbnail with play button → expands to iframe on click |
| DreamSong UI | DreamSong.tsx |
Embedded YouTube iframe (16:9 aspect ratio) |
| DreamTalk Node | DreamTalkSide.tsx |
Circular thumbnail with play icon overlay |
.link file on disk (JSON)
↓
parseLinkFileContent() → LinkFileMetadata
↓
Four Rendering Paths:
1. EDITOR VIEW (when .link file is opened in Obsidian)
→ LinkFileView extends TextFileView
→ renderYouTubePreview() / renderWebsitePreview()
→ Click opens URL in browser
2. CANVAS CARD (file node on Obsidian canvas)
→ CanvasObserverService (MutationObserver)
→ Replaces canvas-node-content innerHTML
→ Click play button → replaces with iframe (autoplay)
3. DREAMSONG PLAYBACK (canvas story flow in DreamSong UI)
→ media-resolver.ts resolveLinkFileInfo()
→ DreamSong.tsx renderMediaElement()
→ Embedded iframe with youtube-nocookie.com
4. DREAMTALK THUMBNAIL (3D sphere node front face)
→ DreamTalkSide.tsx MediaRenderer()
→ Circular thumbnail with red play button overlay
→ Click navigates to node
- Uses
youtube-nocookie.comfor privacy-enhanced embedding - Some videos have embedding disabled by uploaders (shows "Video unavailable")
- Autoplay enabled when expanding from thumbnail on canvas
- 16:9 aspect ratio maintained in DreamSong UI
components/LinkFileView.ts- Obsidian editor view registrationservices/canvas-observer-service.ts- Canvas thumbnail replacementdreamsong/media-resolver.ts- Media path resolution for DreamSong../../drag-and-drop/link-file-utils.ts- Parsing utilities
- Two Parsing Systems:
dreamsong/(pure functions for UI) anddreamsong-parser-service.ts(with caching for relationship extraction) - macOS Case Sensitivity: Directory is
dreamsong/(lowercase) - avoid imports withDreamSong/ - Flip-Flop Layout: Media-text pairs alternate left/right alignment
- Hash-Based Updates: Only re-render when canvas structure actually changes
Support undirected edges between multiple media nodes (without text nodes) to create image clusters rendered as grid layouts in DreamSong:
Canvas layout: DreamSong output:
┌───────┐ ┌───────┐ ┌─────────────────┐
│ img1 │───│ img2 │ │ ┌─────┬─────┐ │
└───────┘ └───────┘ │ │img1 │img2 │ │
│ │ │ ├─────┼─────┤ │
└─────┬─────┘ │ │img3 │img4 │ │
│ │ └─────┴─────┘ │
┌───┴───┐ └─────────────────┘
│ │
┌───────┐ ┌───────┐
│ img3 │─│ img4 │
└───────┘ └───────┘
Implementation approach:
- Detect connected components of file nodes linked only by undirected edges
- Group into cluster if no text nodes are connected
- Render as responsive CSS grid (auto-fill columns)
- Maintain aspect ratios, handle mixed media types