@beyondwork/docx-react-component 1.0.42 → 1.0.45

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.
Files changed (82) hide show
  1. package/README.md +17 -0
  2. package/package.json +5 -4
  3. package/src/api/editor-state-types.ts +110 -0
  4. package/src/api/public-types.ts +333 -4
  5. package/src/core/commands/formatting-commands.ts +7 -1
  6. package/src/core/commands/index.ts +60 -10
  7. package/src/core/commands/text-commands.ts +59 -0
  8. package/src/core/search/search-text.ts +15 -2
  9. package/src/core/selection/review-anchors.ts +131 -21
  10. package/src/index.ts +29 -1
  11. package/src/io/chart-preview-resolver.ts +281 -0
  12. package/src/io/docx-session.ts +692 -2
  13. package/src/io/export/build-app-properties-xml.ts +1 -1
  14. package/src/io/export/serialize-comments.ts +38 -9
  15. package/src/io/export/twip.ts +1 -1
  16. package/src/io/load-scheduler.ts +230 -0
  17. package/src/io/normalize/normalize-text.ts +116 -0
  18. package/src/io/ooxml/parse-comments.ts +0 -33
  19. package/src/io/ooxml/parse-complex-content.ts +14 -0
  20. package/src/io/ooxml/parse-main-document.ts +4 -0
  21. package/src/io/ooxml/workflow-payload-validator.ts +97 -1
  22. package/src/io/ooxml/workflow-payload.ts +172 -1
  23. package/src/preservation/opaque-region.ts +5 -0
  24. package/src/review/store/comment-remapping.ts +2 -2
  25. package/src/runtime/collab-session.ts +1 -1
  26. package/src/runtime/document-runtime.ts +661 -42
  27. package/src/runtime/edit-dispatch/dispatch-text-command.ts +98 -0
  28. package/src/runtime/edit-dispatch/index.ts +2 -0
  29. package/src/runtime/edit-dispatch/list-aware-dispatch.ts +125 -0
  30. package/src/runtime/editor-state-channel.ts +544 -0
  31. package/src/runtime/editor-state-integration.ts +217 -0
  32. package/src/runtime/editor-surface/capabilities.ts +411 -0
  33. package/src/runtime/layout/index.ts +2 -0
  34. package/src/runtime/layout/inert-layout-facet.ts +4 -0
  35. package/src/runtime/layout/layout-engine-instance.ts +63 -2
  36. package/src/runtime/layout/layout-engine-version.ts +41 -0
  37. package/src/runtime/layout/paginated-layout-engine.ts +211 -14
  38. package/src/runtime/layout/public-facet.ts +430 -1
  39. package/src/runtime/perf-counters.ts +28 -0
  40. package/src/runtime/prerender/cache-envelope.ts +29 -0
  41. package/src/runtime/prerender/cache-key.ts +66 -0
  42. package/src/runtime/prerender/font-fingerprint.ts +17 -0
  43. package/src/runtime/prerender/graph-canonicalize.ts +121 -0
  44. package/src/runtime/prerender/indexeddb-cache.ts +184 -0
  45. package/src/runtime/prerender/prerender-document.ts +145 -0
  46. package/src/runtime/render/block-fragment-projection.ts +2 -0
  47. package/src/runtime/render/render-frame-types.ts +17 -0
  48. package/src/runtime/render/render-kernel.ts +172 -29
  49. package/src/runtime/selection/post-edit-validator.ts +77 -0
  50. package/src/runtime/surface-projection.ts +45 -7
  51. package/src/runtime/workflow-markup.ts +71 -16
  52. package/src/ui/WordReviewEditor.tsx +142 -237
  53. package/src/ui/editor-command-bag.ts +14 -0
  54. package/src/ui/editor-runtime-boundary.ts +115 -12
  55. package/src/ui/editor-shell-view.tsx +10 -0
  56. package/src/ui/editor-surface-controller.tsx +5 -0
  57. package/src/ui/headless/selection-helpers.ts +10 -0
  58. package/src/ui/runtime-shortcut-dispatch.ts +28 -68
  59. package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +62 -2
  60. package/src/ui-tailwind/chrome/collab-top-nav-container.tsx +281 -0
  61. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +48 -0
  62. package/src/ui-tailwind/editor-surface/paste-plain-text.ts +72 -0
  63. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +118 -8
  64. package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +76 -165
  65. package/src/ui-tailwind/editor-surface/pm-schema.ts +170 -4
  66. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +58 -7
  67. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -0
  68. package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +265 -0
  69. package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +8 -255
  70. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +47 -0
  71. package/src/ui-tailwind/index.ts +5 -1
  72. package/src/ui-tailwind/page-stack/tw-endnote-area.tsx +57 -0
  73. package/src/ui-tailwind/page-stack/tw-footnote-area.tsx +71 -0
  74. package/src/ui-tailwind/page-stack/tw-page-footer-band.tsx +73 -0
  75. package/src/ui-tailwind/page-stack/tw-page-header-band.tsx +74 -0
  76. package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +477 -0
  77. package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +374 -0
  78. package/src/ui-tailwind/page-stack/use-visible-block-range.ts +157 -0
  79. package/src/ui-tailwind/review/comment-markdown-renderer.tsx +155 -0
  80. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +77 -16
  81. package/src/ui-tailwind/theme/editor-theme.css +47 -14
  82. package/src/ui-tailwind/tw-review-workspace.tsx +303 -123
