@stll/folio-core 0.8.0 → 0.10.0
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/README.md +39 -0
- package/dist/ai-edits/apply.js +18 -4
- package/dist/ai-edits/headless.d.ts +73 -6
- package/dist/ai-edits/headless.js +299 -60
- package/dist/ai-edits/index.d.ts +3 -3
- package/dist/ai-edits/index.js +2 -1
- package/dist/ai-edits/snapshot.js +40 -3
- package/dist/ai-edits/types.d.ts +2 -1
- package/dist/compat/eigenpal.d.ts +6 -4
- package/dist/compat/eigenpal.js +5 -3
- package/dist/controller/latestRequestGate.d.ts +13 -0
- package/dist/controller/latestRequestGate.js +16 -0
- package/dist/controller/layoutPipeline.js +13 -1
- package/dist/document-operations.d.ts +20 -4
- package/dist/document-operations.js +36 -15
- package/dist/document-stories.d.ts +10 -0
- package/dist/document-stories.js +27 -0
- package/dist/docx/blockContentParser.js +2 -2
- package/dist/docx/commentParser.js +1 -1
- package/dist/docx/compatibility.d.ts +3 -7
- package/dist/docx/compatibility.js +0 -11
- package/dist/docx/corePropertiesParser.d.ts +8 -0
- package/dist/docx/corePropertiesParser.js +53 -0
- package/dist/docx/encryption/agileDecryption.js +1 -1
- package/dist/docx/encryption/encryptionInfo.js +1 -1
- package/dist/docx/encryption/openEncryptedDocx.js +1 -1
- package/dist/docx/fontTableParser.d.ts +6 -0
- package/dist/docx/fontTableParser.js +72 -0
- package/dist/docx/groupDrawingParser.js +7 -2
- package/dist/docx/headerFooterParser.js +1 -1
- package/dist/docx/index.d.ts +2 -1
- package/dist/docx/index.js +2 -1
- package/dist/docx/metadataPrivacy.d.ts +40 -0
- package/dist/docx/metadataPrivacy.js +131 -0
- package/dist/docx/normalizeBaseDirection.js +1 -1
- package/dist/docx/numberingParser.js +1 -0
- package/dist/docx/paragraphParser.js +12 -1
- package/dist/docx/parser.d.ts +1 -1
- package/dist/docx/parser.js +16 -10
- package/dist/docx/rezip.d.ts +5 -4
- package/dist/docx/rezip.js +76 -13
- package/dist/docx/runConsolidator.js +4 -0
- package/dist/docx/runParser.js +19 -1
- package/dist/docx/selectiveSave.js +3 -3
- package/dist/docx/serializer/fontTableSerializer.d.ts +6 -0
- package/dist/docx/serializer/fontTableSerializer.js +26 -0
- package/dist/docx/serializer/headerFooterSerializer.js +1 -1
- package/dist/docx/serializer/paragraphSerializer.js +4 -0
- package/dist/docx/serializer/runSerializer.js +7 -0
- package/dist/docx/serializer/settingsSerializer.d.ts +6 -0
- package/dist/docx/serializer/settingsSerializer.js +16 -0
- package/dist/docx/serializer/stylesSerializer.d.ts +6 -0
- package/dist/docx/serializer/stylesSerializer.js +56 -0
- package/dist/docx/serializer/tableSerializer.js +25 -26
- package/dist/docx/serializer/themeSerializer.d.ts +6 -0
- package/dist/docx/serializer/themeSerializer.js +36 -0
- package/dist/docx/settingsParser.js +46 -0
- package/dist/docx/styleParser.js +26 -1
- package/dist/docx/tableParser.js +11 -1
- package/dist/docx/vmlImageParser.js +1 -0
- package/dist/i18n/messages/catalogs.gen.d.ts +5916 -5916
- package/dist/i18n/messages/catalogs.gen.js +5910 -5910
- package/dist/index.d.ts +6 -4
- package/dist/index.js +5 -3
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +3 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +3 -1
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +3 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +6 -1
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +14 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +52 -14
- package/dist/layout-bridge/engine/measuring/index.d.ts +2 -1
- package/dist/layout-bridge/engine/measuring/index.js +2 -1
- package/dist/layout-engine/index.js +36 -28
- package/dist/layout-engine/measure/cache.js +6 -3
- package/dist/layout-engine/measure/effectiveLineBreakPolicy.d.ts +29 -0
- package/dist/layout-engine/measure/effectiveLineBreakPolicy.js +60 -0
- package/dist/layout-engine/measure/font-metrics.worker.js +1 -0
- package/dist/layout-engine/measure/index.d.ts +2 -1
- package/dist/layout-engine/measure/index.js +2 -1
- package/dist/layout-engine/measure/lineBreakProvider.d.ts +33 -0
- package/dist/layout-engine/measure/lineBreakProvider.js +252 -0
- package/dist/layout-engine/measure/lineBreaks.d.ts +7 -2
- package/dist/layout-engine/measure/lineBreaks.js +11 -18
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +1 -0
- package/dist/layout-engine/measure/listMarkerWidth.js +12 -6
- package/dist/layout-engine/measure/measureBlocks.d.ts +1 -2
- package/dist/layout-engine/measure/measureBlocks.js +6 -36
- package/dist/layout-engine/measure/measureContainer.js +32 -9
- package/dist/layout-engine/measure/measureHelpers.js +5 -2
- package/dist/layout-engine/measure/measureParagraph.js +332 -73
- package/dist/layout-engine/measure/measureTypes.d.ts +2 -1
- package/dist/layout-engine/measure/measureWorker.d.ts +2 -2
- package/dist/layout-engine/measure/measureWorker.js +8 -19
- package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +3 -14
- package/dist/layout-engine/measure/tableCellFloating.d.ts +10 -3
- package/dist/layout-engine/measure/tableCellFloating.js +51 -37
- package/dist/layout-engine/measure/tableCellFlow.d.ts +27 -0
- package/dist/layout-engine/measure/tableCellFlow.js +73 -0
- package/dist/layout-engine/measure/tableCellGrid.js +1 -1
- package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +18 -0
- package/dist/layout-engine/measure/textMeasurementPolicy.js +24 -0
- package/dist/layout-engine/renderedBreakReconciliation.d.ts +6 -1
- package/dist/layout-engine/renderedBreakReconciliation.js +34 -11
- package/dist/layout-engine/tableRowBreak.d.ts +5 -2
- package/dist/layout-engine/tableRowBreak.js +62 -29
- package/dist/layout-engine/types.d.ts +43 -8
- package/dist/layout-painter/anchoredImagePosition.d.ts +23 -0
- package/dist/layout-painter/anchoredImagePosition.js +95 -0
- package/dist/layout-painter/borderStroke.d.ts +23 -0
- package/dist/layout-painter/borderStroke.js +39 -0
- package/dist/layout-painter/renderImage.d.ts +5 -4
- package/dist/layout-painter/renderImage.js +18 -4
- package/dist/layout-painter/renderPage.d.ts +2 -27
- package/dist/layout-painter/renderPage.js +13 -99
- package/dist/layout-painter/renderParagraph.js +41 -23
- package/dist/layout-painter/renderTable.d.ts +2 -0
- package/dist/layout-painter/renderTable.js +223 -43
- package/dist/managers/DocumentLoaderManager.d.ts +1 -1
- package/dist/managers/DocumentLoaderManager.js +2 -2
- package/dist/prosemirror/attrs/index.d.ts +4 -2
- package/dist/prosemirror/attrs/index.js +22 -2
- package/dist/prosemirror/commands/propertyChangeScope.js +1 -1
- package/dist/prosemirror/conversion/effectiveTableCellFormatting.d.ts +62 -0
- package/dist/prosemirror/conversion/effectiveTableCellFormatting.js +131 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +34 -18
- package/dist/prosemirror/conversion/toProseDoc.js +81 -77
- package/dist/prosemirror/extensions/StarterKit.js +2 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +7 -2
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +1 -1
- package/dist/prosemirror/extensions/features/ListExtension.js +1 -0
- package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +1 -0
- package/dist/prosemirror/extensions/marks/HighlightExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/LanguageExtension.d.ts +7 -0
- package/dist/prosemirror/extensions/marks/LanguageExtension.js +30 -0
- package/dist/prosemirror/extensions/marks/RunShadingExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/TextColorExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +1 -1
- package/dist/prosemirror/extensions/marks/markUtils.js +12 -0
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +3 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.js +4 -2
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/marks.d.ts +6 -1
- package/dist/prosemirror/schema/nodes.d.ts +15 -12
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
- package/dist/prosemirror/utils/tabCalculator.d.ts +3 -1
- package/dist/prosemirror/utils/tabCalculator.js +12 -10
- package/dist/prosemirror/validation.js +4 -1
- package/dist/redline.d.ts +26 -11
- package/dist/redline.js +107 -64
- package/dist/server.d.ts +9 -5
- package/dist/server.js +8 -4
- package/dist/style-sets/extract.d.ts +35 -0
- package/dist/style-sets/extract.js +123 -0
- package/dist/style-sets/stellaStyle.d.ts +13 -0
- package/dist/style-sets/stellaStyle.js +516 -0
- package/dist/style-sets/types.d.ts +31 -0
- package/dist/style-sets/types.js +5 -0
- package/dist/utils/createDocument.d.ts +12 -2
- package/dist/utils/createDocument.js +42 -31
- package/dist/utils/fontResolver.js +6 -6
- package/dist/utils/formatToStyle.js +1 -1
- package/dist/utils/paragraphFormattingMerge.d.ts +14 -3
- package/dist/utils/paragraphFormattingMerge.js +14 -4
- package/dist/version-comparison.d.ts +65 -11
- package/dist/version-comparison.js +187 -29
- package/package.json +5 -1
- package/dist/docx/capabilities.d.ts +0 -41
- package/dist/docx/capabilities.js +0 -322
|
@@ -2,18 +2,19 @@ import { buildAnnotatedBlockText } from "./clean-text.js";
|
|
|
2
2
|
import { createFolioAIEditSnapshot, normalizeFolioAIBlockText } from "./snapshot.js";
|
|
3
3
|
import { applyFolioDocumentOperations } from "../document-operations.js";
|
|
4
4
|
import { getEndnoteText, getFootnoteText, isSeparatorEndnote, isSeparatorFootnote } from "../docx/footnoteParser.js";
|
|
5
|
+
import { getHeaderFooterText } from "../docx/headerFooterParser.js";
|
|
6
|
+
import { parseDocx } from "../docx/parser.js";
|
|
5
7
|
import { deterministicHexId } from "../utils/hexId.js";
|
|
6
8
|
import { repackDocx } from "../docx/rezip.js";
|
|
7
9
|
import { getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
|
|
8
10
|
import { createReply } from "../docx/replyToComment.js";
|
|
9
11
|
import { attemptSelectiveSave } from "../docx/selectiveSave.js";
|
|
10
|
-
import { getHeaderFooterText } from "../docx/headerFooterParser.js";
|
|
11
|
-
import { parseDocx } from "../docx/parser.js";
|
|
12
12
|
import { acceptAIEditRevision, acceptAllChanges, rejectAIEditRevision, rejectAllChanges } from "../prosemirror/commands/comments.js";
|
|
13
|
-
import { updateDocumentContent } from "../prosemirror/conversion/fromProseDoc.js";
|
|
13
|
+
import { proseDocToBlocks, updateDocumentContent } from "../prosemirror/conversion/fromProseDoc.js";
|
|
14
14
|
import { getChangedParagraphIds, hasStructuralChanges, hasUntrackedChanges, ignoreTrackedChanges } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
|
|
15
15
|
import { schema, singletonManager } from "../prosemirror/schema/index.js";
|
|
16
|
-
import { toProseDoc } from "../prosemirror/conversion/toProseDoc.js";
|
|
16
|
+
import { footnoteToProseDoc, headerFooterToProseDoc, toProseDoc } from "../prosemirror/conversion/toProseDoc.js";
|
|
17
|
+
import { TaggedError } from "better-result";
|
|
17
18
|
import { EditorState } from "prosemirror-state";
|
|
18
19
|
//#region src/ai-edits/headless.ts
|
|
19
20
|
/**
|
|
@@ -33,8 +34,7 @@ import { EditorState } from "prosemirror-state";
|
|
|
33
34
|
* paragraphs in `document.xml` (leaving every untouched part byte-exact), with
|
|
34
35
|
* a full repack as the fallback for structural edits.
|
|
35
36
|
*
|
|
36
|
-
* Scope:
|
|
37
|
-
* of scope here and can build on the note serialization work separately.
|
|
37
|
+
* Scope: main, header, footer, footnote, and endnote blocks.
|
|
38
38
|
*/
|
|
39
39
|
/**
|
|
40
40
|
* Standalone comment-id allocator for the reviewer. The React editor uses
|
|
@@ -114,6 +114,43 @@ const ensureDeterministicParaIdsInState = (state) => {
|
|
|
114
114
|
tr.setMeta("addToHistory", false);
|
|
115
115
|
return state.apply(tr);
|
|
116
116
|
};
|
|
117
|
+
const FOLIO_REVIEWED_VIEWS = Object.freeze([
|
|
118
|
+
"original",
|
|
119
|
+
"current-markup",
|
|
120
|
+
"final"
|
|
121
|
+
]);
|
|
122
|
+
const FOLIO_RESOLVED_REVIEWED_VIEWS = Object.freeze(["original", "final"]);
|
|
123
|
+
var UnsupportedFolioReviewedViewError = class extends TaggedError("UnsupportedFolioReviewedViewError")() {};
|
|
124
|
+
var FolioDocumentStoryNotFoundError = class extends TaggedError("FolioDocumentStoryNotFoundError")() {};
|
|
125
|
+
const MAIN_STORY = Object.freeze({ type: "main" });
|
|
126
|
+
const headerFooterStoryKey = ({ type, relationshipId }) => `${type}:${relationshipId}`;
|
|
127
|
+
const noteStoryKey = ({ type, noteId }) => `${type}:${noteId}`;
|
|
128
|
+
const secondaryStoryKey = (story) => story.type === "header" || story.type === "footer" ? headerFooterStoryKey(story) : noteStoryKey(story);
|
|
129
|
+
const isFolioReviewedView = (value) => FOLIO_REVIEWED_VIEWS.some((view) => view === value);
|
|
130
|
+
const isFolioResolvedReviewedView = (value) => FOLIO_RESOLVED_REVIEWED_VIEWS.some((view) => view === value);
|
|
131
|
+
const applyCommandToState = (state, command) => {
|
|
132
|
+
let nextState = state;
|
|
133
|
+
command(state, (transaction) => {
|
|
134
|
+
nextState = nextState.apply(transaction);
|
|
135
|
+
});
|
|
136
|
+
return nextState;
|
|
137
|
+
};
|
|
138
|
+
const resolveReviewedState = (state, view) => {
|
|
139
|
+
if (view === "current-markup") return state;
|
|
140
|
+
return applyCommandToState(state, view === "original" ? rejectAllChanges() : acceptAllChanges());
|
|
141
|
+
};
|
|
142
|
+
const formatStoryStateForLLM = (state, annotated) => {
|
|
143
|
+
const snapshot = createFolioAIEditSnapshot(state.doc);
|
|
144
|
+
if (!annotated) return snapshot.blocks.map(formatBlockForLLM).join("\n");
|
|
145
|
+
const startById = /* @__PURE__ */ new Map();
|
|
146
|
+
for (const anchor of Object.values(snapshot.anchors)) startById.set(anchor.id, anchor.from);
|
|
147
|
+
return snapshot.blocks.map((block) => {
|
|
148
|
+
const from = startById.get(block.id);
|
|
149
|
+
const node = from === void 0 ? null : state.doc.nodeAt(from);
|
|
150
|
+
const text = node ? buildAnnotatedBlockText(node) : block.text;
|
|
151
|
+
return formatBlockLine(block, text);
|
|
152
|
+
}).join("\n");
|
|
153
|
+
};
|
|
117
154
|
/**
|
|
118
155
|
* One LLM-ready line for a block: `[<blockId>] text`, with an `(h<level>)` tag
|
|
119
156
|
* for headings and the list marker for list items, so a model can copy the
|
|
@@ -162,6 +199,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
162
199
|
baseDocument;
|
|
163
200
|
originalBuffer;
|
|
164
201
|
state;
|
|
202
|
+
secondaryStoryStates = /* @__PURE__ */ new Map();
|
|
165
203
|
createdComments = [];
|
|
166
204
|
documentOperationUndoEntries = [];
|
|
167
205
|
/**
|
|
@@ -206,6 +244,51 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
206
244
|
snapshot() {
|
|
207
245
|
return createFolioAIEditSnapshot(this.state.doc);
|
|
208
246
|
}
|
|
247
|
+
/** Return parsed package metadata without exposing the mutable document model. */
|
|
248
|
+
getDocumentProperties() {
|
|
249
|
+
const properties = this.baseDocument.package.properties;
|
|
250
|
+
if (!properties) return null;
|
|
251
|
+
return Object.freeze({
|
|
252
|
+
...properties,
|
|
253
|
+
...properties.created !== void 0 ? { created: new Date(properties.created.getTime()) } : {},
|
|
254
|
+
...properties.modified !== void 0 ? { modified: new Date(properties.modified.getTime()) } : {}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
/** Snapshot one editable story into stable, operation-ready blocks. */
|
|
258
|
+
snapshotStory(story) {
|
|
259
|
+
const state = this.getEditableStoryState(story);
|
|
260
|
+
return state ? createFolioAIEditSnapshot(state.doc) : null;
|
|
261
|
+
}
|
|
262
|
+
/** Read one story through an immutable reviewed-view projection. */
|
|
263
|
+
readReviewedStory(options = {}) {
|
|
264
|
+
const story = options.story ?? MAIN_STORY;
|
|
265
|
+
const view = options.view ?? "final";
|
|
266
|
+
if (!isFolioReviewedView(view)) throw new UnsupportedFolioReviewedViewError({
|
|
267
|
+
message: "Unsupported reviewed document view.",
|
|
268
|
+
receivedView: view
|
|
269
|
+
});
|
|
270
|
+
const sourceState = this.getEditableStoryState(story);
|
|
271
|
+
if (!sourceState) return null;
|
|
272
|
+
const state = resolveReviewedState(sourceState, view);
|
|
273
|
+
return {
|
|
274
|
+
story,
|
|
275
|
+
view,
|
|
276
|
+
snapshot: createFolioAIEditSnapshot(state.doc),
|
|
277
|
+
text: formatStoryStateForLLM(state, view === "current-markup"),
|
|
278
|
+
changes: getTrackedChangesFromDoc(state.doc)
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
/** Resolve one editable story to its original or final state. */
|
|
282
|
+
resolveReviewedStory({ story = MAIN_STORY, view }) {
|
|
283
|
+
if (!isFolioResolvedReviewedView(view)) throw new UnsupportedFolioReviewedViewError({
|
|
284
|
+
message: "Only original and final views can replace editable story state.",
|
|
285
|
+
receivedView: view
|
|
286
|
+
});
|
|
287
|
+
const sourceState = this.getEditableStoryState(story);
|
|
288
|
+
if (!sourceState) return false;
|
|
289
|
+
this.setEditableStoryState(story, resolveReviewedState(sourceState, view));
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
209
292
|
/**
|
|
210
293
|
* Apply operations against the current state. Reuses the live-editor applier
|
|
211
294
|
* verbatim via a headless `{ state, dispatch }` seam; the resulting state
|
|
@@ -213,10 +296,15 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
213
296
|
*/
|
|
214
297
|
applyOperations(operations, options = {}) {
|
|
215
298
|
const { applied, skipped } = this.applyDocumentOperationsInternal({
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
299
|
+
story: MAIN_STORY,
|
|
300
|
+
batch: {
|
|
301
|
+
version: 1,
|
|
302
|
+
operations,
|
|
303
|
+
mode: options.mode ?? "tracked-changes"
|
|
304
|
+
},
|
|
305
|
+
...options.snapshot !== void 0 && { snapshot: options.snapshot },
|
|
306
|
+
createUndoEntry: false
|
|
307
|
+
});
|
|
220
308
|
return {
|
|
221
309
|
applied,
|
|
222
310
|
skipped
|
|
@@ -229,21 +317,36 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
229
317
|
* edit semantics.
|
|
230
318
|
*/
|
|
231
319
|
applyDocumentOperations(batch, options = {}) {
|
|
232
|
-
return this.applyDocumentOperationsInternal(
|
|
320
|
+
return this.applyDocumentOperationsInternal({
|
|
321
|
+
story: MAIN_STORY,
|
|
322
|
+
batch,
|
|
323
|
+
...options.snapshot !== void 0 && { snapshot: options.snapshot },
|
|
324
|
+
createUndoEntry: true
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
/** Apply a versioned operation batch to one editable document story. */
|
|
328
|
+
applyDocumentOperationsToStory({ story, batch, snapshot }) {
|
|
329
|
+
return this.applyDocumentOperationsInternal({
|
|
330
|
+
story,
|
|
331
|
+
batch,
|
|
332
|
+
...snapshot !== void 0 && { snapshot },
|
|
333
|
+
createUndoEntry: true
|
|
334
|
+
});
|
|
233
335
|
}
|
|
234
|
-
applyDocumentOperationsInternal(batch,
|
|
235
|
-
const beforeState = this.
|
|
336
|
+
applyDocumentOperationsInternal({ story, batch, snapshot, createUndoEntry }) {
|
|
337
|
+
const beforeState = this.requireEditableStoryState(story);
|
|
236
338
|
const createdCommentsLengthBefore = this.createdComments.length;
|
|
237
339
|
const view = {
|
|
238
|
-
state:
|
|
340
|
+
state: beforeState,
|
|
239
341
|
dispatch: (transaction) => {
|
|
240
342
|
view.state = view.state.apply(transaction);
|
|
241
343
|
}
|
|
242
344
|
};
|
|
243
345
|
const result = applyFolioDocumentOperations({
|
|
244
346
|
view,
|
|
245
|
-
snapshot:
|
|
347
|
+
snapshot: snapshot ?? createFolioAIEditSnapshot(beforeState.doc),
|
|
246
348
|
batch,
|
|
349
|
+
story: story.type === "main" ? "main" : story,
|
|
247
350
|
author: this.author,
|
|
248
351
|
createCommentId: (text) => {
|
|
249
352
|
const comment = createReviewerComment(text, this.author);
|
|
@@ -255,11 +358,12 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
255
358
|
id: `headless-${String(undoHandleCursor++)}`
|
|
256
359
|
}) }
|
|
257
360
|
});
|
|
258
|
-
this.
|
|
361
|
+
this.setEditableStoryState(story, view.state);
|
|
259
362
|
if (result.undoHandle !== null) this.documentOperationUndoEntries.push({
|
|
260
363
|
undoHandle: result.undoHandle,
|
|
364
|
+
story,
|
|
261
365
|
beforeState,
|
|
262
|
-
afterState:
|
|
366
|
+
afterState: view.state,
|
|
263
367
|
createdCommentsLengthBefore,
|
|
264
368
|
createdCommentsLengthAfter: this.createdComments.length
|
|
265
369
|
});
|
|
@@ -284,12 +388,12 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
284
388
|
undoHandle,
|
|
285
389
|
reason: "unknownHandle"
|
|
286
390
|
};
|
|
287
|
-
if (this.
|
|
391
|
+
if (this.requireEditableStoryState(entry.story) !== entry.afterState || this.createdComments.length !== entry.createdCommentsLengthAfter) return {
|
|
288
392
|
status: "rejected",
|
|
289
393
|
undoHandle,
|
|
290
394
|
reason: "documentChanged"
|
|
291
395
|
};
|
|
292
|
-
this.
|
|
396
|
+
this.setEditableStoryState(entry.story, entry.beforeState);
|
|
293
397
|
this.createdComments.length = entry.createdCommentsLengthBefore;
|
|
294
398
|
this.documentOperationUndoEntries.pop();
|
|
295
399
|
return {
|
|
@@ -317,31 +421,25 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
317
421
|
* (clean) output flattens tracked changes and is unchanged.
|
|
318
422
|
*/
|
|
319
423
|
getContentAsText(options = {}) {
|
|
320
|
-
|
|
321
|
-
const snapshot = this.snapshot();
|
|
322
|
-
const startById = /* @__PURE__ */ new Map();
|
|
323
|
-
for (const anchor of Object.values(snapshot.anchors)) startById.set(anchor.id, anchor.from);
|
|
324
|
-
return snapshot.blocks.map((block) => {
|
|
325
|
-
const from = startById.get(block.id);
|
|
326
|
-
const node = from === void 0 ? null : this.state.doc.nodeAt(from);
|
|
327
|
-
const text = node ? buildAnnotatedBlockText(node) : block.text;
|
|
328
|
-
return formatBlockLine(block, text);
|
|
329
|
-
}).join("\n");
|
|
424
|
+
return formatStoryStateForLLM(this.state, options.annotated === true);
|
|
330
425
|
}
|
|
331
426
|
/**
|
|
332
427
|
* Header / footer and footnote / endnote text as labeled, LLM-ready lines,
|
|
333
428
|
* one per non-empty part: `[header default] …`, `[footer default] …`,
|
|
334
|
-
* `[footnote #N] …`, `[endnote #N] …`.
|
|
335
|
-
*
|
|
336
|
-
* Empty parts and separator notes are omitted.
|
|
429
|
+
* `[footnote #N] …`, `[endnote #N] …`. Lines reflect in-memory edits. Empty
|
|
430
|
+
* parts and separator notes are omitted.
|
|
337
431
|
*/
|
|
338
432
|
getNotesAsText() {
|
|
339
433
|
const pkg = this.baseDocument.package;
|
|
340
434
|
const lines = [];
|
|
341
435
|
const pushHeaderFooter = (map, label) => {
|
|
342
436
|
if (!map) return;
|
|
343
|
-
for (const hf of map
|
|
344
|
-
const
|
|
437
|
+
for (const [relationshipId, hf] of map) {
|
|
438
|
+
const handle = {
|
|
439
|
+
type: label,
|
|
440
|
+
relationshipId
|
|
441
|
+
};
|
|
442
|
+
const text = this.getHeaderFooterStoryText(handle, hf);
|
|
345
443
|
if (text.length > 0) lines.push(`[${label} ${hf.hdrFtrType}] ${text}`);
|
|
346
444
|
}
|
|
347
445
|
};
|
|
@@ -349,12 +447,20 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
349
447
|
pushHeaderFooter(pkg.footers, "footer");
|
|
350
448
|
for (const footnote of pkg.footnotes ?? []) {
|
|
351
449
|
if (isSeparatorFootnote(footnote)) continue;
|
|
352
|
-
const
|
|
450
|
+
const handle = {
|
|
451
|
+
type: "footnote",
|
|
452
|
+
noteId: footnote.id
|
|
453
|
+
};
|
|
454
|
+
const text = this.getNoteStoryText(handle, footnote);
|
|
353
455
|
if (text.length > 0) lines.push(`[footnote #${footnote.id}] ${text}`);
|
|
354
456
|
}
|
|
355
457
|
for (const endnote of pkg.endnotes ?? []) {
|
|
356
458
|
if (isSeparatorEndnote(endnote)) continue;
|
|
357
|
-
const
|
|
459
|
+
const handle = {
|
|
460
|
+
type: "endnote",
|
|
461
|
+
noteId: endnote.id
|
|
462
|
+
};
|
|
463
|
+
const text = this.getNoteStoryText(handle, endnote);
|
|
358
464
|
if (text.length > 0) lines.push(`[endnote #${endnote.id}] ${text}`);
|
|
359
465
|
}
|
|
360
466
|
return lines.join("\n");
|
|
@@ -366,34 +472,46 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
366
472
|
handle: { type: "main" },
|
|
367
473
|
text: this.getContentAsText()
|
|
368
474
|
}];
|
|
369
|
-
for (const [relationshipId, header] of pkg.headers ?? [])
|
|
370
|
-
handle
|
|
475
|
+
for (const [relationshipId, header] of pkg.headers ?? []) {
|
|
476
|
+
const handle = {
|
|
371
477
|
type: "header",
|
|
372
478
|
relationshipId
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
479
|
+
};
|
|
480
|
+
stories.push({
|
|
481
|
+
handle,
|
|
482
|
+
text: this.getHeaderFooterStoryText(handle, header)
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
for (const [relationshipId, footer] of pkg.footers ?? []) {
|
|
486
|
+
const handle = {
|
|
378
487
|
type: "footer",
|
|
379
488
|
relationshipId
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
489
|
+
};
|
|
490
|
+
stories.push({
|
|
491
|
+
handle,
|
|
492
|
+
text: this.getHeaderFooterStoryText(handle, footer)
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
for (const footnote of pkg.footnotes ?? []) if (!isSeparatorFootnote(footnote)) {
|
|
496
|
+
const handle = {
|
|
385
497
|
type: "footnote",
|
|
386
498
|
noteId: footnote.id
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
499
|
+
};
|
|
500
|
+
stories.push({
|
|
501
|
+
handle,
|
|
502
|
+
text: this.getNoteStoryText(handle, footnote)
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
for (const endnote of pkg.endnotes ?? []) if (!isSeparatorEndnote(endnote)) {
|
|
506
|
+
const handle = {
|
|
392
507
|
type: "endnote",
|
|
393
508
|
noteId: endnote.id
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
|
|
509
|
+
};
|
|
510
|
+
stories.push({
|
|
511
|
+
handle,
|
|
512
|
+
text: this.getNoteStoryText(handle, endnote)
|
|
513
|
+
});
|
|
514
|
+
}
|
|
397
515
|
return stories;
|
|
398
516
|
}
|
|
399
517
|
/** Read one discovered story; returns null when its handle is no longer present. */
|
|
@@ -537,6 +655,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
537
655
|
/** The current document model with edits merged back in. */
|
|
538
656
|
toDocument() {
|
|
539
657
|
const document = updateDocumentContent(this.baseDocument, this.state.doc);
|
|
658
|
+
this.mergeEditedSecondaryStories(document);
|
|
540
659
|
if (this.createdComments.length > 0 || this.resolvedOverrides.size > 0) document.package.document.comments = this.withResolvedOverrides([...document.package.document.comments ?? [], ...this.createdComments]);
|
|
541
660
|
return document;
|
|
542
661
|
}
|
|
@@ -562,16 +681,136 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
562
681
|
* is the graceful handling — no separate error surface is needed here.
|
|
563
682
|
*/
|
|
564
683
|
async trySelectiveSave(document) {
|
|
684
|
+
const changedParaIds = new Set(getChangedParagraphIds(this.state));
|
|
685
|
+
let structuralChange = hasStructuralChanges(this.state);
|
|
686
|
+
let untrackedChanges = hasUntrackedChanges(this.state);
|
|
687
|
+
for (const entry of this.secondaryStoryStates.values()) {
|
|
688
|
+
if (entry.handle.type !== "footnote" && entry.handle.type !== "endnote") continue;
|
|
689
|
+
for (const paraId of getChangedParagraphIds(entry.state)) changedParaIds.add(paraId);
|
|
690
|
+
structuralChange ||= hasStructuralChanges(entry.state);
|
|
691
|
+
untrackedChanges ||= hasUntrackedChanges(entry.state);
|
|
692
|
+
}
|
|
565
693
|
try {
|
|
566
694
|
return await attemptSelectiveSave(document, this.originalBuffer, {
|
|
567
|
-
changedParaIds
|
|
568
|
-
structuralChange
|
|
569
|
-
hasUntrackedChanges:
|
|
695
|
+
changedParaIds,
|
|
696
|
+
structuralChange,
|
|
697
|
+
hasUntrackedChanges: untrackedChanges
|
|
570
698
|
});
|
|
571
699
|
} catch {
|
|
572
700
|
return null;
|
|
573
701
|
}
|
|
574
702
|
}
|
|
703
|
+
getEditableStoryState(story) {
|
|
704
|
+
if (story.type === "main") return this.state;
|
|
705
|
+
const key = secondaryStoryKey(story);
|
|
706
|
+
const existing = this.secondaryStoryStates.get(key);
|
|
707
|
+
if (existing) return existing.state;
|
|
708
|
+
const source = story.type === "header" || story.type === "footer" ? this.getHeaderFooterStory(story) : this.getNoteStory(story);
|
|
709
|
+
if (!source) return null;
|
|
710
|
+
const conversionOptions = {
|
|
711
|
+
...this.baseDocument.package.styles !== void 0 && { styles: this.baseDocument.package.styles },
|
|
712
|
+
...this.baseDocument.package.theme !== void 0 && { theme: this.baseDocument.package.theme }
|
|
713
|
+
};
|
|
714
|
+
const state = ensureDeterministicParaIdsInState(EditorState.create({
|
|
715
|
+
schema,
|
|
716
|
+
doc: story.type === "header" || story.type === "footer" ? headerFooterToProseDoc(source.content, conversionOptions) : footnoteToProseDoc(source.content, conversionOptions),
|
|
717
|
+
plugins: singletonManager.getPlugins()
|
|
718
|
+
}));
|
|
719
|
+
this.secondaryStoryStates.set(key, {
|
|
720
|
+
handle: story,
|
|
721
|
+
initialState: state,
|
|
722
|
+
state
|
|
723
|
+
});
|
|
724
|
+
return state;
|
|
725
|
+
}
|
|
726
|
+
requireEditableStoryState(story) {
|
|
727
|
+
const state = this.getEditableStoryState(story);
|
|
728
|
+
if (state) return state;
|
|
729
|
+
throw new FolioDocumentStoryNotFoundError({
|
|
730
|
+
message: `Document story ${JSON.stringify(story)} was not found.`,
|
|
731
|
+
story
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
setEditableStoryState(story, state) {
|
|
735
|
+
if (story.type === "main") {
|
|
736
|
+
this.state = state;
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
const entry = this.secondaryStoryStates.get(secondaryStoryKey(story));
|
|
740
|
+
if (!entry) throw new FolioDocumentStoryNotFoundError({
|
|
741
|
+
message: `Document story ${JSON.stringify(story)} was not found.`,
|
|
742
|
+
story
|
|
743
|
+
});
|
|
744
|
+
entry.state = state;
|
|
745
|
+
}
|
|
746
|
+
getHeaderFooterStory(story) {
|
|
747
|
+
return (story.type === "header" ? this.baseDocument.package.headers : this.baseDocument.package.footers)?.get(story.relationshipId);
|
|
748
|
+
}
|
|
749
|
+
getNoteStory(story) {
|
|
750
|
+
if (story.type === "footnote") return this.baseDocument.package.footnotes?.find((note) => note.id === story.noteId && !isSeparatorFootnote(note));
|
|
751
|
+
return this.baseDocument.package.endnotes?.find((note) => note.id === story.noteId && !isSeparatorEndnote(note));
|
|
752
|
+
}
|
|
753
|
+
getHeaderFooterStoryText(story, source) {
|
|
754
|
+
const state = this.secondaryStoryStates.get(headerFooterStoryKey(story))?.state;
|
|
755
|
+
return normalizeFolioAIBlockText(state?.doc.textContent ?? getHeaderFooterText(source));
|
|
756
|
+
}
|
|
757
|
+
getNoteStoryText(story, source) {
|
|
758
|
+
const state = this.secondaryStoryStates.get(noteStoryKey(story))?.state;
|
|
759
|
+
const sourceText = source.type === "footnote" ? getFootnoteText(source) : getEndnoteText(source);
|
|
760
|
+
return normalizeFolioAIBlockText(state?.doc.textContent ?? sourceText);
|
|
761
|
+
}
|
|
762
|
+
mergeEditedSecondaryStories(document) {
|
|
763
|
+
let headers;
|
|
764
|
+
let footers;
|
|
765
|
+
let footnotes;
|
|
766
|
+
let endnotes;
|
|
767
|
+
for (const entry of this.secondaryStoryStates.values()) {
|
|
768
|
+
if (entry.state === entry.initialState) continue;
|
|
769
|
+
if (entry.handle.type === "header" || entry.handle.type === "footer") {
|
|
770
|
+
const source = this.getHeaderFooterStory(entry.handle);
|
|
771
|
+
if (!source) continue;
|
|
772
|
+
const edited = {
|
|
773
|
+
...source,
|
|
774
|
+
content: proseDocToBlocks(entry.state.doc)
|
|
775
|
+
};
|
|
776
|
+
if (entry.handle.type === "header") {
|
|
777
|
+
headers ??= new Map(document.package.headers);
|
|
778
|
+
headers.set(entry.handle.relationshipId, edited);
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
footers ??= new Map(document.package.footers);
|
|
782
|
+
footers.set(entry.handle.relationshipId, edited);
|
|
783
|
+
continue;
|
|
784
|
+
}
|
|
785
|
+
if (entry.handle.type === "footnote") {
|
|
786
|
+
const { noteId } = entry.handle;
|
|
787
|
+
const source = this.baseDocument.package.footnotes?.find((note) => note.id === noteId && !isSeparatorFootnote(note));
|
|
788
|
+
if (!source) continue;
|
|
789
|
+
const edited = {
|
|
790
|
+
...source,
|
|
791
|
+
content: proseDocToBlocks(entry.state.doc)
|
|
792
|
+
};
|
|
793
|
+
footnotes ??= [...document.package.footnotes ?? []];
|
|
794
|
+
const index = footnotes.findIndex((note) => note.id === noteId);
|
|
795
|
+
if (index !== -1) footnotes[index] = edited;
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
const { noteId } = entry.handle;
|
|
799
|
+
const source = this.baseDocument.package.endnotes?.find((note) => note.id === noteId && !isSeparatorEndnote(note));
|
|
800
|
+
if (!source) continue;
|
|
801
|
+
const edited = {
|
|
802
|
+
...source,
|
|
803
|
+
content: proseDocToBlocks(entry.state.doc)
|
|
804
|
+
};
|
|
805
|
+
endnotes ??= [...document.package.endnotes ?? []];
|
|
806
|
+
const index = endnotes.findIndex((note) => note.id === noteId);
|
|
807
|
+
if (index !== -1) endnotes[index] = edited;
|
|
808
|
+
}
|
|
809
|
+
if (headers) document.package.headers = headers;
|
|
810
|
+
if (footers) document.package.footers = footers;
|
|
811
|
+
if (footnotes) document.package.footnotes = footnotes;
|
|
812
|
+
if (endnotes) document.package.endnotes = endnotes;
|
|
813
|
+
}
|
|
575
814
|
/**
|
|
576
815
|
* Count every tracked change an accept-all / reject-all sweep resolves: the
|
|
577
816
|
* inline insertion / deletion groups {@link getChanges} enumerates, plus the
|
|
@@ -654,4 +893,4 @@ const applyFolioAIEditsToBuffer = async (buffer, operations, options = {}) => {
|
|
|
654
893
|
};
|
|
655
894
|
};
|
|
656
895
|
//#endregion
|
|
657
|
-
export { FolioDocxReviewer, applyFolioAIEditsToBuffer };
|
|
896
|
+
export { FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioDocumentStoryNotFoundError, FolioDocxReviewer, UnsupportedFolioReviewedViewError, applyFolioAIEditsToBuffer, isFolioResolvedReviewedView, isFolioReviewedView };
|
package/dist/ai-edits/index.d.ts
CHANGED
|
@@ -2,11 +2,11 @@ import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreview
|
|
|
2
2
|
import { FolioAIEditView, applyFolioAIEditOperations } from "./apply.js";
|
|
3
3
|
import { DocPositionRange, clampRangeToDocSize, resolveFolioAIBlockRange, resolveFolioAITextRange } from "./blockRange.js";
|
|
4
4
|
import { buildAnnotatedBlockText } from "./clean-text.js";
|
|
5
|
-
import { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationStatus, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "../document-operations.js";
|
|
5
|
+
import { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "../document-operations.js";
|
|
6
6
|
import { FolioCommentAnchor, FolioReviewChange, FolioReviewChangeKind, getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
|
|
7
|
-
import { FolioDocumentStory, FolioDocumentStoryHandle } from "./headless.js";
|
|
7
|
+
import { FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioApplyDocumentOperationsToStoryOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioEditableDocumentStoryHandle, FolioReadReviewedStoryOptions, FolioResolveReviewedStoryOptions, FolioResolvedReviewedView, FolioReviewedStory, FolioReviewedView, UnsupportedFolioReviewedViewError, isFolioResolvedReviewedView, isFolioReviewedView } from "./headless.js";
|
|
8
8
|
import { createFolioAIEditSnapshot, createFolioAITextRangeHandle, hashFolioAIBlockText, normalizeFolioAIBlockText } from "./snapshot.js";
|
|
9
9
|
import { getFolioDocumentOutline, readFolioDocumentSection } from "./scoped-reading.js";
|
|
10
10
|
import { getFolioParaIdFromBlockId } from "../types/block-id.js";
|
|
11
11
|
import { WordDiffSegment, diffWordSegments } from "./word-diff.js";
|
|
12
|
-
export { type ApplyFolioDocumentOperationsOptions, type DocPositionRange, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIEditView, type FolioAIInlineFormatting, type FolioAISignatureParty, type FolioAITextRangeHandle, type FolioCommentAnchor, type FolioDocumentNavigationTarget, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationStatus, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocumentOutline, type FolioDocumentOutlineEntry, type FolioDocumentSection, type FolioDocumentSectionHandle, type FolioDocumentSectionReadResult, type FolioDocumentStory, type FolioDocumentStoryHandle, type FolioReviewChange, type FolioReviewChangeKind, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, type WordDiffSegment, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, clampRangeToDocSize, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, readFolioDocumentSection, resolveFolioAIBlockRange, resolveFolioAITextRange };
|
|
12
|
+
export { type ApplyFolioDocumentOperationsOptions, type DocPositionRange, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIEditView, type FolioAIInlineFormatting, type FolioAISignatureParty, type FolioAITextRangeHandle, type FolioApplyDocumentOperationsToStoryOptions, type FolioCommentAnchor, type FolioDocumentNavigationTarget, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocumentOutline, type FolioDocumentOutlineEntry, type FolioDocumentSection, type FolioDocumentSectionHandle, type FolioDocumentSectionReadResult, type FolioDocumentStory, type FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, type FolioEditableDocumentStoryHandle, type FolioReadReviewedStoryOptions, type FolioResolveReviewedStoryOptions, type FolioResolvedReviewedView, type FolioReviewChange, type FolioReviewChangeKind, type FolioReviewedStory, type FolioReviewedView, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioReviewedViewError, type WordDiffSegment, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, clampRangeToDocSize, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioDocumentOperationModeSupported, isFolioResolvedReviewedView, isFolioReviewedView, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, readFolioDocumentSection, resolveFolioAIBlockRange, resolveFolioAITextRange };
|
package/dist/ai-edits/index.js
CHANGED
|
@@ -7,4 +7,5 @@ import { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT
|
|
|
7
7
|
import { clampRangeToDocSize, resolveFolioAIBlockRange, resolveFolioAITextRange } from "./blockRange.js";
|
|
8
8
|
import { getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
|
|
9
9
|
import { getFolioDocumentOutline, readFolioDocumentSection } from "./scoped-reading.js";
|
|
10
|
-
|
|
10
|
+
import { FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioDocumentStoryNotFoundError, UnsupportedFolioReviewedViewError, isFolioResolvedReviewedView, isFolioReviewedView } from "./headless.js";
|
|
11
|
+
export { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioDocumentStoryNotFoundError, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioReviewedViewError, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, clampRangeToDocSize, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioDocumentOperationModeSupported, isFolioResolvedReviewedView, isFolioReviewedView, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, readFolioDocumentSection, resolveFolioAIBlockRange, resolveFolioAITextRange };
|
|
@@ -22,12 +22,28 @@ const createFolioAIEditSnapshot = (doc) => {
|
|
|
22
22
|
const draftBlocks = [];
|
|
23
23
|
const hashCounts = /* @__PURE__ */ new Map();
|
|
24
24
|
const usedBlockIds = /* @__PURE__ */ new Set();
|
|
25
|
+
const emptyAnchorState = {
|
|
26
|
+
candidate: null,
|
|
27
|
+
textblockCount: 0
|
|
28
|
+
};
|
|
25
29
|
let blockIndex = 0;
|
|
26
|
-
doc.descendants((node, pos) => {
|
|
30
|
+
doc.descendants((node, pos, parent) => {
|
|
27
31
|
if (!node.isTextblock) return;
|
|
28
32
|
const { text } = buildCleanBlockText(node, pos);
|
|
29
33
|
const normalizedText = normalizeFolioAIBlockText(text);
|
|
30
|
-
if (normalizedText.length === 0)
|
|
34
|
+
if (normalizedText.length === 0) {
|
|
35
|
+
if (parent?.type !== doc.type) return;
|
|
36
|
+
emptyAnchorState.textblockCount++;
|
|
37
|
+
if (emptyAnchorState.candidate === null) {
|
|
38
|
+
const paraIdAttr = node.attrs["paraId"];
|
|
39
|
+
emptyAnchorState.candidate = {
|
|
40
|
+
from: pos,
|
|
41
|
+
to: pos + node.nodeSize,
|
|
42
|
+
paraId: typeof paraIdAttr === "string" && paraIdAttr.length > 0 ? paraIdAttr : null
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
31
47
|
const textHash = hashFolioAIBlockText(normalizedText);
|
|
32
48
|
hashCounts.set(textHash, (hashCounts.get(textHash) ?? 0) + 1);
|
|
33
49
|
blockIndex++;
|
|
@@ -72,10 +88,31 @@ const createFolioAIEditSnapshot = (doc) => {
|
|
|
72
88
|
hashOccurrenceCount: hashCounts.get(draft.anchor.textHash) ?? 0
|
|
73
89
|
};
|
|
74
90
|
}
|
|
75
|
-
|
|
91
|
+
const emptyAnchorCandidate = emptyAnchorState.candidate;
|
|
92
|
+
if (blocks.length > 0 || emptyAnchorCandidate === null) return {
|
|
76
93
|
blocks,
|
|
77
94
|
anchors
|
|
78
95
|
};
|
|
96
|
+
const emptyDocumentAnchorId = deriveBlockId({
|
|
97
|
+
paraId: emptyAnchorCandidate.paraId,
|
|
98
|
+
index: 1,
|
|
99
|
+
taken: usedBlockIds
|
|
100
|
+
});
|
|
101
|
+
const normalizedText = "";
|
|
102
|
+
anchors[emptyDocumentAnchorId] = {
|
|
103
|
+
id: emptyDocumentAnchorId,
|
|
104
|
+
from: emptyAnchorCandidate.from,
|
|
105
|
+
to: emptyAnchorCandidate.to,
|
|
106
|
+
text: "",
|
|
107
|
+
normalizedText,
|
|
108
|
+
textHash: hashFolioAIBlockText(normalizedText),
|
|
109
|
+
hashOccurrenceCount: emptyAnchorState.textblockCount
|
|
110
|
+
};
|
|
111
|
+
return {
|
|
112
|
+
blocks,
|
|
113
|
+
anchors,
|
|
114
|
+
emptyDocumentAnchorId
|
|
115
|
+
};
|
|
79
116
|
};
|
|
80
117
|
const getBlockKind = (node, headingLevel) => {
|
|
81
118
|
const listMarker = node.attrs["listMarker"];
|
package/dist/ai-edits/types.d.ts
CHANGED
|
@@ -21,7 +21,8 @@ type FolioAIBlock = {
|
|
|
21
21
|
};
|
|
22
22
|
type FolioAIEditSnapshot = {
|
|
23
23
|
blocks: FolioAIBlock[];
|
|
24
|
-
anchors: Record<string, FolioAIBlockAnchor>;
|
|
24
|
+
anchors: Record<string, FolioAIBlockAnchor>; /** Hidden empty paragraph used to anchor insertions when `blocks` is empty. */
|
|
25
|
+
emptyDocumentAnchorId?: string;
|
|
25
26
|
};
|
|
26
27
|
type FolioAIBlockAnchor = {
|
|
27
28
|
id: string;
|