@tangle-network/ui 11.4.0 → 11.6.0

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +27 -2
  3. package/dist/chat.js +3 -3
  4. package/dist/{chunk-4ORVXSMU.js → chunk-J2G7KCYH.js} +13 -14
  5. package/dist/{chunk-7O22XQ4D.js → chunk-PLWRAS5Z.js} +1 -1
  6. package/dist/{chunk-NZLLDHEG.js → chunk-QGTIBEPX.js} +608 -420
  7. package/dist/{chunk-SRAKCVPO.js → chunk-R5GVQDL3.js} +1 -1
  8. package/dist/{chunk-IH2VRPGZ.js → chunk-RKC3GXHE.js} +2 -2
  9. package/dist/{chunk-J3YZIQNI.js → chunk-XPYPZMYT.js} +1 -1
  10. package/dist/{chunk-HW4ROISY.js → chunk-XROQW5J7.js} +1 -1
  11. package/dist/{document-editor-pane-MX3FIZSE.js → document-editor-pane-A3JURPW5.js} +1 -1
  12. package/dist/{document-editor-pane-C3cYEDWZ.d.ts → document-editor-pane-IAEQn_Dl.d.ts} +8 -4
  13. package/dist/editor.d.ts +5 -4
  14. package/dist/editor.js +1 -1
  15. package/dist/files.d.ts +1 -1
  16. package/dist/files.js +1 -1
  17. package/dist/hooks.js +3 -3
  18. package/dist/index.d.ts +2 -2
  19. package/dist/index.js +12 -8
  20. package/dist/run.d.ts +48 -1
  21. package/dist/run.js +9 -5
  22. package/dist/sdk-hooks.js +3 -3
  23. package/package.json +1 -1
  24. package/src/editor/document-editor-pane.test.tsx +58 -0
  25. package/src/editor/editor-lazy.test.tsx +102 -0
  26. package/src/editor/editor-lazy.ts +40 -0
  27. package/src/editor/editor-loading.tsx +25 -0
  28. package/src/editor/editor-peers.test.ts +250 -0
  29. package/src/editor/editor-peers.ts +272 -0
  30. package/src/editor/editor-provider.test.tsx +226 -0
  31. package/src/editor/editor-provider.tsx +301 -265
  32. package/src/editor/editor-toolbar.tsx +1 -1
  33. package/src/editor/markdown-document-editor.test.tsx +121 -0
  34. package/src/editor/markdown-document-editor.tsx +114 -82
  35. package/src/editor/tiptap-editor.test.tsx +228 -0
  36. package/src/editor/tiptap-editor.tsx +162 -130
  37. package/src/run/index.ts +6 -0
  38. /package/dist/{chunk-47XH56SV.js → chunk-LQS34IGP.js} +0 -0
@@ -19,16 +19,12 @@ import { useEffect as useEffect5, useMemo as useMemo4, useState as useState3 } f
19
19
  import { PencilLine, Save, Users, Wifi, WifiOff } from "lucide-react";
20
20
 
21
21
  // src/editor/tiptap-editor.tsx
22
- import Collaboration from "@tiptap/extension-collaboration";
23
- import CollaborationCaret from "@tiptap/extension-collaboration-caret";
24
- import { EditorContent, useEditor } from "@tiptap/react";
25
- import StarterKit from "@tiptap/starter-kit";
26
- import { useEffect as useEffect2, useMemo as useMemo2 } from "react";
22
+ import { Suspense as Suspense2, useEffect as useEffect2, useMemo as useMemo2 } from "react";
27
23
 
28
24
  // src/editor/editor-provider.tsx
