@fileverse-dev/ddoc 6.0.0-drop-in.2 → 6.0.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 (52) hide show
  1. package/README.md +18 -89
  2. package/dist/ddoc-persistence-C2ay3lLb.mjs +370 -0
  3. package/dist/headless-editor-utils.es.js +1 -1
  4. package/dist/{index-8MQH4TSF.mjs → index-D9uM5S2Y.mjs} +30 -30
  5. package/dist/index.d.ts +3 -1
  6. package/dist/index.es.js +12496 -12445
  7. package/dist/{markdown-source-pane-CXO7aIOp.mjs → markdown-source-pane-DWliRVLH.mjs} +287 -287
  8. package/dist/package/components/editor-toolbar.d.ts +1 -2
  9. package/dist/package/components/fullscreen-toolbar.d.ts +2 -2
  10. package/dist/package/components/mobile-toolbar.d.ts +1 -2
  11. package/dist/package/components/tabs/document-mobile-tab-panel.d.ts +1 -3
  12. package/dist/package/components/tabs/document-tabs-sidebar.d.ts +1 -2
  13. package/dist/package/components/toc/document-outline.d.ts +1 -1
  14. package/dist/package/components/toc/toc.d.ts +1 -1
  15. package/dist/package/components/toc/types.d.ts +0 -7
  16. package/dist/package/hooks/use-doc-schema-version.d.ts +3 -1
  17. package/dist/package/hooks/use-export-headless-editor-content.d.ts +6 -2
  18. package/dist/package/hooks/use-focus-mode.d.ts +3 -1
  19. package/dist/package/hooks/use-headless-editor.d.ts +12 -4
  20. package/dist/package/hooks/use-tab-editor.d.ts +1 -4
  21. package/dist/package/hooks/use-tab-manager.d.ts +3 -1
  22. package/dist/package/hooks/use-yjs-setup.d.ts +7 -1
  23. package/dist/package/persistence/compaction.d.ts +27 -0
  24. package/dist/package/persistence/content-fingerprint.d.ts +27 -0
  25. package/dist/package/persistence/ddoc-persistence.d.ts +96 -0
  26. package/dist/package/persistence/index.d.ts +2 -0
  27. package/dist/package/persistence/provider-lifecycle.d.ts +37 -0
  28. package/dist/package/sync-local/SyncManager.d.ts +8 -1
  29. package/dist/package/sync-local/server-clock.d.ts +26 -0
  30. package/dist/package/sync-local/socketClient.d.ts +1 -1
  31. package/dist/package/sync-local/types/index.d.ts +2 -0
  32. package/dist/package/types.d.ts +89 -43
  33. package/dist/package/use-ddoc-editor.d.ts +1 -3
  34. package/dist/package/utils/focus-mode-canvas-click.d.ts +2 -0
  35. package/dist/package/utils/responsive.d.ts +1 -3
  36. package/dist/package/utils/schema-version.d.ts +2 -0
  37. package/dist/package/utils/storage-adapter.d.ts +30 -0
  38. package/dist/persistence.d.ts +1 -0
  39. package/dist/persistence.es.js +10 -0
  40. package/dist/{split-view-toolbar-BnfwwwTs.mjs → split-view-toolbar-CbVFNI6B.mjs} +1 -1
  41. package/dist/style.css +1 -1
  42. package/dist/tab-utils-CAcIp-Dt.mjs +313 -0
  43. package/dist/{use-headless-editor-Buqsz3UI.mjs → use-headless-editor-C1g_Z67w.mjs} +11127 -11430
  44. package/package.json +7 -2
  45. package/tailwind.preset.cjs +0 -42
  46. package/dist/package/components/editor-navbar.d.ts +0 -12
  47. package/dist/package/hooks/use-canvas-metrics.d.ts +0 -16
  48. package/dist/package/hooks/use-controllable-state.d.ts +0 -3
  49. package/dist/package/hooks/use-editor-media-query.d.ts +0 -4
  50. package/dist/package/utils/container-query-support.d.ts +0 -1
  51. package/dist/package/utils/editor-width-store.d.ts +0 -6
  52. package/dist/package/utils/stats-publisher.d.ts +0 -8