@@ -19,7 +19,14 @@ export type ValidatorIssueCode =
19
19
  | "external_field_body_ignored"
20
20
  | "internal_entry_unexpected_storage_ref"
21
21
  | "internal_field_unexpected_storage_ref"
22
- | "unsupported_version";
22
+ | "unsupported_version"
23
+ // Schema 1.2 editor-state codes
24
+ | "editor_state_unknown_namespace"
25
+ | "editor_state_duplicate_content"
26
+ | "editor_state_empty_content"
27
+ | "editor_state_invalid_inline_json"
28
+ | "editor_state_missing_entry_key"
29
+ | "editor_state_invalid_location";
23
30
 
24
31
  export type ValidatorIssueSeverity = "error" | "warning";
25
32
 
@@ -145,6 +152,95 @@ export function validateWorkflowPayloadEnvelope(xml: string): ValidatorIssue[] {
145
152
  }
146
153
  }
147
154
 
155
+ // Schema 1.2: <bw:editorState> checks.
156
+ const editorStateMatch = xml.match(/<bw:editorState\b[^>]*>([\s\S]*?)<\/bw:editorState>/u);
157
+ if (editorStateMatch) {
158
+ const editorStateBody = editorStateMatch[1] ?? "";
159
+ const nsRe = /<bw:namespace\b([^>]*)>([\s\S]*?)<\/bw:namespace>/gu;
160
+ for (const nsMatch of editorStateBody.matchAll(nsRe)) {
161
+ const attrsStr = nsMatch[1] ?? "";
162
+ const nsBody = nsMatch[2] ?? "";
163
+ const attrs = parseAttrs(attrsStr);
164
+ const name = attrs.name ?? "";
165
+ const nsPath = `bw:editorState/bw:namespace[@name='${name}']`;
166
+
167
+ // 1. Unknown namespace name (not in closed set) → warning.
168
+ const knownNames = ["hostAnnotations", "workflowOverlay", "workflowMetadata", "workItems"];
169
+ if (!knownNames.includes(name)) {
170
+ issues.push({
171
+ code: "editor_state_unknown_namespace",
172
+ path: nsPath,
173
+ severity: "warning",
174
+ value: name,
175
+ });
176
+ // Still check structural rules below for forward-compat awareness.
177
+ }
178
+
179
+ // 2. Detect presence of storageRef and inline.
180
+ const hasStorageRef = /<bw:storageRef\b/u.test(nsBody);
181
+ const hasInline = /<bw:inline\b/u.test(nsBody);
182
+
183
+ if (hasStorageRef && hasInline) {
184
+ // Both present → duplicate_content error.
185
+ issues.push({
186
+ code: "editor_state_duplicate_content",
187
+ path: nsPath,
188
+ severity: "error",
189
+ });
190
+ } else if (!hasStorageRef && !hasInline) {
191
+ // Neither present → empty_content error.
192
+ issues.push({
193
+ code: "editor_state_empty_content",
194
+ path: nsPath,
195
+ severity: "error",
196
+ });
197
+ } else if (hasStorageRef) {
198
+ // 3a. storageRef checks.
199
+ const refMatch = nsBody.match(/<bw:storageRef\b([^>]*)\/>/u);
200
+ if (refMatch) {
201
+ const refAttrs = parseAttrs(refMatch[1] ?? "");
202
+ const entryKey = refAttrs.entryKey ?? "";
203
+ const location = refAttrs.location ?? "";
204
+
205
+ if (entryKey === "") {
206
+ issues.push({
207
+ code: "editor_state_missing_entry_key",
208
+ path: `${nsPath}/bw:storageRef`,
209
+ severity: "error",
210
+ });
211
+ }
212
+
213
+ const knownLocations = ["rowstore", "key-only"];
214
+ if (location !== "" && !knownLocations.includes(location)) {
215
+ issues.push({
216
+ code: "editor_state_invalid_location",
217
+ path: `${nsPath}/bw:storageRef/@location`,
218
+ severity: "warning",
219
+ value: location,
220
+ });
221
+ }
222
+ }
223
+ } else if (hasInline) {
224
+ // 3b. inline JSON parse check.
225
+ const inlineMatch = nsBody.match(/<bw:inline\b[^>]*>([\s\S]*?)<\/bw:inline>/u);
226
+ if (inlineMatch) {
227
+ const rawContent = inlineMatch[1] ?? "";
228
+ // Strip CDATA markers (handles split CDATA too).
229
+ const text = rawContent.replace(/<!\[CDATA\[|\]\]>/g, "").trim();
230
+ try {
231
+ JSON.parse(text);
232
+ } catch {
233
+ issues.push({
234
+ code: "editor_state_invalid_inline_json",
235
+ path: `${nsPath}/bw:inline`,
236
+ severity: "error",
237
+ });
238
+ }
239
+ }
240
+ }
241
+ }
242
+ }
243
+
148
244
  // Top-level entries (bw:metadata body, outside scopes).
