@kedataindo/docflow-core 0.0.71 → 0.0.73
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/Collaboration-CGv9OZlB.d.cts +109 -0
- package/dist/Collaboration-CGv9OZlB.d.ts +109 -0
- package/dist/chunk-XJG72F7M.js +140 -0
- package/dist/collab/index.cjs +286 -0
- package/dist/collab/index.d.cts +71 -0
- package/dist/collab/index.d.ts +71 -0
- package/dist/collab/index.js +116 -0
- package/dist/index.cjs +155 -127
- package/dist/index.d.cts +18 -150
- package/dist/index.d.ts +18 -150
- package/dist/index.js +151 -254
- package/package.json +6 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Awareness } from 'y-protocols/awareness';
|
|
2
|
+
import { WebsocketProvider } from 'y-websocket';
|
|
3
|
+
import * as Y from 'yjs';
|
|
4
|
+
export { A as AwarenessState, C as CollaborationOptions, a as CollaborationSetup, c as collaborationExtensions, b as createCollaboration } from '../Collaboration-CGv9OZlB.cjs';
|
|
5
|
+
import '@tiptap/core';
|
|
6
|
+
import 'y-indexeddb';
|
|
7
|
+
import 'y-webrtc';
|
|
8
|
+
|
|
9
|
+
interface SubdocState {
|
|
10
|
+
id: string;
|
|
11
|
+
doc: Y.Doc;
|
|
12
|
+
provider: WebsocketProvider | null;
|
|
13
|
+
active: boolean;
|
|
14
|
+
}
|
|
15
|
+
interface SubdocumentProviderOptions {
|
|
16
|
+
/** WebSocket URL for the collaboration server. */
|
|
17
|
+
websocketUrl: string;
|
|
18
|
+
/** Room prefix — subdocuments use `${roomPrefix}/${id}` as room name. */
|
|
19
|
+
roomPrefix: string;
|
|
20
|
+
/** User info for awareness. */
|
|
21
|
+
user: {
|
|
22
|
+
name: string;
|
|
23
|
+
color: string;
|
|
24
|
+
};
|
|
25
|
+
/** Number of subdocuments to keep active (buffer around visible viewport). */
|
|
26
|
+
maxActive?: number;
|
|
27
|
+
/** Called when subdocument state changes (active/deactivated). */
|
|
28
|
+
onStateChange?: (states: SubdocState[]) => void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Manages multiple Y.Doc subdocuments under a parent document.
|
|
32
|
+
*
|
|
33
|
+
* Use case: large documents (100+ pages) split into per-section subdocuments.
|
|
34
|
+
* Only active (visible) subdocuments are connected to the network via
|
|
35
|
+
* WebSocketProvider; inactive ones keep local state but stop syncing.
|
|
36
|
+
*
|
|
37
|
+
* Usage:
|
|
38
|
+
* const provider = new SubdocumentProvider({ websocketUrl, roomPrefix, user })
|
|
39
|
+
* provider.createSubdoc('section-1')
|
|
40
|
+
* provider.activate('section-1') // starts syncing
|
|
41
|
+
* provider.deactivate('section-1') // stops syncing
|
|
42
|
+
* const frag = provider.getFragment('section-1') // Y.XmlFragment for editor
|
|
43
|
+
*/
|
|
44
|
+
declare class SubdocumentProvider {
|
|
45
|
+
readonly parentDoc: Y.Doc;
|
|
46
|
+
readonly subdocs: Y.Map<Y.Doc>;
|
|
47
|
+
readonly awareness: Awareness;
|
|
48
|
+
private readonly options;
|
|
49
|
+
private readonly states;
|
|
50
|
+
private readonly activeQueue;
|
|
51
|
+
private readonly parentProvider;
|
|
52
|
+
constructor(options: SubdocumentProviderOptions);
|
|
53
|
+
/** Create a new subdocument. Does NOT activate it. */
|
|
54
|
+
createSubdoc(id: string, initialState?: Uint8Array): Y.Doc;
|
|
55
|
+
/** Activate a subdocument — connects it to the network for live sync. */
|
|
56
|
+
activate(id: string): void;
|
|
57
|
+
/** Deactivate a subdocument — disconnects it from the network. */
|
|
58
|
+
deactivate(id: string): void;
|
|
59
|
+
/** Get the Y.XmlFragment for a subdocument. */
|
|
60
|
+
getFragment(id: string): Y.XmlFragment | null;
|
|
61
|
+
/** Check if a subdocument is currently active (syncing). */
|
|
62
|
+
isActive(id: string): boolean;
|
|
63
|
+
/** Get all subdocument states. */
|
|
64
|
+
getAllStates(): SubdocState[];
|
|
65
|
+
/** Get IDs of all active subdocuments. */
|
|
66
|
+
getActiveIds(): string[];
|
|
67
|
+
/** Destroy all subdocuments and providers. */
|
|
68
|
+
destroy(): void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { type SubdocState, SubdocumentProvider, type SubdocumentProviderOptions };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Awareness } from 'y-protocols/awareness';
|
|
2
|
+
import { WebsocketProvider } from 'y-websocket';
|
|
3
|
+
import * as Y from 'yjs';
|
|
4
|
+
export { A as AwarenessState, C as CollaborationOptions, a as CollaborationSetup, c as collaborationExtensions, b as createCollaboration } from '../Collaboration-CGv9OZlB.js';
|
|
5
|
+
import '@tiptap/core';
|
|
6
|
+
import 'y-indexeddb';
|
|
7
|
+
import 'y-webrtc';
|
|
8
|
+
|
|
9
|
+
interface SubdocState {
|
|
10
|
+
id: string;
|
|
11
|
+
doc: Y.Doc;
|
|
12
|
+
provider: WebsocketProvider | null;
|
|
13
|
+
active: boolean;
|
|
14
|
+
}
|
|
15
|
+
interface SubdocumentProviderOptions {
|
|
16
|
+
/** WebSocket URL for the collaboration server. */
|
|
17
|
+
websocketUrl: string;
|
|
18
|
+
/** Room prefix — subdocuments use `${roomPrefix}/${id}` as room name. */
|
|
19
|
+
roomPrefix: string;
|
|
20
|
+
/** User info for awareness. */
|
|
21
|
+
user: {
|
|
22
|
+
name: string;
|
|
23
|
+
color: string;
|
|
24
|
+
};
|
|
25
|
+
/** Number of subdocuments to keep active (buffer around visible viewport). */
|
|
26
|
+
maxActive?: number;
|
|
27
|
+
/** Called when subdocument state changes (active/deactivated). */
|
|
28
|
+
onStateChange?: (states: SubdocState[]) => void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Manages multiple Y.Doc subdocuments under a parent document.
|
|
32
|
+
*
|
|
33
|
+
* Use case: large documents (100+ pages) split into per-section subdocuments.
|
|
34
|
+
* Only active (visible) subdocuments are connected to the network via
|
|
35
|
+
* WebSocketProvider; inactive ones keep local state but stop syncing.
|
|
36
|
+
*
|
|
37
|
+
* Usage:
|
|
38
|
+
* const provider = new SubdocumentProvider({ websocketUrl, roomPrefix, user })
|
|
39
|
+
* provider.createSubdoc('section-1')
|
|
40
|
+
* provider.activate('section-1') // starts syncing
|
|
41
|
+
* provider.deactivate('section-1') // stops syncing
|
|
42
|
+
* const frag = provider.getFragment('section-1') // Y.XmlFragment for editor
|
|
43
|
+
*/
|
|
44
|
+
declare class SubdocumentProvider {
|
|
45
|
+
readonly parentDoc: Y.Doc;
|
|
46
|
+
readonly subdocs: Y.Map<Y.Doc>;
|
|
47
|
+
readonly awareness: Awareness;
|
|
48
|
+
private readonly options;
|
|
49
|
+
private readonly states;
|
|
50
|
+
private readonly activeQueue;
|
|
51
|
+
private readonly parentProvider;
|
|
52
|
+
constructor(options: SubdocumentProviderOptions);
|
|
53
|
+
/** Create a new subdocument. Does NOT activate it. */
|
|
54
|
+
createSubdoc(id: string, initialState?: Uint8Array): Y.Doc;
|
|
55
|
+
/** Activate a subdocument — connects it to the network for live sync. */
|
|
56
|
+
activate(id: string): void;
|
|
57
|
+
/** Deactivate a subdocument — disconnects it from the network. */
|
|
58
|
+
deactivate(id: string): void;
|
|
59
|
+
/** Get the Y.XmlFragment for a subdocument. */
|
|
60
|
+
getFragment(id: string): Y.XmlFragment | null;
|
|
61
|
+
/** Check if a subdocument is currently active (syncing). */
|
|
62
|
+
isActive(id: string): boolean;
|
|
63
|
+
/** Get all subdocument states. */
|
|
64
|
+
getAllStates(): SubdocState[];
|
|
65
|
+
/** Get IDs of all active subdocuments. */
|
|
66
|
+
getActiveIds(): string[];
|
|
67
|
+
/** Destroy all subdocuments and providers. */
|
|
68
|
+
destroy(): void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { type SubdocState, SubdocumentProvider, type SubdocumentProviderOptions };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import {
|
|
2
|
+
collaborationExtensions,
|
|
3
|
+
createCollaboration
|
|
4
|
+
} from "../chunk-XJG72F7M.js";
|
|
5
|
+
|
|
6
|
+
// src/SubdocumentProvider.ts
|
|
7
|
+
import { Awareness as AwarenessClass } from "y-protocols/awareness";
|
|
8
|
+
import { WebsocketProvider } from "y-websocket";
|
|
9
|
+
import * as Y from "yjs";
|
|
10
|
+
var SubdocumentProvider = class {
|
|
11
|
+
parentDoc;
|
|
12
|
+
subdocs;
|
|
13
|
+
awareness;
|
|
14
|
+
options;
|
|
15
|
+
states = /* @__PURE__ */ new Map();
|
|
16
|
+
activeQueue = [];
|
|
17
|
+
parentProvider = null;
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.options = {
|
|
20
|
+
maxActive: options.maxActive ?? 5,
|
|
21
|
+
onStateChange: options.onStateChange ?? (() => {
|
|
22
|
+
}),
|
|
23
|
+
...options
|
|
24
|
+
};
|
|
25
|
+
this.parentDoc = new Y.Doc();
|
|
26
|
+
this.subdocs = this.parentDoc.getMap("subdocs");
|
|
27
|
+
this.awareness = new AwarenessClass(this.parentDoc);
|
|
28
|
+
this.awareness.setLocalStateField("user", this.options.user);
|
|
29
|
+
this.parentProvider = new WebsocketProvider(
|
|
30
|
+
this.options.websocketUrl,
|
|
31
|
+
`${this.options.roomPrefix}/_parent`,
|
|
32
|
+
this.parentDoc
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
/** Create a new subdocument. Does NOT activate it. */
|
|
36
|
+
createSubdoc(id, initialState) {
|
|
37
|
+
if (this.states.has(id)) return this.states.get(id).doc;
|
|
38
|
+
const doc = new Y.Doc();
|
|
39
|
+
if (initialState) Y.applyUpdate(doc, initialState);
|
|
40
|
+
doc.getXmlFragment("default");
|
|
41
|
+
this.subdocs.set(id, doc);
|
|
42
|
+
this.states.set(id, {
|
|
43
|
+
id,
|
|
44
|
+
doc,
|
|
45
|
+
provider: null,
|
|
46
|
+
active: false
|
|
47
|
+
});
|
|
48
|
+
this.options.onStateChange(this.getAllStates());
|
|
49
|
+
return doc;
|
|
50
|
+
}
|
|
51
|
+
/** Activate a subdocument — connects it to the network for live sync. */
|
|
52
|
+
activate(id) {
|
|
53
|
+
const state = this.states.get(id);
|
|
54
|
+
if (!state || state.active) return;
|
|
55
|
+
if (this.activeQueue.length >= this.options.maxActive) {
|
|
56
|
+
const oldest = this.activeQueue.shift();
|
|
57
|
+
if (oldest) this.deactivate(oldest);
|
|
58
|
+
}
|
|
59
|
+
const doc = state.doc;
|
|
60
|
+
const roomName = `${this.options.roomPrefix}/${id}`;
|
|
61
|
+
const provider = new WebsocketProvider(
|
|
62
|
+
this.options.websocketUrl,
|
|
63
|
+
roomName,
|
|
64
|
+
doc
|
|
65
|
+
);
|
|
66
|
+
provider.awareness.setLocalStateField("user", this.options.user);
|
|
67
|
+
state.provider = provider;
|
|
68
|
+
state.active = true;
|
|
69
|
+
this.activeQueue.push(id);
|
|
70
|
+
this.options.onStateChange(this.getAllStates());
|
|
71
|
+
}
|
|
72
|
+
/** Deactivate a subdocument — disconnects it from the network. */
|
|
73
|
+
deactivate(id) {
|
|
74
|
+
const state = this.states.get(id);
|
|
75
|
+
if (!state || !state.active) return;
|
|
76
|
+
state.provider?.destroy();
|
|
77
|
+
state.provider = null;
|
|
78
|
+
state.active = false;
|
|
79
|
+
const idx = this.activeQueue.indexOf(id);
|
|
80
|
+
if (idx >= 0) this.activeQueue.splice(idx, 1);
|
|
81
|
+
this.options.onStateChange(this.getAllStates());
|
|
82
|
+
}
|
|
83
|
+
/** Get the Y.XmlFragment for a subdocument. */
|
|
84
|
+
getFragment(id) {
|
|
85
|
+
const doc = this.states.get(id)?.doc ?? this.subdocs.get(id);
|
|
86
|
+
if (!doc) return null;
|
|
87
|
+
return doc.getXmlFragment("default");
|
|
88
|
+
}
|
|
89
|
+
/** Check if a subdocument is currently active (syncing). */
|
|
90
|
+
isActive(id) {
|
|
91
|
+
return this.states.get(id)?.active ?? false;
|
|
92
|
+
}
|
|
93
|
+
/** Get all subdocument states. */
|
|
94
|
+
getAllStates() {
|
|
95
|
+
return Array.from(this.states.values());
|
|
96
|
+
}
|
|
97
|
+
/** Get IDs of all active subdocuments. */
|
|
98
|
+
getActiveIds() {
|
|
99
|
+
return [...this.activeQueue];
|
|
100
|
+
}
|
|
101
|
+
/** Destroy all subdocuments and providers. */
|
|
102
|
+
destroy() {
|
|
103
|
+
for (const [id] of this.states) {
|
|
104
|
+
this.deactivate(id);
|
|
105
|
+
}
|
|
106
|
+
this.parentProvider?.destroy();
|
|
107
|
+
this.parentDoc.destroy();
|
|
108
|
+
this.states.clear();
|
|
109
|
+
this.activeQueue.length = 0;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
export {
|
|
113
|
+
SubdocumentProvider,
|
|
114
|
+
collaborationExtensions,
|
|
115
|
+
createCollaboration
|
|
116
|
+
};
|
package/dist/index.cjs
CHANGED
|
@@ -39,7 +39,6 @@ __export(index_exports, {
|
|
|
39
39
|
PaginationPlus: () => PaginationPlus,
|
|
40
40
|
PerformanceMonitor: () => PerformanceMonitor,
|
|
41
41
|
SearchAndReplaceExtension: () => SearchAndReplaceExtension,
|
|
42
|
-
SubdocumentProvider: () => SubdocumentProvider,
|
|
43
42
|
buildAIPrompt: () => buildAIPrompt,
|
|
44
43
|
clearSearch: () => clearSearch,
|
|
45
44
|
collaborationExtensions: () => collaborationExtensions,
|
|
@@ -72,8 +71,8 @@ __export(index_exports, {
|
|
|
72
71
|
module.exports = __toCommonJS(index_exports);
|
|
73
72
|
|
|
74
73
|
// src/Editor.ts
|
|
75
|
-
var
|
|
76
|
-
var
|
|
74
|
+
var import_core6 = require("@tiptap/core");
|
|
75
|
+
var import_state5 = require("@tiptap/pm/state");
|
|
77
76
|
var import_starter_kit = __toESM(require("@tiptap/starter-kit"), 1);
|
|
78
77
|
var import_extension_text_style = __toESM(require("@tiptap/extension-text-style"), 1);
|
|
79
78
|
|
|
@@ -128,9 +127,6 @@ function createActionMap(editor, plugins) {
|
|
|
128
127
|
var import_extension_collaboration = require("@tiptap/extension-collaboration");
|
|
129
128
|
var import_extension_collaboration_cursor = require("@tiptap/extension-collaboration-cursor");
|
|
130
129
|
var import_awareness = require("y-protocols/awareness");
|
|
131
|
-
var import_y_indexeddb = require("y-indexeddb");
|
|
132
|
-
var import_y_webrtc = require("y-webrtc");
|
|
133
|
-
var import_y_websocket = require("y-websocket");
|
|
134
130
|
var Y = __toESM(require("yjs"), 1);
|
|
135
131
|
var LOCAL_SIGNALING = ["ws://localhost:4444"];
|
|
136
132
|
var warnedLocalSignaling = false;
|
|
@@ -161,25 +157,28 @@ function createAwarenessStates(awareness) {
|
|
|
161
157
|
});
|
|
162
158
|
return states;
|
|
163
159
|
}
|
|
164
|
-
function createCollaboration(options) {
|
|
160
|
+
async function createCollaboration(options) {
|
|
165
161
|
const ydoc = new Y.Doc();
|
|
166
162
|
if (options.initialStorageState) {
|
|
167
163
|
Y.applyUpdate(ydoc, options.initialStorageState);
|
|
168
164
|
}
|
|
169
165
|
let persistence;
|
|
170
166
|
if (options.offline) {
|
|
171
|
-
|
|
167
|
+
const { IndexeddbPersistence } = await import("y-indexeddb");
|
|
168
|
+
persistence = new IndexeddbPersistence(`docflow-${options.room}`, ydoc);
|
|
172
169
|
}
|
|
173
170
|
let provider = null;
|
|
174
171
|
if (options.provider === "webrtc") {
|
|
175
|
-
|
|
172
|
+
const { WebrtcProvider } = await import("y-webrtc");
|
|
173
|
+
provider = new WebrtcProvider(options.room, ydoc, {
|
|
176
174
|
signaling: resolveSignalingUrls(options.signaling)
|
|
177
175
|
});
|
|
178
176
|
} else if (options.provider === "websocket") {
|
|
179
177
|
if (!options.websocketUrl) {
|
|
180
178
|
throw new Error('[Collaboration] websocketUrl is required when provider is "websocket"');
|
|
181
179
|
}
|
|
182
|
-
|
|
180
|
+
const { WebsocketProvider } = await import("y-websocket");
|
|
181
|
+
provider = new WebsocketProvider(options.websocketUrl, options.room, ydoc);
|
|
183
182
|
}
|
|
184
183
|
const awareness = provider?.awareness ?? new import_awareness.Awareness(ydoc);
|
|
185
184
|
awareness.setLocalStateField("user", options.user);
|
|
@@ -245,8 +244,7 @@ function createCollaboration(options) {
|
|
|
245
244
|
};
|
|
246
245
|
return { ydoc, provider, awareness, persistence, whenReady, destroy, setLocalCursorEnabled };
|
|
247
246
|
}
|
|
248
|
-
function collaborationExtensions(
|
|
249
|
-
const setup = "ydoc" in options ? options : createCollaboration(options);
|
|
247
|
+
function collaborationExtensions(setup) {
|
|
250
248
|
const extensions = [
|
|
251
249
|
import_extension_collaboration.Collaboration.configure({ document: setup.ydoc })
|
|
252
250
|
];
|
|
@@ -322,6 +320,42 @@ var BlockAttributesExtension = import_core.Extension.create({
|
|
|
322
320
|
}
|
|
323
321
|
});
|
|
324
322
|
|
|
323
|
+
// src/collab/seedEmptyFragment.ts
|
|
324
|
+
var Y2 = __toESM(require("yjs"), 1);
|
|
325
|
+
var PROVISIONAL_SEED_ORIGIN = "docflow:provisional-seed";
|
|
326
|
+
function isPristineEmptyParagraph(element) {
|
|
327
|
+
return element.length === 0 && Object.keys(element.getAttributes()).length === 0;
|
|
328
|
+
}
|
|
329
|
+
function seedEmptyFragment(doc, field = "default") {
|
|
330
|
+
const fragment = doc.getXmlFragment(field);
|
|
331
|
+
const paragraph = new Y2.XmlElement("paragraph");
|
|
332
|
+
const observer = (_events, transaction) => {
|
|
333
|
+
if (transaction.origin === PROVISIONAL_SEED_ORIGIN) return;
|
|
334
|
+
if (paragraph.parent !== fragment) return;
|
|
335
|
+
if (!isPristineEmptyParagraph(paragraph)) return;
|
|
336
|
+
if (fragment.length < 2) return;
|
|
337
|
+
const index = fragment.toArray().indexOf(paragraph);
|
|
338
|
+
if (index === -1) return;
|
|
339
|
+
doc.transact(() => {
|
|
340
|
+
fragment.delete(index);
|
|
341
|
+
}, PROVISIONAL_SEED_ORIGIN);
|
|
342
|
+
};
|
|
343
|
+
let disposed = false;
|
|
344
|
+
const dispose = () => {
|
|
345
|
+
if (disposed) return;
|
|
346
|
+
disposed = true;
|
|
347
|
+
fragment.unobserveDeep(observer);
|
|
348
|
+
};
|
|
349
|
+
if (fragment.length > 0) {
|
|
350
|
+
return { seeded: false, dispose };
|
|
351
|
+
}
|
|
352
|
+
fragment.observeDeep(observer);
|
|
353
|
+
doc.transact(() => {
|
|
354
|
+
fragment.insert(0, [paragraph]);
|
|
355
|
+
}, PROVISIONAL_SEED_ORIGIN);
|
|
356
|
+
return { seeded: true, dispose };
|
|
357
|
+
}
|
|
358
|
+
|
|
325
359
|
// src/EditorContext.ts
|
|
326
360
|
var import_core2 = require("@tiptap/core");
|
|
327
361
|
var EditorContextExtension = import_core2.Extension.create({
|
|
@@ -1272,6 +1306,94 @@ var PaginationPlus = import_core4.Extension.create({
|
|
|
1272
1306
|
}
|
|
1273
1307
|
});
|
|
1274
1308
|
|
|
1309
|
+
// src/pagination/PaginationCaretFix.ts
|
|
1310
|
+
var import_core5 = require("@tiptap/core");
|
|
1311
|
+
var import_state4 = require("@tiptap/pm/state");
|
|
1312
|
+
var PAGINATION_CLASS = "rm-with-pagination";
|
|
1313
|
+
var PAGINATION_DISABLED_ATTR = "rm-pagination-disabled";
|
|
1314
|
+
var dragStates = /* @__PURE__ */ new WeakMap();
|
|
1315
|
+
var isPaginationActive = (view) => {
|
|
1316
|
+
const dom = view.dom;
|
|
1317
|
+
return dom.classList.contains(PAGINATION_CLASS) && dom.getAttribute(PAGINATION_DISABLED_ATTR) === null;
|
|
1318
|
+
};
|
|
1319
|
+
var isEditable = (view) => view.dom.getAttribute("contenteditable") !== "false";
|
|
1320
|
+
var isPlainSingleLeftClick = (event) => event.button === 0 && event.detail === 1 && !event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey;
|
|
1321
|
+
function posFromTextOffset(view, textNode, offset) {
|
|
1322
|
+
if (textNode.nodeType !== Node.TEXT_NODE || !view.dom.contains(textNode)) return null;
|
|
1323
|
+
const text = textNode;
|
|
1324
|
+
const clamped = Math.min(Math.max(0, offset), text.length);
|
|
1325
|
+
const parent = text.parentElement;
|
|
1326
|
+
if (parent?.closest?.('[contenteditable="false"]')) return null;
|
|
1327
|
+
let pos = view.posAtDOM(text, clamped, -1);
|
|
1328
|
+
if (pos < 0) pos = view.posAtDOM(text, clamped, 1);
|
|
1329
|
+
if (pos < 0) return null;
|
|
1330
|
+
return view.state.doc.resolve(pos).parent.isTextblock ? pos : null;
|
|
1331
|
+
}
|
|
1332
|
+
function resolvePosFromNativePoint(view, clientX, clientY) {
|
|
1333
|
+
const doc = view.dom.ownerDocument;
|
|
1334
|
+
if (typeof doc.caretRangeFromPoint !== "function") return null;
|
|
1335
|
+
const range = doc.caretRangeFromPoint(clientX, clientY);
|
|
1336
|
+
if (!range) return null;
|
|
1337
|
+
return posFromTextOffset(view, range.startContainer, range.startOffset);
|
|
1338
|
+
}
|
|
1339
|
+
function stopDrag(view) {
|
|
1340
|
+
const drag = dragStates.get(view);
|
|
1341
|
+
if (!drag) return;
|
|
1342
|
+
dragStates.delete(view);
|
|
1343
|
+
const doc = view.dom.ownerDocument;
|
|
1344
|
+
doc.removeEventListener("mousemove", drag.onMouseMove);
|
|
1345
|
+
doc.removeEventListener("mouseup", drag.onMouseUp);
|
|
1346
|
+
}
|
|
1347
|
+
function beginDrag(view, anchorPos) {
|
|
1348
|
+
stopDrag(view);
|
|
1349
|
+
const doc = view.dom.ownerDocument;
|
|
1350
|
+
const onMouseMove = (event) => {
|
|
1351
|
+
const drag = dragStates.get(view);
|
|
1352
|
+
if (!drag) return;
|
|
1353
|
+
const pos = resolvePosFromNativePoint(view, event.clientX, event.clientY);
|
|
1354
|
+
if (pos === null) return;
|
|
1355
|
+
const from = Math.min(drag.anchorPos, pos);
|
|
1356
|
+
const to = Math.max(drag.anchorPos, pos);
|
|
1357
|
+
view.dispatch(view.state.tr.setSelection(import_state4.TextSelection.create(view.state.doc, from, to)));
|
|
1358
|
+
};
|
|
1359
|
+
const onMouseUp = () => {
|
|
1360
|
+
stopDrag(view);
|
|
1361
|
+
};
|
|
1362
|
+
dragStates.set(view, { anchorPos, onMouseMove, onMouseUp });
|
|
1363
|
+
doc.addEventListener("mousemove", onMouseMove);
|
|
1364
|
+
doc.addEventListener("mouseup", onMouseUp);
|
|
1365
|
+
}
|
|
1366
|
+
function interceptPaginationMouseDown(view, event) {
|
|
1367
|
+
if (!isPaginationActive(view) || !isPlainSingleLeftClick(event) || !isEditable(view)) return false;
|
|
1368
|
+
const pos = resolvePosFromNativePoint(view, event.clientX, event.clientY);
|
|
1369
|
+
if (pos === null) return false;
|
|
1370
|
+
view.focus();
|
|
1371
|
+
view.dispatch(view.state.tr.setSelection(import_state4.TextSelection.create(view.state.doc, pos)));
|
|
1372
|
+
beginDrag(view, pos);
|
|
1373
|
+
return true;
|
|
1374
|
+
}
|
|
1375
|
+
var PaginationCaretFix = import_core5.Extension.create({
|
|
1376
|
+
name: "paginationCaretFix",
|
|
1377
|
+
addProseMirrorPlugins() {
|
|
1378
|
+
return [
|
|
1379
|
+
new import_state4.Plugin({
|
|
1380
|
+
key: new import_state4.PluginKey("paginationCaretFix"),
|
|
1381
|
+
props: {
|
|
1382
|
+
handleDOMEvents: {
|
|
1383
|
+
mousedown: (view, event) => {
|
|
1384
|
+
try {
|
|
1385
|
+
return interceptPaginationMouseDown(view, event);
|
|
1386
|
+
} catch {
|
|
1387
|
+
return false;
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
})
|
|
1393
|
+
];
|
|
1394
|
+
}
|
|
1395
|
+
});
|
|
1396
|
+
|
|
1275
1397
|
// src/PerformanceMonitor.ts
|
|
1276
1398
|
var TARGET_FRAME_MS = 1e3 / 60;
|
|
1277
1399
|
var formatMb = (bytes) => `${Math.round(bytes / 1024 / 1024)}MB`;
|
|
@@ -1639,7 +1761,7 @@ function createEditor(options = {}) {
|
|
|
1639
1761
|
content: options.content ? migrateContent(options.content) : options.content
|
|
1640
1762
|
};
|
|
1641
1763
|
const plugins = migratedOptions.plugins ?? [];
|
|
1642
|
-
const collaborationSetup =
|
|
1764
|
+
const collaborationSetup = migratedOptions.collaboration;
|
|
1643
1765
|
const performanceMonitor = migratedOptions.debug ? createPerformanceMonitor() : void 0;
|
|
1644
1766
|
let tiptapEditor = createTiptapEditor(migratedOptions, plugins, collaborationSetup);
|
|
1645
1767
|
let pluginActions = createActionMap(tiptapEditor, plugins);
|
|
@@ -1700,11 +1822,11 @@ function createTiptapEditor(options, plugins, collaborationSetup) {
|
|
|
1700
1822
|
const pluginExtensions = collectExtensions(plugins);
|
|
1701
1823
|
const blockAttrs = options.getPageMap ? BlockAttributesExtension.configure({ pageMap: options.getPageMap }) : BlockAttributesExtension;
|
|
1702
1824
|
const paginationExt = options.paginationOptions ? PaginationPlus.configure(options.paginationOptions) : PaginationPlus;
|
|
1703
|
-
const ManualColumnResize =
|
|
1825
|
+
const ManualColumnResize = import_core6.Extension.create({
|
|
1704
1826
|
name: "manualColumnResize",
|
|
1705
1827
|
addProseMirrorPlugins() {
|
|
1706
|
-
const key2 = new
|
|
1707
|
-
return [new
|
|
1828
|
+
const key2 = new import_state5.PluginKey("manualColumnResize");
|
|
1829
|
+
return [new import_state5.Plugin({
|
|
1708
1830
|
key: key2,
|
|
1709
1831
|
view(view) {
|
|
1710
1832
|
function pauseObserver() {
|
|
@@ -2026,6 +2148,10 @@ function createTiptapEditor(options, plugins, collaborationSetup) {
|
|
|
2026
2148
|
import_extension_text_style.default,
|
|
2027
2149
|
blockAttrs,
|
|
2028
2150
|
paginationExt,
|
|
2151
|
+
// Always present — fixes caret-on-click misplacement caused by the
|
|
2152
|
+
// pagination DOM (header/footer/gap widgets breaking linear coordinates).
|
|
2153
|
+
// No-op (gated at runtime) when pagination is off or disabled.
|
|
2154
|
+
PaginationCaretFix,
|
|
2029
2155
|
// Always present — carries host-injected ports (onImageUpload, citation, …)
|
|
2030
2156
|
// in storage so plugin commands can reach them through the editor instance.
|
|
2031
2157
|
EditorContextExtension.configure({
|
|
@@ -2040,9 +2166,18 @@ function createTiptapEditor(options, plugins, collaborationSetup) {
|
|
|
2040
2166
|
];
|
|
2041
2167
|
const content = options.collaboration ? void 0 : options.content;
|
|
2042
2168
|
if (options.collaboration && collaborationSetup) {
|
|
2169
|
+
const seed = seedEmptyFragment(collaborationSetup.ydoc);
|
|
2170
|
+
if (seed.seeded) {
|
|
2171
|
+
extensions.push(
|
|
2172
|
+
import_core6.Extension.create({
|
|
2173
|
+
name: "provisionalSeedCleanup",
|
|
2174
|
+
onDestroy: () => seed.dispose()
|
|
2175
|
+
})
|
|
2176
|
+
);
|
|
2177
|
+
}
|
|
2043
2178
|
extensions = [...extensions, ...collaborationExtensions(collaborationSetup)];
|
|
2044
2179
|
}
|
|
2045
|
-
|
|
2180
|
+
const tiptapEditor = new import_core6.Editor({
|
|
2046
2181
|
element: options.target,
|
|
2047
2182
|
content,
|
|
2048
2183
|
extensions,
|
|
@@ -2057,10 +2192,11 @@ function createTiptapEditor(options, plugins, collaborationSetup) {
|
|
|
2057
2192
|
transformPastedHTML: sanitizePastedHTML
|
|
2058
2193
|
}
|
|
2059
2194
|
});
|
|
2195
|
+
return tiptapEditor;
|
|
2060
2196
|
}
|
|
2061
2197
|
|
|
2062
2198
|
// src/FontSize.ts
|
|
2063
|
-
var
|
|
2199
|
+
var import_core7 = require("@tiptap/core");
|
|
2064
2200
|
function mergeFontSize(textStyleType, existing, fontSize) {
|
|
2065
2201
|
if (fontSize === null) {
|
|
2066
2202
|
const { fontSize: _removed, ...rest } = existing?.attrs ?? {};
|
|
@@ -2071,7 +2207,7 @@ function mergeFontSize(textStyleType, existing, fontSize) {
|
|
|
2071
2207
|
function hasAttrs(mark) {
|
|
2072
2208
|
return Object.values(mark.attrs).some((v) => v != null);
|
|
2073
2209
|
}
|
|
2074
|
-
var FontSizeExtension =
|
|
2210
|
+
var FontSizeExtension = import_core7.Extension.create({
|
|
2075
2211
|
name: "fontSize",
|
|
2076
2212
|
addGlobalAttributes() {
|
|
2077
2213
|
return [
|
|
@@ -2169,113 +2305,6 @@ var FontSizeExtension = import_core6.Extension.create({
|
|
|
2169
2305
|
}
|
|
2170
2306
|
});
|
|
2171
2307
|
|
|
2172
|
-
// src/SubdocumentProvider.ts
|
|
2173
|
-
var import_awareness2 = require("y-protocols/awareness");
|
|
2174
|
-
var import_y_websocket2 = require("y-websocket");
|
|
2175
|
-
var Y2 = __toESM(require("yjs"), 1);
|
|
2176
|
-
var SubdocumentProvider = class {
|
|
2177
|
-
parentDoc;
|
|
2178
|
-
subdocs;
|
|
2179
|
-
awareness;
|
|
2180
|
-
options;
|
|
2181
|
-
states = /* @__PURE__ */ new Map();
|
|
2182
|
-
activeQueue = [];
|
|
2183
|
-
parentProvider = null;
|
|
2184
|
-
constructor(options) {
|
|
2185
|
-
this.options = {
|
|
2186
|
-
maxActive: options.maxActive ?? 5,
|
|
2187
|
-
onStateChange: options.onStateChange ?? (() => {
|
|
2188
|
-
}),
|
|
2189
|
-
...options
|
|
2190
|
-
};
|
|
2191
|
-
this.parentDoc = new Y2.Doc();
|
|
2192
|
-
this.subdocs = this.parentDoc.getMap("subdocs");
|
|
2193
|
-
this.awareness = new import_awareness2.Awareness(this.parentDoc);
|
|
2194
|
-
this.awareness.setLocalStateField("user", this.options.user);
|
|
2195
|
-
this.parentProvider = new import_y_websocket2.WebsocketProvider(
|
|
2196
|
-
this.options.websocketUrl,
|
|
2197
|
-
`${this.options.roomPrefix}/_parent`,
|
|
2198
|
-
this.parentDoc
|
|
2199
|
-
);
|
|
2200
|
-
}
|
|
2201
|
-
/** Create a new subdocument. Does NOT activate it. */
|
|
2202
|
-
createSubdoc(id, initialState) {
|
|
2203
|
-
if (this.states.has(id)) return this.states.get(id).doc;
|
|
2204
|
-
const doc = new Y2.Doc();
|
|
2205
|
-
if (initialState) Y2.applyUpdate(doc, initialState);
|
|
2206
|
-
doc.getXmlFragment("default");
|
|
2207
|
-
this.subdocs.set(id, doc);
|
|
2208
|
-
this.states.set(id, {
|
|
2209
|
-
id,
|
|
2210
|
-
doc,
|
|
2211
|
-
provider: null,
|
|
2212
|
-
active: false
|
|
2213
|
-
});
|
|
2214
|
-
this.options.onStateChange(this.getAllStates());
|
|
2215
|
-
return doc;
|
|
2216
|
-
}
|
|
2217
|
-
/** Activate a subdocument — connects it to the network for live sync. */
|
|
2218
|
-
activate(id) {
|
|
2219
|
-
const state = this.states.get(id);
|
|
2220
|
-
if (!state || state.active) return;
|
|
2221
|
-
if (this.activeQueue.length >= this.options.maxActive) {
|
|
2222
|
-
const oldest = this.activeQueue.shift();
|
|
2223
|
-
if (oldest) this.deactivate(oldest);
|
|
2224
|
-
}
|
|
2225
|
-
const doc = state.doc;
|
|
2226
|
-
const roomName = `${this.options.roomPrefix}/${id}`;
|
|
2227
|
-
const provider = new import_y_websocket2.WebsocketProvider(
|
|
2228
|
-
this.options.websocketUrl,
|
|
2229
|
-
roomName,
|
|
2230
|
-
doc
|
|
2231
|
-
);
|
|
2232
|
-
provider.awareness.setLocalStateField("user", this.options.user);
|
|
2233
|
-
state.provider = provider;
|
|
2234
|
-
state.active = true;
|
|
2235
|
-
this.activeQueue.push(id);
|
|
2236
|
-
this.options.onStateChange(this.getAllStates());
|
|
2237
|
-
}
|
|
2238
|
-
/** Deactivate a subdocument — disconnects it from the network. */
|
|
2239
|
-
deactivate(id) {
|
|
2240
|
-
const state = this.states.get(id);
|
|
2241
|
-
if (!state || !state.active) return;
|
|
2242
|
-
state.provider?.destroy();
|
|
2243
|
-
state.provider = null;
|
|
2244
|
-
state.active = false;
|
|
2245
|
-
const idx = this.activeQueue.indexOf(id);
|
|
2246
|
-
if (idx >= 0) this.activeQueue.splice(idx, 1);
|
|
2247
|
-
this.options.onStateChange(this.getAllStates());
|
|
2248
|
-
}
|
|
2249
|
-
/** Get the Y.XmlFragment for a subdocument. */
|
|
2250
|
-
getFragment(id) {
|
|
2251
|
-
const doc = this.states.get(id)?.doc ?? this.subdocs.get(id);
|
|
2252
|
-
if (!doc) return null;
|
|
2253
|
-
return doc.getXmlFragment("default");
|
|
2254
|
-
}
|
|
2255
|
-
/** Check if a subdocument is currently active (syncing). */
|
|
2256
|
-
isActive(id) {
|
|
2257
|
-
return this.states.get(id)?.active ?? false;
|
|
2258
|
-
}
|
|
2259
|
-
/** Get all subdocument states. */
|
|
2260
|
-
getAllStates() {
|
|
2261
|
-
return Array.from(this.states.values());
|
|
2262
|
-
}
|
|
2263
|
-
/** Get IDs of all active subdocuments. */
|
|
2264
|
-
getActiveIds() {
|
|
2265
|
-
return [...this.activeQueue];
|
|
2266
|
-
}
|
|
2267
|
-
/** Destroy all subdocuments and providers. */
|
|
2268
|
-
destroy() {
|
|
2269
|
-
for (const [id] of this.states) {
|
|
2270
|
-
this.deactivate(id);
|
|
2271
|
-
}
|
|
2272
|
-
this.parentProvider?.destroy();
|
|
2273
|
-
this.parentDoc.destroy();
|
|
2274
|
-
this.states.clear();
|
|
2275
|
-
this.activeQueue.length = 0;
|
|
2276
|
-
}
|
|
2277
|
-
};
|
|
2278
|
-
|
|
2279
2308
|
// src/ai/prompts.ts
|
|
2280
2309
|
var CONTEXT_CHAR_CAP = 1500;
|
|
2281
2310
|
var ONLY_RESULT = "Return ONLY the resulting text \u2014 no preamble, no explanation, no commentary, no markdown fences.";
|
|
@@ -2549,7 +2578,6 @@ function openaiCompatibleProvider(config) {
|
|
|
2549
2578
|
PaginationPlus,
|
|
2550
2579
|
PerformanceMonitor,
|
|
2551
2580
|
SearchAndReplaceExtension,
|
|
2552
|
-
SubdocumentProvider,
|
|
2553
2581
|
buildAIPrompt,
|
|
2554
2582
|
clearSearch,
|
|
2555
2583
|
collaborationExtensions,
|