29
- import { HocuspocusProvider } from "@hocuspocus/provider";
30
25
  import {
31
26
  createContext,
27
+ Suspense,
32
28
  useCallback,
33
29
  useContext,
34
30
  useEffect,
@@ -36,7 +32,137 @@ import {
36
32
  useRef,
37
33
  useState
38
34
  } from "react";
39
- import * as Y from "yjs";
35
+
36
+ // src/editor/editor-lazy.ts
37
+ import { lazy } from "react";
38
+
39
+ // src/editor/editor-peers.ts
40
+ var DOCUMENT_PEERS_MISSING = "@tangle-network/ui/editor needs its optional editor peers. Install @tiptap/react and @tiptap/starter-kit.";
41
+ var PROVIDER_PEERS_MISSING = "@tangle-network/ui/editor needs its optional collaboration transport peers. Install @hocuspocus/provider and yjs.";
42
+ var COLLABORATION_PEERS_MISSING = "@tangle-network/ui/editor needs its optional collaboration peers. Install @tiptap/react, @tiptap/starter-kit, @tiptap/extension-collaboration, @tiptap/extension-collaboration-caret, @hocuspocus/provider and yjs.";
43
+ var MissingEditorPeersError = class extends Error {
44
+ constructor(message, options) {
45
+ super(message, options);
46
+ this.name = "MissingEditorPeersError";
47
+ }
48
+ };
49
+ function isMissingEditorPeersError(error) {
50
+ return error instanceof Error && error.name === "MissingEditorPeersError";
51
+ }
52
+ var RESOLUTION_FAILURE = /could not resolve|cannot find (?:module|package)|can't resolve|failed to resolve|module not found/i;
53
+ function isMissingPeerError(error) {
54
+ return error instanceof Error && RESOLUTION_FAILURE.test(error.message);
55
+ }
56
+ function rethrow(error) {
57
+ throw error;
58
+ }
59
+ var DEFERRED_PEERS = [
60
+ "@tiptap/core",
61
+ "@tiptap/react",
62
+ "@tiptap/starter-kit",
63
+ "@tiptap/extension-collaboration",
64
+ "@tiptap/extension-collaboration-caret",
65
+ "@hocuspocus/provider",
66
+ "yjs"
67
+ ];
68
+ function unresolvedPeersFrom(error) {
69
+ if (!(error instanceof Error)) return [];
70
+ const named = DEFERRED_PEERS.filter((name) => error.message.includes(name));
71
+ return named.filter(
72
+ (name) => !named.some((other) => other !== name && other.includes(name))
73
+ );
74
+ }
75
+ function asMissingEditorPeersError(error, missingMessage) {
76
+ if (!isMissingPeerError(error)) return error;
77
+ const unresolved = unresolvedPeersFrom(error);
78
+ if (unresolved.length === 0) return error;
79
+ const detail = unresolved.length === 1 ? ` ${unresolved[0]} did not resolve.` : "";
80
+ return new MissingEditorPeersError(missingMessage + detail, { cause: error });
81
+ }
82
+ async function loadPeers(load, missingMessage) {
83
+ try {
84
+ return await load();
85
+ } catch (error) {
86
+ throw asMissingEditorPeersError(error, missingMessage);
87
+ }
88
+ }
89
+ function isConfigurableExtension(value) {
90
+ return typeof value?.configure === "function";
91
+ }
92
+ function assertDocumentEditorPeers(peers, missingMessage) {
93
+ if (typeof peers.react.useEditor !== "function" || peers.react.EditorContent === void 0 || !isConfigurableExtension(peers.starterKit.default)) {
94
+ throw new MissingEditorPeersError(missingMessage);
95
+ }
96
+ }
97
+ function assertEditorProviderPeers(peers, missingMessage) {
98
+ if (typeof peers.hocuspocus.HocuspocusProvider !== "function" || typeof peers.yjs.Doc !== "function") {
99
+ throw new MissingEditorPeersError(missingMessage);
100
+ }
101
+ }
102
+ function assertCollaborationPeers(peers) {
103
+ assertDocumentEditorPeers(peers, COLLABORATION_PEERS_MISSING);
104
+ assertEditorProviderPeers(peers, COLLABORATION_PEERS_MISSING);
105
+ if (!isConfigurableExtension(peers.collaboration.default) || !isConfigurableExtension(peers.collaborationCaret.default)) {
106
+ throw new MissingEditorPeersError(COLLABORATION_PEERS_MISSING);
107
+ }
108
+ }
109
+ async function importDocumentEditorPeers() {
110
+ const [react, starterKit] = await Promise.all([
111
+ import("@tiptap/react").catch(rethrow),
112
+ import("@tiptap/starter-kit").catch(rethrow)
113
+ ]);
114
+ return { react, starterKit };
115
+ }
116
+ async function importEditorProviderPeers() {
117
+ const [hocuspocus, yjs] = await Promise.all([
118
+ import("@hocuspocus/provider").catch(rethrow),
119
+ import("yjs").catch(rethrow)
120
+ ]);
121
+ return { hocuspocus, yjs };
122
+ }
123
+ async function loadEditorProviderPeers() {
124
+ const peers = await loadPeers(importEditorProviderPeers, PROVIDER_PEERS_MISSING);
125
+ assertEditorProviderPeers(peers, PROVIDER_PEERS_MISSING);
126
+ return peers;
127
+ }
128
+ async function loadDocumentEditorPeers() {
129
+ const peers = await loadPeers(importDocumentEditorPeers, DOCUMENT_PEERS_MISSING);
130
+ assertDocumentEditorPeers(peers, DOCUMENT_PEERS_MISSING);
131
+ return peers;
132
+ }
133
+ async function loadCollaborationPeers() {
134
+ const peers = await loadPeers(async () => {
135
+ const [documentPeers, providerPeers, collaboration, collaborationCaret] = await Promise.all([
136
+ importDocumentEditorPeers(),
137
+ importEditorProviderPeers(),
138
+ import("@tiptap/extension-collaboration").catch(rethrow),
139
+ import("@tiptap/extension-collaboration-caret").catch(rethrow)
140
+ ]);
141
+ return { ...documentPeers, ...providerPeers, collaboration, collaborationCaret };
142
+ }, COLLABORATION_PEERS_MISSING);
143
+ assertCollaborationPeers(peers);
144
+ return peers;
145
+ }
146
+
147
+ // src/editor/editor-lazy.ts
148
+ function retryableLazyEditor(loadComponent) {
149
+ function build() {
150
+ return lazy(async () => {
151
+ try {
152
+ return { default: await loadComponent() };
153
+ } catch (error) {
154
+ if (!isMissingEditorPeersError(error)) {
155
+ current = build();
156
+ }
157
+ throw error;
158
+ }
159
+ });
160
+ }
161
+ let current = build();
162
+ return () => current;
163
+ }
164
+
165
+ // src/editor/editor-provider.tsx
40
166
  import { jsx } from "react/jsx-runtime";
41
167
  var EditorContext = createContext(null);
42
168
  function generateUserColor() {
@@ -64,225 +190,236 @@ function generateUserColor() {
64
190
  ];
65
191
  return colors[Math.floor(Math.random() * colors.length)];
66
192
  }
67
- function EditorProvider({
68
- websocketUrl,
69
- documentName,
70
- token,
71
- tokenExpiresAt,
72
- user,
73
- autoConnect = true,
74
- autoReconnect = true,
75
- maxReconnectAttempts = 5,
76
- onConnectionChange,
77
- onSync,
78
- onAuthError,
79
- onRefreshToken,
80
- children
81
- }) {
82
- const [connectionState, setConnectionState] = useState("disconnected");
83
- const [collaborators, setCollaborators] = useState([]);
84
- const [isSynced, setIsSynced] = useState(false);
85
- const docRef = useRef(null);
86
- const providerRef = useRef(null);
87
- const reconnectAttemptsRef = useRef(0);
88
- const tokenRef = useRef(token);
89
- const tokenExpiryRef = useRef(tokenExpiresAt);
90
- const refreshPromiseRef = useRef(null);
91
- const refreshTimerRef = useRef(null);
92
- tokenRef.current = token;
93
- tokenExpiryRef.current = tokenExpiresAt;
94
- if (!docRef.current) {
95
- docRef.current = new Y.Doc();
96
- }
97
- const doc = docRef.current;
98
- const userColor = useMemo(
99
- () => user.color ?? generateUserColor(),
100
- [user.color]
101
- );
102
- const updateConnectionState = useCallback(
103
- (state) => {
104
- setConnectionState(state);
105
- onConnectionChange?.(state);
106
- },
107
- [onConnectionChange]
108
- );
109
- const updateCollaborators = useCallback(
110
- (awareness) => {
111
- if (!awareness) return;
112
- const states = awareness.getStates();
113
- const collabs = [];
114
- states.forEach((state, clientId) => {
115
- if (clientId === awareness.clientID) return;
116
- if (state.user) {
117
- collabs.push({
118
- clientId,
119
- user: state.user
120
- });
121
- }
122
- });
123
- setCollaborators(collabs);
124
- },
125
- []
126
- );
127
- const clearRefreshTimer = useCallback(() => {
128
- if (refreshTimerRef.current != null) {
129
- window.clearTimeout(refreshTimerRef.current);
130
- refreshTimerRef.current = null;
131
- }
132
- }, []);
133
- const refreshToken = useCallback(async () => {
134
- if (!onRefreshToken) {
135
- return null;
136
- }
137
- if (refreshPromiseRef.current) {
138
- return refreshPromiseRef.current;
139
- }
140
- const refreshPromise = (async () => {
141
- const next = await onRefreshToken();
142
- const resolvedToken = typeof next === "string" ? next : next.token;
143
- const resolvedExpiry = typeof next === "string" ? void 0 : next.expiresAt;
144
- tokenRef.current = resolvedToken;
145
- tokenExpiryRef.current = resolvedExpiry;
146
- return resolvedToken;
147
- })().catch((error) => {
148
- onAuthError?.(
149
- error instanceof Error ? error : new Error(String(error))
150
- );
151
- return null;
152
- }).finally(() => {
153
- refreshPromiseRef.current = null;
154
- });
155
- refreshPromiseRef.current = refreshPromise;
156
- return refreshPromise;
157
- }, [onAuthError, onRefreshToken]);
158
- const scheduleTokenRefresh = useCallback(() => {
159
- clearRefreshTimer();
160
- if (!tokenExpiryRef.current || !onRefreshToken || typeof window === "undefined") {
161
- return;
162
- }
163
- const refreshAtMs = tokenExpiryRef.current * 1e3 - 6e4;
164
- const delay = refreshAtMs - Date.now();
165
- if (delay <= 0) {
166
- void refreshToken();
167
- return;
168
- }
169
- refreshTimerRef.current = window.setTimeout(() => {
170
- void refreshToken();
171
- }, delay);
172
- }, [clearRefreshTimer, onRefreshToken, refreshToken]);
173
- const connect = useCallback(() => {
174
- if (providerRef.current) {
175
- providerRef.current.connect();
176
- return;
177
- }
178
- updateConnectionState("connecting");
179
- const provider = new HocuspocusProvider({
180
- url: websocketUrl,
181
- name: documentName,
182
- document: doc,
183
- token: async () => tokenRef.current,
184
- onConnect: () => {
185
- reconnectAttemptsRef.current = 0;
186
- updateConnectionState("connected");
187
- scheduleTokenRefresh();
188
- },
189
- onSynced: () => {
190
- setIsSynced(true);
191
- updateConnectionState("synced");
192
- onSync?.();
193
- },
194
- onDisconnect: () => {
195
- updateConnectionState("disconnected");
196
- setIsSynced(false);
197
- clearRefreshTimer();
198
- if (autoReconnect && reconnectAttemptsRef.current < maxReconnectAttempts) {
199
- reconnectAttemptsRef.current += 1;
200
- const delay = Math.min(
201
- 1e3 * 2 ** reconnectAttemptsRef.current,
202
- 3e4
203
- );
204
- setTimeout(() => {
205
- if (providerRef.current && !providerRef.current.isConnected) {
206
- providerRef.current.connect();
207
- }
208
- }, delay);
209
- }
210
- },
211
- onAuthenticationFailed: ({ reason }) => {
212
- const error = new Error(reason ?? "Authentication failed");
213
- updateConnectionState("disconnected");
214
- clearRefreshTimer();
215
- if (onRefreshToken) {
216
- void refreshToken().then((nextToken) => {
217
- if (nextToken && providerRef.current) {
218
- providerRef.current.connect();
219
- return;
220
- }
221
- onAuthError?.(error);
222
- });
223
- return;
224
- }
225
- onAuthError?.(error);
226
- },
227
- onAwarenessUpdate: () => {
228
- updateCollaborators(provider.awareness);
229
- }
230
- });
231
- provider.awareness?.setLocalStateField("user", {
232
- name: user.name,
233
- color: userColor,
234
- userId: user.userId
235
- });
236
- providerRef.current = provider;
237
- }, [
193
+ function createEditorProvider(peers) {
194
+ const { Doc } = peers.yjs;
195
+ const { HocuspocusProvider } = peers.hocuspocus;
196
+ return function EditorProvider2({
238
197
  websocketUrl,
239
198
  documentName,
240
- doc,
241
- user.name,
242
- user.userId,
243
- userColor,
244
- autoReconnect,
245
- maxReconnectAttempts,
246
- clearRefreshTimer,
247
- updateConnectionState,
248
- updateCollaborators,
199
+ token,
200
+ tokenExpiresAt,
201
+ user,
202
+ autoConnect = true,
203
+ autoReconnect = true,
204
+ maxReconnectAttempts = 5,
205
+ onConnectionChange,
249
206
  onSync,
250
207
  onAuthError,
251
208
  onRefreshToken,
252
- refreshToken,
253
- scheduleTokenRefresh
254
- ]);
255
- const disconnect = useCallback(() => {
256
- if (providerRef.current) {
257
- providerRef.current.disconnect();
258
- updateConnectionState("disconnected");
259
- }
260
- }, [updateConnectionState]);
261
- useEffect(() => {
262
- if (autoConnect) {
263
- connect();
209
+ children
210
+ }) {
211
+ const [connectionState, setConnectionState] = useState("disconnected");
212
+ const [collaborators, setCollaborators] = useState([]);
213
+ const [isSynced, setIsSynced] = useState(false);
214
+ const docRef = useRef(null);
215
+ const providerRef = useRef(null);
216
+ const reconnectAttemptsRef = useRef(0);
217
+ const tokenRef = useRef(token);
218
+ const tokenExpiryRef = useRef(tokenExpiresAt);
219
+ const refreshPromiseRef = useRef(null);
220
+ const refreshTimerRef = useRef(null);
221
+ tokenRef.current = token;
222
+ tokenExpiryRef.current = tokenExpiresAt;
223
+ if (!docRef.current) {
224
+ docRef.current = new Doc();
264
225
  }
265
- return () => {
226
+ const doc = docRef.current;
227
+ const userColor = useMemo(
228
+ () => user.color ?? generateUserColor(),
229
+ [user.color]
230
+ );
231
+ const updateConnectionState = useCallback(
232
+ (state) => {
233
+ setConnectionState(state);
234
+ onConnectionChange?.(state);
235
+ },
236
+ [onConnectionChange]
237
+ );
238
+ const updateCollaborators = useCallback(
239
+ (awareness) => {
240
+ if (!awareness) return;
241
+ const states = awareness.getStates();
242
+ const collabs = [];
243
+ states.forEach((state, clientId) => {
244
+ if (clientId === awareness.clientID) return;
245
+ if (state.user) {
246
+ collabs.push({
247
+ clientId,
248
+ user: state.user
249
+ });
250
+ }
251
+ });
252
+ setCollaborators(collabs);
253
+ },
254
+ []
255
+ );
256
+ const clearRefreshTimer = useCallback(() => {
257
+ if (refreshTimerRef.current != null) {
258
+ window.clearTimeout(refreshTimerRef.current);
259
+ refreshTimerRef.current = null;
260
+ }
261
+ }, []);
262
+ const refreshToken = useCallback(async () => {
263
+ if (!onRefreshToken) {
264
+ return null;
265
+ }
266
+ if (refreshPromiseRef.current) {
267
+ return refreshPromiseRef.current;
268
+ }
269
+ const refreshPromise = (async () => {
270
+ const next = await onRefreshToken();
271
+ const resolvedToken = typeof next === "string" ? next : next.token;
272
+ const resolvedExpiry = typeof next === "string" ? void 0 : next.expiresAt;
273
+ tokenRef.current = resolvedToken;
274
+ tokenExpiryRef.current = resolvedExpiry;
275
+ return resolvedToken;
276
+ })().catch((error) => {
277
+ onAuthError?.(
278
+ error instanceof Error ? error : new Error(String(error))
279
+ );
280
+ return null;
281
+ }).finally(() => {
282
+ refreshPromiseRef.current = null;
283
+ });
284
+ refreshPromiseRef.current = refreshPromise;
285
+ return refreshPromise;
286
+ }, [onAuthError, onRefreshToken]);
287
+ const scheduleTokenRefresh = useCallback(() => {
266
288
  clearRefreshTimer();
289
+ if (!tokenExpiryRef.current || !onRefreshToken || typeof window === "undefined") {
290
+ return;
291
+ }
292
+ const refreshAtMs = tokenExpiryRef.current * 1e3 - 6e4;
293
+ const delay = refreshAtMs - Date.now();
294
+ if (delay <= 0) {
295
+ void refreshToken();
296
+ return;
297
+ }
298
+ refreshTimerRef.current = window.setTimeout(() => {
299
+ void refreshToken();
300
+ }, delay);
301
+ }, [clearRefreshTimer, onRefreshToken, refreshToken]);
302
+ const connect = useCallback(() => {
267
303
  if (providerRef.current) {
268
- providerRef.current.destroy();
269
- providerRef.current = null;
304
+ providerRef.current.connect();
305
+ return;
270
306
  }
271
- };
272
- }, [autoConnect, clearRefreshTimer, connect]);
273
- const contextValue = useMemo(
274
- () => ({
307
+ updateConnectionState("connecting");
308
+ const provider = new HocuspocusProvider({
309
+ url: websocketUrl,
310
+ name: documentName,
311
+ document: doc,
312
+ token: async () => tokenRef.current,
313
+ onConnect: () => {
314
+ reconnectAttemptsRef.current = 0;
315
+ updateConnectionState("connected");
316
+ scheduleTokenRefresh();
317
+ },
318
+ onSynced: () => {
319
+ setIsSynced(true);
320
+ updateConnectionState("synced");
321
+ onSync?.();
322
+ },
323
+ onDisconnect: () => {
324
+ updateConnectionState("disconnected");
325
+ setIsSynced(false);
326
+ clearRefreshTimer();
327
+ if (autoReconnect && reconnectAttemptsRef.current < maxReconnectAttempts) {
328
+ reconnectAttemptsRef.current += 1;
329
+ const delay = Math.min(
330
+ 1e3 * 2 ** reconnectAttemptsRef.current,
331
+ 3e4
332
+ );
333
+ setTimeout(() => {
334
+ if (providerRef.current && !providerRef.current.isConnected) {
335
+ providerRef.current.connect();
336
+ }
337
+ }, delay);
338
+ }
339
+ },
340
+ onAuthenticationFailed: ({ reason }) => {
341
+ const error = new Error(reason ?? "Authentication failed");
342
+ updateConnectionState("disconnected");
343
+ clearRefreshTimer();
344
+ if (onRefreshToken) {
345
+ void refreshToken().then((nextToken) => {
346
+ if (nextToken && providerRef.current) {
347
+ providerRef.current.connect();
348
+ return;
349
+ }
350
+ onAuthError?.(error);
351
+ });
352
+ return;
353
+ }
354
+ onAuthError?.(error);
355
+ },
356
+ onAwarenessUpdate: () => {
357
+ updateCollaborators(provider.awareness);
358
+ }
359
+ });
360
+ provider.awareness?.setLocalStateField("user", {
361
+ name: user.name,
362
+ color: userColor,
363
+ userId: user.userId
364
+ });
365
+ providerRef.current = provider;
366
+ }, [
367
+ websocketUrl,
368
+ documentName,
275
369
  doc,
276
- provider: providerRef.current,
277
- connectionState,
278
- collaborators,
279
- isSynced,
280
- connect,
281
- disconnect
282
- }),
283
- [doc, connectionState, collaborators, isSynced, connect, disconnect]
284
- );
285
- return /* @__PURE__ */ jsx(EditorContext.Provider, { value: contextValue, children });
370
+ user.name,
371
+ user.userId,
372
+ userColor,
373
+ autoReconnect,
374
+ maxReconnectAttempts,
375
+ clearRefreshTimer,
376
+ updateConnectionState,
377
+ updateCollaborators,
378
+ onSync,
379
+ onAuthError,
380
+ onRefreshToken,
381
+ refreshToken,
382
+ scheduleTokenRefresh
383
+ ]);
384
+ const disconnect = useCallback(() => {
385
+ if (providerRef.current) {
386
+ providerRef.current.disconnect();
387
+ updateConnectionState("disconnected");
388
+ }
389
+ }, [updateConnectionState]);
390
+ useEffect(() => {
391
+ if (autoConnect) {
392
+ connect();
393
+ }
394
+ return () => {
395
+ clearRefreshTimer();
396
+ if (providerRef.current) {
397
+ providerRef.current.destroy();
398
+ providerRef.current = null;
399
+ }
400
+ };
401
+ }, [autoConnect, clearRefreshTimer, connect]);
402
+ const contextValue = useMemo(
403
+ () => ({
404
+ doc,
405
+ provider: providerRef.current,
406
+ connectionState,
407
+ collaborators,
408
+ isSynced,
409
+ connect,
410
+ disconnect
411
+ }),
412
+ [doc, connectionState, collaborators, isSynced, connect, disconnect]
413
+ );
414
+ return /* @__PURE__ */ jsx(EditorContext.Provider, { value: contextValue, children });
415
+ };
416
+ }
417
+ var lazyEditorProvider = retryableLazyEditor(
418
+ async () => createEditorProvider(await loadEditorProviderPeers())
419
+ );
420
+ function EditorProvider(props) {
421
+ const LazyEditorProvider = lazyEditorProvider();
422
+ return /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(LazyEditorProvider, { ...props }) });
286
423
  }
287
424
  function useEditorContext() {
288
425
  const context = useContext(EditorContext);
@@ -292,8 +429,25 @@ function useEditorContext() {
292
429
  return context;
293
430
  }
294
431
 
295
- // src/editor/editor-toolbar.tsx
432
+ // src/editor/editor-loading.tsx
296
433
  import { jsx as jsx2 } from "react/jsx-runtime";
434
+ function EditorLoadingPlaceholder({ className }) {
435
+ return /* @__PURE__ */ jsx2(
436
+ "div",
437
+ {
438
+ role: "status",
439
+ "aria-live": "polite",
440
+ className: cn(
441
+ "flex w-full items-center justify-center rounded-lg border border-border border-dashed bg-muted text-muted-foreground text-sm",
442
+ className
443
+ ),
444
+ children: "Loading editor\u2026"
445
+ }
446
+ );
447
+ }
448
+
449
+ // src/editor/editor-toolbar.tsx
450
+ import { jsx as jsx3 } from "react/jsx-runtime";
297
451
  function EditorToolbar({
298
452
  editor,
299
453
  className
@@ -375,7 +529,7 @@ function EditorToolbar({
375
529
  isActive: editor.isActive("blockquote")
376
530
  }
377
531
  ];
378
- return /* @__PURE__ */ jsx2(
532
+ return /* @__PURE__ */ jsx3(
379
533
  "div",
380
534
  {
381
535
  className: cn(
@@ -384,9 +538,9 @@ function EditorToolbar({
384
538
  ),
385
539
  children: buttons.map((button) => {
386
540
  if ("type" in button && button.type === "separator") {
387
- return /* @__PURE__ */ jsx2("div", { className: "mx-1 h-6 w-px bg-border" }, button.id);
541
+ return /* @__PURE__ */ jsx3("div", { className: "mx-1 h-6 w-px bg-border" }, button.id);
388
542
  }
389
- return /* @__PURE__ */ jsx2(
543
+ return /* @__PURE__ */ jsx3(
390
544
  "button",
391
545
  {
392
546
  onClick: button.action,
@@ -407,7 +561,7 @@ function EditorToolbar({
407
561
  }
408
562
 
409
563
  // src/editor/tiptap-editor.tsx
410
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
564
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
411
565
  var cursorColors = {
412
566
  "#FF6B6B": { background: "#FF6B6B", text: "#FFFFFF" },
413
567
  "#4ECDC4": { background: "#4ECDC4", text: "#000000" },
@@ -423,107 +577,112 @@ var cursorColors = {
423
577
  function getCursorColors(color) {
424
578
  return cursorColors[color] ?? { background: color, text: "#FFFFFF" };
425
579
  }
426
- function TiptapEditor({
427
- initialContent,
428
- placeholder = "Start writing...",
429
- readOnly = false,
430
- autoFocus = false,
431
- className,
432
- contentClassName,
433
- onUpdate,
434
- onSelectionUpdate,
435
- onReady
436
- }) {
437
- const { doc, provider, connectionState } = useEditorContext();
438
- const fragment = useMemo2(() => doc.getXmlFragment("prosemirror"), [doc]);
439
- const extensions = useMemo2(() => {
440
- const baseExtensions = [
441
- StarterKit.configure({
442
- // Disable history - Y.js handles undo/redo
443
- ...{ history: false },
444
- // Configure code block for syntax highlighting placeholder
445
- codeBlock: {
446
- HTMLAttributes: {
447
- class: "hljs"
448
- }
449
- }
450
- }),
451
- Collaboration.configure({
452
- fragment
453
- })
454
- ];
455
- if (provider?.awareness) {
456
- baseExtensions.push(
457
- CollaborationCaret.configure({
458
- provider,
459
- user: provider.awareness.getLocalState()?.user ?? {
460
- name: "Anonymous",
461
- color: "#808080"
462
- },
463
- render: (user) => {
464
- const { background, text } = getCursorColors(user.color);
465
- const cursor = document.createElement("span");
466
- cursor.className = "collaboration-cursor";
467
- cursor.style.borderColor = background;
468
- const label = document.createElement("span");
469
- label.className = "collaboration-cursor-label";
470
- label.style.backgroundColor = background;
471
- label.style.color = text;
472
- label.textContent = user.name;
473
- cursor.appendChild(label);
474
- return cursor;
580
+ function createTiptapEditor(peers) {
581
+ const { EditorContent, useEditor } = peers.react;
582
+ const StarterKit = peers.starterKit.default;
583
+ const Collaboration = peers.collaboration.default;
584
+ const CollaborationCaret = peers.collaborationCaret.default;
585
+ return function TiptapEditor2({
586
+ initialContent,
587
+ placeholder = "Start writing...",
588
+ readOnly = false,
589
+ autoFocus = false,
590
+ className,
591
+ contentClassName,
592
+ onUpdate,
593
+ onSelectionUpdate,
594
+ onReady
595
+ }) {
596
+ const { doc, provider, connectionState } = useEditorContext();
597
+ const fragment = useMemo2(() => doc.getXmlFragment("prosemirror"), [doc]);
598
+ const extensions = useMemo2(() => {
599
+ const baseExtensions = [
600
+ StarterKit.configure({
601
+ // Disable history - Y.js handles undo/redo
602
+ ...{ history: false },
603
+ // Configure code block for syntax highlighting placeholder
604
+ codeBlock: {
605
+ HTMLAttributes: {
606
+ class: "hljs"
607
+ }
475
608
  }
609
+ }),
610
+ Collaboration.configure({
611
+ fragment
476
612
  })
477
- );
478
- }
479
- return baseExtensions;
480
- }, [fragment, provider]);
481
- const editor = useEditor({
482
- extensions,
483
- editable: !readOnly,
484
- autofocus: autoFocus,
485
- editorProps: {
486
- attributes: {
487
- class: cn(
488
- "prose prose-sm sm:prose-base dark:prose-invert max-w-none",
489
- "focus:outline-none",
490
- contentClassName
491
- ),
492
- "data-placeholder": placeholder
613
+ ];
614
+ if (provider?.awareness) {
615
+ baseExtensions.push(
616
+ CollaborationCaret.configure({
617
+ provider,
618
+ user: provider.awareness.getLocalState()?.user ?? {
619
+ name: "Anonymous",
620
+ color: "#808080"
621
+ },
622
+ render: (user) => {
623
+ const { background, text } = getCursorColors(user.color);
624
+ const cursor = document.createElement("span");
625
+ cursor.className = "collaboration-cursor";
626
+ cursor.style.borderColor = background;
627
+ const label = document.createElement("span");
628
+ label.className = "collaboration-cursor-label";
629
+ label.style.backgroundColor = background;
630
+ label.style.color = text;
631
+ label.textContent = user.name;
632
+ cursor.appendChild(label);
633
+ return cursor;
634
+ }
635
+ })
636
+ );
493
637
  }
494
- },
495
- onUpdate: ({ editor: ed }) => {
496
- onUpdate?.(ed);
497
- },
498
- onSelectionUpdate: ({ editor: ed }) => {
499
- onSelectionUpdate?.(ed);
500
- },
501
- onCreate: ({ editor: ed }) => {
502
- onReady?.(ed);
503
- }
504
- });
505
- useEffect2(() => {
506
- if (editor) {
507
- editor.setEditable(!readOnly);
508
- }
509
- }, [editor, readOnly]);
510
- useEffect2(() => {
511
- if (editor && initialContent && connectionState === "synced" && editor.isEmpty) {
512
- editor.commands.setContent(initialContent);
513
- }
514
- }, [editor, initialContent, connectionState]);
515
- return /* @__PURE__ */ jsxs(
516
- "div",
517
- {
518
- className: cn(
519
- "relative min-h-[200px] w-full rounded-lg border border-border",
520
- "bg-background",
521
- className
522
- ),
523
- children: [
524
- /* @__PURE__ */ jsx3("div", { className: "absolute top-2 right-2 z-10", children: /* @__PURE__ */ jsx3(ConnectionIndicator, { state: connectionState }) }),
525
- /* @__PURE__ */ jsx3("div", { className: "p-4 pt-10", children: /* @__PURE__ */ jsx3(EditorContent, { editor }) }),
526
- /* @__PURE__ */ jsx3("style", { children: `
638
+ return baseExtensions;
639
+ }, [fragment, provider]);
640
+ const editor = useEditor({
641
+ extensions,
642
+ editable: !readOnly,
643
+ autofocus: autoFocus,
644
+ editorProps: {
645
+ attributes: {
646
+ class: cn(
647
+ "prose prose-sm sm:prose-base dark:prose-invert max-w-none",
648
+ "focus:outline-none",
649
+ contentClassName
650
+ ),
651
+ "data-placeholder": placeholder
652
+ }
653
+ },
654
+ onUpdate: ({ editor: ed }) => {
655
+ onUpdate?.(ed);
656
+ },
657
+ onSelectionUpdate: ({ editor: ed }) => {
658
+ onSelectionUpdate?.(ed);
659
+ },
660
+ onCreate: ({ editor: ed }) => {
661
+ onReady?.(ed);
662
+ }
663
+ });
664
+ useEffect2(() => {
665
+ if (editor) {
666
+ editor.setEditable(!readOnly);
667
+ }
668
+ }, [editor, readOnly]);
669
+ useEffect2(() => {
670
+ if (editor && initialContent && connectionState === "synced" && editor.isEmpty) {
671
+ editor.commands.setContent(initialContent);
672
+ }
673
+ }, [editor, initialContent, connectionState]);
674
+ return /* @__PURE__ */ jsxs(
675
+ "div",
676
+ {
677
+ className: cn(
678
+ "relative min-h-[200px] w-full rounded-lg border border-border",
679
+ "bg-background",
680
+ className
681
+ ),
682
+ children: [
683
+ /* @__PURE__ */ jsx4("div", { className: "absolute top-2 right-2 z-10", children: /* @__PURE__ */ jsx4(ConnectionIndicator, { state: connectionState }) }),
684
+ /* @__PURE__ */ jsx4("div", { className: "p-4 pt-10", children: /* @__PURE__ */ jsx4(EditorContent, { editor }) }),
685
+ /* @__PURE__ */ jsx4("style", { children: `
527
686
  .ProseMirror p.is-editor-empty:first-child::before {
528
687
  content: attr(data-placeholder);
529
688
  float: left;
@@ -586,7 +745,21 @@ function TiptapEditor({
586
745
  font-size: inherit;
587
746
  }
588
747
  ` })
589
- ]
748
+ ]
749
+ }
750
+ );
751
+ };
752
+ }
753
+ var lazyTiptapEditor = retryableLazyEditor(
754
+ async () => createTiptapEditor(await loadCollaborationPeers())
755
+ );
756
+ function TiptapEditor(props) {
757
+ const LazyTiptapEditor = lazyTiptapEditor();
758
+ return /* @__PURE__ */ jsx4(
759
+ Suspense2,
760
+ {
761
+ fallback: /* @__PURE__ */ jsx4(EditorLoadingPlaceholder, { className: cn("min-h-[200px]", props.className) }),
762
+ children: /* @__PURE__ */ jsx4(LazyTiptapEditor, { ...props })
590
763
  }
591
764
  );
592
765
  }
@@ -612,8 +785,8 @@ function ConnectionIndicator({
612
785
  }
613
786
  }[state];
614
787
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-muted-foreground text-xs", children: [
615
- /* @__PURE__ */ jsx3("span", { className: cn("h-2 w-2 rounded-full", config.color) }),
616
- /* @__PURE__ */ jsx3("span", { children: config.label })
788
+ /* @__PURE__ */ jsx4("span", { className: cn("h-2 w-2 rounded-full", config.color) }),
789
+ /* @__PURE__ */ jsx4("span", { children: config.label })
617
790
  ] });
618
791
  }
619
792
  function CollaboratorsList({
@@ -624,7 +797,7 @@ function CollaboratorsList({
624
797
  return null;
625
798
  }
626
799
  return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-1", className), children: [
627
- collaborators.slice(0, 5).map((collab) => /* @__PURE__ */ jsx3(
800
+ collaborators.slice(0, 5).map((collab) => /* @__PURE__ */ jsx4(
628
801
  "div",
629
802
  {
630
803
  className: "flex h-6 w-6 items-center justify-center rounded-full font-medium text-xs",
@@ -642,9 +815,7 @@ function CollaboratorsList({
642
815
  }
643
816
 
644
817
  // src/editor/markdown-document-editor.tsx
645
- import { EditorContent as EditorContent2, useEditor as useEditor2 } from "@tiptap/react";
646
- import StarterKit2 from "@tiptap/starter-kit";
647
- import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2 } from "react";
818
+ import { Suspense as Suspense3, useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2 } from "react";
648
819
 
649
820
  // src/editor/markdown-conversion.ts
650
821
  import { marked } from "marked";
@@ -666,85 +837,88 @@ function normalizeMarkdown(markdown) {
666
837
  }
667
838
 
668
839
  // src/editor/markdown-document-editor.tsx
669
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
670
- function MarkdownDocumentEditor({
671
- value = "",
672
- placeholder = "Start writing...",
673
- readOnly = false,
674
- autoFocus = false,
675
- className,
676
- contentClassName,
677
- onChange,
678
- onReady
679
- }) {
680
- const initialHtml = useMemo3(() => markdownToHtml(value), []);
681
- const lastAppliedMarkdownRef = useRef2(normalizeMarkdown(value));
682
- const editor = useEditor2({
683
- extensions: [
684
- StarterKit2.configure({
685
- codeBlock: {
686
- HTMLAttributes: {
687
- class: "hljs"
840
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
841
+ function createMarkdownDocumentEditor(peers) {
842
+ const { EditorContent, useEditor } = peers.react;
843
+ const StarterKit = peers.starterKit.default;
844
+ return function MarkdownDocumentEditor2({
845
+ value = "",
846
+ placeholder = "Start writing...",
847
+ readOnly = false,
848
+ autoFocus = false,
849
+ className,
850
+ contentClassName,
851
+ onChange,
852
+ onReady
853
+ }) {
854
+ const initialHtml = useMemo3(() => markdownToHtml(value), []);
855
+ const lastAppliedMarkdownRef = useRef2(normalizeMarkdown(value));
856
+ const editor = useEditor({
857
+ extensions: [
858
+ StarterKit.configure({
859
+ codeBlock: {
860
+ HTMLAttributes: {
861
+ class: "hljs"
862
+ }
688
863
  }
864
+ })
865
+ ],
866
+ content: initialHtml,
867
+ editable: !readOnly,
868
+ autofocus: autoFocus,
869
+ editorProps: {
870
+ attributes: {
871
+ class: cn(
872
+ "prose prose-sm sm:prose-base max-w-none focus:outline-none",
873
+ "prose-headings:text-foreground prose-p:text-foreground prose-li:text-foreground",
874
+ "prose-strong:text-foreground prose-code:text-foreground prose-pre:text-foreground",
875
+ contentClassName
876
+ ),
877
+ "data-placeholder": placeholder
689
878
  }
690
- })
691
- ],
692
- content: initialHtml,
693
- editable: !readOnly,
694
- autofocus: autoFocus,
695
- editorProps: {
696
- attributes: {
697
- class: cn(
698
- "prose prose-sm sm:prose-base max-w-none focus:outline-none",
699
- "prose-headings:text-foreground prose-p:text-foreground prose-li:text-foreground",
700
- "prose-strong:text-foreground prose-code:text-foreground prose-pre:text-foreground",
701
- contentClassName
702
- ),
703
- "data-placeholder": placeholder
879
+ },
880
+ onUpdate: ({ editor: currentEditor }) => {
881
+ const nextMarkdown = normalizeMarkdown(htmlToMarkdown(currentEditor.getHTML()));
882
+ lastAppliedMarkdownRef.current = nextMarkdown;
883
+ onChange?.(nextMarkdown, currentEditor);
884
+ },
885
+ onCreate: ({ editor: currentEditor }) => {
886
+ onReady?.(currentEditor);
704
887
  }
705
- },
706
- onUpdate: ({ editor: currentEditor }) => {
707
- const nextMarkdown = normalizeMarkdown(htmlToMarkdown(currentEditor.getHTML()));
708
- lastAppliedMarkdownRef.current = nextMarkdown;
709
- onChange?.(nextMarkdown, currentEditor);
710
- },
711
- onCreate: ({ editor: currentEditor }) => {
712
- onReady?.(currentEditor);
713
- }
714
- });
715
- useEffect3(() => {
716
- if (editor) {
717
- editor.setEditable(!readOnly);
718
- }
719
- }, [editor, readOnly]);
720
- useEffect3(() => {
721
- if (!editor) {
722
- return;
723
- }
724
- const normalizedValue = normalizeMarkdown(value);
725
- if (normalizedValue === lastAppliedMarkdownRef.current) {
726
- return;
727
- }
728
- editor.commands.setContent(markdownToHtml(value), { emitUpdate: false });
729
- lastAppliedMarkdownRef.current = normalizedValue;
730
- }, [editor, value]);
731
- return /* @__PURE__ */ jsxs2(
732
- "div",
733
- {
734
- className: cn(
735
- "flex min-h-[14rem] w-full flex-col overflow-hidden rounded-lg border border-border bg-background",
736
- className
737
- ),
738
- children: [
739
- /* @__PURE__ */ jsx4(
740
- EditorToolbar,
741
- {
742
- editor,
743
- className: "border-border bg-card px-2 py-2"
744
- }
888
+ });
889
+ useEffect3(() => {
890
+ if (editor) {
891
+ editor.setEditable(!readOnly);
892
+ }
893
+ }, [editor, readOnly]);
894
+ useEffect3(() => {
895
+ if (!editor) {
896
+ return;
897
+ }
898
+ const normalizedValue = normalizeMarkdown(value);
899
+ if (normalizedValue === lastAppliedMarkdownRef.current) {
900
+ return;
901
+ }
902
+ editor.commands.setContent(markdownToHtml(value), { emitUpdate: false });
903
+ lastAppliedMarkdownRef.current = normalizedValue;
904
+ }, [editor, value]);
905
+ return /* @__PURE__ */ jsxs2(
906
+ "div",
907
+ {
908
+ className: cn(
909
+ "flex min-h-[14rem] w-full flex-col overflow-hidden rounded-lg border border-border bg-background",
910
+ className
745
911
  ),
746
- /* @__PURE__ */ jsx4("div", { className: "min-h-0 flex-1 overflow-auto px-5 py-4", children: /* @__PURE__ */ jsx4(EditorContent2, { editor }) }),
747
- /* @__PURE__ */ jsx4("style", { children: `
912
+ children: [
913
+ /* @__PURE__ */ jsx5(
914
+ EditorToolbar,
915
+ {
916
+ editor,
917
+ className: "border-border bg-card px-2 py-2"
918
+ }
919
+ ),
920
+ /* @__PURE__ */ jsx5("div", { className: "min-h-0 flex-1 overflow-auto px-5 py-4", children: /* @__PURE__ */ jsx5(EditorContent, { editor }) }),
921
+ /* @__PURE__ */ jsx5("style", { children: `
748
922
  .ProseMirror p.is-editor-empty:first-child::before {
749
923
  content: attr(data-placeholder);
750
924
  float: left;
@@ -772,7 +946,21 @@ function MarkdownDocumentEditor({
772
946
  padding: 0;
773
947
  }
774
948
  ` })
775
- ]
949
+ ]
950
+ }
951
+ );
952
+ };
953
+ }
954
+ var lazyMarkdownDocumentEditor = retryableLazyEditor(
955
+ async () => createMarkdownDocumentEditor(await loadDocumentEditorPeers())
956
+ );
957
+ function MarkdownDocumentEditor(props) {
958
+ const LazyMarkdownDocumentEditor = lazyMarkdownDocumentEditor();
959
+ return /* @__PURE__ */ jsx5(
960
+ Suspense3,
961
+ {
962
+ fallback: /* @__PURE__ */ jsx5(EditorLoadingPlaceholder, { className: cn("min-h-[14rem]", props.className) }),
963
+ children: /* @__PURE__ */ jsx5(LazyMarkdownDocumentEditor, { ...props })
776
964
  }
777
965
  );
778
966
  }
@@ -926,7 +1114,7 @@ function useAwareness() {
926
1114
  }
927
1115
 
928
1116
  // src/editor/document-editor-pane.tsx
929
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1117
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
930
1118
  function connectionTone(state) {
931
1119
  switch (state) {
932
1120
  case "synced":
@@ -991,21 +1179,21 @@ function CollaborativeDocumentSurface({
991
1179
  connectionTone(state)
992
1180
  ),
993
1181
  children: [
994
- state === "disconnected" ? /* @__PURE__ */ jsx5(WifiOff, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx5(Wifi, { className: "h-3.5 w-3.5" }),
1182
+ state === "disconnected" ? /* @__PURE__ */ jsx6(WifiOff, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx6(Wifi, { className: "h-3.5 w-3.5" }),
995
1183
  connectionLabel(state)
996
1184
  ]
997
1185
  }
998
1186
  ),
999
1187
  /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-background px-2.5 py-1", children: [
1000
- /* @__PURE__ */ jsx5(Users, { className: "h-3.5 w-3.5" }),
1188
+ /* @__PURE__ */ jsx6(Users, { className: "h-3.5 w-3.5" }),
1001
1189
  collaborators.length === 0 ? "Solo editing" : `${collaboratorCount} active`
1002
1190
  ] })
1003
1191
  ] }),
1004
- /* @__PURE__ */ jsx5("p", { className: "text-xs text-muted-foreground", children: connectionDescription(state, collaborators.length, readOnly) })
1192
+ /* @__PURE__ */ jsx6("p", { className: "text-xs text-muted-foreground", children: connectionDescription(state, collaborators.length, readOnly) })
1005
1193
  ] }),
1006
- /* @__PURE__ */ jsx5(CollaboratorsList, { collaborators })
1194
+ /* @__PURE__ */ jsx6(CollaboratorsList, { collaborators })
1007
1195
  ] }),
1008
- /* @__PURE__ */ jsx5(
1196
+ /* @__PURE__ */ jsx6(
1009
1197
  TiptapEditor,
1010
1198
  {
1011
1199
  initialContent,
@@ -1080,7 +1268,7 @@ function DocumentEditorPane({
1080
1268
  variant: "underline",
1081
1269
  className: "h-auto gap-4 border-0 bg-transparent p-0 text-muted-foreground",
1082
1270
  children: [
1083
- /* @__PURE__ */ jsx5(
1271
+ /* @__PURE__ */ jsx6(
1084
1272
  TabsTrigger,
1085
1273
  {
1086
1274
  value: "preview",
@@ -1096,7 +1284,7 @@ function DocumentEditorPane({
1096
1284
  variant: "underline",
1097
1285
  className: "flex items-center gap-2 pb-2 data-[state=active]:border-primary data-[state=active]:text-foreground",
1098
1286
  children: [
1099
- /* @__PURE__ */ jsx5(PencilLine, { className: "h-3.5 w-3.5" }),
1287
+ /* @__PURE__ */ jsx6(PencilLine, { className: "h-3.5 w-3.5" }),
1100
1288
  isCollaborative ? "Live edit" : "Edit"
1101
1289
  ]
1102
1290
  }
@@ -1106,8 +1294,8 @@ function DocumentEditorPane({
1106
1294
  ),
1107
1295
  /* @__PURE__ */ jsxs3("div", { className: "flex flex-wrap items-center gap-2 text-xs text-muted-foreground", children: [
1108
1296
  toolbar,
1109
- /* @__PURE__ */ jsx5("span", { className: "rounded-full border border-border bg-card px-2.5 py-1 font-medium", children: isCollaborative ? "Live document" : "Local draft" }),
1110
- /* @__PURE__ */ jsx5("span", { className: "rounded-full border border-border bg-background px-2.5 py-1", children: saveStateLabel }),
1297
+ /* @__PURE__ */ jsx6("span", { className: "rounded-full border border-border bg-card px-2.5 py-1 font-medium", children: isCollaborative ? "Live document" : "Local draft" }),
1298
+ /* @__PURE__ */ jsx6("span", { className: "rounded-full border border-border bg-background px-2.5 py-1", children: saveStateLabel }),
1111
1299
  onSave && !readOnly && /* @__PURE__ */ jsxs3(
1112
1300
  "button",
1113
1301
  {
@@ -1116,14 +1304,14 @@ function DocumentEditorPane({
1116
1304
  disabled: saving || !isDirty,
1117
1305
  className: "inline-flex items-center gap-2 rounded-[var(--radius-full)] border border-border bg-card px-3 py-1.5 text-xs font-semibold text-foreground transition-colors hover:border-primary/40 hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50",
1118
1306
  children: [
1119
- /* @__PURE__ */ jsx5(Save, { className: "h-3.5 w-3.5" }),
1307
+ /* @__PURE__ */ jsx6(Save, { className: "h-3.5 w-3.5" }),
1120
1308
  saving ? "Saving..." : saveLabel
1121
1309
  ]
1122
1310
  }
1123
1311
  )
1124
1312
  ] })
1125
1313
  ] });
1126
- const preview = /* @__PURE__ */ jsx5(
1314
+ const preview = /* @__PURE__ */ jsx6(
1127
1315
  "div",
1128
1316
  {
1129
1317
  className: cn(
@@ -1133,10 +1321,10 @@ function DocumentEditorPane({
1133
1321
  "m-4 rounded-[var(--radius-lg)] border border-border bg-card p-5",
1134
1322
  previewClassName
1135
1323
  ),
1136
- children: /* @__PURE__ */ jsx5(Markdown, { children: draft })
1324
+ children: /* @__PURE__ */ jsx6(Markdown, { children: draft })
1137
1325
  }
1138
1326
  );
1139
- const localEditor = /* @__PURE__ */ jsx5(
1327
+ const localEditor = /* @__PURE__ */ jsx6(
1140
1328
  MarkdownDocumentEditor,
1141
1329
  {
1142
1330
  value: draft,
@@ -1149,7 +1337,7 @@ function DocumentEditorPane({
1149
1337
  className: editorClassName
1150
1338
  }
1151
1339
  );
1152
- const collaborativeEditor = collaboration ? /* @__PURE__ */ jsx5(EditorProvider, { ...collaboration, children: /* @__PURE__ */ jsx5(
1340
+ const collaborativeEditor = collaboration ? /* @__PURE__ */ jsx6(EditorProvider, { ...collaboration, children: /* @__PURE__ */ jsx6(
1153
1341
  CollaborativeDocumentSurface,
1154
1342
  {
1155
1343
  markdown: draft,
@@ -1160,7 +1348,7 @@ function DocumentEditorPane({
1160
1348
  onChange: handleChange
1161
1349
  }
1162
1350
  ) }, collaboration.documentName) : localEditor;
1163
- return /* @__PURE__ */ jsx5(
1351
+ return /* @__PURE__ */ jsx6(
1164
1352
  Tabs,
1165
1353
  {
1166
1354
  value: activeMode,
@@ -1182,8 +1370,8 @@ function DocumentEditorPane({
1182
1370
  toolbar: editorToolbar,
1183
1371
  hideTitleBlock,
1184
1372
  children: [
1185
- /* @__PURE__ */ jsx5(TabsContent, { value: "preview", className: "mt-0 h-full", children: preview }),
1186
- /* @__PURE__ */ jsx5(TabsContent, { value: "edit", className: "mt-0 h-full px-4 py-4", children: isCollaborative ? collaborativeEditor : localEditor })
1373
+ /* @__PURE__ */ jsx6(TabsContent, { value: "preview", className: "mt-0 h-full", children: preview }),
1374
+ /* @__PURE__ */ jsx6(TabsContent, { value: "edit", className: "mt-0 h-full px-4 py-4", children: isCollaborative ? collaborativeEditor : localEditor })
1187
1375
  ]
1188
1376
  }
1189
1377
  )