149
245
  // These resolve against overlay-only (no scope context available unless
150
246
  // the entry carries its own `scope="scope:{id}"` attribute, in which case
@@ -12,11 +12,36 @@ import type {
12
12
  WorkflowScope,
13
13
  WorkflowWorkItem,
14
14
  } from "../../api/public-types.ts";
15
+ import type { EditorStateNamespace, EditorStateLocation } from "../../api/editor-state-types.ts";
15
16
  import {
16
17
  validateWorkflowPayloadEnvelope,
17
18
  type ValidatorIssue,
18
19
  } from "./workflow-payload-validator.ts";
19
20
 
21
+ // ---------------------------------------------------------------------------
22
+ // Schema 1.2 editor-state types (edge-of-module shape, channel-free)
23
+ // ---------------------------------------------------------------------------
24
+
25
+ export interface EditorStatePayloadNamespaceEntry {
26
+ namespace: EditorStateNamespace;
27
+ schemaVersion: string;
28
+ /** JSON-serializable blob (CDATA-wrapped on serialize). Exactly one of inline/storageRef. */
29
+ inline?: unknown;
30
+ storageRef?: { location: Exclude<EditorStateLocation, "in-document">; entryKey: string };
31
+ /**
32
+ * Parser-internal flag: set when the `<bw:inline>` CDATA block contained
33
+ * malformed JSON. Hydration surfaces this as `editor_state_part_load_failed`
34
+ * rather than silently dropping the entry.
35
+ */
36
+ malformedInline?: boolean;
37
+ }
38
+
39
+ export interface EditorStatePayload {
40
+ entries: EditorStatePayloadNamespaceEntry[];
41
+ /** Unknown namespaces preserved for round-trip — raw XML fragment per entry, keyed by name. */
42
+ unknownNamespaces?: Array<{ name: string; rawXml: string }>;
43
+ }
44
+
20
45
  // ---------------------------------------------------------------------------
21
46
  // Schema 1.1 parser helpers (fail-closed per spec §8.2)
22
47
  // ---------------------------------------------------------------------------
