@fileverse-dev/ddoc 4.2.0-yjs-dedupe-1 → 4.2.0-yjs-dedupe-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/{index-DYlSDaET.mjs → index-CM9WymRI.mjs} +46065 -50973
  2. package/dist/{index-BfVM4ukb.mjs → index-QQcgsp20.mjs} +1 -1
  3. package/dist/index.d.ts +4 -0
  4. package/dist/index.es.js +8 -5
  5. package/dist/{markdown-source-pane-ve9MJKFE.mjs → markdown-source-pane-PIk1fw5v.mjs} +2 -2
  6. package/dist/package/components/editor-bubble-menu/types.d.ts +0 -1
  7. package/dist/package/components/editor-utils.d.ts +1 -25
  8. package/dist/package/components/inline-comment/comment-drawer-desktop.d.ts +1 -2
  9. package/dist/package/components/inline-comment/comment-drawer-mobile.d.ts +1 -2
  10. package/dist/package/components/inline-comment/comment-drawer.d.ts +1 -1
  11. package/dist/package/components/inline-comment/comment-input-field.d.ts +1 -2
  12. package/dist/package/components/inline-comment/comment-reply-input.d.ts +1 -2
  13. package/dist/package/components/inline-comment/comment-section.d.ts +1 -1
  14. package/dist/package/components/inline-comment/floating-comment/comment-floating-container.d.ts +1 -1
  15. package/dist/package/components/inline-comment/floating-comment/suggestion-thread-floating-card.d.ts +1 -1
  16. package/dist/package/components/inline-comment/floating-comment/thread-floating-card.d.ts +1 -1
  17. package/dist/package/components/inline-comment/floating-comment/types.d.ts +0 -2
  18. package/dist/package/components/inline-comment/types.d.ts +0 -2
  19. package/dist/package/components/split-view/split-view-css-accordion.d.ts +13 -0
  20. package/dist/package/components/split-view/split-view-markdown-pane.d.ts +3 -1
  21. package/dist/package/extensions/mardown-paste-handler/index.d.ts +6 -0
  22. package/dist/package/hooks/use-editor-commands.d.ts +20 -0
  23. package/dist/package/hooks/use-focus-mode.d.ts +6 -1
  24. package/dist/package/hooks/use-headless-editor.d.ts +9 -1
  25. package/dist/package/hooks/use-tab-editor.d.ts +3 -3
  26. package/dist/package/hooks/use-yjs-setup.d.ts +7 -1
  27. package/dist/package/stores/comment-store-provider.d.ts +3 -1
  28. package/dist/package/stores/comment-store.d.ts +15 -0
  29. package/dist/package/sync-local/SyncManager.d.ts +30 -7
  30. package/dist/package/sync-local/floor.d.ts +6 -0
  31. package/dist/package/sync-local/presence.d.ts +11 -0
  32. package/dist/package/sync-local/socketClient.d.ts +55 -8
  33. package/dist/package/sync-local/types/index.d.ts +82 -11
  34. package/dist/package/sync-local/useSyncManager.d.ts +4 -0
  35. package/dist/package/types.d.ts +20 -2
  36. package/dist/package/use-ddoc-editor.d.ts +5 -0
  37. package/dist/package/utils/colors.d.ts +1 -0
  38. package/dist/package/utils/insert-commands.d.ts +20 -0
  39. package/dist/package/utils/make-editor.d.ts +9 -0
  40. package/dist/package/utils/sanitize-css.d.ts +45 -0
  41. package/dist/package/utils/selection-utils.d.ts +35 -0
  42. package/dist/package/utils/typography.d.ts +26 -0
  43. package/dist/{split-view-toolbar-8avjvjIR.mjs → split-view-toolbar-B7EkoqMb.mjs} +2 -2
  44. package/dist/style.css +1 -1
  45. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { ISocketInitConfig, RoomMember, SocketStatusEnum, SendUpdateResponse, CommitResponse, AckResponse } from './types';
1
+ import { ISocketInitConfig, SocketStatusEnum, SendUpdateResponse, HydrationResponse, SnapshotResponse, AckResponse } from './types';
2
2
  import { Awareness } from 'y-protocols/awareness.js';
3
3
 
