@kedataindo/docflow-core 0.0.2 → 0.0.4
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/LICENSE +44 -0
- package/dist/index.cjs +1519 -28
- package/dist/index.d.cts +337 -1
- package/dist/index.d.ts +337 -1
- package/dist/index.js +1499 -21
- package/package.json +5 -4
package/dist/index.d.cts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { AnyExtension, Editor, Extension } from '@tiptap/core';
|
|
2
2
|
import { Awareness } from 'y-protocols/awareness';
|
|
3
|
+
import { IndexeddbPersistence } from 'y-indexeddb';
|
|
3
4
|
import { WebrtcProvider } from 'y-webrtc';
|
|
4
5
|
import { WebsocketProvider } from 'y-websocket';
|
|
5
6
|
import * as Y from 'yjs';
|
|
6
7
|
import { PaginationPlusOptions } from 'tiptap-pagination-plus';
|
|
7
8
|
export { PAGE_SIZES, PageSize, PaginationPlus, PaginationPlusOptions } from 'tiptap-pagination-plus';
|
|
9
|
+
import { PluginKey } from '@tiptap/pm/state';
|
|
10
|
+
import { Node } from '@tiptap/pm/model';
|
|
8
11
|
|
|
9
12
|
interface ToolbarItem {
|
|
10
13
|
id: string;
|
|
@@ -45,6 +48,15 @@ interface AwarenessState {
|
|
|
45
48
|
from: number;
|
|
46
49
|
to: number;
|
|
47
50
|
} | null;
|
|
51
|
+
/**
|
|
52
|
+
* Phase 9 PR2 — *present* flag. False when the local user has navigated
|
|
53
|
+
* away from the editor route (the Yjs provider is still alive but the
|
|
54
|
+
* host has gated cursor emission off, per the §6 *\"TOC sidebar orphan
|
|
55
|
+
* wiring\"* / REST-heartbeat scope notes). Peers see this instantly
|
|
56
|
+
* via the awareness `change` event — far faster than the 15s REST
|
|
57
|
+
* heartbeat fallback. Defaults to true when the user is in the editor.
|
|
58
|
+
*/
|
|
59
|
+
present: boolean;
|
|
48
60
|
}
|
|
49
61
|
interface CollaborationOptions {
|
|
50
62
|
room: string;
|
|
@@ -57,16 +69,215 @@ interface CollaborationOptions {
|
|
|
57
69
|
};
|
|
58
70
|
onAwarenessChange?: (states: AwarenessState[]) => void;
|
|
59
71
|
initialStorageState?: Uint8Array;
|
|
72
|
+
/**
|
|
73
|
+
* Phase 9 PR2 — when false, the local user is treated as
|
|
74
|
+
* out-of-editor: their cursor field is nulled and `present` is false.
|
|
75
|
+
* Default is true (in editor). Toggle at runtime via
|
|
76
|
+
* `setLocalCursorEnabled()` returned from `createCollaboration`.
|
|
77
|
+
*/
|
|
78
|
+
emitCursor?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Phase 9 OF1 — when true, mirror the room's Y.Doc into IndexedDB
|
|
81
|
+
* (`docflow-<room>`) via y-indexeddb so edits survive offline and reload.
|
|
82
|
+
* Reconcile-on-reconnect is plain Yjs merge — no conflict resolution
|
|
83
|
+
* needed. The IndexedDB mirror is NEVER a seed source: the server (or the
|
|
84
|
+
* legacy-JSON seed path) still owns initial state; the mirror only
|
|
85
|
+
* contributes local offline edits via the normal Yjs update exchange.
|
|
86
|
+
*/
|
|
87
|
+
offline?: boolean;
|
|
60
88
|
}
|
|
61
89
|
interface CollaborationSetup {
|
|
62
90
|
ydoc: Y.Doc;
|
|
63
91
|
provider: WebrtcProvider | WebsocketProvider | null;
|
|
64
92
|
awareness: Awareness;
|
|
93
|
+
/**
|
|
94
|
+
* Phase 9 OF1 — y-indexeddb persistence instance, present only when
|
|
95
|
+
* `options.offline` is true. Exposed so hosts/tests can `await
|
|
96
|
+
* persistence.whenSynced` before asserting the mirror contents. Do not
|
|
97
|
+
* use it as a seed source.
|
|
98
|
+
*/
|
|
99
|
+
persistence?: IndexeddbPersistence;
|
|
65
100
|
destroy: () => void;
|
|
101
|
+
/**
|
|
102
|
+
* Phase 9 PR2 — toggle the local cursor emission + present flag.
|
|
103
|
+
* Pass `false` when the editor route unmounts, `true` when it remounts.
|
|
104
|
+
* Triggers an awareness `change` event so peers see the leave/rejoin
|
|
105
|
+
* instantly (no REST-heartbeat lag).
|
|
106
|
+
*/
|
|
107
|
+
setLocalCursorEnabled: (enabled: boolean) => void;
|
|
66
108
|
}
|
|
67
109
|
declare function createCollaboration(options: CollaborationOptions): CollaborationSetup;
|
|
68
110
|
declare function collaborationExtensions(options: CollaborationOptions | CollaborationSetup): AnyExtension[];
|
|
69
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Injectable ports — the narrow interfaces through which the library reaches
|
|
114
|
+
* host-provided backend capabilities (storage, persistence, …). The library
|
|
115
|
+
* declares them; the host app implements them. See docs/LIBRARY_CONTRACT.md.
|
|
116
|
+
*/
|
|
117
|
+
interface ImageUploadResult {
|
|
118
|
+
/** Resolved URL/href the host stored the file at. */
|
|
119
|
+
src: string;
|
|
120
|
+
alt?: string;
|
|
121
|
+
title?: string;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Host-supplied image upload handler. Called with the file picked by the user;
|
|
125
|
+
* resolves to the stored location to insert into the document. Reject to abort
|
|
126
|
+
* the insert (the failure is logged, nothing is inserted).
|
|
127
|
+
*/
|
|
128
|
+
type ImageUploadHandler = (file: File) => Promise<ImageUploadResult>;
|
|
129
|
+
/** CSL-JSON name (see https://citeproc-js.readthedocs.io/csl-json/). */
|
|
130
|
+
interface CslName {
|
|
131
|
+
family?: string;
|
|
132
|
+
given?: string;
|
|
133
|
+
literal?: string;
|
|
134
|
+
}
|
|
135
|
+
/** CSL-JSON date. */
|
|
136
|
+
interface CslDate {
|
|
137
|
+
'date-parts'?: number[][];
|
|
138
|
+
raw?: string;
|
|
139
|
+
literal?: string;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* A CSL-JSON item — the structured source metadata citeproc-js consumes.
|
|
143
|
+
* Nodes store only `{ sourceId, locator }`; all rendered text is derived from
|
|
144
|
+
* this data by the citation engine (never persisted in the document).
|
|
145
|
+
*/
|
|
146
|
+
interface CslItemData {
|
|
147
|
+
id: string;
|
|
148
|
+
type: string;
|
|
149
|
+
title?: string;
|
|
150
|
+
author?: CslName[];
|
|
151
|
+
editor?: CslName[];
|
|
152
|
+
issued?: CslDate;
|
|
153
|
+
'container-title'?: string;
|
|
154
|
+
publisher?: string;
|
|
155
|
+
'publisher-place'?: string;
|
|
156
|
+
page?: string;
|
|
157
|
+
volume?: string;
|
|
158
|
+
issue?: string;
|
|
159
|
+
DOI?: string;
|
|
160
|
+
URL?: string;
|
|
161
|
+
ISBN?: string;
|
|
162
|
+
abstract?: string;
|
|
163
|
+
[k: string]: unknown;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Host-supplied citation port. The library never fetches sources itself — the
|
|
167
|
+
* host owns the reference library and injects CSL-JSON through this port,
|
|
168
|
+
* mirroring the `onImageUpload` injection pattern.
|
|
169
|
+
*/
|
|
170
|
+
interface CitationPort {
|
|
171
|
+
/** CSL-JSON provider — a snapshot array or a getter for live data. */
|
|
172
|
+
sources: CslItemData[] | (() => CslItemData[]);
|
|
173
|
+
/** Active CSL style id (e.g. 'chicago-notes-bibliography'). */
|
|
174
|
+
style?: string;
|
|
175
|
+
/**
|
|
176
|
+
* Called when the user inserts a citation: the host opens its source
|
|
177
|
+
* picker and resolves with the chosen source id (null = cancelled).
|
|
178
|
+
*/
|
|
179
|
+
onSourceRequest?: () => Promise<string | null>;
|
|
180
|
+
/**
|
|
181
|
+
* Called when the set of cited source ids changes, so the host can update
|
|
182
|
+
* its embedded per-document source snapshot.
|
|
183
|
+
*/
|
|
184
|
+
onSourcesChange?: (ids: string[]) => void;
|
|
185
|
+
/**
|
|
186
|
+
* Optional importer (Phase 6C-2): resolve a DOI/URL into a new persisted
|
|
187
|
+
* source via the host's backend (CrossRef lookup). Returns the created
|
|
188
|
+
* (or deduplicated) source, null on failure.
|
|
189
|
+
*/
|
|
190
|
+
onImportDoi?: (doi: string) => Promise<CslItemData | null>;
|
|
191
|
+
/**
|
|
192
|
+
* Optional importer (Phase 6C-3): parse + persist a BibTeX/RIS document
|
|
193
|
+
* via the host's backend. Returns the imported sources and how many
|
|
194
|
+
* entries failed.
|
|
195
|
+
*/
|
|
196
|
+
onImportBibliography?: (payload: {
|
|
197
|
+
format: 'bibtex' | 'ris';
|
|
198
|
+
text: string;
|
|
199
|
+
}) => Promise<{
|
|
200
|
+
imported: CslItemData[];
|
|
201
|
+
failed: number;
|
|
202
|
+
}>;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Shared AI action types (Phase 7).
|
|
207
|
+
*
|
|
208
|
+
* The library (aiPlugin) is provider-agnostic: it never names a URL or holds a
|
|
209
|
+
* key. The app implements `AIStreamFn` (apps/web/src/ai/aiStream.ts) and injects
|
|
210
|
+
* it; the server owns provider selection via env config.
|
|
211
|
+
*/
|
|
212
|
+
type AIAction = 'rewrite' | 'summarize' | 'grammar' | 'tone' | 'translate' | 'expand' | 'shorten' | 'generate' | 'chat' | 'draft';
|
|
213
|
+
interface AIActionRequest {
|
|
214
|
+
action: AIAction;
|
|
215
|
+
/** Selected text (7B). */
|
|
216
|
+
selection?: string;
|
|
217
|
+
/** Bounded surrounding text — never the whole document. */
|
|
218
|
+
context?: {
|
|
219
|
+
before: string;
|
|
220
|
+
after: string;
|
|
221
|
+
};
|
|
222
|
+
/** User instruction (/ai prompt, chat message, tone target, target language). */
|
|
223
|
+
prompt?: string;
|
|
224
|
+
/** For doc-aware chat / RAG scoping. */
|
|
225
|
+
documentId?: string;
|
|
226
|
+
options?: Record<string, unknown>;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* The app implements this and injects it; the library never knows the URL.
|
|
230
|
+
* Yields streamed text chunks in order; stops at end of stream, throws on error.
|
|
231
|
+
*/
|
|
232
|
+
type AIStreamFn = (req: AIActionRequest, signal: AbortSignal) => AsyncIterable<string>;
|
|
233
|
+
/** One entry in the citation table carried by the SSE `done` event (§3.4). */
|
|
234
|
+
interface AIDraftCitation {
|
|
235
|
+
/** 1-based index matching a `[n]` marker in the streamed text. */
|
|
236
|
+
ref: number;
|
|
237
|
+
/** `Source._id` string — the library maps markers through this table. */
|
|
238
|
+
sourceId: string;
|
|
239
|
+
/** "first author family + year" tag, e.g. "Doe (2024)". */
|
|
240
|
+
label: string;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Events yielded by `aiDraft` (§3.5). The `done` event carries the citation
|
|
244
|
+
* table; the `error` event does NOT (the bug-hunter carry-forward — the client
|
|
245
|
+
* treats BOTH `done` and `error` as terminal; Insert is enabled only on `done`).
|
|
246
|
+
*/
|
|
247
|
+
type AIDraftEvent = {
|
|
248
|
+
type: 'delta';
|
|
249
|
+
text: string;
|
|
250
|
+
} | {
|
|
251
|
+
type: 'done';
|
|
252
|
+
citations: AIDraftCitation[];
|
|
253
|
+
} | {
|
|
254
|
+
type: 'error';
|
|
255
|
+
message: string;
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* The app implements this and injects it; the library never knows the URL.
|
|
259
|
+
* Mirrors `AIStreamFn` but yields richer `AIDraftEvent`s (carries the citation
|
|
260
|
+
* table on the terminal `done` event). Injected exactly like `aiStream`.
|
|
261
|
+
*/
|
|
262
|
+
type AIDraftFn = (req: {
|
|
263
|
+
prompt: string;
|
|
264
|
+
context?: {
|
|
265
|
+
before: string;
|
|
266
|
+
after: string;
|
|
267
|
+
};
|
|
268
|
+
k?: number;
|
|
269
|
+
}, signal: AbortSignal) => AsyncIterable<AIDraftEvent>;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Sanitize pasted HTML content (e.g. from Google Docs) to prevent crashes
|
|
273
|
+
* during ProseMirror parsing. Strips non-content tags like <meta>, <style>,
|
|
274
|
+
* and HTML comments that are common in rich clipboard data but not valid
|
|
275
|
+
* in the ProseMirror schema.
|
|
276
|
+
*
|
|
277
|
+
* Also converts Google Docs-specific markup into standard TipTap-compatible
|
|
278
|
+
* HTML so that text color and background-color survive the paste.
|
|
279
|
+
*/
|
|
280
|
+
declare function sanitizePastedHTML(html: string): string;
|
|
70
281
|
interface EditorOptions {
|
|
71
282
|
target?: HTMLElement;
|
|
72
283
|
content?: object | string;
|
|
@@ -79,6 +290,14 @@ interface EditorOptions {
|
|
|
79
290
|
blockIndex: number;
|
|
80
291
|
}>;
|
|
81
292
|
paginationOptions?: PaginationPlusOptions;
|
|
293
|
+
/** Host-injected image upload port (see docs/LIBRARY_CONTRACT.md). */
|
|
294
|
+
onImageUpload?: ImageUploadHandler;
|
|
295
|
+
/** Host-injected citation port (Phase 6 — reference library + CSL styles). */
|
|
296
|
+
citation?: CitationPort;
|
|
297
|
+
/** Host-injected AI transport (Phase 7 — editor → server → LLM). */
|
|
298
|
+
aiStream?: AIStreamFn;
|
|
299
|
+
/** Host-injected cited-draft transport (Phase 7E — editor → server → RAG LLM). */
|
|
300
|
+
aiDraft?: AIDraftFn;
|
|
82
301
|
}
|
|
83
302
|
interface DocsEditor {
|
|
84
303
|
editor: Editor;
|
|
@@ -119,4 +338,121 @@ declare module '@tiptap/core' {
|
|
|
119
338
|
*/
|
|
120
339
|
declare const FontSizeExtension: Extension<any, any>;
|
|
121
340
|
|
|
122
|
-
|
|
341
|
+
interface EditorContextOptions {
|
|
342
|
+
onImageUpload?: ImageUploadHandler;
|
|
343
|
+
citation?: CitationPort;
|
|
344
|
+
aiStream?: AIStreamFn;
|
|
345
|
+
aiDraft?: AIDraftFn;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Internal carrier for host-injected ports. Always registered (like TextStyle)
|
|
349
|
+
* so plugin commands can reach the ports through the editor instance:
|
|
350
|
+
*
|
|
351
|
+
* editor.storage.editorContext.onImageUpload
|
|
352
|
+
* editor.storage.editorContext.citation
|
|
353
|
+
* editor.storage.editorContext.aiStream
|
|
354
|
+
* editor.storage.editorContext.aiDraft
|
|
355
|
+
*
|
|
356
|
+
* Plugins must never import backend concerns — they read injected ports from
|
|
357
|
+
* this storage instead. See docs/LIBRARY_CONTRACT.md.
|
|
358
|
+
*/
|
|
359
|
+
declare const EditorContextExtension: Extension<EditorContextOptions, any>;
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Find & Replace — core editing infrastructure (always registered, like
|
|
363
|
+
* TextStyle). Plain-string, case-insensitive search with ProseMirror
|
|
364
|
+
* decorations for match highlighting. No regex (deliberate — see the
|
|
365
|
+
* edit-menu plan §5.6).
|
|
366
|
+
*/
|
|
367
|
+
interface SearchMatch {
|
|
368
|
+
from: number;
|
|
369
|
+
to: number;
|
|
370
|
+
}
|
|
371
|
+
interface SearchState {
|
|
372
|
+
query: string;
|
|
373
|
+
matches: SearchMatch[];
|
|
374
|
+
activeIndex: number;
|
|
375
|
+
}
|
|
376
|
+
declare const searchAndReplaceKey: PluginKey<SearchState>;
|
|
377
|
+
/**
|
|
378
|
+
* Case-insensitive plain-string search across all text nodes.
|
|
379
|
+
* Matches are **non-overlapping** (the scan advances by the needle length) so
|
|
380
|
+
* replace-all can apply them in one transaction without position drift.
|
|
381
|
+
*/
|
|
382
|
+
declare function findMatches(doc: Node, query: string): SearchMatch[];
|
|
383
|
+
declare const SearchAndReplaceExtension: Extension<any, any>;
|
|
384
|
+
declare function getSearchState(editor: Editor): SearchState;
|
|
385
|
+
declare function setSearchQuery(editor: Editor, query: string): void;
|
|
386
|
+
declare function clearSearch(editor: Editor): void;
|
|
387
|
+
/** Move to the next match (wraps). Returns the newly active match, if any. */
|
|
388
|
+
declare function searchNext(editor: Editor): SearchMatch | null;
|
|
389
|
+
/** Move to the previous match (wraps). Returns the newly active match, if any. */
|
|
390
|
+
declare function searchPrev(editor: Editor): SearchMatch | null;
|
|
391
|
+
/** Replace the active match. The doc change recomputes matches automatically. */
|
|
392
|
+
declare function replaceCurrent(editor: Editor, replacement: string): boolean;
|
|
393
|
+
/** Replace every match in one transaction (applied back-to-front). */
|
|
394
|
+
declare function replaceAll(editor: Editor, replacement: string): number;
|
|
395
|
+
|
|
396
|
+
interface SubdocState {
|
|
397
|
+
id: string;
|
|
398
|
+
doc: Y.Doc;
|
|
399
|
+
provider: WebsocketProvider | null;
|
|
400
|
+
active: boolean;
|
|
401
|
+
}
|
|
402
|
+
interface SubdocumentProviderOptions {
|
|
403
|
+
/** WebSocket URL for the collaboration server. */
|
|
404
|
+
websocketUrl: string;
|
|
405
|
+
/** Room prefix — subdocuments use `${roomPrefix}/${id}` as room name. */
|
|
406
|
+
roomPrefix: string;
|
|
407
|
+
/** User info for awareness. */
|
|
408
|
+
user: {
|
|
409
|
+
name: string;
|
|
410
|
+
color: string;
|
|
411
|
+
};
|
|
412
|
+
/** Number of subdocuments to keep active (buffer around visible viewport). */
|
|
413
|
+
maxActive?: number;
|
|
414
|
+
/** Called when subdocument state changes (active/deactivated). */
|
|
415
|
+
onStateChange?: (states: SubdocState[]) => void;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Manages multiple Y.Doc subdocuments under a parent document.
|
|
419
|
+
*
|
|
420
|
+
* Use case: large documents (100+ pages) split into per-section subdocuments.
|
|
421
|
+
* Only active (visible) subdocuments are connected to the network via
|
|
422
|
+
* WebSocketProvider; inactive ones keep local state but stop syncing.
|
|
423
|
+
*
|
|
424
|
+
* Usage:
|
|
425
|
+
* const provider = new SubdocumentProvider({ websocketUrl, roomPrefix, user })
|
|
426
|
+
* provider.createSubdoc('section-1')
|
|
427
|
+
* provider.activate('section-1') // starts syncing
|
|
428
|
+
* provider.deactivate('section-1') // stops syncing
|
|
429
|
+
* const frag = provider.getFragment('section-1') // Y.XmlFragment for editor
|
|
430
|
+
*/
|
|
431
|
+
declare class SubdocumentProvider {
|
|
432
|
+
readonly parentDoc: Y.Doc;
|
|
433
|
+
readonly subdocs: Y.Map<Y.Doc>;
|
|
434
|
+
readonly awareness: Awareness;
|
|
435
|
+
private readonly options;
|
|
436
|
+
private readonly states;
|
|
437
|
+
private readonly activeQueue;
|
|
438
|
+
private readonly parentProvider;
|
|
439
|
+
constructor(options: SubdocumentProviderOptions);
|
|
440
|
+
/** Create a new subdocument. Does NOT activate it. */
|
|
441
|
+
createSubdoc(id: string, initialState?: Uint8Array): Y.Doc;
|
|
442
|
+
/** Activate a subdocument — connects it to the network for live sync. */
|
|
443
|
+
activate(id: string): void;
|
|
444
|
+
/** Deactivate a subdocument — disconnects it from the network. */
|
|
445
|
+
deactivate(id: string): void;
|
|
446
|
+
/** Get the Y.XmlFragment for a subdocument. */
|
|
447
|
+
getFragment(id: string): Y.XmlFragment | null;
|
|
448
|
+
/** Check if a subdocument is currently active (syncing). */
|
|
449
|
+
isActive(id: string): boolean;
|
|
450
|
+
/** Get all subdocument states. */
|
|
451
|
+
getAllStates(): SubdocState[];
|
|
452
|
+
/** Get IDs of all active subdocuments. */
|
|
453
|
+
getActiveIds(): string[];
|
|
454
|
+
/** Destroy all subdocuments and providers. */
|
|
455
|
+
destroy(): void;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export { type AIAction, type AIActionRequest, type AIDraftCitation, type AIDraftEvent, type AIDraftFn, type AIStreamFn, type AwarenessState, BlockAttributesExtension, type CitationPort, type CollaborationOptions, type CollaborationSetup, type CslDate, type CslItemData, type CslName, type DocsEditor, type DocsEditorPlugin, EditorContextExtension, type EditorContextOptions, type EditorOptions, FontSizeExtension, type ImageUploadHandler, type ImageUploadResult, SearchAndReplaceExtension, type SearchMatch, type SearchState, type SlashCommand, type SubdocState, SubdocumentProvider, type SubdocumentProviderOptions, type ToolbarItem, clearSearch, collaborationExtensions, collectExtensions, createActionMap, createCollaboration, createEditor, definePlugin, findMatches, getSearchState, replaceAll, replaceCurrent, resolveAction, sanitizePastedHTML, searchAndReplaceKey, searchNext, searchPrev, setSearchQuery };
|