cosveti-sync
v0.0.8
Published
A convex component for syncing tiptap in a svelte project
Maintainers
Readme
cosveti-sync
Real-time collaborative editing for TipTap in Svelte with Convex synchronization.
Translated to svelte from React version
Features
- Real-time synchronization for TipTap editors across multiple clients
- Convex-powered backend for reliable data synchronization
- Optimistic updates for smooth user experience
- Client-side caching with localStorage fallback
- Configurable sync options and error handling
- Easy integration with SvelteKit projects
- Support for pausing/resuming synchronization
- Prosemirror collab protocol implementation
Installation
Install the package using your preferred package manager:
# npm
npm install cosveti-sync
# yarn
yarn add cosveti-sync
# pnpm
pnpm add cosveti-sync
# bun
bun install cosveti-syncThis package has the following peer dependencies that you'll also need to install:
@tiptap/pm(v3.18.0+)@tiptap/core(v3.18.0+)convex(v1.10.0+)svelte(v5.0.0+)
Basic Setup
1. Convex Config
First, set up the backend API in your Convex project. Create a file like convex/convex.config.ts:
import { defineApp } from 'convex/server';
import tiptapSync from 'cosveti-sync/convex.config.js';
const app = defineApp();
app.use(tiptapSync);
export default app;2. Convex Backend Configuration
First, set up the backend API in your Convex project. Create a file like convex/tiptapSync.ts:
import { TiptapSyncClient } from 'cosveti-sync';
import { components } from './_generated/api.js';
const tiptapSync = new TiptapSyncClient(components.tiptapSync);
export const { getSnapshot, submitSnapshot, latestVersion, getSteps, submitSteps } =
tiptapSync.syncApi();
API Documentation
useTiptapSync()
The main hook for enabling real-time synchronization.
Parameters
convex: Convex client instancesyncApi: The sync API object from your Convex backendid: Unique identifier for the documentopts(optional): Options object with the following properties:onSyncError: Callback function for handling synchronization errorssnapshotDebounceMs: Debounce time for snapshot submissions (default: 1000ms)debug: Enable debug logging (default: false)
Returns
isSyncEnabled: Svelte store for controlling sync enable/disable stateisLoading: Svelte store indicating if the initial content is loadinginitialContent: Svelte store with the initial document contentextension: TipTap extension to add to your editorcreate: Function to create a new document with initial content
Configuration Options
Sync Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| onSyncError | (error: Error) => void | undefined | Callback for handling synchronization errors |
| snapshotDebounceMs | number | 1000 | Debounce time in milliseconds for submitting snapshots |
| debug | boolean | false | Enable debug logging to console |
Backend API Options
When creating the sync API in Convex, you can configure:
checkRead: Function to check read permissionscheckWrite: Function to check write permissionsonSnapshot: Callback when new snapshots are availablepruneSnapshots: Whether to prune old snapshots (default: true)
Example Usage in Svelte
Here's a complete example showing how to integrate the library:
<script lang="ts">
import { useTiptapSync } from 'cosveti-sync/tiptap';
import { api } from '$convex/_generated/api.js';
import type { ConvexClient } from 'convex/browser';
import { useConvexClient } from 'convex-svelte';
import Editor from './Editor.svelte';
// This is if you have used convex-svelte pakage in +layout.svelte to setup the client
const convex: ConvexClient = useConvexClient();
const { isSyncEnabled, extension, isLoading, initialContent, create } = $derived(
useTiptapSync(convex, api.tiptapSyncSvelte, 'test-doc-id')
);
let editor = $state<Editor>();
</script>
{#if $isLoading}
Loading...
{:else if !$initialContent}
<button
onclick={() => {
create({ type: 'doc', content: [] });
}}
>Create doc
</button>
{:else if $extension != undefined}
<div class="py-4 text-center text-xl font-bold">
Shadcn Example
<button
onclick={() => {
isSyncEnabled.update((n) => !n);
console.log($isSyncEnabled);
}}
>
{#if $isSyncEnabled}Pause Sync{:else}Resume Sync{/if}
</button>
<div class="z-50 mt-12 size-full w-screen rounded-md border border-dashed bg-background">
<Editor
class="h-120 max-h-screen overflow-y-scroll pr-2 pl-6"
additionalExtensions={$extension}
content={$initialContent}
bind:editor
/>
</div>
</div>
{/if}
License
Apache-2.0
