@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
package/dist/index.js
CHANGED
|
@@ -10,10 +10,14 @@ import {
|
|
|
10
10
|
trimContextAfter,
|
|
11
11
|
trimContextBefore
|
|
12
12
|
} from "./chunk-HMOWKFPE.js";
|
|
13
|
+
import {
|
|
14
|
+
collaborationExtensions,
|
|
15
|
+
createCollaboration
|
|
16
|
+
} from "./chunk-XJG72F7M.js";
|
|
13
17
|
|
|
14
18
|
// src/Editor.ts
|
|
15
|
-
import { Extension as
|
|
16
|
-
import { Plugin as
|
|
19
|
+
import { Extension as Extension6, Editor as TiptapEditor } from "@tiptap/core";
|
|
20
|
+
import { Plugin as Plugin5, PluginKey as PluginKey5 } from "@tiptap/pm/state";
|
|
17
21
|
import StarterKit from "@tiptap/starter-kit";
|
|
18
22
|
import TextStyle from "@tiptap/extension-text-style";
|
|
19
23
|
|
|
@@ -64,143 +68,6 @@ function createActionMap(editor, plugins) {
|
|
|
64
68
|
return map;
|
|
65
69
|
}
|
|
66
70
|
|
|
67
|
-
// src/Collaboration.ts
|
|
68
|
-
import { Collaboration } from "@tiptap/extension-collaboration";
|
|
69
|
-
import { CollaborationCursor } from "@tiptap/extension-collaboration-cursor";
|
|
70
|
-
import { Awareness } from "y-protocols/awareness";
|
|
71
|
-
import { IndexeddbPersistence } from "y-indexeddb";
|
|
72
|
-
import { WebrtcProvider } from "y-webrtc";
|
|
73
|
-
import { WebsocketProvider } from "y-websocket";
|
|
74
|
-
import * as Y from "yjs";
|
|
75
|
-
var LOCAL_SIGNALING = ["ws://localhost:4444"];
|
|
76
|
-
var warnedLocalSignaling = false;
|
|
77
|
-
function resolveSignalingUrls(signaling) {
|
|
78
|
-
if (signaling && signaling.length > 0) return signaling;
|
|
79
|
-
if (!warnedLocalSignaling) {
|
|
80
|
-
warnedLocalSignaling = true;
|
|
81
|
-
console.warn(
|
|
82
|
-
'[docflow] webrtc provider has no `signaling` configured \u2014 falling back to localhost-only sync (ws://localhost:4444 + same-browser tabs). For production self-host, run your own signaling server and pass `signaling`, or use `provider: "websocket"` with your own websocketUrl.'
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
return LOCAL_SIGNALING;
|
|
86
|
-
}
|
|
87
|
-
var LOCAL_PRESENT_DEFAULT = true;
|
|
88
|
-
function createAwarenessStates(awareness) {
|
|
89
|
-
const states = [];
|
|
90
|
-
awareness.getStates().forEach((state, clientId) => {
|
|
91
|
-
const raw = state;
|
|
92
|
-
states.push({
|
|
93
|
-
clientId,
|
|
94
|
-
user: raw.user ?? { name: "", color: "" },
|
|
95
|
-
cursor: raw.cursor ?? null,
|
|
96
|
-
// `present` defaults to true for backwards compat (legacy states
|
|
97
|
-
// don't set it) and for the local user (always considered present
|
|
98
|
-
// until PR2 toggles it off).
|
|
99
|
-
present: raw.present ?? LOCAL_PRESENT_DEFAULT
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
return states;
|
|
103
|
-
}
|
|
104
|
-
function createCollaboration(options) {
|
|
105
|
-
const ydoc = new Y.Doc();
|
|
106
|
-
if (options.initialStorageState) {
|
|
107
|
-
Y.applyUpdate(ydoc, options.initialStorageState);
|
|
108
|
-
}
|
|
109
|
-
let persistence;
|
|
110
|
-
if (options.offline) {
|
|
111
|
-
persistence = new IndexeddbPersistence(`docflow-${options.room}`, ydoc);
|
|
112
|
-
}
|
|
113
|
-
let provider = null;
|
|
114
|
-
if (options.provider === "webrtc") {
|
|
115
|
-
provider = new WebrtcProvider(options.room, ydoc, {
|
|
116
|
-
signaling: resolveSignalingUrls(options.signaling)
|
|
117
|
-
});
|
|
118
|
-
} else if (options.provider === "websocket") {
|
|
119
|
-
if (!options.websocketUrl) {
|
|
120
|
-
throw new Error('[Collaboration] websocketUrl is required when provider is "websocket"');
|
|
121
|
-
}
|
|
122
|
-
provider = new WebsocketProvider(options.websocketUrl, options.room, ydoc);
|
|
123
|
-
}
|
|
124
|
-
const awareness = provider?.awareness ?? new Awareness(ydoc);
|
|
125
|
-
awareness.setLocalStateField("user", options.user);
|
|
126
|
-
let emitCursor = options.emitCursor ?? true;
|
|
127
|
-
const setLocalCursorEnabled = (enabled) => {
|
|
128
|
-
if (emitCursor === enabled) return;
|
|
129
|
-
emitCursor = enabled;
|
|
130
|
-
if (!enabled) {
|
|
131
|
-
awareness.setLocalStateField("cursor", null);
|
|
132
|
-
}
|
|
133
|
-
awareness.setLocalStateField("emitCursor", enabled);
|
|
134
|
-
};
|
|
135
|
-
awareness.setLocalStateField("present", true);
|
|
136
|
-
awareness.setLocalStateField("emitCursor", emitCursor);
|
|
137
|
-
let awarenessHandler;
|
|
138
|
-
if (options.onAwarenessChange) {
|
|
139
|
-
const notify = () => {
|
|
140
|
-
options.onAwarenessChange?.(createAwarenessStates(awareness));
|
|
141
|
-
};
|
|
142
|
-
awarenessHandler = notify;
|
|
143
|
-
awareness.on("change", notify);
|
|
144
|
-
notify();
|
|
145
|
-
}
|
|
146
|
-
const readyParts = [];
|
|
147
|
-
if (persistence) {
|
|
148
|
-
readyParts.push(
|
|
149
|
-
persistence.whenSynced.then(
|
|
150
|
-
() => void 0,
|
|
151
|
-
() => void 0
|
|
152
|
-
)
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
if (provider && options.provider === "websocket") {
|
|
156
|
-
const ws = provider;
|
|
157
|
-
readyParts.push(
|
|
158
|
-
new Promise((resolve) => {
|
|
159
|
-
let settled = false;
|
|
160
|
-
const done = () => {
|
|
161
|
-
if (settled) return;
|
|
162
|
-
settled = true;
|
|
163
|
-
resolve();
|
|
164
|
-
};
|
|
165
|
-
if (ws.synced) {
|
|
166
|
-
done();
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
ws.once("sync", done);
|
|
170
|
-
ws.once("synced", done);
|
|
171
|
-
setTimeout(done, 5e3);
|
|
172
|
-
})
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
const whenReady = readyParts.length === 0 ? Promise.resolve() : Promise.all(readyParts).then(() => void 0);
|
|
176
|
-
const destroy = () => {
|
|
177
|
-
if (awarenessHandler) {
|
|
178
|
-
awareness.off("change", awarenessHandler);
|
|
179
|
-
}
|
|
180
|
-
awareness.setLocalState(null);
|
|
181
|
-
provider?.destroy?.();
|
|
182
|
-
awareness.destroy?.();
|
|
183
|
-
void persistence?.destroy();
|
|
184
|
-
ydoc.destroy();
|
|
185
|
-
};
|
|
186
|
-
return { ydoc, provider, awareness, persistence, whenReady, destroy, setLocalCursorEnabled };
|
|
187
|
-
}
|
|
188
|
-
function collaborationExtensions(options) {
|
|
189
|
-
const setup = "ydoc" in options ? options : createCollaboration(options);
|
|
190
|
-
const extensions = [
|
|
191
|
-
Collaboration.configure({ document: setup.ydoc })
|
|
192
|
-
];
|
|
193
|
-
if (setup.provider) {
|
|
194
|
-
extensions.push(
|
|
195
|
-
CollaborationCursor.configure({
|
|
196
|
-
provider: setup.provider,
|
|
197
|
-
user: setup.awareness.getLocalState()?.user ?? { name: "", color: "" }
|
|
198
|
-
})
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
return extensions;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
71
|
// src/BlockAttributes.ts
|
|
205
72
|
import { Extension } from "@tiptap/core";
|
|
206
73
|
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
|
@@ -262,6 +129,42 @@ var BlockAttributesExtension = Extension.create({
|
|
|
262
129
|
}
|
|
263
130
|
});
|
|
264
131
|
|
|
132
|
+
// src/collab/seedEmptyFragment.ts
|
|
133
|
+
import * as Y from "yjs";
|
|
134
|
+
var PROVISIONAL_SEED_ORIGIN = "docflow:provisional-seed";
|
|
135
|
+
function isPristineEmptyParagraph(element) {
|
|
136
|
+
return element.length === 0 && Object.keys(element.getAttributes()).length === 0;
|
|
137
|
+
}
|
|
138
|
+
function seedEmptyFragment(doc, field = "default") {
|
|
139
|
+
const fragment = doc.getXmlFragment(field);
|
|
140
|
+
const paragraph = new Y.XmlElement("paragraph");
|
|
141
|
+
const observer = (_events, transaction) => {
|
|
142
|
+
if (transaction.origin === PROVISIONAL_SEED_ORIGIN) return;
|
|
143
|
+
if (paragraph.parent !== fragment) return;
|
|
144
|
+
if (!isPristineEmptyParagraph(paragraph)) return;
|
|
145
|
+
if (fragment.length < 2) return;
|
|
146
|
+
const index = fragment.toArray().indexOf(paragraph);
|
|
147
|
+
if (index === -1) return;
|
|
148
|
+
doc.transact(() => {
|
|
149
|
+
fragment.delete(index);
|
|
150
|
+
}, PROVISIONAL_SEED_ORIGIN);
|
|
151
|
+
};
|
|
152
|
+
let disposed = false;
|
|
153
|
+
const dispose = () => {
|
|
154
|
+
if (disposed) return;
|
|
155
|
+
disposed = true;
|
|
156
|
+
fragment.unobserveDeep(observer);
|
|
157
|
+
};
|
|
158
|
+
if (fragment.length > 0) {
|
|
159
|
+
return { seeded: false, dispose };
|
|
160
|
+
}
|
|
161
|
+
fragment.observeDeep(observer);
|
|
162
|
+
doc.transact(() => {
|
|
163
|
+
fragment.insert(0, [paragraph]);
|
|
164
|
+
}, PROVISIONAL_SEED_ORIGIN);
|
|
165
|
+
return { seeded: true, dispose };
|
|
166
|
+
}
|
|
167
|
+
|
|
265
168
|
// src/EditorContext.ts
|
|
266
169
|
import { Extension as Extension2 } from "@tiptap/core";
|
|
267
170
|
var EditorContextExtension = Extension2.create({
|
|
@@ -1212,6 +1115,94 @@ var PaginationPlus = Extension4.create({
|
|
|
1212
1115
|
}
|
|
1213
1116
|
});
|
|
1214
1117
|
|
|
1118
|
+
// src/pagination/PaginationCaretFix.ts
|
|
1119
|
+
import { Extension as Extension5 } from "@tiptap/core";
|
|
1120
|
+
import { Plugin as Plugin4, PluginKey as PluginKey4, TextSelection as TextSelection2 } from "@tiptap/pm/state";
|
|
1121
|
+
var PAGINATION_CLASS = "rm-with-pagination";
|
|
1122
|
+
var PAGINATION_DISABLED_ATTR = "rm-pagination-disabled";
|
|
1123
|
+
var dragStates = /* @__PURE__ */ new WeakMap();
|
|
1124
|
+
var isPaginationActive = (view) => {
|
|
1125
|
+
const dom = view.dom;
|
|
1126
|
+
return dom.classList.contains(PAGINATION_CLASS) && dom.getAttribute(PAGINATION_DISABLED_ATTR) === null;
|
|
1127
|
+
};
|
|
1128
|
+
var isEditable = (view) => view.dom.getAttribute("contenteditable") !== "false";
|
|
1129
|
+
var isPlainSingleLeftClick = (event) => event.button === 0 && event.detail === 1 && !event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey;
|
|
1130
|
+
function posFromTextOffset(view, textNode, offset) {
|
|
1131
|
+
if (textNode.nodeType !== Node.TEXT_NODE || !view.dom.contains(textNode)) return null;
|
|
1132
|
+
const text = textNode;
|
|
1133
|
+
const clamped = Math.min(Math.max(0, offset), text.length);
|
|
1134
|
+
const parent = text.parentElement;
|
|
1135
|
+
if (parent?.closest?.('[contenteditable="false"]')) return null;
|
|
1136
|
+
let pos = view.posAtDOM(text, clamped, -1);
|
|
1137
|
+
if (pos < 0) pos = view.posAtDOM(text, clamped, 1);
|
|
1138
|
+
if (pos < 0) return null;
|
|
1139
|
+
return view.state.doc.resolve(pos).parent.isTextblock ? pos : null;
|
|
1140
|
+
}
|
|
1141
|
+
function resolvePosFromNativePoint(view, clientX, clientY) {
|
|
1142
|
+
const doc = view.dom.ownerDocument;
|
|
1143
|
+
if (typeof doc.caretRangeFromPoint !== "function") return null;
|
|
1144
|
+
const range = doc.caretRangeFromPoint(clientX, clientY);
|
|
1145
|
+
if (!range) return null;
|
|
1146
|
+
return posFromTextOffset(view, range.startContainer, range.startOffset);
|
|
1147
|
+
}
|
|
1148
|
+
function stopDrag(view) {
|
|
1149
|
+
const drag = dragStates.get(view);
|
|
1150
|
+
if (!drag) return;
|
|
1151
|
+
dragStates.delete(view);
|
|
1152
|
+
const doc = view.dom.ownerDocument;
|
|
1153
|
+
doc.removeEventListener("mousemove", drag.onMouseMove);
|
|
1154
|
+
doc.removeEventListener("mouseup", drag.onMouseUp);
|
|
1155
|
+
}
|
|
1156
|
+
function beginDrag(view, anchorPos) {
|
|
1157
|
+
stopDrag(view);
|
|
1158
|
+
const doc = view.dom.ownerDocument;
|
|
1159
|
+
const onMouseMove = (event) => {
|
|
1160
|
+
const drag = dragStates.get(view);
|
|
1161
|
+
if (!drag) return;
|
|
1162
|
+
const pos = resolvePosFromNativePoint(view, event.clientX, event.clientY);
|
|
1163
|
+
if (pos === null) return;
|
|
1164
|
+
const from = Math.min(drag.anchorPos, pos);
|
|
1165
|
+
const to = Math.max(drag.anchorPos, pos);
|
|
1166
|
+
view.dispatch(view.state.tr.setSelection(TextSelection2.create(view.state.doc, from, to)));
|
|
1167
|
+
};
|
|
1168
|
+
const onMouseUp = () => {
|
|
1169
|
+
stopDrag(view);
|
|
1170
|
+
};
|
|
1171
|
+
dragStates.set(view, { anchorPos, onMouseMove, onMouseUp });
|
|
1172
|
+
doc.addEventListener("mousemove", onMouseMove);
|
|
1173
|
+
doc.addEventListener("mouseup", onMouseUp);
|
|
1174
|
+
}
|
|
1175
|
+
function interceptPaginationMouseDown(view, event) {
|
|
1176
|
+
if (!isPaginationActive(view) || !isPlainSingleLeftClick(event) || !isEditable(view)) return false;
|
|
1177
|
+
const pos = resolvePosFromNativePoint(view, event.clientX, event.clientY);
|
|
1178
|
+
if (pos === null) return false;
|
|
1179
|
+
view.focus();
|
|
1180
|
+
view.dispatch(view.state.tr.setSelection(TextSelection2.create(view.state.doc, pos)));
|
|
1181
|
+
beginDrag(view, pos);
|
|
1182
|
+
return true;
|
|
1183
|
+
}
|
|
1184
|
+
var PaginationCaretFix = Extension5.create({
|
|
1185
|
+
name: "paginationCaretFix",
|
|
1186
|
+
addProseMirrorPlugins() {
|
|
1187
|
+
return [
|
|
1188
|
+
new Plugin4({
|
|
1189
|
+
key: new PluginKey4("paginationCaretFix"),
|
|
1190
|
+
props: {
|
|
1191
|
+
handleDOMEvents: {
|
|
1192
|
+
mousedown: (view, event) => {
|
|
1193
|
+
try {
|
|
1194
|
+
return interceptPaginationMouseDown(view, event);
|
|
1195
|
+
} catch {
|
|
1196
|
+
return false;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
})
|
|
1202
|
+
];
|
|
1203
|
+
}
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1215
1206
|
// src/PerformanceMonitor.ts
|
|
1216
1207
|
var TARGET_FRAME_MS = 1e3 / 60;
|
|
1217
1208
|
var formatMb = (bytes) => `${Math.round(bytes / 1024 / 1024)}MB`;
|
|
@@ -1579,7 +1570,7 @@ function createEditor(options = {}) {
|
|
|
1579
1570
|
content: options.content ? migrateContent(options.content) : options.content
|
|
1580
1571
|
};
|
|
1581
1572
|
const plugins = migratedOptions.plugins ?? [];
|
|
1582
|
-
const collaborationSetup =
|
|
1573
|
+
const collaborationSetup = migratedOptions.collaboration;
|
|
1583
1574
|
const performanceMonitor = migratedOptions.debug ? createPerformanceMonitor() : void 0;
|
|
1584
1575
|
let tiptapEditor = createTiptapEditor(migratedOptions, plugins, collaborationSetup);
|
|
1585
1576
|
let pluginActions = createActionMap(tiptapEditor, plugins);
|
|
@@ -1640,11 +1631,11 @@ function createTiptapEditor(options, plugins, collaborationSetup) {
|
|
|
1640
1631
|
const pluginExtensions = collectExtensions(plugins);
|
|
1641
1632
|
const blockAttrs = options.getPageMap ? BlockAttributesExtension.configure({ pageMap: options.getPageMap }) : BlockAttributesExtension;
|
|
1642
1633
|
const paginationExt = options.paginationOptions ? PaginationPlus.configure(options.paginationOptions) : PaginationPlus;
|
|
1643
|
-
const ManualColumnResize =
|
|
1634
|
+
const ManualColumnResize = Extension6.create({
|
|
1644
1635
|
name: "manualColumnResize",
|
|
1645
1636
|
addProseMirrorPlugins() {
|
|
1646
|
-
const key2 = new
|
|
1647
|
-
return [new
|
|
1637
|
+
const key2 = new PluginKey5("manualColumnResize");
|
|
1638
|
+
return [new Plugin5({
|
|
1648
1639
|
key: key2,
|
|
1649
1640
|
view(view) {
|
|
1650
1641
|
function pauseObserver() {
|
|
@@ -1966,6 +1957,10 @@ function createTiptapEditor(options, plugins, collaborationSetup) {
|
|
|
1966
1957
|
TextStyle,
|
|
1967
1958
|
blockAttrs,
|
|
1968
1959
|
paginationExt,
|
|
1960
|
+
// Always present — fixes caret-on-click misplacement caused by the
|
|
1961
|
+
// pagination DOM (header/footer/gap widgets breaking linear coordinates).
|
|
1962
|
+
// No-op (gated at runtime) when pagination is off or disabled.
|
|
1963
|
+
PaginationCaretFix,
|
|
1969
1964
|
// Always present — carries host-injected ports (onImageUpload, citation, …)
|
|
1970
1965
|
// in storage so plugin commands can reach them through the editor instance.
|
|
1971
1966
|
EditorContextExtension.configure({
|
|
@@ -1980,9 +1975,18 @@ function createTiptapEditor(options, plugins, collaborationSetup) {
|
|
|
1980
1975
|
];
|
|
1981
1976
|
const content = options.collaboration ? void 0 : options.content;
|
|
1982
1977
|
if (options.collaboration && collaborationSetup) {
|
|
1978
|
+
const seed = seedEmptyFragment(collaborationSetup.ydoc);
|
|
1979
|
+
if (seed.seeded) {
|
|
1980
|
+
extensions.push(
|
|
1981
|
+
Extension6.create({
|
|
1982
|
+
name: "provisionalSeedCleanup",
|
|
1983
|
+
onDestroy: () => seed.dispose()
|
|
1984
|
+
})
|
|
1985
|
+
);
|
|
1986
|
+
}
|
|
1983
1987
|
extensions = [...extensions, ...collaborationExtensions(collaborationSetup)];
|
|
1984
1988
|
}
|
|
1985
|
-
|
|
1989
|
+
const tiptapEditor = new TiptapEditor({
|
|
1986
1990
|
element: options.target,
|
|
1987
1991
|
content,
|
|
1988
1992
|
extensions,
|
|
@@ -1997,10 +2001,11 @@ function createTiptapEditor(options, plugins, collaborationSetup) {
|
|
|
1997
2001
|
transformPastedHTML: sanitizePastedHTML
|
|
1998
2002
|
}
|
|
1999
2003
|
});
|
|
2004
|
+
return tiptapEditor;
|
|
2000
2005
|
}
|
|
2001
2006
|
|
|
2002
2007
|
// src/FontSize.ts
|
|
2003
|
-
import { Extension as
|
|
2008
|
+
import { Extension as Extension7 } from "@tiptap/core";
|
|
2004
2009
|
function mergeFontSize(textStyleType, existing, fontSize) {
|
|
2005
2010
|
if (fontSize === null) {
|
|
2006
2011
|
const { fontSize: _removed, ...rest } = existing?.attrs ?? {};
|
|
@@ -2011,7 +2016,7 @@ function mergeFontSize(textStyleType, existing, fontSize) {
|
|
|
2011
2016
|
function hasAttrs(mark) {
|
|
2012
2017
|
return Object.values(mark.attrs).some((v) => v != null);
|
|
2013
2018
|
}
|
|
2014
|
-
var FontSizeExtension =
|
|
2019
|
+
var FontSizeExtension = Extension7.create({
|
|
2015
2020
|
name: "fontSize",
|
|
2016
2021
|
addGlobalAttributes() {
|
|
2017
2022
|
return [
|
|
@@ -2108,113 +2113,6 @@ var FontSizeExtension = Extension6.create({
|
|
|
2108
2113
|
};
|
|
2109
2114
|
}
|
|
2110
2115
|
});
|
|
2111
|
-
|
|
2112
|
-
// src/SubdocumentProvider.ts
|
|
2113
|
-
import { Awareness as AwarenessClass } from "y-protocols/awareness";
|
|
2114
|
-
import { WebsocketProvider as WebsocketProvider2 } from "y-websocket";
|
|
2115
|
-
import * as Y2 from "yjs";
|
|
2116
|
-
var SubdocumentProvider = class {
|
|
2117
|
-
parentDoc;
|
|
2118
|
-
subdocs;
|
|
2119
|
-
awareness;
|
|
2120
|
-
options;
|
|
2121
|
-
states = /* @__PURE__ */ new Map();
|
|
2122
|
-
activeQueue = [];
|
|
2123
|
-
parentProvider = null;
|
|
2124
|
-
constructor(options) {
|
|
2125
|
-
this.options = {
|
|
2126
|
-
maxActive: options.maxActive ?? 5,
|
|
2127
|
-
onStateChange: options.onStateChange ?? (() => {
|
|
2128
|
-
}),
|
|
2129
|
-
...options
|
|
2130
|
-
};
|
|
2131
|
-
this.parentDoc = new Y2.Doc();
|
|
2132
|
-
this.subdocs = this.parentDoc.getMap("subdocs");
|
|
2133
|
-
this.awareness = new AwarenessClass(this.parentDoc);
|
|
2134
|
-
this.awareness.setLocalStateField("user", this.options.user);
|
|
2135
|
-
this.parentProvider = new WebsocketProvider2(
|
|
2136
|
-
this.options.websocketUrl,
|
|
2137
|
-
`${this.options.roomPrefix}/_parent`,
|
|
2138
|
-
this.parentDoc
|
|
2139
|
-
);
|
|
2140
|
-
}
|
|
2141
|
-
/** Create a new subdocument. Does NOT activate it. */
|
|
2142
|
-
createSubdoc(id, initialState) {
|
|
2143
|
-
if (this.states.has(id)) return this.states.get(id).doc;
|
|
2144
|
-
const doc = new Y2.Doc();
|
|
2145
|
-
if (initialState) Y2.applyUpdate(doc, initialState);
|
|
2146
|
-
doc.getXmlFragment("default");
|
|
2147
|
-
this.subdocs.set(id, doc);
|
|
2148
|
-
this.states.set(id, {
|
|
2149
|
-
id,
|
|
2150
|
-
doc,
|
|
2151
|
-
provider: null,
|
|
2152
|
-
active: false
|
|
2153
|
-
});
|
|
2154
|
-
this.options.onStateChange(this.getAllStates());
|
|
2155
|
-
return doc;
|
|
2156
|
-
}
|
|
2157
|
-
/** Activate a subdocument — connects it to the network for live sync. */
|
|
2158
|
-
activate(id) {
|
|
2159
|
-
const state = this.states.get(id);
|
|
2160
|
-
if (!state || state.active) return;
|
|
2161
|
-
if (this.activeQueue.length >= this.options.maxActive) {
|
|
2162
|
-
const oldest = this.activeQueue.shift();
|
|
2163
|
-
if (oldest) this.deactivate(oldest);
|
|
2164
|
-
}
|
|
2165
|
-
const doc = state.doc;
|
|
2166
|
-
const roomName = `${this.options.roomPrefix}/${id}`;
|
|
2167
|
-
const provider = new WebsocketProvider2(
|
|
2168
|
-
this.options.websocketUrl,
|
|
2169
|
-
roomName,
|
|
2170
|
-
doc
|
|
2171
|
-
);
|
|
2172
|
-
provider.awareness.setLocalStateField("user", this.options.user);
|
|
2173
|
-
state.provider = provider;
|
|
2174
|
-
state.active = true;
|
|
2175
|
-
this.activeQueue.push(id);
|
|
2176
|
-
this.options.onStateChange(this.getAllStates());
|
|
2177
|
-
}
|
|
2178
|
-
/** Deactivate a subdocument — disconnects it from the network. */
|
|
2179
|
-
deactivate(id) {
|
|
2180
|
-
const state = this.states.get(id);
|
|
2181
|
-
if (!state || !state.active) return;
|
|
2182
|
-
state.provider?.destroy();
|
|
2183
|
-
state.provider = null;
|
|
2184
|
-
state.active = false;
|
|
2185
|
-
const idx = this.activeQueue.indexOf(id);
|
|
2186
|
-
if (idx >= 0) this.activeQueue.splice(idx, 1);
|
|
2187
|
-
this.options.onStateChange(this.getAllStates());
|
|
2188
|
-
}
|
|
2189
|
-
/** Get the Y.XmlFragment for a subdocument. */
|
|
2190
|
-
getFragment(id) {
|
|
2191
|
-
const doc = this.states.get(id)?.doc ?? this.subdocs.get(id);
|
|
2192
|
-
if (!doc) return null;
|
|
2193
|
-
return doc.getXmlFragment("default");
|
|
2194
|
-
}
|
|
2195
|
-
/** Check if a subdocument is currently active (syncing). */
|
|
2196
|
-
isActive(id) {
|
|
2197
|
-
return this.states.get(id)?.active ?? false;
|
|
2198
|
-
}
|
|
2199
|
-
/** Get all subdocument states. */
|
|
2200
|
-
getAllStates() {
|
|
2201
|
-
return Array.from(this.states.values());
|
|
2202
|
-
}
|
|
2203
|
-
/** Get IDs of all active subdocuments. */
|
|
2204
|
-
getActiveIds() {
|
|
2205
|
-
return [...this.activeQueue];
|
|
2206
|
-
}
|
|
2207
|
-
/** Destroy all subdocuments and providers. */
|
|
2208
|
-
destroy() {
|
|
2209
|
-
for (const [id] of this.states) {
|
|
2210
|
-
this.deactivate(id);
|
|
2211
|
-
}
|
|
2212
|
-
this.parentProvider?.destroy();
|
|
2213
|
-
this.parentDoc.destroy();
|
|
2214
|
-
this.states.clear();
|
|
2215
|
-
this.activeQueue.length = 0;
|
|
2216
|
-
}
|
|
2217
|
-
};
|
|
2218
2116
|
export {
|
|
2219
2117
|
BlockAttributesExtension,
|
|
2220
2118
|
CONTEXT_CHAR_CAP,
|
|
@@ -2225,7 +2123,6 @@ export {
|
|
|
2225
2123
|
PaginationPlus,
|
|
2226
2124
|
PerformanceMonitor,
|
|
2227
2125
|
SearchAndReplaceExtension,
|
|
2228
|
-
SubdocumentProvider,
|
|
2229
2126
|
buildAIPrompt,
|
|
2230
2127
|
clearSearch,
|
|
2231
2128
|
collaborationExtensions,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kedataindo/docflow-core",
|
|
3
3
|
"license": "UNLICENSED",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.73",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.js",
|
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
"types": "./dist/ai/index.d.ts",
|
|
17
17
|
"import": "./dist/ai/index.js",
|
|
18
18
|
"require": "./dist/ai/index.cjs"
|
|
19
|
+
},
|
|
20
|
+
"./collab": {
|
|
21
|
+
"types": "./dist/collab/index.d.ts",
|
|
22
|
+
"import": "./dist/collab/index.js",
|
|
23
|
+
"require": "./dist/collab/index.cjs"
|
|
19
24
|
}
|
|
20
25
|
},
|
|
21
26
|
"publishConfig": {
|