Collaboration System
show.fm includes a real-time collaborative editing system for show notes, enabling podcast hosts, team members, and guests to work together seamlessly.
Architecture Overview
Key Components
| Component | Location | Purpose |
|---|---|---|
| Show Notes Service | src/lib/services/show-notes-service.ts | Creates and manages show notes documents |
| Collaboration Provider | src/lib/components/editor/collaboration-provider.ts | Yjs + Supabase Realtime synchronization |
| Collaborative Editor | src/lib/components/editor/CollaborativeEditor.svelte | TipTap editor with collaboration |
| Presence Store | src/lib/stores/presence.svelte.ts | Tracks active users per section |
| Cursor Extension | src/lib/components/editor/cursor-extension.ts | Remote cursor display |
Feature Capabilities
Real-Time Editing
- Conflict-Free: Yjs CRDT ensures consistent state across all clients
- Offline Support: IndexedDB persistence for offline editing
- Auto-Sync: Automatic synchronization when reconnecting
Presence & Cursors
- Live Presence: See who's currently editing
- Remote Cursors: View other users' cursor positions in real-time
- Section Tracking: Know which section each user is working on
Permission-Based Sections
- Shared Sections: Everyone can view and edit
- Host Private: Hidden from guests, visible to team only
- Guest Sections: Each guest has their own private section
Documentation
| Document | Description |
|---|---|
| Database Schema | Tables, RLS policies, and triggers |
| Show Notes Auto-Create & Per-Section Templates | Resolution precedence, service API, guest snapshot, unresolved-tag highlight |
| Collaboration Provider | Yjs + Supabase Realtime integration |
| Editor Component | TipTap integration and UI |
| Presence System | Real-time user tracking |
Quick Start
Creating Show Notes
Show notes are auto-created on episode creation (via /e/new) and on booking confirmation. To call the service directly, resolve per-section templates first, then apply:
typescript
import {
resolveShowNotesTemplates,
applyShowNotesTemplate
} from '$lib/services/show-notes-service';
// Resolution precedence: per-episode override → booking-link override
// → podcast default → blank.
const resolved = await resolveShowNotesTemplates(
supabase,
podcast.id,
bookingLinkId // or null
);
const result = await applyShowNotesTemplate(supabase, {
episodeId: 'uuid-here',
resolved, // { shared, hostPrivate, guest } of `string | null`
createGuestSections: true
});For the full resolution model, the guest-template snapshot, and the unresolved-tag highlight, see Show Notes Auto-Create & Per-Section Templates.
Initializing Collaboration
typescript
import {
createCollaborationProvider,
generateUserColor
} from '$lib/components/editor/collaboration-provider';
const provider = createCollaborationProvider({
supabase,
sectionId: 'section-uuid',
user: {
id: 'user-id',
name: 'User Name',
color: generateUserColor('user-id')
},
onStatusChange: (status) => console.log('Connection:', status),
onUsersChange: (users) => console.log('Active users:', users)
});Related Documentation
- Episode Chat - Real-time messaging system (separate from show notes editing)
- Supabase Integration - Database and auth patterns
- Architecture Overview - System architecture
- Multi-Tenancy - Permission model