4
4
  interface ISocketClientConfig {
@@ -8,6 +8,22 @@ interface ISocketClientConfig {
8
8
  ownerEdSecret?: string;
9
9
  contractAddress?: string;
10
10
  ownerAddress?: string;
11
+ ownerIdentityDid?: string;
12
+ editLock?: string;
13
+ encryptedTitle?: string;
14
+ identityToken?: string;
15
+ identityContractAddress?: string;
16
+ editUcan?: string;
17
+ refreshEditClaim?: () => Promise<{
18
+ status: 'ok';
19
+ token: string;
20
+ } | {
21
+ status: 'demoted';
22
+ } | {
23
+ status: 'unavailable';
24
+ }>;
25
+ actorHandle?: string;
26
+ joinOnly?: boolean;
11
27
  onHandshakeData?: (response: {
12
28
  data: AckResponse;
13
29
  roomKey: string;
@@ -20,6 +36,8 @@ interface ISocketClientConfig {
20
36
  }
21
37
  export declare class SocketClient {
22
38
  private _socketUrl;
39
+ private _restBase;
40
+ private _lastSessionToken;
23
41
  private _socket;
24
42
  private _webSocketStatus;
25
43
  private _isIntentionalDisconnect;
@@ -27,13 +45,26 @@ export declare class SocketClient {
27
45
  get status(): SocketStatusEnum;
28
46
  private _websocketServiceDid;
29
47
  private roomId;
30
- roomMembers: RoomMember[];
48
+ roomMembers: string[];
49
+ private _lastPresenceSignature;
50
+ private _onPresenceChange?;
51
+ private _pendingAwarenessUpdates;
31
52
  private collaborationKeyPair;
32
53
  private ownerKeyPair?;
33
54
  private contractAddress?;
34
55
  private ownerUcan?;
35
56
  private collaborationUcan?;
36
57
  private ownerAddress?;
58
+ private ownerIdentityDid?;
59
+ private editLock?;
60
+ private encryptedTitle?;
61
+ private identityToken?;
62
+ private identityContractAddress?;
63
+ private editUcan?;
64
+ private refreshEditClaim?;
65
+ private lastGoodEditUcan?;
66
+ private actorHandle?;
67
+ private joinOnly?;
37
68
  private roomKey;
38
69
  private roomInfo?;
39
70
  private awareness;
@@ -41,20 +72,36 @@ export declare class SocketClient {
41
72
  private _onHandshakeData;
42
73
  constructor(config: ISocketClientConfig);
43
74
  registerAwareness(awareness: Awareness): void;
75
+ private _stampSocketId;
44
76
  private _emitWithAck;
45
77
  private _fetchRoomMembers;
78
+ private _recomputePresence;
46
79
  sendUpdate({ update }: {
47
80
  update: string;
48
81
  }): Promise<SendUpdateResponse>;
49
- commitUpdates({ updates, cid }: {
50
- updates: string[];
51
- cid: string;
52
- }): Promise<CommitResponse>;
53
- fetchLatestCommit(): Promise<AckResponse<any>>;
54
- getUncommittedChanges(): Promise<AckResponse<any>>;
82
+ fetchHydrationRange(sinceSeq?: number): Promise<HydrationResponse>;
83
+ sendSnapshot({ data, floorSeq, publishedMarker, }: {
84
+ data: string;
85
+ floorSeq: number;
86
+ publishedMarker?: string | null;
87
+ }): Promise<SnapshotResponse>;
88
+ sendMirrorSnapshot({ data, fileKeyEpoch, }: {
89
+ data: string;
90
+ fileKeyEpoch: number;
91
+ }): Promise<AckResponse<any>>;
92
+ setDocumentMeta(): Promise<void>;
93
+ /** Owner rename mid-session: refresh the connect-frozen title artifacts (so
94
+ * every later /auth re-sends current values), persist server-side, and let
95
+ * the server broadcast /document/meta_update to room peers. */
96
+ updateDocumentMeta(args: {
97
+ encryptedTitle: string;
98
+ documentTitle: string;
99
+ }): Promise<void>;
55
100
  broadcastAwareness(awarenessUpdate: string): Promise<void>;
56
101
  disconnect: () => void;
102
+ reauth: () => void;
57
103
  terminateSession: () => Promise<void>;
104
+ flushBeacon(mergedUpdate: string): void;
58
105
  private getCollaborationKeyPair;
59
106
  private isUcanValid;
60
107
  private getOwnerToken;
@@ -7,6 +7,49 @@ export interface CollabConnectionConfig {
7
7
  roomId: string;
8
8
  wsUrl: string;
9
9
  isOwner: boolean;
10
+ /** Host signal: doc is in an active live-collaboration context (shared / peers present). Keeps the socket connected. */
11
+ livePresence?: boolean;
12
+ /** Host signal: connect once on open to initialise durability even without an edit. */
13
+ connectOnOpen?: boolean;
14
+ /** Host signal (owner-only): the owner identity signingKey DID. Recorded by the durable server on the first owner /auth as the R3 owner binding. */
15
+ ownerIdentityDid?: string;
16
+ /** Host signal (owner-only): wrapped roomKey bytes (owner-lock construction), uploaded as the server editLock recovery artifact. Opaque to the package. */
17
+ editLock?: string;
18
+ /** Host signal (owner-only): the document title encrypted with the roomKey, uploaded as the server title recovery artifact. Opaque to the package. */
19
+ encryptedTitle?: string;
20
+ /** Host signal (owner-only): Ed25519 UCAN proving the owner identity, sent on /auth so the server binds the CRYPTOGRAPHICALLY PROVEN owner DID (not a bare asserted string). Opaque to the package. */
21
+ identityToken?: string;
22
+ /** Host signal (owner-only): the identity contract address the server reads to resolve the on-chain signingDid that verifies identityToken. */
23
+ identityContractAddress?: string;
24
+ /** Host signal (non-owner editor): the gate-minted edit-admission UCAN, forwarded on /auth so the
25
+ * server admits this connection as a GP-rail editor. Opaque to the package. */
26
+ editUcan?: string;
27
+ /** Host signal (non-owner editor): re-mints a CURRENT-epoch editUcan. Called at every /auth
28
+ * (incl. reconnect after a force-drop). Tri-state: `ok` (fresh token; overrides the static
29
+ * `editUcan`), `demoted` (no longer an editor → terminal, drop to viewer), or `unavailable`
30
+ * (transient/network → retry on the last-good claim). Opaque to the package. */
31
+ refreshEditClaim?: () => Promise<{
32
+ status: 'ok';
33
+ token: string;
34
+ } | {
35
+ status: 'demoted';
36
+ } | {
37
+ status: 'unavailable';
38
+ }>;
39
+ /** Host signal: an anonymous per-connection actor handle (public rail). Record-only; opaque. */
40
+ actorHandle?: string;
41
+ /** Host signal (non-creator workspace member): join-only. The server never creates or
42
+ * binds a session for this connection and caps the role at editor; every handshake
43
+ * rejection resolves to a quiet terminated state ('ROOM_NOT_ESTABLISHED' when the room
44
+ * was never established, 'JOIN_REJECTED' otherwise) instead of an error. */
45
+ joinOnly?: boolean;
46
+ /** Host signal (editor): produces the view-plane mirror ciphertext from the live Yjs state. The app
47
+ * AES-GCM-encrypts `{ file: base64(yjsUpdate), source }` under the fileKey in the publish wire
48
+ * format. Absent ⇒ the mirror is not written. The package never sees the fileKey or the crypto. */
49
+ encryptMirror?: (yjsUpdate: Uint8Array) => Promise<string>;
50
+ /** Host signal: the fileKey's rotation epoch (the gate `currentEpoch` / fileKey version — NOT
51
+ * `editGrantEpoch`; default 0 for non-rotating plain-public docs). */
52
+ fileKeyEpoch?: number;
10
53
  ownerEdSecret?: string;
11
54
  contractAddress?: string;
12
55
  ownerAddress?: string;
@@ -21,11 +64,7 @@ export interface CollabSessionMeta {
21
64
  username: string;
22
65
  isEns?: boolean;
23
66
  }
24
- /** Storage integrations the sync engine depends on */
25
- export interface CollabServices {
26
- commitToStorage?: (file: File) => Promise<string>;
27
- fetchFromStorage?: (cid: string) => Promise<any>;
28
- }
67
+ export type CollabServices = Record<string, never>;
29
68
  export type CollabErrorCode = 'CONNECTION_FAILED' | 'AUTH_FAILED' | 'SYNC_FAILED' | 'TIMEOUT' | 'UNKNOWN';
30
69
  export type CollabError = {
31
70
  code: CollabErrorCode;
@@ -93,6 +132,8 @@ export interface CollabCallbacks {
93
132
  data: AckResponse;
94
133
  roomKey: string;
95
134
  }) => void;
135
+ /** Live rename from the room owner; title is roomKey-encrypted. */
136
+ onTitleUpdate?: (encryptedTitle: string | null) => void;
96
137
  }
97
138
  /** Discriminated union — TypeScript enforces config+services only when enabled */
98
139
  export type CollaborationProps = {
@@ -127,7 +168,10 @@ export declare enum ServerErrorCode {
127
168
  INVALID_ADDRESS = "INVALID_ADDRESS",
128
169
  NOT_AUTHENTICATED = "NOT_AUTHENTICATED",
129
170
  DB_ERROR = "DB_ERROR",
130
- INTERNAL_ERROR = "INTERNAL_ERROR"
171
+ INTERNAL_ERROR = "INTERNAL_ERROR",
172
+ JOIN_DISABLED = "JOIN_DISABLED",
173
+ EDIT_REVOKED = "EDIT_REVOKED",
174
+ ROOM_NOT_ESTABLISHED = "ROOM_NOT_ESTABLISHED"
131
175
  }
132
176
  export interface AckResponse<T = Record<string, any>> {
133
177
  status: boolean;
@@ -145,11 +189,30 @@ export interface SendUpdateResponse extends AckResponse<{
145
189
  createdAt: number;
146
190
  }> {
147
191
  }
148
- export interface CommitResponse extends AckResponse<{
149
- cid: string;
150
- createdAt: number;
192
+ export interface HydrationRow {
193
+ id: string;
151
194
  documentId: string;
152
- updates: string[];
195
+ seq: number;
196
+ data: string;
197
+ updateType: string;
198
+ committed?: boolean;
199
+ commitCid?: string | null;
200
+ createdAt?: number;
201
+ sessionDid?: string;
202
+ publishedMarker?: string | null;
203
+ floorSeq?: number | null;
204
+ }
205
+ export interface HydrationResponse extends AckResponse<{
206
+ history: HydrationRow[];
207
+ total: number;
208
+ snapshot: HydrationRow | null;
209
+ nextSeq: number | null;
210
+ hasMore: boolean;
211
+ }> {
212
+ }
213
+ export interface SnapshotResponse extends AckResponse<{
214
+ id: string;
215
+ seq: number;
153
216
  }> {
154
217
  }
155
218
  export interface ISocketInitConfig {
@@ -157,7 +220,7 @@ export interface ISocketInitConfig {
157
220
  onDisconnect: () => void;
158
221
  onSocketDropped: () => void;
159
222
  onError: (err: Error) => void;
160
- onHandShakeError: (err: Error, statusCode?: number) => void;
223
+ onHandShakeError: (err: Error, statusCode?: number, errorCode?: ServerErrorCode) => void;
161
224
  onContentUpdate: (data: {
162
225
  id: string;
163
226
  data: string;
@@ -171,6 +234,8 @@ export interface ISocketInitConfig {
171
234
  };
172
235
  roomId: string;
173
236
  }) => void;
237
+ onPresenceChange?: (collaborators: IDocCollabUsers[]) => void;
238
+ onTitleUpdate?: (encryptedTitle: string | null) => void;
174
239
  onSessionTerminated: (data: {
175
240
  roomId: string;
176
241
  }) => void;
@@ -196,4 +261,10 @@ export interface IAuthArgs {
196
261
  contractAddress?: string;
197
262
  sessionDid?: string;
198
263
  roomInfo?: string;
264
+ ownerIdentityDid?: string;
265
+ identityToken?: string;
266
+ identityContractAddress?: string;
267
+ editUcan?: string;
268
+ actorHandle?: string;
269
+ joinOnly?: boolean;
199
270
  }
@@ -5,6 +5,10 @@ export declare const useSyncManager: (config: SyncManagerConfig) => {
5
5
  connect: (connectConfig: CollabConnectionConfig) => void;
6
6
  disconnect: () => void;
7
7
  terminateSession: () => void;
8
+ updateTitle: (args: {
9
+ encryptedTitle: string;
10
+ documentTitle: string;
11
+ }) => void;
8
12
  isReady: boolean;
9
13
  isSyncing: boolean;
10
14
  awareness: import('y-protocols/awareness.js').Awareness | null;
@@ -9,10 +9,11 @@ import { CollaborationProps } from './sync-local/types';
9
9
  export type { CollaborationProps, CollabConnectionConfig, CollabSessionMeta, CollabServices, CollabCallbacks, CollabState, CollabError, CollabErrorCode, CollabStatus, } from './sync-local/types';
10
10
  export declare const DdocEditorProps: EditorProps;
11
11
  export interface IDocCollabUsers {
12
- clientId: number;
12
+ clientId: string | number;
13
13
  name: string;
14
14
  isEns: string;
15
15
  color: string;
16
+ isPlaceholder?: boolean;
16
17
  }
17
18
  export type InlineCommentData = {
18
19
  inlineCommentText: string;
@@ -103,6 +104,17 @@ export interface DocumentStyling {
103
104
  * @example "portrait" | "landscape"
104
105
  */
105
106
  orientation?: 'portrait' | 'landscape';
107
+ /**
108
+ * Custom CSS
109
+ * @description The author's escape hatch for styling the document beyond the
110
+ * structured options above. Write BARE selectors (`h1 { … }`, `p { … }`,
111
+ * `blockquote { … }`) — they are auto-scoped to the document content, so rules
112
+ * can't leak into the toolbar or the host app. Bare top-level declarations
113
+ * (e.g. `background`, `font-family`) style the document surface itself.
114
+ * Applies live in the editor, in preview, and in exports (Markdown/HTML).
115
+ * @example "h1 { letter-spacing: -0.02em } p { line-height: 1.8 }"
116
+ */
117
+ customCSS?: string;
106
118
  }
107
119
  export interface DdocProps extends CommentAccountProps {
108
120
  /**
@@ -169,8 +181,11 @@ export interface DdocProps extends CommentAccountProps {
169
181
  walletAddress?: string | null;
170
182
  username?: string | null;
171
183
  setUsername?: React.Dispatch<SetStateAction<string>>;
172
- renderNavbar?: ({ editor }: {
184
+ renderNavbar?: ({ editor, liveEditor, }: {
185
+ /** JSON snapshot — legacy field, unchanged. */
173
186
  editor: JSONContent;
187
+ /** Live Tiptap editor instance (D8) — null until the editor is ready. */
188
+ liveEditor: Editor | null;
174
189
  }) => JSX.Element;
175
190
  onChange?: (updatedDocContent: string | JSONContent, updateChunk: string) => void;
176
191
  onCollaboratorChange?: (collaborators: undefined | IDocCollabUsers[]) => void;
@@ -198,6 +213,9 @@ export interface DdocProps extends CommentAccountProps {
198
213
  onComment?: () => void;
199
214
  onInlineComment?: () => void;
200
215
  onFocusMode?: (isFocusMode: boolean) => void;
216
+ /** Controlled focus mode (D6). Omit for the legacy internal-state behavior. */
217
+ isFocusMode?: boolean;
218
+ onFocusModeChange?: (isFocusMode: boolean) => void;
201
219
  onMarkdownExport?: () => void;
202
220
  onMarkdownImport?: () => void;
203
221
  onPdfExport?: () => void;
@@ -28,8 +28,13 @@ export declare const useDdocEditor: ({ isPreviewMode, viewerMode, initialContent
28
28
  duplicateTab: (tabId: string) => string | undefined;
29
29
  orderTab: (destinationTabId: string, movedTabId: string) => void;
30
30
  onConnect: (connectConfig: import('./types').CollabConnectionConfig) => void;
31
+ onDisconnect: () => void;
31
32
  isReady: boolean;
32
33
  isSyncing: boolean;
34
+ updateTitle: (args: {
35
+ encryptedTitle: string;
36
+ documentTitle: string;
37
+ }) => void;
33
38
  hasCollabContentInitialised: boolean;
34
39
  isIndexeddbSynced: boolean;
35
40
  initialiseYjsIndexedDbProvider: () => Promise<void>;
@@ -9,4 +9,5 @@ export declare const textColors: {
9
9
  light: string;
10
10
  dark: string;
11
11
  }[];
12
+ export declare const resolveEditorColorVars: (value: string) => string;
12
13
  export declare const getResponsiveColor: (color?: string, theme?: ThemeKey) => string | undefined;
@@ -0,0 +1,20 @@
1
+ import { Editor, Range } from '@tiptap/core';
2
+ import { IpfsImageUploadResponse } from '../types';
3
+
4
+ type InsertCommand = (editor: Editor, range?: Range) => void;
5
+ export type UploadImageOptions = {
6
+ onError?: (errorString: string) => void;
7
+ ipfsImageUploadFn?: (file: File) => Promise<IpfsImageUploadResponse>;
8
+ };
9
+ /**
10
+ * File-picker image upload — mirrors the toolbar flow
11
+ * (editor-utils.tsx "Upload Image") and the slash "Image" item.
12
+ */
13
+ export declare const uploadImageCommand: (editor: Editor, { onError, ipfsImageUploadFn }?: UploadImageOptions) => void;
14
+ /**
15
+ * Insert commands shared by the slash menu and `useEditorCommands`.
16
+ * Bodies are moved verbatim from `slash-command-utils.tsx`; the optional
17
+ * `range` only differs when invoked from the slash menu.
18
+ */
19
+ export declare const insertCommands: Record<string, InsertCommand>;
20
+ export {};
@@ -0,0 +1,9 @@
1
+ import { Editor } from '@tiptap/react';
2
+
3
+ /**
4
+ * Shared jsdom editor factory for unit tests (use-editor-commands.test.tsx,
5
+ * selection-utils.test.ts, ...). Collaboration owns the doc, so content must
6
+ * be set via `setContent` *after* construction — passing `content` to the
7
+ * `Editor` constructor is silently ignored once Collaboration is configured.
8
+ */
9
+ export declare const makeEditor: (content?: string) => Editor;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Sanitize + scope + validate author-supplied custom CSS before it is injected
3
+ * into the document.
4
+ *
5
+ * Authors write **normal, full-page CSS** — `body { … }`, `html body h1 { … }`,
6
+ * `h1 { … }`, `* { … }` — the way they'd style any web page. We transparently
7
+ * scope every rule to the document so it can't leak into the app, AND we map the
8
+ * page-root selectors (`html`, `body`, `:root`) onto the document root so that
9
+ * `body { background: … }` styles the doc surface and `html body h1 { … }`
10
+ * styles the doc's headings. No `.ProseMirror` prefix, no learning curve.
11
+ *
12
+ * Because the raw string reaches VIEWERS of a published doc, it is untrusted.
13
+ * Every selector is force-scoped (so a `}` breakout is neutralised — the escaped
14
+ * rule is simply scoped too), and dangerous declarations are stripped:
15
+ * - external resource loads / exfiltration: `url(…)`, `@import`;
16
+ * - clickjacking / UI redressing: `position: fixed | sticky`;
17
+ * - legacy script vectors: `expression()`, `-moz-binding`, `behavior:`.
18
+ *
19
+ * We parse with the browser's own CSS engine (no dependency), rewrite each
20
+ * selector, strip dangerous declarations at every nesting level, and let the
21
+ * engine re-serialise — so the output is always a valid, balanced stylesheet.
22
+ *
23
+ * The sanitizer knows exactly what it removed, so it returns non-blocking
24
+ * `diagnostics`; the editing UI surfaces them (CSS is forgiving — we warn,
25
+ * never block).
26
+ */
27
+ export interface CssDiagnostic {
28
+ level: 'warning' | 'error';
29
+ message: string;
30
+ }
31
+ export interface CssValidationResult {
32
+ /** Sanitized, scoped CSS, safe to inject. '' when nothing survives. */
33
+ css: string;
34
+ /** What was stripped/ignored, deduped. Empty when the CSS was fully clean. */
35
+ diagnostics: CssDiagnostic[];
36
+ }
37
+ /**
38
+ * Sanitize + scope AND report. Use this from the editing UI for diagnostics.
39
+ */
40
+ export declare const validateCustomCss: (raw: string | undefined | null, scope?: string) => CssValidationResult;
41
+ /**
42
+ * Sanitized + scoped CSS only — used at injection/export sinks that just need
43
+ * the safe string. Diagnostics are surfaced separately via validateCustomCss.
44
+ */
45
+ export declare const sanitizeCustomCss: (raw: string | undefined | null, scope?: string) => string;
@@ -0,0 +1,35 @@
1
+ import { Editor } from '@tiptap/core';
2
+
3
+ /**
4
+ * Cursor-relative word range in the current textblock, or null when the
5
+ * cursor is not adjacent to a word character (whitespace runs, empty
6
+ * blocks). Position math is relative ($from.start() + parentOffset), so it
7
+ * is schema-agnostic (works under dBlock wrappers).
8
+ */
9
+ export declare const findWordRangeAtCursor: (editor: Editor) => {
10
+ from: number;
11
+ to: number;
12
+ } | null;
13
+ /**
14
+ * Ensure a text target for anchored actions (inline comments): keep any
15
+ * existing non-empty TextSelection; otherwise select the word under the
16
+ * collapsed cursor. Shares findWordRangeAtCursor with enablement so
17
+ * "enabled" always implies "dispatch does something".
18
+ *
19
+ * Only a TextSelection passes through untouched — a non-empty NodeSelection
20
+ * (e.g. an image or horizontal rule selected as a node) looks "non-empty"
21
+ * but has no text content, so `textBetween` over it is '' and anything
22
+ * downstream that needs real text (e.g. createFloatingDraft) would silently
23
+ * no-op. Falling through to findWordRangeAtCursor for any other selection
24
+ * type gives either a legitimate adjacent word or null (schema-dependent —
25
+ * e.g. false for a NodeSelection whose dBlock wrapper has no text).
26
+ */
27
+ export declare const selectWordAtCursor: (editor: Editor) => boolean;
28
+ /**
29
+ * Single enablement predicate for anchored text actions (inline comments):
30
+ * true when there's already a non-empty TextSelection, or a word is
31
+ * reachable from a collapsed cursor via findWordRangeAtCursor. Mirrors the
32
+ * passthrough condition in selectWordAtCursor so "enabled" always implies
33
+ * selectWordAtCursor will produce a real text target.
34
+ */
35
+ export declare const hasTextTargetAtSelection: (editor: Editor) => boolean;
@@ -0,0 +1,26 @@
1
+ import { Editor } from '@tiptap/core';
2
+
3
+ export declare const FONT_SIZES: readonly [8, 9, 10, 11, 12, 14, 16, 18, 24, 30, 32, 36, 48, 60, 72, 96];
4
+ export declare const getFontSizeOptions: (editor?: Editor) => {
5
+ title: string;
6
+ value: string;
7
+ label: string;
8
+ command: (editor: Editor) => void;
9
+ isActive: () => boolean | undefined;
10
+ }[];
11
+ export declare const getCurrentFontSize: (editor: Editor | null, currentSize: string) => string;
12
+ export declare const uiValueToPercentage: (uiValue: string) => string;
13
+ export declare const percentageToUiValue: (percentage: string) => string;
14
+ export declare const LINE_HEIGHT_OPTIONS: {
15
+ value: string;
16
+ label: string;
17
+ uiValue: string;
18
+ description: string;
19
+ }[];
20
+ export declare const getLineHeightOptions: () => {
21
+ value: string;
22
+ label: string;
23
+ uiValue: string;
24
+ description: string;
25
+ }[];
26
+ export declare const getCurrentLineHeight: (editor: Editor | null, currentLineHeight?: string) => string;
@@ -1,7 +1,7 @@
1
- import { j as d } from "./index-DYlSDaET.mjs";
1
+ import { j as d } from "./index-CM9WymRI.mjs";
2
2
  import { useRef as I, useState as N, useEffect as D } from "react";
3
3
  import { DynamicDropdown as E, Tooltip as R, IconButton as O } from "@fileverse/ui";
4
- import { a3 as A, a4 as P, c as y } from "./index-BfVM4ukb.mjs";
4
+ import { a3 as A, a4 as P, c as y } from "./index-QQcgsp20.mjs";
5
5
  const p = (e, t, o = t) => {
6
6
  const { state: s } = e, l = s.changeByRange((n) => {
7
7
  const u = s.sliceDoc(n.from, n.to), r = s.sliceDoc(