@@ -116,13 +116,6 @@ export interface DocumentStyling {
116
116
  */
117
117
  customCSS?: string;
118
118
  }
119
- export type DdocStats = {
120
- words: number;
121
- characters: number;
122
- selectedWords: number;
123
- /** null until the active tab has been measured. */
124
- pages: number | null;
125
- };
126
119
  export interface DdocProps extends CommentAccountProps {
127
120
  /**
128
121
  * Optional catalog of fonts available to the editor in addition to the
@@ -143,6 +136,15 @@ export interface DdocProps extends CommentAccountProps {
143
136
  };
144
137
  tabSectionContainer?: HTMLElement;
145
138
  isCollabDocumentPublished?: boolean;
139
+ /**
140
+ * Storage-agnostic image fetch function. Receives the stored location of an
141
+ * encrypted image (`url` + `contentRef`, e.g. an IPFS gateway URL + CID or a
142
+ * Swarm URL + reference) along with its decryption parameters, and returns
143
+ * the decrypted image. Preferred over the deprecated `ipfsImageFetchFn`;
144
+ * when both are provided, `imageFetchFn` wins.
145
+ */
146
+ imageFetchFn?: ImageFetchFn;
147
+ /** @deprecated Use the storage-agnostic `imageFetchFn` instead. */
146
148
  ipfsImageFetchFn?: (_data: IpfsImageFetchPayload) => Promise<{
147
149
  url: string;
148
150
  file: File;
@@ -150,8 +152,6 @@ export interface DdocProps extends CommentAccountProps {
150
152
  fetchV1ImageFn?: (url: string) => Promise<ArrayBuffer | undefined>;
151
153
  disableInlineComment?: boolean;
152
154
  commentDrawerOpen?: boolean;
153
- onCommentDrawerOpenChange?: (open: boolean) => void;
154
- /** @deprecated Use onCommentDrawerOpenChange. */
155
155
  setCommentDrawerOpen?: React.Dispatch<SetStateAction<boolean>>;
156
156
  initialComments?: IComment[];
157
157
  initialCommentAnchors?: SerializedCommentAnchor[];
@@ -165,8 +165,6 @@ export interface DdocProps extends CommentAccountProps {
165
165
  onDeleteComment?: (activeCommentId: string, meta?: CommentMutationMeta) => void;
166
166
  onDeleteReply?: (activeCommentId: string, replyId: string) => void;
167
167
  showTOC?: boolean;
168
- onShowTOCChange?: (show: boolean) => void;
169
- /** @deprecated Use onShowTOCChange. */
170
168
  setShowTOC?: React.Dispatch<SetStateAction<boolean>>;
171
169
  extensions?: Record<string, Extension | any>;
172
170
  selectedTags?: TagType[];
@@ -176,21 +174,24 @@ export interface DdocProps extends CommentAccountProps {
176
174
  inlineCommentData?: InlineCommentData;
177
175
  setInlineCommentData?: React.Dispatch<React.SetStateAction<InlineCommentData>>;
178
176
  theme?: ThemeKey;
179
- /** Defaults to '1' when omitted. */
180
- zoomLevel?: string;
181
- onZoomLevelChange?: (zoomLevel: string) => void;
182
- /** @deprecated Use onZoomLevelChange. */
183
- setZoomLevel?: React.Dispatch<SetStateAction<string>>;
184
- /** Defaults to true when omitted. */
185
- isNavbarVisible?: boolean;
186
- onNavbarVisibleChange?: (visible: boolean) => void;
187
- /** @deprecated Use onNavbarVisibleChange. */
188
- setIsNavbarVisible?: React.Dispatch<SetStateAction<boolean>>;
177
+ zoomLevel: string;
178
+ setZoomLevel: React.Dispatch<SetStateAction<string>>;
179
+ isNavbarVisible: boolean;
180
+ setIsNavbarVisible: React.Dispatch<SetStateAction<boolean>>;
189
181
  editorCanvasClassNames?: string;
190
182
  isCommentSectionOpen?: boolean;
191
- isPreviewMode?: boolean;
183
+ isPreviewMode: boolean;
192
184
  viewerMode?: 'suggest' | 'view-only';
193
185
  ensResolutionUrl?: string;
186
+ /**
187
+ * Storage-agnostic image upload function. Encrypts and stores the file on
188
+ * the host's storage backend (IPFS, Swarm, ...) and returns its location
189
+ * (`url` + `contentRef`) plus the decryption parameters. Preferred over the
190
+ * deprecated `ipfsImageUploadFn`; when both are provided, `imageUploadFn`
191
+ * wins.
192
+ */
193
+ imageUploadFn?: ImageUploadFn;
194
+ /** @deprecated Use the storage-agnostic `imageUploadFn` instead. */
194
195
  ipfsImageUploadFn?: (file: File) => Promise<IpfsImageUploadResponse>;
195
196
  enableIndexeddbSync?: boolean;
196
197
  ddocId?: string;
@@ -204,13 +205,6 @@ export interface DdocProps extends CommentAccountProps {
204
205
  walletAddress?: string | null;
205
206
  username?: string | null;
206
207
  setUsername?: React.Dispatch<SetStateAction<string>>;
207
- /**
208
- * Host element to render the navbar into. Omit to render it as the editor's
209
- * first row. Pass null while the element is not attached yet.
210
- */
211
- navbarContainer?: HTMLElement | null;
212
- /** Show the toolbar's navbar collapse chevron. Default true. */
213
- showNavbarToggle?: boolean;
214
208
  renderNavbar?: ({ editor, liveEditor, }: {
215
209
  /** JSON snapshot — legacy field, unchanged. */
216
210
  editor: JSONContent;
@@ -218,6 +212,24 @@ export interface DdocProps extends CommentAccountProps {
218
212
  liveEditor: Editor | null;
219
213
  }) => JSX.Element;
220
214
  onChange?: (updatedDocContent: string | JSONContent, updateChunk: string) => void;
215
+ /**
216
+ * Called when the document changes, from the same places as `onChange`
217
+ * (after typing pauses, on a flushed structural change, and when a
218
+ * collaborator's update arrives), but with no arguments. A host that passes
219
+ * this and not `onChange` never pays for encoding the whole document on
220
+ * every change. A host that keeps the content elsewhere (the package's
221
+ * y-indexeddb store) only needs to know that an edit happened.
222
+ */
223
+ onLocalChange?: () => void;
224
+ /**
225
+ * Whether this is a brand-new document. When omitted the editor derives it
226
+ * as "owner, no collaboration, no `initialContent`". A host that loads an
227
+ * existing document from the package's y-indexeddb store passes no
228
+ * `initialContent`, so it must pass `false` here: a new document is stamped
229
+ * with the preferred schema version, which would relabel an existing older
230
+ * document.
231
+ */
232
+ isNewDocument?: boolean;
221
233
  onCollaboratorChange?: (collaborators: undefined | IDocCollabUsers[]) => void;
222
234
  onTextSelection?: (data: IEditorSelectionData) => void;
223
235
  onCommentInteraction?: (data: IEditorSelectionData) => void;
@@ -230,15 +242,9 @@ export interface DdocProps extends CommentAccountProps {
230
242
  */
231
243
  enableFanficTemplate?: boolean;
232
244
  onError?: (error: string) => void;
233
- /** Active-tab stats. Fires with all four fields whenever one changes. */
234
- onStatsChange?: (stats: DdocStats) => void;
235
- /** @deprecated Use onStatsChange. */
236
245
  setCharacterCount?: React.Dispatch<SetStateAction<number>>;
237
- /** @deprecated Use onStatsChange. */
238
246
  setWordCount?: React.Dispatch<SetStateAction<number>>;
239
- /** @deprecated Use onStatsChange. */
240
247
  setSelectedWordCount?: React.Dispatch<SetStateAction<number>>;
241
- /** @deprecated Use onStatsChange. */
242
248
  setPageCount?: React.Dispatch<SetStateAction<number>>;
243
249
  tags?: Array<{
244
250
  name: string;
@@ -247,17 +253,12 @@ export interface DdocProps extends CommentAccountProps {
247
253
  className?: string;
248
254
  unFocused?: boolean;
249
255
  isPresentationMode?: boolean;
250
- onPresentationModeChange?: (active: boolean) => void;
251
- /** @deprecated Use onPresentationModeChange. */
252
256
  setIsPresentationMode?: React.Dispatch<SetStateAction<boolean>>;
253
257
  /** Split View: edit markdown on the left, see the read-only ddoc render on the right. */
254
258
  isSplitView?: boolean;
255
- onSplitViewChange?: (active: boolean) => void;
256
- /** @deprecated Use onSplitViewChange. */
257
259
  setIsSplitView?: React.Dispatch<SetStateAction<boolean>>;
258
260
  onComment?: () => void;
259
261
  onInlineComment?: () => void;
260
- /** @deprecated Use onFocusModeChange. Still fires on every change. */
261
262
  onFocusMode?: (isFocusMode: boolean) => void;
262
263
  /** Controlled focus mode (D6). Omit for the legacy internal-state behavior. */
263
264
  isFocusMode?: boolean;
@@ -283,7 +284,6 @@ export interface DdocProps extends CommentAccountProps {
283
284
  renderToolbarLeading?: () => React.ReactNode;
284
285
  metadataProxyUrl?: string;
285
286
  onCopyHeadingLink?: (link: string) => void;
286
- /** @deprecated No effect. The host footer sits outside the editor. */
287
287
  footerHeight?: string;
288
288
  activeModel?: CustomModel;
289
289
  maxTokens?: number;
@@ -294,11 +294,23 @@ export interface DdocProps extends CommentAccountProps {
294
294
  */
295
295
  documentStyling?: DocumentStyling;
296
296
  /**
297
- * Callback when IndexedDB initialization fails
298
- * @description Called when the IndexedDB persistence provider fails to initialize (e.g., private browsing, quota exceeded, corrupted DB). The editor will continue to function without local persistence.
297
+ * Callback when local saving to IndexedDB has a problem
298
+ * @description Called when the IndexedDB persistence provider fails to initialize (e.g., private browsing, corrupted DB), when a save is rejected (e.g., quota exceeded), when the browser closes or another tab deletes the database (the editor detaches from it), and when a corrupt store was repaired on load. The editor keeps working in memory; `refreshYjsIndexedDbProvider` attaches again and saves what it holds.
299
299
  */
300
300
  onIndexedDbError?: (error: Error) => void;
301
+ /**
302
+ * Called when the schema-version stamp was refused or reverted
303
+ * @description The editor stamps brand-new docs with the preferred schema. If the host passed an empty body for a doc that turns out to hold v1 content, the stamp is blocked (or reverted when the content arrived late). Hosts report these to see how often an existing doc is mounted without its body.
304
+ */
305
+ onSchemaStampGuard?: (event: SchemaStampGuardEvent) => void;
301
306
  }
307
+ export type SchemaStampGuardEvent = {
308
+ kind: 'blocked';
309
+ preferredSchemaVersion: number;
310
+ } | {
311
+ kind: 'reverted';
312
+ preferredSchemaVersion: number;
313
+ };
302
314
  export interface IEditorSelectionData {
303
315
  from: number;
304
316
  to: number;
@@ -314,6 +326,39 @@ export interface IUser {
314
326
  isEns: boolean;
315
327
  }
316
328
  export { type IComment };
329
+ /**
330
+ * Storage-agnostic result of an image upload. The editor treats both fields
331
+ * as opaque: `url` is a fetchable location of the encrypted blob (e.g. an
332
+ * IPFS gateway URL or a Swarm `/bytes` URL) and `contentRef` is the backend's
333
+ * content address (IPFS CID, Swarm reference, ...). Both are stored in the
334
+ * document and handed back verbatim to `imageFetchFn`.
335
+ */
336
+ export interface StorageImageUploadResponse {
337
+ encryptionKey: string;
338
+ nonce: string;
339
+ authTag: string;
340
+ url: string;
341
+ contentRef: string;
342
+ }
343
+ /**
344
+ * Storage-agnostic payload handed to `imageFetchFn`: the `url`/`contentRef`
345
+ * returned by `imageUploadFn` at upload time, plus the parameters needed to
346
+ * decrypt the blob.
347
+ */
348
+ export interface StorageImageFetchPayload {
349
+ encryptionKey: string;
350
+ nonce: string;
351
+ authTag: string;
352
+ mimeType: string;
353
+ url: string;
354
+ contentRef: string;
355
+ }
356
+ export type ImageUploadFn = (file: File) => Promise<StorageImageUploadResponse>;
357
+ export type ImageFetchFn = (_data: StorageImageFetchPayload) => Promise<{
358
+ url: string;
359
+ file: File;
360
+ }>;
361
+ /** @deprecated Use the storage-agnostic {@link StorageImageUploadResponse} instead. */
317
362
  export interface IpfsImageUploadResponse {
318
363
  encryptionKey: string;
319
364
  nonce: string;
@@ -321,6 +366,7 @@ export interface IpfsImageUploadResponse {
321
366
  ipfsHash: string;
322
367
  authTag: string;
323
368
  }
369
+ /** @deprecated Use the storage-agnostic {@link StorageImageFetchPayload} instead. */
324
370
  export interface IpfsImageFetchPayload {
325
371
  encryptionKey: string;
326
372
  nonce: string;
@@ -1,12 +1,10 @@
1
1
  import { DdocProps } from './types';
2
2
  import { Editor } from '@tiptap/react';
3
3
  import { DBlockRuntimeState } from './extensions/d-block/dblock-runtime';
4
- import { EditorWidthStore } from './utils/editor-width-store';
5
4
 
6
- export declare const useDdocEditor: ({ isPreviewMode, viewerMode, initialContent, versionHistoryState, collaboration, onChange, onCollaboratorChange, onCommentInteraction, onError, setCharacterCount, setWordCount, setSelectedWordCount, setPageCount, onStatsChange, ipfsImageUploadFn, ddocId, enableIndexeddbSync, unFocused, isFocusMode, theme, zoomLevel, onInvalidContentError, ignoreCorruptedData, isPresentationMode, metadataProxyUrl, extensions: externalExtensions, onCopyHeadingLink, ipfsImageFetchFn, fetchV1ImageFn, isConnected, activeModel, maxTokens, isAIAgentEnabled, onIndexedDbError, disableInlineComment, initialCommentAnchors, isPreviewEditor, fonts, preferredSchemaVersion, editorWidthStore, ...rest }: Partial<DdocProps> & {
5
+ export declare const useDdocEditor: ({ isPreviewMode, viewerMode, initialContent, versionHistoryState, collaboration, onChange, onLocalChange, isNewDocument, onCollaboratorChange, onCommentInteraction, onError, setCharacterCount, setWordCount, setSelectedWordCount, setPageCount, ipfsImageUploadFn, ddocId, enableIndexeddbSync, unFocused, isFocusMode, theme, zoomLevel, onInvalidContentError, ignoreCorruptedData, isPresentationMode, metadataProxyUrl, extensions: externalExtensions, onCopyHeadingLink, ipfsImageFetchFn, fetchV1ImageFn, isConnected, activeModel, maxTokens, isAIAgentEnabled, onIndexedDbError, disableInlineComment, initialCommentAnchors, isPreviewEditor, fonts, preferredSchemaVersion, onSchemaStampGuard, ...rest }: Partial<DdocProps> & {
7
6
  isFocusMode?: boolean;
8
7
  isPreviewEditor?: boolean;
9
- editorWidthStore?: EditorWidthStore;
10
8
  }) => {
11
9
  ydoc: import('yjs').Doc;
12
10
  awareness: import('y-protocols/awareness.js').Awareness | null;
@@ -0,0 +1,2 @@
1
+ /** Whether a focus-mode mousedown landed on the bare canvas around the page. */
2
+ export declare const isFocusModeCanvasClick: (target: Element, editorContent: Element | null) => boolean;
@@ -1,6 +1,4 @@
1
- import { EditorWidthStore } from './editor-width-store';
2
-
3
- export declare const useResponsive: (store?: EditorWidthStore) => {
1
+ export declare const useResponsive: () => {
4
2
  isBelow1024px: boolean;
5
3
  isBelow1280px: boolean;
6
4
  isMobileScreen: boolean;
@@ -4,3 +4,5 @@ export declare const SCHEMA_VERSION_META_KEY = "schemaVersion";
4
4
  export declare const SUPPORTED_SCHEMA_VERSION = 2;
5
5
  export declare const getDocSchemaVersion: (doc: Y.Doc) => number;
6
6
  export declare const isDocSchemaSupported: (doc: Y.Doc) => boolean;
7
+ export declare const docHasBlockContent: (doc: Y.Doc) => boolean;
8
+ export declare const docHasV1Blocks: (doc: Y.Doc) => boolean;
@@ -0,0 +1,30 @@
1
+ import { ImageFetchFn, ImageUploadFn, IpfsImageFetchPayload, IpfsImageUploadResponse } from '../types';
2
+
3
+ /**
4
+ * Boundary adapters between the storage-agnostic public props
5
+ * (`imageUploadFn` / `imageFetchFn`) and the IPFS-shaped interface the editor
6
+ * internals still use. Internals are untouched: a storage-agnostic host
7
+ * function is wrapped here, at the prop boundary, so that downstream code
8
+ * keeps seeing the legacy `ipfsUrl` / `ipfsHash` field names regardless of
9
+ * which backend the host actually stores on.
10
+ */
11
+ type LegacyImageUploadFn = (file: File) => Promise<IpfsImageUploadResponse>;
12
+ type LegacyImageFetchFn = (_data: IpfsImageFetchPayload) => Promise<{
13
+ url: string;
14
+ file: File;
15
+ }>;
16
+ /**
17
+ * Resolve the upload function to use internally. The storage-agnostic
18
+ * `imageUploadFn` wins over the deprecated `ipfsImageUploadFn`; its response
19
+ * is mapped onto the legacy field names (`url` → `ipfsUrl`,
20
+ * `contentRef` → `ipfsHash`).
21
+ */
22
+ export declare const resolveImageUploadFn: (imageUploadFn?: ImageUploadFn, ipfsImageUploadFn?: LegacyImageUploadFn) => LegacyImageUploadFn | undefined;
23
+ /**
24
+ * Resolve the fetch function to use internally. The storage-agnostic
25
+ * `imageFetchFn` wins over the deprecated `ipfsImageFetchFn`; the legacy
26
+ * payload built by the internals is mapped onto the agnostic field names
27
+ * (`ipfsUrl` → `url`, `ipfsHash` → `contentRef`).
28
+ */
29
+ export declare const resolveImageFetchFn: (imageFetchFn?: ImageFetchFn, ipfsImageFetchFn?: LegacyImageFetchFn) => LegacyImageFetchFn | undefined;
30
+ export {};
@@ -0,0 +1 @@
1
+ export * from './package/persistence';
@@ -0,0 +1,10 @@
1
+ import { D as n, a as o, d as D, g as a, m as r, r as d, b as C } from "./ddoc-persistence-C2ay3lLb.mjs";
2
+ export {
3
+ n as DDOC_STORE_REPAIRED,
4
+ o as DEFAULT_DDOC_PERSISTENCE_TIMEOUT_MS,
5
+ D as deleteDdocContent,
6
+ a as getDdocContentFingerprint,
7
+ r as mergeDdocContent,
8
+ d as readDdocContent,
9
+ C as repairDdocContent
10
+ };
@@ -1,7 +1,7 @@
1
1
  import { jsx as m, jsxs as L } from "react/jsx-runtime";
2
2
  import { useRef as D, useState as E, useEffect as A } from "react";
3
3
  import { DynamicDropdown as P, Tooltip as O, IconButton as T } from "@fileverse/ui";
4
- import { a3 as _, a4 as w, c as R } from "./index-8MQH4TSF.mjs";
4
+ import { r as _, u as w, E as R } from "./index-D9uM5S2Y.mjs";
5
5
  const p = (e, n, o = n) => {
6
6
  const { state: i } = e, l = i.changeByRange((t) => {
7
7
  const d = i.sliceDoc(t.from, t.to), r = i.sliceDoc(