Skip to content

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

ComponentLocationPurpose
Show Notes Servicesrc/lib/services/show-notes-service.tsCreates and manages show notes documents
Collaboration Providersrc/lib/components/editor/collaboration-provider.tsYjs + Supabase Realtime synchronization
Collaborative Editorsrc/lib/components/editor/CollaborativeEditor.svelteTipTap editor with collaboration
Presence Storesrc/lib/stores/presence.svelte.tsTracks active users per section
Cursor Extensionsrc/lib/components/editor/cursor-extension.tsRemote 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

DocumentDescription
Database SchemaTables, RLS policies, and triggers
Show Notes Auto-Create & Per-Section TemplatesResolution precedence, service API, guest snapshot, unresolved-tag highlight
Collaboration ProviderYjs + Supabase Realtime integration
Editor ComponentTipTap integration and UI
Presence SystemReal-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)
});

Internal documentation - Not for public distribution