@fileverse-dev/ddoc 6.0.0-drop-in.3 → 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 (46) 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-eP_xxZsk.mjs → index-D9uM5S2Y.mjs} +30 -30
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.es.js +12073 -12098
  7. package/dist/{markdown-source-pane-D-8OmL1r.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-export-headless-editor-content.d.ts +6 -2
  17. package/dist/package/hooks/use-focus-mode.d.ts +3 -1
  18. package/dist/package/hooks/use-headless-editor.d.ts +12 -4
  19. package/dist/package/hooks/use-tab-editor.d.ts +1 -4
  20. package/dist/package/hooks/use-tab-manager.d.ts +3 -1
  21. package/dist/package/hooks/use-yjs-setup.d.ts +7 -1
  22. package/dist/package/persistence/compaction.d.ts +27 -0
  23. package/dist/package/persistence/content-fingerprint.d.ts +27 -0
  24. package/dist/package/persistence/ddoc-persistence.d.ts +96 -0
  25. package/dist/package/persistence/index.d.ts +2 -0
  26. package/dist/package/persistence/provider-lifecycle.d.ts +37 -0
  27. package/dist/package/sync-local/SyncManager.d.ts +8 -1
  28. package/dist/package/sync-local/types/index.d.ts +2 -0
  29. package/dist/package/types.d.ts +25 -43
  30. package/dist/package/use-ddoc-editor.d.ts +1 -3
  31. package/dist/package/utils/responsive.d.ts +1 -3
  32. package/dist/persistence.d.ts +1 -0
  33. package/dist/persistence.es.js +10 -0
  34. package/dist/{split-view-toolbar-NTimkQK1.mjs → split-view-toolbar-CbVFNI6B.mjs} +1 -1
  35. package/dist/style.css +1 -1
  36. package/dist/tab-utils-CAcIp-Dt.mjs +313 -0
  37. package/dist/{use-headless-editor-KdKS6vWp.mjs → use-headless-editor-C1g_Z67w.mjs} +11491 -11814
  38. package/package.json +7 -3
  39. package/tailwind.preset.cjs +0 -42
  40. package/dist/package/components/editor-navbar.d.ts +0 -12
  41. package/dist/package/hooks/use-canvas-metrics.d.ts +0 -16
  42. package/dist/package/hooks/use-controllable-state.d.ts +0 -3
  43. package/dist/package/hooks/use-editor-media-query.d.ts +0 -4
  44. package/dist/package/utils/container-query-support.d.ts +0 -1
  45. package/dist/package/utils/editor-width-store.d.ts +0 -6
  46. package/dist/package/utils/stats-publisher.d.ts +0 -8
@@ -4,7 +4,7 @@ import { FontDescriptor, IpfsImageFetchPayload, IpfsImageUploadResponse } from '
4
4
  import { Tab } from './tabs/utils/tab-utils';
5
5
 
6
6
  import * as Y from 'yjs';
7
- declare const MemoizedTiptapToolBar: React.MemoExoticComponent<({ editor, onError, isPresentationMode, setIsPresentationMode, enableCollaboration, zoomLevel, setZoomLevel, isNavbarVisible, setIsNavbarVisible, showNavbarToggle, ipfsImageUploadFn, onMarkdownExport, onMarkdownImport, onPdfExport, onHtmlExport, onTxtExport, onOdtExport, onDocxImport, ipfsImageFetchFn, fetchV1ImageFn, isConnected, tabs, ydoc, onRegisterExportTrigger, toggleFocusMode, isSplitView, onToggleSplitView, fonts: consumerFonts, renderToolbarLeading, }: {
7
+ declare const MemoizedTiptapToolBar: React.MemoExoticComponent<({ editor, onError, isPresentationMode, setIsPresentationMode, enableCollaboration, zoomLevel, setZoomLevel, isNavbarVisible, setIsNavbarVisible, ipfsImageUploadFn, onMarkdownExport, onMarkdownImport, onPdfExport, onHtmlExport, onTxtExport, onOdtExport, onDocxImport, ipfsImageFetchFn, fetchV1ImageFn, isConnected, tabs, ydoc, onRegisterExportTrigger, toggleFocusMode, isSplitView, onToggleSplitView, fonts: consumerFonts, renderToolbarLeading, }: {
8
8
  editor: Editor | null;
9
9
  onError?: (errorString: string) => void;
10
10
  isPresentationMode?: boolean;
@@ -14,7 +14,6 @@ declare const MemoizedTiptapToolBar: React.MemoExoticComponent<({ editor, onErro
14
14
  setZoomLevel: (zoom: string) => void;
15
15
  isNavbarVisible: boolean;
16
16
  setIsNavbarVisible: React.Dispatch<React.SetStateAction<boolean>>;
17
- showNavbarToggle?: boolean;
18
17
  ipfsImageUploadFn?: (file: File) => Promise<IpfsImageUploadResponse>;
19
18
  onMarkdownExport?: () => void;
20
19
  onMarkdownImport?: () => void;
@@ -4,8 +4,8 @@ import { DdocProps } from '../types';
4
4
  type FullScreenToolbarProps = {
5
5
  dropdownOpen: boolean;
6
6
  setDropdownOpen: Dispatch<SetStateAction<boolean>>;
7
- zoomLevel: string;
8
- setZoomLevel: Dispatch<SetStateAction<string>>;
7
+ zoomLevel: DdocProps['zoomLevel'];
8
+ setZoomLevel: DdocProps['setZoomLevel'];
9
9
  showTOC: DdocProps['showTOC'];
10
10
  setShowTOC: DdocProps['setShowTOC'];
11
11
  toggleFocusMode: () => Promise<void>;
@@ -2,13 +2,12 @@ import { default as React } from 'react';
2
2
  import { Editor } from '@tiptap/react';
3
3
  import { FontDescriptor, IpfsImageFetchPayload, IpfsImageUploadResponse } from '../types';
4
4
 
5
- declare const MemoizedMobileToolbar: React.MemoExoticComponent<({ editor, onError, isKeyboardVisible, isNavbarVisible, setIsNavbarVisible, showNavbarToggle, ipfsImageUploadFn, ipfsImageFetchFn, fetchV1ImageFn, fonts, }: {
5
+ declare const MemoizedMobileToolbar: React.MemoExoticComponent<({ editor, onError, isKeyboardVisible, isNavbarVisible, setIsNavbarVisible, ipfsImageUploadFn, ipfsImageFetchFn, fetchV1ImageFn, fonts, }: {
6
6
  editor: Editor | null;
7
7
  onError?: (errorString: string) => void;
8
8
  isKeyboardVisible: boolean;
9
9
  isNavbarVisible: boolean;
10
10
  setIsNavbarVisible: React.Dispatch<React.SetStateAction<boolean>>;
11
- showNavbarToggle?: boolean;
12
11
  ipfsImageUploadFn?: (file: File) => Promise<IpfsImageUploadResponse>;
13
12
  ipfsImageFetchFn?: (_data: IpfsImageFetchPayload) => Promise<{
14
13
  url: string;
@@ -25,7 +25,5 @@ export interface DocumentMobileTabPanelProps {
25
25
  isVersionHistoryMode: boolean;
26
26
  isConnected?: boolean;
27
27
  isFocusMode?: boolean;
28
- layout?: DocumentOutlineProps['layout'];
29
- tabPanelSlot?: HTMLElement | null;
30
28
  }
31
- export declare const DocumentMobileTabPanel: ({ tabs, activeTabId, setActiveTabId, editor, items, setItems, orientation, renameTab, createTab, duplicateTab, orderTab, deleteTab, tabCommentCounts, isPreviewMode, tabConfig, isVersionHistoryMode, isConnected, isFocusMode, layout, tabPanelSlot, }: DocumentMobileTabPanelProps) => import("react/jsx-runtime").JSX.Element | null;
29
+ export declare const DocumentMobileTabPanel: ({ tabs, activeTabId, setActiveTabId, editor, items, setItems, orientation, renameTab, createTab, duplicateTab, orderTab, deleteTab, tabCommentCounts, isPreviewMode, tabConfig, isVersionHistoryMode, isConnected, isFocusMode, }: DocumentMobileTabPanelProps) => import("react/jsx-runtime").JSX.Element;
@@ -32,10 +32,9 @@ export interface DocumentTabsSidebarProps {
32
32
  deleteTab?: (tabId: string) => void;
33
33
  isConnected?: boolean;
34
34
  isFocusMode?: boolean;
35
- layout?: DocumentOutlineProps['layout'];
36
35
  }
37
36
  export declare const DocumentTabsSidebar: ({ tabSectionContainer, ...rest }: DocumentTabsSidebarProps) => import("react/jsx-runtime").JSX.Element | null;
38
- export declare const TabSidebar: ({ tabs, activeTabId, setActiveTabId, showTOC, setShowTOC, hasToC, isPreviewMode, editor, items, setItems, orientation, createTab, renameTab, duplicateTab, orderTab, tabCommentCounts, isVersionHistoryMode, tabConfig, deleteTab, isConnected, isFocusMode, layout, }: DocumentTabsSidebarProps) => import("react/jsx-runtime").JSX.Element;
37
+ export declare const TabSidebar: ({ tabs, activeTabId, setActiveTabId, showTOC, setShowTOC, hasToC, isPreviewMode, editor, items, setItems, orientation, createTab, renameTab, duplicateTab, orderTab, tabCommentCounts, isVersionHistoryMode, tabConfig, deleteTab, isConnected, isFocusMode, }: DocumentTabsSidebarProps) => import("react/jsx-runtime").JSX.Element;
39
38
  export declare const DdocTab: ({ tab, tabIndex, tabCount, handleEmojiChange, handleNameChange, onClick, editor, tocItem, setTocItems, orientation, activeTabId, duplicateTab, commentCount, moveTabUp, moveTabDown, isPreviewMode, isVersionHistoryMode, tabConfig, onDelete, showOutline, onShowOutlineChange, isConnected, }: {
40
39
  tab: Tab;
41
40
  tabIndex: number;
@@ -1,3 +1,3 @@
1
1
  import { DocumentOutlineProps } from './types';
2
2
 
3
- export declare const DocumentOutline: ({ editor, hasToC, items, setItems, showTOC, setShowTOC, isPreviewMode, orientation, tabs, setTabs, activeTabId, setActiveTabId, createTab, renameTab, duplicateTab, orderTab, ydoc, tabCommentCounts, tabSectionContainer, isVersionHistoryMode, tabConfig, deleteTab, isConnected, isFocusMode, layout, tabPanelSlot, }: DocumentOutlineProps) => import("react/jsx-runtime").JSX.Element;
3
+ export declare const DocumentOutline: ({ editor, hasToC, items, setItems, showTOC, setShowTOC, isPreviewMode, orientation, tabs, setTabs, activeTabId, setActiveTabId, createTab, renameTab, duplicateTab, orderTab, ydoc, tabCommentCounts, tabSectionContainer, isVersionHistoryMode, tabConfig, deleteTab, isConnected, isFocusMode, }: DocumentOutlineProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import { ToCProps, ToCItemProps } from './types';
2
2
 
3
- export declare const ToCItem: import('react').MemoExoticComponent<({ item, onItemClick, index, orientation, isMobile, isWide, }: ToCItemProps) => import("react/jsx-runtime").JSX.Element>;
3
+ export declare const ToCItem: import('react').MemoExoticComponent<({ item, onItemClick, index, orientation, }: ToCItemProps) => import("react/jsx-runtime").JSX.Element>;
4
4
  export declare const ToCEmptyState: import('react').MemoExoticComponent<() => import("react/jsx-runtime").JSX.Element>;
5
5
  export declare const ToC: import('react').MemoExoticComponent<({ items, editor, setItems, orientation }: ToCProps) => import("react/jsx-runtime").JSX.Element>;
@@ -22,10 +22,6 @@ export type ToCItemProps = {
22
22
  onItemRemove: (e: React.MouseEvent, id: string) => void;
23
23
  index: number;
24
24
  orientation?: 'portrait' | 'landscape';
25
- /** At or below 1280px: the item is in the mobile list. */
26
- isMobile?: boolean;
27
- /** At or above 1280px: the item takes the rail's width caps. */
28
- isWide?: boolean;
29
25
  };
30
26
  export interface DocumentOutlineProps {
31
27
  editor: Editor;
@@ -58,7 +54,4 @@ export interface DocumentOutlineProps {
58
54
  deleteTab?: (tabId: string) => void;
59
55
  isConnected?: boolean;
60
56
  isFocusMode?: boolean;
61
- /** 'contained': sized from the editor canvas. Only DdocEditor passes it. */
62
- layout?: 'viewport' | 'contained';
63
- tabPanelSlot?: HTMLElement | null;
64
57
  }
@@ -33,6 +33,10 @@ export declare const useExportHeadlessEditorContent: (props?: UseHeadlessEditorP
33
33
  downloadContentAsTxt: (content: string | string[] | JSONContent, title: string) => Promise<void>;
34
34
  mergeYjsUpdates: (contents: string[]) => string;
35
35
  handleMarkdownContent: typeof import('../extensions/mardown-paste-handler').handleMarkdownContent;
36
- getYjsContentFromMarkdown: (file: File, ipfsImageUploadFn: (file: File) => Promise<import('../types').IpfsImageUploadResponse>) => Promise<string | null>;
37
- getYjsContentFromDocx: (file: File, ipfsImageUploadFn: (file: File) => Promise<import('../types').IpfsImageUploadResponse>) => Promise<string | null>;
36
+ getYjsContentFromMarkdown: (file: File, ipfsImageUploadFn: (file: File) => Promise<import('../types').IpfsImageUploadResponse>, options?: {
37
+ schemaVersion?: number;
38
+ }) => Promise<string | null>;
39
+ getYjsContentFromDocx: (file: File, ipfsImageUploadFn: (file: File) => Promise<import('../types').IpfsImageUploadResponse>, options?: {
40
+ schemaVersion?: number;
41
+ }) => Promise<string | null>;
38
42
  };
@@ -1,7 +1,9 @@
1
1
  type UseFocusModeOptions = {
2
- /** @deprecated Use onFocusModeChange. Still fires on every change. */
2
+ /** Notify callback — fires on every toggle in both modes (existing API). */
3
3
  onFocusMode?: (isFocusMode: boolean) => void;
4
+ /** Controlled value (D6). Omit for the legacy internal-state behavior. */
4
5
  isFocusMode?: boolean;
6
+ /** Controlled setter — receives the next value instead of internal mutation. */
5
7
  onFocusModeChange?: (isFocusMode: boolean) => void;
6
8
  };
7
9
  export declare const useFocusMode: ({ onFocusMode, isFocusMode: controlledValue, onFocusModeChange, }?: UseFocusModeOptions) => {
@@ -53,8 +53,12 @@ export declare const createHeadlessEditorRuntime: (props?: UseHeadlessEditorProp
53
53
  downloadContentAsTxt: (content: string | string[] | JSONContent, title: string) => Promise<void>;
54
54
  mergeYjsUpdates: (contents: string[]) => string;
55
55
  handleMarkdownContent: typeof handleMarkdownContent;
56
- getYjsContentFromMarkdown: (file: File, ipfsImageUploadFn: (file: File) => Promise<IpfsImageUploadResponse>) => Promise<string | null>;
57
- getYjsContentFromDocx: (file: File, ipfsImageUploadFn: (file: File) => Promise<IpfsImageUploadResponse>) => Promise<string | null>;
56
+ getYjsContentFromMarkdown: (file: File, ipfsImageUploadFn: (file: File) => Promise<IpfsImageUploadResponse>, options?: {
57
+ schemaVersion?: number;
58
+ }) => Promise<string | null>;
59
+ getYjsContentFromDocx: (file: File, ipfsImageUploadFn: (file: File) => Promise<IpfsImageUploadResponse>, options?: {
60
+ schemaVersion?: number;
61
+ }) => Promise<string | null>;
58
62
  };
59
63
  export declare const useHeadlessEditor: (props?: UseHeadlessEditorProps) => {
60
64
  setContent: (initialContent: string | string[] | JSONContent, editor: Editor, ydoc: Y.Doc) => void;
@@ -76,6 +80,10 @@ export declare const useHeadlessEditor: (props?: UseHeadlessEditorProps) => {
76
80
  downloadContentAsTxt: (content: string | string[] | JSONContent, title: string) => Promise<void>;
77
81
  mergeYjsUpdates: (contents: string[]) => string;
78
82
  handleMarkdownContent: typeof handleMarkdownContent;
79
- getYjsContentFromMarkdown: (file: File, ipfsImageUploadFn: (file: File) => Promise<IpfsImageUploadResponse>) => Promise<string | null>;
80
- getYjsContentFromDocx: (file: File, ipfsImageUploadFn: (file: File) => Promise<IpfsImageUploadResponse>) => Promise<string | null>;
83
+ getYjsContentFromMarkdown: (file: File, ipfsImageUploadFn: (file: File) => Promise<IpfsImageUploadResponse>, options?: {
84
+ schemaVersion?: number;
85
+ }) => Promise<string | null>;
86
+ getYjsContentFromDocx: (file: File, ipfsImageUploadFn: (file: File) => Promise<IpfsImageUploadResponse>, options?: {
87
+ schemaVersion?: number;
88
+ }) => Promise<string | null>;
81
89
  };
@@ -4,14 +4,12 @@ import { AnyExtension, Editor } from '@tiptap/react';
4
4
  import { CommentAnchor } from '../extensions/comment/comment-decoration-plugin';
5
5
  import { DBlockRuntimeStateRef } from '../extensions/d-block/dblock-runtime';
6
6
  import { ToCItemType } from '../components/toc/types';
7
- import { EditorWidthStore } from '../utils/editor-width-store';
8
7
  import { CollabConnectionConfig, CollaborationProps } from '../sync-local/types';
9
8
 
10
9
  import * as Y from 'yjs';
11
10
  export declare const TAB_EDITOR_ATTRIBUTES: Record<string, string>;
12
11
  interface UseTabEditorArgs {
13
12
  ydoc: Y.Doc;
14
- editorWidthStore?: EditorWidthStore;
15
13
  isVersionMode?: boolean;
16
14
  hasTabState?: boolean;
17
15
  versionId?: string;
@@ -38,7 +36,6 @@ interface UseTabEditorArgs {
38
36
  setWordCount?: DdocProps['setWordCount'];
39
37
  setSelectedWordCount?: DdocProps['setSelectedWordCount'];
40
38
  setPageCount?: DdocProps['setPageCount'];
41
- onStatsChange?: DdocProps['onStatsChange'];
42
39
  setIsContentLoading: Dispatch<SetStateAction<boolean>>;
43
40
  setIsCollabContentLoading: Dispatch<SetStateAction<boolean>>;
44
41
  unFocused?: boolean;
@@ -61,7 +58,7 @@ interface UseTabEditorArgs {
61
58
  dBlockRuntimeStateRef?: DBlockRuntimeStateRef;
62
59
  docSchemaVersion?: number;
63
60
  }
64
- export declare const useTabEditor: ({ ydoc, editorWidthStore, isVersionMode, hasTabState, versionId, isPreviewMode, viewerMode, initialContent, collaboration, isReady, awareness, disableInlineComment, isFocusMode, onCommentInteraction, onError, ipfsImageUploadFn, metadataProxyUrl, onCopyHeadingLink, ipfsImageFetchFn, fetchV1ImageFn, isConnected, activeModel, maxTokens, isAIAgentEnabled, setCharacterCount, setWordCount, setSelectedWordCount, setPageCount, onStatsChange, setIsContentLoading, setIsCollabContentLoading, unFocused, zoomLevel, isPresentationMode, onInvalidContentError, ignoreCorruptedData, onConnect, onDisconnect, isIndexeddbSynced, hasCollabContentInitialised, initialiseYjsIndexedDbProvider, externalExtensions, isContentLoading, activeTabId, tabIds, theme, editorRef, initialCommentAnchors, dBlockRuntimeStateRef, docSchemaVersion, }: UseTabEditorArgs) => {
61
+ export declare const useTabEditor: ({ ydoc, isVersionMode, hasTabState, versionId, isPreviewMode, viewerMode, initialContent, collaboration, isReady, awareness, disableInlineComment, isFocusMode, onCommentInteraction, onError, ipfsImageUploadFn, metadataProxyUrl, onCopyHeadingLink, ipfsImageFetchFn, fetchV1ImageFn, isConnected, activeModel, maxTokens, isAIAgentEnabled, setCharacterCount, setWordCount, setSelectedWordCount, setPageCount, setIsContentLoading, setIsCollabContentLoading, unFocused, zoomLevel, isPresentationMode, onInvalidContentError, ignoreCorruptedData, onConnect, onDisconnect, isIndexeddbSynced, hasCollabContentInitialised, initialiseYjsIndexedDbProvider, externalExtensions, isContentLoading, activeTabId, tabIds, theme, editorRef, initialCommentAnchors, dBlockRuntimeStateRef, docSchemaVersion, }: UseTabEditorArgs) => {
65
62
  editor: Editor | null;
66
63
  cachedEditorEntries: import('./use-tab-editor-cache').CachedTabEditorRenderEntry[];
67
64
  ref: import('react').RefObject<HTMLDivElement>;
@@ -7,6 +7,8 @@ interface UseTabManagerArgs {
7
7
  initialContent: DdocProps['initialContent'];
8
8
  enableCollaboration?: boolean;
9
9
  isDDocOwner: boolean;
10
+ /** Overrides the derived "brand-new document" (see DdocProps.isNewDocument). */
11
+ isNewDocument?: boolean;
10
12
  createDefaultTabIfMissing: boolean;
11
13
  shouldSyncActiveTab: boolean;
12
14
  defaultTabId?: string;
@@ -16,7 +18,7 @@ interface UseTabManagerArgs {
16
18
  flushPendingUpdate?: () => void;
17
19
  }
18
20
  export declare const getNewTabId: () => string;
19
- export declare const useTabManager: ({ ydoc, initialContent, enableCollaboration, isDDocOwner, createDefaultTabIfMissing, shouldSyncActiveTab, defaultTabId, preferFirstTabOnInit, onVersionHistoryActiveTabChange, flushPendingUpdate, getEditor, }: UseTabManagerArgs) => {
21
+ export declare const useTabManager: ({ ydoc, initialContent, enableCollaboration, isDDocOwner, isNewDocument, createDefaultTabIfMissing, shouldSyncActiveTab, defaultTabId, preferFirstTabOnInit, onVersionHistoryActiveTabChange, flushPendingUpdate, getEditor, }: UseTabManagerArgs) => {
20
22
  tabs: Tab[];
21
23
  hasTabState: boolean;
22
24
  activeTabId: string;
@@ -3,13 +3,19 @@ import { DdocProps } from '../types';
3
3
  import * as Y from 'yjs';
4
4
  interface UseYjsSetupArgs {
5
5
  onChange?: DdocProps['onChange'];
6
+ onLocalChange?: DdocProps['onLocalChange'];
6
7
  enableIndexeddbSync?: boolean;
7
8
  ddocId?: string;
8
9
  collaboration?: CollaborationProps;
9
10
  onCollaboratorChange?: DdocProps['onCollaboratorChange'];
10
11
  onIndexedDbError?: (error: Error) => void;
12
+ /**
13
+ * How long to wait for y-indexeddb before checking whether the store is
14
+ * corrupt. Internal; tests shorten it.
15
+ */
16
+ indexeddbStallCheckMs?: number;
11
17
  }
12
- export declare const useYjsSetup: ({ onChange, enableIndexeddbSync, ddocId, collaboration, onCollaboratorChange, onIndexedDbError, }: UseYjsSetupArgs) => {
18
+ export declare const useYjsSetup: ({ onChange, onLocalChange, enableIndexeddbSync, ddocId, collaboration, onCollaboratorChange, onIndexedDbError, indexeddbStallCheckMs, }: UseYjsSetupArgs) => {
13
19
  ydoc: Y.Doc;
14
20
  onConnect: (connectConfig: import('../types').CollabConnectionConfig) => void;
15
21
  onDisconnect: () => void;
@@ -0,0 +1,27 @@
1
+ import { IndexeddbPersistence } from 'y-indexeddb';
2
+
3
+ /**
4
+ * Below this many already-replayed rows the store is left alone. A normal
5
+ * open leaves two or three (the previous compacted row, the last session's
6
+ * edits and y-indexeddb's attach row), so rewriting the full doc then would
7
+ * buy little.
8
+ */
9
+ export declare const DEFAULT_COMPACTION_MIN_ROWS = 3;
10
+ /** Is this one of compaction's own transactions? */
11
+ export declare const isCompactionTransaction: (transaction: IDBTransaction) => boolean;
12
+ /**
13
+ * y-indexeddb appends a copy of the doc every time a provider attaches, and
14
+ * only trims after 500 rows. The editor applies the consumer's initialContent
15
+ * before attaching, so each open stores a FULL copy of the doc and every load
16
+ * replays them all.
17
+ *
18
+ * During sync y-indexeddb applies every stored row to the doc and then sets
19
+ * `_dbref` to one past the last key. Every row below `_dbref` is therefore
20
+ * already in `persistence.doc`, so those rows are replaced with a single
21
+ * `encodeStateAsUpdate(doc)` without reading or merging them. Rows at or above
22
+ * `_dbref` (later edits, writes from another tab) are never touched.
23
+ *
24
+ * Runs in ONE readwrite transaction: any failure rolls back to the original
25
+ * rows. Returns the number of rows replaced (0 when nothing was compacted).
26
+ */
27
+ export declare const compactDdocPersistence: (persistence: IndexeddbPersistence, minRows?: number) => Promise<number>;
@@ -0,0 +1,27 @@
1
+ import * as Y from 'yjs';
2
+ type ClockRange = [start: number, end: number];
3
+ /**
4
+ * Join ranges that touch. Input is sorted by start, as the store keeps it.
5
+ * Lossless: the result covers exactly the same clocks, written one way, so
6
+ * the description does not depend on how Yjs split or joined items.
7
+ */
8
+ export declare const mergeRanges: (ranges: ClockRange[]) => ClockRange[];
9
+ /**
10
+ * A canonical description of a doc's content: for every writer, which clock
11
+ * ranges it inserted and which of those are deleted, leaving out the
12
+ * active-tab pointer.
13
+ *
14
+ * Every content edit (text, formatting, a tab rename or reorder, any root a
15
+ * later package version adds) inserts or deletes a struct, so it changes this.
16
+ * The active tab is per-user UI state written with the `'self'` origin
17
+ * (use-tab-manager.ts) and is skipped, so a tab switch does not. The rule
18
+ * lists what to skip rather than what to include, so a mistake can only make
19
+ * an unchanged doc look changed, never hide an edit.
20
+ *
21
+ * Ranges are merged so the result does not depend on how Yjs split or joined
22
+ * items: the same state gives the same description however it was produced.
23
+ */
24
+ export declare const describeDdocContent: (doc: Y.Doc) => string;
25
+ /** SHA-256 of `describeDdocContent`, base64 (44 characters). */
26
+ export declare const fingerprintDdocContent: (doc: Y.Doc) => Promise<string>;
27
+ export {};
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Read, merge into, delete and fingerprint a dDoc's y-indexeddb database
3
+ * without mounting an editor. The editor's IndexeddbPersistence names the
4
+ * database after the ddocId and stores Yjs updates in the `updates` store;
5
+ * these functions read and write that same layout.
6
+ *
7
+ * Nothing here imports React or the editor, so it is safe to call from app
8
+ * services and workers.
9
+ */
10
+ export declare const DEFAULT_DDOC_PERSISTENCE_TIMEOUT_MS = 8000;
11
+ /**
12
+ * `name` of the Error passed to `onIndexedDbError` when the editor repaired a
13
+ * corrupt store on load. It is a notice, not a failed save: the store is
14
+ * working again, but the unreadable rows it dropped are gone.
15
+ */
16
+ export declare const DDOC_STORE_REPAIRED = "DdocStoreRepaired";
17
+ export type DdocContentStatus = 'available' | 'empty' | 'missing' | 'corrupt' | 'timed-out' | 'unavailable';
18
+ export type DdocContentSnapshot = {
19
+ ddocId: string;
20
+ status: DdocContentStatus;
21
+ /** base64 Yjs update of the whole doc; null unless available or empty. */
22
+ encodedState: string | null;
23
+ error?: Error;
24
+ };
25
+ export type DdocContentMergeResult = DdocContentSnapshot & {
26
+ /** True when the merge wrote something the database did not have. */
27
+ changed: boolean;
28
+ /** True when the database did not exist and this merge created it. */
29
+ created: boolean;
30
+ };
31
+ export type DdocContentDeleteResult = {
32
+ ddocId: string;
33
+ status: 'deleted' | 'blocked' | 'unavailable';
34
+ error?: Error;
35
+ };
36
+ export type DdocPersistenceOptions = {
37
+ timeoutMs?: number;
38
+ };
39
+ /**
40
+ * Read the doc's database as stored: every row replayed in key order, the
41
+ * same thing the editor loads on open. Never creates a database. Every call
42
+ * reads afresh: a read already under way may have started before the latest
43
+ * edit was written, so its result is never handed to a later caller.
44
+ */
45
+ export declare const readDdocContent: (ddocId: string, options?: DdocPersistenceOptions) => Promise<DdocContentSnapshot>;
46
+ /**
47
+ * Merge a full Yjs state into the doc's database, creating the database if
48
+ * it does not exist. Only the part the database does not already hold is
49
+ * written, as one row, in one readwrite transaction; merging state that is
50
+ * already stored writes nothing.
51
+ *
52
+ * An editor open in another tab sees the new row on its next load. An editor
53
+ * open in this tab does not (its Y.Doc is in memory), so callers with a
54
+ * mounted editor merge into its live doc instead.
55
+ *
56
+ * Expects a complete state (`Y.encodeStateAsUpdate` of a doc). An update whose
57
+ * new parts all depend on updates the store does not have yet changes nothing
58
+ * visible, and is not stored.
59
+ */
60
+ export declare const mergeDdocContent: (ddocId: string, encodedState: string, options?: DdocPersistenceOptions) => Promise<DdocContentMergeResult>;
61
+ export type DdocContentRepairResult = {
62
+ ddocId: string;
63
+ /**
64
+ * `repaired`: bad rows were dropped. `healthy`: nothing to drop, nothing
65
+ * written. `missing`: no database. `corrupt`: the database has no `updates`
66
+ * store, so there are no rows to keep.
67
+ */
68
+ status: 'repaired' | 'healthy' | 'missing' | 'corrupt' | 'timed-out' | 'unavailable';
69
+ droppedRows: number;
70
+ error?: Error;
71
+ };
72
+ /**
73
+ * Drop the stored rows that are not valid Yjs updates. One row y-indexeddb
74
+ * cannot apply makes its replay throw and never finish, so an editor waiting
75
+ * for it never loads. Each row is checked on its own; only the bad ones are
76
+ * deleted, in one readwrite transaction, and the good rows are left exactly as
77
+ * they are. Their content is lost, but it was already unreadable. Writes
78
+ * nothing when every row is good. Never creates a database.
79
+ */
80
+ export declare const repairDdocContent: (ddocId: string, options?: DdocPersistenceOptions) => Promise<DdocContentRepairResult>;
81
+ /**
82
+ * Delete the doc's database. Resolves `blocked` when another connection
83
+ * (an editor in another tab, for example) still holds it after the timeout;
84
+ * the browser keeps the delete queued and completes it once that connection
85
+ * closes. A missing database counts as deleted.
86
+ */
87
+ export declare const deleteDdocContent: (ddocId: string, options?: DdocPersistenceOptions) => Promise<DdocContentDeleteResult>;
88
+ /**
89
+ * Short, stable fingerprint of a doc's content (44 characters). Changes on
90
+ * every content edit, including delete-only and formatting-only edits; does
91
+ * not change when only the active tab changes. The same content gives the
92
+ * same fingerprint however the state was produced. See
93
+ * `describeDdocContent` for the rule. Uses `crypto.subtle`, which browsers
94
+ * only provide on secure (https or localhost) pages.
95
+ */
96
+ export declare const getDdocContentFingerprint: (encodedState: string) => Promise<string>;
@@ -0,0 +1,2 @@
1
+ export { DDOC_STORE_REPAIRED, DEFAULT_DDOC_PERSISTENCE_TIMEOUT_MS, deleteDdocContent, getDdocContentFingerprint, mergeDdocContent, readDdocContent, repairDdocContent, } from './ddoc-persistence';
2
+ export type { DdocContentDeleteResult, DdocContentMergeResult, DdocContentRepairResult, DdocContentSnapshot, DdocContentStatus, DdocPersistenceOptions, } from './ddoc-persistence';
@@ -0,0 +1,37 @@
1
+ import { IndexeddbPersistence } from 'y-indexeddb';
2
+
3
+ /**
4
+ * y-indexeddb's `whenSynced` resolves only on its 'synced' event. If
5
+ * `indexedDB.open` rejects, it never settles and nothing reports the failure.
6
+ * Reject on an open failure so the caller's error path runs.
7
+ */
8
+ export declare const waitForIndexeddbSync: (persistence: IndexeddbPersistence) => Promise<void>;
9
+ /**
10
+ * Like `waitForIndexeddbSync`, but resolves `stalled` if the provider has not
11
+ * synced after `stallMs`, so the caller can check why. The underlying wait is
12
+ * not cancelled; wait again to keep waiting.
13
+ */
14
+ export declare const waitForIndexeddbSyncOrStall: (persistence: IndexeddbPersistence, stallMs: number) => Promise<"synced" | "stalled">;
15
+ type WatchOptions = {
16
+ /** Called for every failed write and for a lost connection. */
17
+ onError: (error: Error) => void;
18
+ /** Called once the provider has been detached from the doc. */
19
+ onDetached: () => void;
20
+ };
21
+ /**
22
+ * Watch a synced provider's connection for the failures y-indexeddb itself
23
+ * ignores:
24
+ *
25
+ * - A failed write aborts its transaction, and `abort` bubbles to the
26
+ * database. y-indexeddb never looks, so quota errors and the like are lost.
27
+ * - The browser can close the connection (site data cleared, storage
28
+ * pressure: `close`), and a delete from another tab waits on it
29
+ * (`versionchange`). After either, y-indexeddb's update handler throws
30
+ * InvalidStateError into the next edit. Destroy the provider instead: it
31
+ * unsubscribes from doc updates before closing, so editing continues in
32
+ * memory, and a pending delete can proceed.
33
+ *
34
+ * Returns a function that removes the listeners.
35
+ */
36
+ export declare const watchIndexeddbConnection: (persistence: IndexeddbPersistence, { onError, onDetached }: WatchOptions) => (() => void);
37
+ export {};
@@ -57,9 +57,10 @@ export declare class SyncManager {
57
57
  private ydoc;
58
58
  private callbacksRef;
59
59
  private onLocalUpdate?;
60
+ private onLocalChange?;
60
61
  constructor(config: SyncManagerConfig, onCollabStateChange: (state: CollabState) => void);
61
62
  /** Called by useSyncManager on every render to keep refs fresh */
62
- updateRefs(_services: CollabServices | undefined, callbacks: CollabCallbacks | undefined, onLocalUpdate?: (updatedDocContent: string, updateChunk: string) => void): void;
63
+ updateRefs(_services: CollabServices | undefined, callbacks: CollabCallbacks | undefined, onLocalUpdate?: (updatedDocContent: string, updateChunk: string) => void, onLocalChange?: () => void): void;
63
64
  get isConnected(): boolean;
64
65
  get isReady(): boolean;
65
66
  get isResuming(): boolean;
@@ -135,6 +136,12 @@ export declare class SyncManager {
135
136
  private processNextUpdate;
136
137
  private handleRemoteContentUpdate;
137
138
  private applyRemoteYjsUpdate;
139
+ /**
140
+ * Tell the host that a collaborator's update changed the doc. The full
141
+ * document is encoded only for a host that asked for it (`onLocalUpdate`);
142
+ * `onLocalChange` gets the signal without that cost.
143
+ */
144
+ private notifyHostOfRemoteChange;
138
145
  private applyQueuedRemoteContents;
139
146
  private withRetry;
140
147
  private disconnectInternal;
@@ -199,6 +199,8 @@ export interface SyncManagerConfig {
199
199
  services?: CollabServices;
200
200
  callbacks?: CollabCallbacks;
201
201
  onLocalUpdate?: (updatedDocContent: Data['editorJSONData'], updateChunk: string) => void;
202
+ /** Like `onLocalUpdate` but without arguments, so no full-document encode. */
203
+ onLocalChange?: () => void;
202
204
  /** Origins to ignore in the ydoc update handler (e.g. IndexedDB provider) */
203
205
  ignoredOrigins?: Array<{
204
206
  current: unknown;
@@ -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
@@ -159,8 +152,6 @@ export interface DdocProps extends CommentAccountProps {
159
152
  fetchV1ImageFn?: (url: string) => Promise<ArrayBuffer | undefined>;
160
153
  disableInlineComment?: boolean;
161
154
  commentDrawerOpen?: boolean;
162
- onCommentDrawerOpenChange?: (open: boolean) => void;
163
- /** @deprecated Use onCommentDrawerOpenChange. */
164
155
  setCommentDrawerOpen?: React.Dispatch<SetStateAction<boolean>>;
165
156
  initialComments?: IComment[];
166
157
  initialCommentAnchors?: SerializedCommentAnchor[];
@@ -174,8 +165,6 @@ export interface DdocProps extends CommentAccountProps {
174
165
  onDeleteComment?: (activeCommentId: string, meta?: CommentMutationMeta) => void;
175
166
  onDeleteReply?: (activeCommentId: string, replyId: string) => void;
176
167
  showTOC?: boolean;
177
- onShowTOCChange?: (show: boolean) => void;
178
- /** @deprecated Use onShowTOCChange. */
179
168
  setShowTOC?: React.Dispatch<SetStateAction<boolean>>;
180
169
  extensions?: Record<string, Extension | any>;
181
170
  selectedTags?: TagType[];
@@ -185,19 +174,13 @@ export interface DdocProps extends CommentAccountProps {
185
174
  inlineCommentData?: InlineCommentData;
186
175
  setInlineCommentData?: React.Dispatch<React.SetStateAction<InlineCommentData>>;
187
176
  theme?: ThemeKey;
188
- /** Defaults to '1' when omitted. */
189
- zoomLevel?: string;
190
- onZoomLevelChange?: (zoomLevel: string) => void;
191
- /** @deprecated Use onZoomLevelChange. */
192
- setZoomLevel?: React.Dispatch<SetStateAction<string>>;
193
- /** Defaults to true when omitted. */
194
- isNavbarVisible?: boolean;
195
- onNavbarVisibleChange?: (visible: boolean) => void;
196
- /** @deprecated Use onNavbarVisibleChange. */
197
- setIsNavbarVisible?: React.Dispatch<SetStateAction<boolean>>;
177
+ zoomLevel: string;
178
+ setZoomLevel: React.Dispatch<SetStateAction<string>>;
179
+ isNavbarVisible: boolean;
180
+ setIsNavbarVisible: React.Dispatch<SetStateAction<boolean>>;
198
181
  editorCanvasClassNames?: string;
199
182
  isCommentSectionOpen?: boolean;
200
- isPreviewMode?: boolean;
183
+ isPreviewMode: boolean;
201
184
  viewerMode?: 'suggest' | 'view-only';
202
185
  ensResolutionUrl?: string;
203
186
  /**
@@ -222,13 +205,6 @@ export interface DdocProps extends CommentAccountProps {
222
205
  walletAddress?: string | null;
223
206
  username?: string | null;
224
207
  setUsername?: React.Dispatch<SetStateAction<string>>;
225
- /**
226
- * Host element to render the navbar into. Omit to render it as the editor's
227
- * first row. Pass null while the element is not attached yet.
228
- */
229
- navbarContainer?: HTMLElement | null;
230
- /** Show the toolbar's navbar collapse chevron. Default true. */
231
- showNavbarToggle?: boolean;
232
208
  renderNavbar?: ({ editor, liveEditor, }: {
233
209
  /** JSON snapshot — legacy field, unchanged. */
234
210
  editor: JSONContent;
@@ -236,6 +212,24 @@ export interface DdocProps extends CommentAccountProps {
236
212
  liveEditor: Editor | null;
237
213
  }) => JSX.Element;
238
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;
239
233
  onCollaboratorChange?: (collaborators: undefined | IDocCollabUsers[]) => void;
240
234
  onTextSelection?: (data: IEditorSelectionData) => void;
241
235
  onCommentInteraction?: (data: IEditorSelectionData) => void;
@@ -248,15 +242,9 @@ export interface DdocProps extends CommentAccountProps {
248
242
  */
249
243
  enableFanficTemplate?: boolean;
250
244
  onError?: (error: string) => void;
251
- /** Active-tab stats. Fires with all four fields whenever one changes. */
252
- onStatsChange?: (stats: DdocStats) => void;
253
- /** @deprecated Use onStatsChange. */
254
245
  setCharacterCount?: React.Dispatch<SetStateAction<number>>;
255
- /** @deprecated Use onStatsChange. */
256
246
  setWordCount?: React.Dispatch<SetStateAction<number>>;
257
- /** @deprecated Use onStatsChange. */
258
247
  setSelectedWordCount?: React.Dispatch<SetStateAction<number>>;
259
- /** @deprecated Use onStatsChange. */
260
248
  setPageCount?: React.Dispatch<SetStateAction<number>>;
261
249
  tags?: Array<{
262
250
  name: string;
@@ -265,17 +253,12 @@ export interface DdocProps extends CommentAccountProps {
265
253
  className?: string;
266
254
  unFocused?: boolean;
267
255
  isPresentationMode?: boolean;
268
- onPresentationModeChange?: (active: boolean) => void;
269
- /** @deprecated Use onPresentationModeChange. */
270
256
  setIsPresentationMode?: React.Dispatch<SetStateAction<boolean>>;
271
257
  /** Split View: edit markdown on the left, see the read-only ddoc render on the right. */
272
258
  isSplitView?: boolean;
273
- onSplitViewChange?: (active: boolean) => void;
274
- /** @deprecated Use onSplitViewChange. */
275
259
  setIsSplitView?: React.Dispatch<SetStateAction<boolean>>;
276
260
  onComment?: () => void;
277
261
  onInlineComment?: () => void;
278
- /** @deprecated Use onFocusModeChange. Still fires on every change. */
279
262
  onFocusMode?: (isFocusMode: boolean) => void;
280
263
  /** Controlled focus mode (D6). Omit for the legacy internal-state behavior. */
281
264
  isFocusMode?: boolean;
@@ -301,7 +284,6 @@ export interface DdocProps extends CommentAccountProps {
301
284
  renderToolbarLeading?: () => React.ReactNode;
302
285
  metadataProxyUrl?: string;
303
286
  onCopyHeadingLink?: (link: string) => void;
304
- /** @deprecated No effect. The host footer sits outside the editor. */
305
287
  footerHeight?: string;
306
288
  activeModel?: CustomModel;
307
289
  maxTokens?: number;
@@ -312,8 +294,8 @@ export interface DdocProps extends CommentAccountProps {
312
294
  */
313
295
  documentStyling?: DocumentStyling;
314
296
  /**
315
- * Callback when IndexedDB initialization fails
316
- * @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.
317
299
  */
318
300
  onIndexedDbError?: (error: Error) => void;
319
301
  /**
@@ -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, onSchemaStampGuard, ...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;