cosveti-sync 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -167
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# cosveti-sync
|
|
2
2
|
|
|
3
|
-
Real-time collaborative editing for TipTap in Svelte with Convex synchronization
|
|
3
|
+
Real-time collaborative editing for TipTap in Svelte with Convex synchronization.
|
|
4
|
+
Translated to svelte from [React version](https://github.com/get-convex/prosemirror-sync)
|
|
4
5
|
|
|
5
6
|
## Features
|
|
6
7
|
|
|
@@ -26,6 +27,9 @@ yarn add cosveti-sync
|
|
|
26
27
|
|
|
27
28
|
# pnpm
|
|
28
29
|
pnpm add cosveti-sync
|
|
30
|
+
|
|
31
|
+
# bun
|
|
32
|
+
bun install cosveti-sync
|
|
29
33
|
```
|
|
30
34
|
|
|
31
35
|
This package has the following peer dependencies that you'll also need to install:
|
|
@@ -41,119 +45,33 @@ This package has the following peer dependencies that you'll also need to instal
|
|
|
41
45
|
First, set up the backend API in your Convex project. Create a file like `convex/tiptapSync.ts`:
|
|
42
46
|
|
|
43
47
|
```typescript
|
|
44
|
-
import { TiptapSyncSvelte } from '
|
|
45
|
-
import {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// Create the sync instance
|
|
52
|
-
const tiptapSyncSvelte = new TiptapSyncSvelte(tiptapSyncComponent);
|
|
53
|
-
|
|
54
|
-
// Export the sync API with optional permission checks
|
|
55
|
-
export const {
|
|
56
|
-
getSnapshot,
|
|
57
|
-
submitSnapshot,
|
|
58
|
-
latestVersion,
|
|
59
|
-
getSteps,
|
|
60
|
-
submitSteps
|
|
61
|
-
} = tiptapSyncSvelte.syncApi({
|
|
62
|
-
// Optional: Add read/write permission checks
|
|
63
|
-
checkRead: async (ctx, id) => {
|
|
64
|
-
// Add your authorization logic here
|
|
65
|
-
},
|
|
66
|
-
checkWrite: async (ctx, id) => {
|
|
67
|
-
// Add your authorization logic here
|
|
68
|
-
},
|
|
69
|
-
// Optional: Callback when a new snapshot is available
|
|
70
|
-
onSnapshot: async (ctx, id, snapshot, version) => {
|
|
71
|
-
// Do something when a new snapshot is saved
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
// Export the API for the client to use
|
|
76
|
-
export default {
|
|
77
|
-
tiptapSyncSvelte
|
|
78
|
-
};
|
|
48
|
+
import { TiptapSyncSvelte } from '../lib/client/index.js';
|
|
49
|
+
import { components } from './_generated/api.js';
|
|
50
|
+
|
|
51
|
+
const tiptapSyncSvelte = new TiptapSyncSvelte(components.tiptapSyncSvelte);
|
|
52
|
+
export const { getSnapshot, submitSnapshot, latestVersion, getSteps, submitSteps } =
|
|
53
|
+
tiptapSyncSvelte.syncApi();
|
|
54
|
+
|
|
79
55
|
```
|
|
80
56
|
|
|
81
|
-
### 2.
|
|
57
|
+
### 2. Convex Config
|
|
82
58
|
|
|
83
|
-
|
|
59
|
+
First, set up the backend API in your Convex project. Create a file like `convex/convex.config.ts`:
|
|
84
60
|
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
import { useTiptapSync } from 'cosveti-sync';
|
|
88
|
-
import { api } from '$convex/_generated/api';
|
|
89
|
-
import { useConvexClient } from 'convex-svelte';
|
|
90
|
-
import { Editor } from '@tiptap/core';
|
|
91
|
-
import { StarterKit } from '@tiptap/starter-kit';
|
|
92
|
-
import { onMount } from 'svelte';
|
|
93
|
-
|
|
94
|
-
// Get the Convex client (assuming convex-svelte is set up)
|
|
95
|
-
const convex = useConvexClient();
|
|
96
|
-
|
|
97
|
-
// Initialize the sync functionality
|
|
98
|
-
const { isSyncEnabled, extension, isLoading, initialContent, create } = $derived(
|
|
99
|
-
useTiptapSync(
|
|
100
|
-
convex,
|
|
101
|
-
api.tiptapSyncSvelte,
|
|
102
|
-
'your-document-id', // Unique document ID
|
|
103
|
-
{
|
|
104
|
-
onSyncError: (error) => {
|
|
105
|
-
console.error('Sync error:', error);
|
|
106
|
-
},
|
|
107
|
-
snapshotDebounceMs: 1000, // Debounce snapshot submissions (default: 1000ms)
|
|
108
|
-
debug: false // Enable debug logging (default: false)
|
|
109
|
-
}
|
|
110
|
-
)
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
let editorContainer: HTMLElement;
|
|
114
|
-
let editor: Editor;
|
|
115
|
-
|
|
116
|
-
onMount(() => {
|
|
117
|
-
if (!$extension) return;
|
|
118
|
-
|
|
119
|
-
editor = new Editor({
|
|
120
|
-
element: editorContainer,
|
|
121
|
-
extensions: [
|
|
122
|
-
StarterKit,
|
|
123
|
-
$extension // Add the sync extension from useTiptapSync
|
|
124
|
-
],
|
|
125
|
-
content: $initialContent
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
function handleCreateDocument() {
|
|
130
|
-
create({
|
|
131
|
-
type: 'doc',
|
|
132
|
-
content: [
|
|
133
|
-
{ type: 'paragraph', content: [{ type: 'text', text: 'Hello world!' }] }
|
|
134
|
-
]
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
</script>
|
|
61
|
+
```typescript
|
|
62
|
+
import { defineApp } from 'convex/server';
|
|
138
63
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
<div>
|
|
147
|
-
<button
|
|
148
|
-
on:click={() => $isSyncEnabled.update(n => !n)}
|
|
149
|
-
>
|
|
150
|
-
{$isSyncEnabled ? 'Pause Sync' : 'Resume Sync'}
|
|
151
|
-
</button>
|
|
152
|
-
<div bind:this={editorContainer}></div>
|
|
153
|
-
</div>
|
|
154
|
-
{/if}
|
|
64
|
+
import tiptapSyncSvelte from '../lib/component/convex.config.js';
|
|
65
|
+
|
|
66
|
+
const app = defineApp();
|
|
67
|
+
|
|
68
|
+
app.use(tiptapSyncSvelte);
|
|
69
|
+
|
|
70
|
+
export default app;
|
|
155
71
|
```
|
|
156
72
|
|
|
73
|
+
|
|
74
|
+
|
|
157
75
|
## API Documentation
|
|
158
76
|
|
|
159
77
|
### `useTiptapSync()`
|
|
@@ -203,68 +121,53 @@ Here's a complete example showing how to integrate the library:
|
|
|
203
121
|
|
|
204
122
|
```svelte
|
|
205
123
|
<script lang="ts">
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
initialContent,
|
|
221
|
-
create
|
|
222
|
-
} = $derived(
|
|
223
|
-
useTiptapSync(convex, api.tiptapSyncSvelte, documentId)
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
let editorContainer: HTMLElement;
|
|
227
|
-
let editor: Editor;
|
|
228
|
-
|
|
229
|
-
$: if ($extension && $initialContent && !editor) {
|
|
230
|
-
editor = new Editor({
|
|
231
|
-
element: editorContainer,
|
|
232
|
-
extensions: [
|
|
233
|
-
StarterKit,
|
|
234
|
-
$extension
|
|
235
|
-
],
|
|
236
|
-
content: $initialContent
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
onDestroy(() => {
|
|
241
|
-
editor?.destroy();
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
function handleCreate() {
|
|
245
|
-
create({
|
|
246
|
-
type: 'doc',
|
|
247
|
-
content: [{ type: 'paragraph', content: [{ type: 'text', text: 'New document' }] }]
|
|
248
|
-
});
|
|
249
|
-
}
|
|
124
|
+
import { useTiptapSync } from '$lib/index.js';
|
|
125
|
+
import { api } from '$convex/_generated/api.js';
|
|
126
|
+
import type { ConvexClient } from 'convex/browser';
|
|
127
|
+
import { useConvexClient } from 'convex-svelte';
|
|
128
|
+
import Editor from './Editor.svelte';
|
|
129
|
+
|
|
130
|
+
// This is if you have used convex-svelte pakage in +layout.svelte to setup the client
|
|
131
|
+
const convex: ConvexClient = useConvexClient();
|
|
132
|
+
|
|
133
|
+
const { isSyncEnabled, extension, isLoading, initialContent, create } = $derived(
|
|
134
|
+
useTiptapSync(convex, api.tiptapSyncSvelte, 'test-doc-id')
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
let editor = $state<Editor>();
|
|
250
138
|
</script>
|
|
251
139
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
140
|
+
{#if $isLoading}
|
|
141
|
+
Loading...
|
|
142
|
+
{:else if !$initialContent}
|
|
143
|
+
<button
|
|
144
|
+
onclick={() => {
|
|
145
|
+
create({ type: 'doc', content: [] });
|
|
146
|
+
}}
|
|
147
|
+
>Create doc
|
|
148
|
+
</button>
|
|
149
|
+
{:else if $extension != undefined}
|
|
150
|
+
<div class="py-4 text-center text-xl font-bold">
|
|
151
|
+
Shadcn Example
|
|
152
|
+
<button
|
|
153
|
+
onclick={() => {
|
|
154
|
+
isSyncEnabled.update((n) => !n);
|
|
155
|
+
console.log($isSyncEnabled);
|
|
156
|
+
}}
|
|
157
|
+
>
|
|
158
|
+
{#if $isSyncEnabled}Pause Sync{:else}Resume Sync{/if}
|
|
159
|
+
</button>
|
|
160
|
+
<div class="z-50 mt-12 size-full w-screen rounded-md border border-dashed bg-background">
|
|
161
|
+
<Editor
|
|
162
|
+
class="h-120 max-h-screen overflow-y-scroll pr-2 pl-6"
|
|
163
|
+
additionalExtensions={$extension}
|
|
164
|
+
content={$initialContent}
|
|
165
|
+
bind:editor
|
|
166
|
+
/>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
{/if}
|
|
170
|
+
|
|
268
171
|
```
|
|
269
172
|
|
|
270
173
|
## License
|