@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.
- package/dist/{index-DYlSDaET.mjs → index-CM9WymRI.mjs} +46065 -50973
- package/dist/{index-BfVM4ukb.mjs → index-QQcgsp20.mjs} +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.es.js +8 -5
- package/dist/{markdown-source-pane-ve9MJKFE.mjs → markdown-source-pane-PIk1fw5v.mjs} +2 -2
- package/dist/package/components/editor-bubble-menu/types.d.ts +0 -1
- package/dist/package/components/editor-utils.d.ts +1 -25
- package/dist/package/components/inline-comment/comment-drawer-desktop.d.ts +1 -2
- package/dist/package/components/inline-comment/comment-drawer-mobile.d.ts +1 -2
- package/dist/package/components/inline-comment/comment-drawer.d.ts +1 -1
- package/dist/package/components/inline-comment/comment-input-field.d.ts +1 -2
- package/dist/package/components/inline-comment/comment-reply-input.d.ts +1 -2
- package/dist/package/components/inline-comment/comment-section.d.ts +1 -1
- package/dist/package/components/inline-comment/floating-comment/comment-floating-container.d.ts +1 -1
- package/dist/package/components/inline-comment/floating-comment/suggestion-thread-floating-card.d.ts +1 -1
- package/dist/package/components/inline-comment/floating-comment/thread-floating-card.d.ts +1 -1
- package/dist/package/components/inline-comment/floating-comment/types.d.ts +0 -2
- package/dist/package/components/inline-comment/types.d.ts +0 -2
- package/dist/package/components/split-view/split-view-css-accordion.d.ts +13 -0
- package/dist/package/components/split-view/split-view-markdown-pane.d.ts +3 -1
- package/dist/package/extensions/mardown-paste-handler/index.d.ts +6 -0
- package/dist/package/hooks/use-editor-commands.d.ts +20 -0
- package/dist/package/hooks/use-focus-mode.d.ts +6 -1
- package/dist/package/hooks/use-headless-editor.d.ts +9 -1
- package/dist/package/hooks/use-tab-editor.d.ts +3 -3
- package/dist/package/hooks/use-yjs-setup.d.ts +7 -1
- package/dist/package/stores/comment-store-provider.d.ts +3 -1
- package/dist/package/stores/comment-store.d.ts +15 -0
- package/dist/package/sync-local/SyncManager.d.ts +30 -7
- package/dist/package/sync-local/floor.d.ts +6 -0
- package/dist/package/sync-local/presence.d.ts +11 -0
- package/dist/package/sync-local/socketClient.d.ts +55 -8
- package/dist/package/sync-local/types/index.d.ts +82 -11
- package/dist/package/sync-local/useSyncManager.d.ts +4 -0
- package/dist/package/types.d.ts +20 -2
- package/dist/package/use-ddoc-editor.d.ts +5 -0
- package/dist/package/utils/colors.d.ts +1 -0
- package/dist/package/utils/insert-commands.d.ts +20 -0
- package/dist/package/utils/make-editor.d.ts +9 -0
- package/dist/package/utils/sanitize-css.d.ts +45 -0
- package/dist/package/utils/selection-utils.d.ts +35 -0
- package/dist/package/utils/typography.d.ts +26 -0
- package/dist/{split-view-toolbar-8avjvjIR.mjs → split-view-toolbar-B7EkoqMb.mjs} +2 -2
- package/dist/style.css +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ISocketInitConfig,
|
|
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:
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
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
|
|
149
|
-
|
|
150
|
-
createdAt: number;
|
|
192
|
+
export interface HydrationRow {
|
|
193
|
+
id: string;
|
|
151
194
|
documentId: string;
|
|
152
|
-
|
|
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;
|
package/dist/package/types.d.ts
CHANGED
|
@@ -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>;
|
|
@@ -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-
|
|
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-
|
|
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(
|