@@ -62,6 +87,8 @@ type EmbeddedWorkflowPayloadDescriptor = {
62
87
  export interface WorkflowPayloadEnvelope {
63
88
  workflowMetadata: WorkflowMetadataSnapshot;
64
89
  workflowOverlay?: WorkflowOverlay;
90
+ /** Present only when the payload was version 1.2+ and carried a non-empty <bw:editorState> block. */
91
+ editorState?: EditorStatePayload;
65
92
  validatorIssues?: readonly ValidatorIssue[];
66
93
  }
67
94
 
@@ -87,6 +114,8 @@ export function buildWorkflowPayloadParts(input: {
87
114
  sourcePackage: OpcPackage;
88
115
  workflowMetadata: WorkflowMetadataSnapshot | undefined;
89
116
  workflowOverlay?: WorkflowOverlay;
117
+ /** Optional schema 1.2 editor-state block. Omitted when empty. */
118
+ editorState?: EditorStatePayload;
90
119
  documentId: string;
91
120
  createdAt: string;
92
121
  updatedAt: string;
@@ -126,6 +155,7 @@ export function buildWorkflowPayloadParts(input: {
126
155
  producerVersion: input.producerVersion,
127
156
  workflowMetadata,
128
157
  workflowOverlay: input.workflowOverlay,
158
+ editorState: input.editorState,
129
159
  preservedExtensionsXml: getPreservedExtensionsXml(input.sourcePackage, descriptor.payloadPartPath),
130
160
  });
131
161
  const itemPropsXml = buildItemPropsXml(descriptor.itemId);
@@ -238,12 +268,14 @@ export function parseWorkflowPayloadEnvelopeFromPackage(
238
268
  }
239
269
 
240
270
  const validatorIssues = validateWorkflowPayloadEnvelope(xml);
271
+ const editorState = parseEditorStateXml(xml);
241
272
  return {
242
273
  workflowMetadata: {
243
274
  definitions,
244
275
  entries,
245
276
  },
246
277
  workflowOverlay: parseWorkflowOverlay(xml),
278
+ ...(editorState !== undefined ? { editorState } : {}),
247
279
  ...(validatorIssues.length > 0 ? { validatorIssues } : {}),
248
280
  };
249
281
  }
@@ -287,6 +319,136 @@ function needsSchemaV11(input: {
287
319
  return false;
288
320
  }
289
321
 
322
+ /** Returns true when the payload has at least one namespace entry or unknown namespace to emit. */
323
+ function hasNonEmptyEditorState(es: EditorStatePayload | undefined): boolean {
324
+ if (!es) return false;
325
+ return (es.entries.length > 0) || ((es.unknownNamespaces?.length ?? 0) > 0);
326
+ }
327
+
328
+ // ---------------------------------------------------------------------------
329
+ // Known namespace names (closed set for round-trip; unknown names are opaque)
330
+ // ---------------------------------------------------------------------------
331
+
332
+ const KNOWN_EDITOR_STATE_NAMESPACES: readonly EditorStateNamespace[] = [
333
+ "hostAnnotations",
334
+ "workflowOverlay",
335
+ "workflowMetadata",
336
+ "workItems",
337
+ ];
338
+
339
+ const KNOWN_STORAGE_LOCATIONS: ReadonlyArray<Exclude<EditorStateLocation, "in-document">> = [
340
+ "rowstore",
341
+ "key-only",
342
+ ];
343
+
344
+ /**
345
+ * Escapes `]]>` inside CDATA content using the standard XML split:
346
+ * `]]>` → `]]]]><![CDATA[>`
347
+ */
348
+ function escapeCdata(text: string): string {
349
+ return text.replace(/\]\]>/g, "]]]]><![CDATA[>");
350
+ }
351
+
352
+ /**
353
+ * Builds the `<bw:editorState>…</bw:editorState>` block.
354
+ * Returns an empty string when both entries and unknownNamespaces are empty.
355
+ */
356
+ export function buildEditorStateXml(payload: EditorStatePayload): string {
357
+ if (!hasNonEmptyEditorState(payload)) {
358
+ return "";
359
+ }
360
+
361
+ const knownLines = payload.entries.map((entry) => {
362
+ const nsOpen = `<bw:namespace name="${escapeXml(entry.namespace)}" schemaVersion="${escapeXml(entry.schemaVersion)}">`;
363
+ let content: string;
364
+ if (entry.storageRef !== undefined) {
365
+ content = `<bw:storageRef location="${escapeXml(entry.storageRef.location)}" entryKey="${escapeXml(entry.storageRef.entryKey)}" />`;
366
+ } else {
367
+ const json = escapeCdata(JSON.stringify(entry.inline));
368
+ content = `<bw:inline><![CDATA[${json}]]></bw:inline>`;
369
+ }
370
+ return `${nsOpen}${content}</bw:namespace>`;
371
+ });
372
+
373
+ const unknownLines = (payload.unknownNamespaces ?? []).map((u) => u.rawXml);
374
+
375
+ const innerXml = [...knownLines, ...unknownLines].join("\n");
376
+ return `<bw:editorState>\n${indentLines(innerXml, 2)}\n</bw:editorState>`;
377
+ }
378
+
379
+ /**
380
+ * Parses the `<bw:editorState>` block from a full workflow payload XML string.
381
+ * Returns `undefined` when no block is present.
382
+ * Malformed JSON is silently skipped (validator flags it separately).
383
+ */
384
+ export function parseEditorStateXml(xml: string): EditorStatePayload | undefined {
385
+ const blockMatch = xml.match(/<bw:editorState\b[^>]*>([\s\S]*?)<\/bw:editorState>/u);
386
+ if (!blockMatch) {
387
+ return undefined;
388
+ }
389
+ const blockBody = blockMatch[1] ?? "";
390
+
391
+ const entries: EditorStatePayloadNamespaceEntry[] = [];
392
+ const unknownNamespaces: Array<{ name: string; rawXml: string }> = [];
393
+
394
+ // Match each <bw:namespace ... > ... </bw:namespace>
395
+ const nsRe = /<bw:namespace\b([^>]*)>([\s\S]*?)<\/bw:namespace>/gu;
396
+ for (const nsMatch of blockBody.matchAll(nsRe)) {
397
+ const attrsStr = nsMatch[1] ?? "";
398
+ const nsBody = nsMatch[2] ?? "";
399
+ const rawXml = nsMatch[0] ?? "";
400
+ const attrs = parseAttributes(attrsStr);
401
+ const name = attrs.name ?? "";
402
+ const schemaVersion = attrs.schemaVersion ?? "";
403
+
404
+ if (!(KNOWN_EDITOR_STATE_NAMESPACES as readonly string[]).includes(name)) {
405
+ unknownNamespaces.push({ name, rawXml });
406
+ continue;
407
+ }
408
+
409
+ const namespace = name as EditorStateNamespace;
410
+
411
+ // Parse <bw:storageRef ... />
412
+ const storageRefMatch = nsBody.match(/<bw:storageRef\b([^>]*)\/>/u);
413
+ if (storageRefMatch) {
414
+ const refAttrs = parseAttributes(storageRefMatch[1] ?? "");
415
+ const location = refAttrs.location as Exclude<EditorStateLocation, "in-document"> | undefined;
416
+ const entryKey = refAttrs.entryKey ?? "";
417
+ entries.push({
418
+ namespace,
419
+ schemaVersion,
420
+ storageRef: { location: location ?? "rowstore", entryKey },
421
+ });
422
+ continue;
423
+ }
424
+
425
+ // Parse <bw:inline>...</bw:inline> — extract CDATA content
426
+ const inlineMatch = nsBody.match(/<bw:inline\b[^>]*>([\s\S]*?)<\/bw:inline>/u);
427
+ if (inlineMatch) {
428
+ const raw = inlineMatch[1] ?? "";
429
+ // Extract CDATA content — handles split CDATA sections from ]]> escaping
430
+ const cdataText = raw.replace(/<!\[CDATA\[|\]\]>/g, "").trim();
431
+ try {
432
+ const parsed = JSON.parse(cdataText) as unknown;
433
+ entries.push({ namespace, schemaVersion, inline: parsed });
434
+ } catch {
435
+ // Malformed JSON: surface to the runtime so the host sees a
436
+ // load-failure event rather than silently losing the entry.
437
+ entries.push({ namespace, schemaVersion, malformedInline: true });
438
+ }
439
+ }
440
+ }
441
+
442
+ if (entries.length === 0 && unknownNamespaces.length === 0) {
443
+ return undefined;
444
+ }
445
+
446
+ return {
447
+ entries,
448
+ ...(unknownNamespaces.length > 0 ? { unknownNamespaces } : {}),
449
+ };
450
+ }
451
+
290
452
  function buildPayloadXml(input: {
291
453
  descriptor: EmbeddedWorkflowPayloadDescriptor;
292
454
  createdAt: string;
@@ -294,9 +456,15 @@ function buildPayloadXml(input: {
294
456
  producerVersion: string;
295
457
  workflowMetadata: WorkflowMetadataSnapshot;
296
458
  workflowOverlay?: WorkflowOverlay;
459
+ editorState?: EditorStatePayload;
297
460
  preservedExtensionsXml: string;
298
461
  }): string {
299
- const schemaVersion = needsSchemaV11(input) ? "1.1" : "1.0";
462
+ const hasEditorState = hasNonEmptyEditorState(input.editorState);
463
+ const schemaVersion = hasEditorState
464
+ ? "1.2"
465
+ : needsSchemaV11(input)
466
+ ? "1.1"
467
+ : "1.0";
300
468
 
301
469
  const definitionEntriesXml = input.workflowMetadata.definitions
302
470
  .map((definition) => [
@@ -351,6 +519,8 @@ function buildPayloadXml(input: {
351
519
  .filter((value) => value.trim().length > 0)
352
520
  .join("\n");
353
521
 
522
+ const editorStateXml = hasEditorState ? buildEditorStateXml(input.editorState!) : "";
523
+
354
524
  return [
355
525
  `<?xml version="1.0" encoding="UTF-8"?>`,
356
526
  `<bw:workflowPayload xmlns:bw="urn:beyondwork:workflow-payload:1" version="${schemaVersion}" payloadId="${escapeXml(input.descriptor.payloadId)}" itemId="${escapeXml(input.descriptor.itemId)}" documentId="${escapeXml(input.descriptor.documentId)}" createdAt="${escapeXml(input.createdAt)}" updatedAt="${escapeXml(input.updatedAt)}">`,
@@ -362,6 +532,7 @@ function buildPayloadXml(input: {
362
532
  definitionEntriesXml ? indentLines(definitionEntriesXml, 4) : "",
363
533
  metadataEntriesXml ? indentLines(metadataEntriesXml, 4) : "",
364
534
  ` </bw:metadata>`,
535
+ editorStateXml,
365
536
  extensionsXml
366
537
  ? ` <bw:extensions>\n${indentLines(extensionsXml, 4)}\n </bw:extensions>`
367
538
  : ` <bw:extensions />`,
@@ -121,6 +121,11 @@ export function createOpaqueRegionSurfaceModel(
121
121
  export function createOpaqueRegionSurfaceModelFromSurfaceBlock(
122
122
  block: Extract<SurfaceBlockSnapshot, { kind: "opaque_block" }>,
123
123
  ): OpaqueRegionSurfaceModel {
124
+ if (block.state === "placeholder-culled") {
125
+ throw new Error(
126
+ "createOpaqueRegionSurfaceModelFromSurfaceBlock: placeholder-culled block has no real fragment",
127
+ );
128
+ }
124
129
  return {
125
130
  fragmentId: block.fragmentId,
126
131
  warningId: block.warningId,
@@ -4,7 +4,7 @@ import {
4
4
  getAnchorRange,
5
5
  mapReviewAnchor,
6
6
  mappingTouchesAnchorContent,
7
- rangeStaysWithinSingleParagraph,
7
+ rangeStaysWithinCommentableStory,
8
8
  type ReviewAnchor,
9
9
  } from "../../core/selection/review-anchors.ts";
10
10
  import type { TransactionMapping } from "../../core/selection/mapping.ts";
@@ -85,7 +85,7 @@ function normalizeCommentAnchor(
85
85
 
86
86
  if (
87
87
  mappedAnchor.kind === "range" &&
88
- !rangeStaysWithinSingleParagraph(nextContent, mappedAnchor.range)
88
+ !rangeStaysWithinCommentableStory(nextContent, mappedAnchor.range)
89
89
  ) {
90
90
  return detachReviewAnchor(previousRange, "invalidatedByStructureChange");
91
91
  }
@@ -64,7 +64,7 @@ export interface AttachPayloadArgs {
64
64
 
65
65
  export type SendToExternalCallArgs = Omit<
66
66
  RuntimeSendToExternalArgs,
67
- "bridge" | "tamperGate" | "signer" | "role"
67
+ "bridge" | "tamperGate" | "signer" | "role" | "resolver"
68
68
  > & {
69
69
  role?: "author" | "reviewer" | "observer";
70
70
  };