@beyondwork/docx-react-component 1.0.105 → 1.0.108

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 (193) hide show
  1. package/package.json +19 -5
  2. package/src/api/geometry-overlay-rects.ts +5 -0
  3. package/src/api/package-version.ts +1 -1
  4. package/src/api/page-anchor-id.ts +5 -0
  5. package/src/api/public-types.ts +16 -9
  6. package/src/api/table-node-specs.ts +6 -0
  7. package/src/api/v3/_create.ts +10 -2
  8. package/src/api/v3/_page-anchor-id.ts +52 -0
  9. package/src/api/v3/_runtime-handle.ts +92 -1
  10. package/src/api/v3/ai/_audit-reference.ts +28 -0
  11. package/src/api/v3/ai/_audit-time.ts +5 -0
  12. package/src/api/v3/ai/_pe2-evidence.ts +310 -6
  13. package/src/api/v3/ai/attach.ts +29 -4
  14. package/src/api/v3/ai/bundle.ts +6 -2
  15. package/src/api/v3/ai/inspect.ts +6 -2
  16. package/src/api/v3/ai/replacement.ts +112 -18
  17. package/src/api/v3/ai/resolve.ts +2 -2
  18. package/src/api/v3/ai/review.ts +177 -3
  19. package/src/api/v3/index.ts +8 -0
  20. package/src/api/v3/runtime/collab.ts +462 -0
  21. package/src/api/v3/runtime/document.ts +503 -20
  22. package/src/api/v3/runtime/geometry.ts +97 -0
  23. package/src/api/v3/runtime/layout.ts +744 -0
  24. package/src/api/v3/runtime/perf-probe.ts +14 -0
  25. package/src/api/v3/runtime/viewport.ts +9 -8
  26. package/src/api/v3/ui/_types.ts +202 -55
  27. package/src/api/v3/ui/chrome-preset-model.ts +5 -5
  28. package/src/api/v3/ui/debug.ts +115 -2
  29. package/src/api/v3/ui/index.ts +17 -0
  30. package/src/api/v3/ui/overlays.ts +0 -8
  31. package/src/api/v3/ui/surface.ts +56 -0
  32. package/src/api/v3/ui/viewport.ts +119 -9
  33. package/src/core/commands/image-commands.ts +1 -0
  34. package/src/core/commands/index.ts +6 -0
  35. package/src/core/schema/text-schema.ts +43 -5
  36. package/src/core/selection/mapping.ts +8 -1
  37. package/src/core/selection/review-anchors.ts +5 -1
  38. package/src/core/state/text-transaction.ts +8 -2
  39. package/src/io/export/serialize-revisions.ts +149 -1
  40. package/src/io/normalize/normalize-text.ts +6 -0
  41. package/src/io/ooxml/parse-bookmark-references.ts +55 -0
  42. package/src/io/ooxml/parse-fields.ts +24 -2
  43. package/src/io/ooxml/parse-headers-footers.ts +38 -5
  44. package/src/io/ooxml/parse-main-document.ts +153 -9
  45. package/src/io/ooxml/parse-numbering.ts +20 -0
  46. package/src/io/ooxml/parse-revisions.ts +19 -8
  47. package/src/io/opc/package-reader.ts +98 -8
  48. package/src/model/anchor.ts +4 -3
  49. package/src/model/canonical-document.ts +220 -2
  50. package/src/model/canonical-hash.ts +221 -0
  51. package/src/model/canonical-layout-inputs.ts +245 -6
  52. package/src/model/layout/index.ts +1 -0
  53. package/src/model/layout/page-graph-types.ts +147 -1
  54. package/src/model/review/revision-types.ts +14 -3
  55. package/src/preservation/store.ts +20 -4
  56. package/src/review/README.md +1 -1
  57. package/src/review/store/revision-actions.ts +14 -2
  58. package/src/runtime/collab/event-types.ts +67 -1
  59. package/src/runtime/collab/runtime-collab-sync.ts +177 -5
  60. package/src/runtime/diagnostics/layout-guard-warning.ts +18 -0
  61. package/src/runtime/document-heading-outline.ts +147 -0
  62. package/src/runtime/document-navigation.ts +8 -243
  63. package/src/runtime/document-runtime.ts +279 -115
  64. package/src/runtime/edit-dispatch/dispatch-text-command.ts +11 -0
  65. package/src/runtime/formatting/layout-inputs.ts +38 -5
  66. package/src/runtime/formatting/numbering/geometry.ts +28 -2
  67. package/src/runtime/geometry/adjacent-geometry-intake.ts +835 -0
  68. package/src/runtime/geometry/caret-geometry.ts +5 -6
  69. package/src/runtime/geometry/geometry-facet.ts +60 -10
  70. package/src/runtime/geometry/geometry-index.ts +661 -16
  71. package/src/runtime/geometry/geometry-types.ts +59 -0
  72. package/src/runtime/geometry/hit-test.ts +11 -1
  73. package/src/runtime/geometry/overlay-rects.ts +5 -3
  74. package/src/runtime/geometry/project-anchors.ts +1 -1
  75. package/src/runtime/geometry/word-layout-v2-line-intake.ts +323 -0
  76. package/src/runtime/layout/index.ts +6 -0
  77. package/src/runtime/layout/layout-engine-instance.ts +6 -1
  78. package/src/runtime/layout/layout-engine-version.ts +188 -16
  79. package/src/runtime/layout/layout-facet-types.ts +6 -0
  80. package/src/runtime/layout/page-graph.ts +23 -4
  81. package/src/runtime/layout/paginated-layout-engine.ts +149 -15
  82. package/src/runtime/layout/project-block-fragments.ts +351 -14
  83. package/src/runtime/layout/public-facet.ts +162 -24
  84. package/src/runtime/layout/table-row-continuation-contract.ts +107 -0
  85. package/src/runtime/layout/table-row-split.ts +92 -35
  86. package/src/runtime/prerender/cache-envelope.ts +2 -2
  87. package/src/runtime/prerender/cache-key.ts +5 -4
  88. package/src/runtime/prerender/customxml-cache.ts +0 -1
  89. package/src/runtime/render/render-kernel.ts +1 -1
  90. package/src/runtime/revision-runtime.ts +112 -10
  91. package/src/runtime/scopes/_scope-dependencies.ts +1 -0
  92. package/src/runtime/scopes/action-validation.ts +22 -2
  93. package/src/runtime/scopes/capabilities.ts +316 -0
  94. package/src/runtime/scopes/compile-scope-bundle.ts +14 -0
  95. package/src/runtime/scopes/compiler-service.ts +108 -4
  96. package/src/runtime/scopes/content-control-evidence.ts +79 -0
  97. package/src/runtime/scopes/create-issue.ts +5 -5
  98. package/src/runtime/scopes/evidence.ts +91 -0
  99. package/src/runtime/scopes/formatting/apply.ts +2 -0
  100. package/src/runtime/scopes/geometry-evidence.ts +130 -0
  101. package/src/runtime/scopes/index.ts +54 -0
  102. package/src/runtime/scopes/issue-lifecycle.ts +224 -0
  103. package/src/runtime/scopes/layout-evidence.ts +374 -0
  104. package/src/runtime/scopes/multi-paragraph-refusal.ts +37 -0
  105. package/src/runtime/scopes/preservation-boundary.ts +7 -1
  106. package/src/runtime/scopes/replacement/apply.ts +97 -34
  107. package/src/runtime/scopes/scope-kinds/paragraph.ts +108 -12
  108. package/src/runtime/scopes/semantic-scope-types.ts +242 -3
  109. package/src/runtime/scopes/visualization.ts +28 -0
  110. package/src/runtime/surface-projection.ts +44 -5
  111. package/src/runtime/telemetry/perf-probe.ts +216 -0
  112. package/src/runtime/virtualized-rendering.ts +36 -1
  113. package/src/runtime/workflow/ai-issue-lifecycle.ts +253 -0
  114. package/src/runtime/workflow/coordinator.ts +39 -11
  115. package/src/runtime/workflow/derived-scope-resolver.ts +63 -9
  116. package/src/runtime/workflow/index.ts +4 -0
  117. package/src/runtime/workflow/overlay-lane-types.ts +58 -0
  118. package/src/runtime/workflow/overlay-lanes.ts +386 -0
  119. package/src/runtime/workflow/overlay-store.ts +2 -2
  120. package/src/runtime/workflow/redline-posture-calibration.ts +257 -0
  121. package/src/runtime/workflow/word-field-matrix-calibration.ts +231 -0
  122. package/src/session/_sync-legacy.ts +17 -27
  123. package/src/session/import/loader.ts +6 -4
  124. package/src/session/import/source-package-evidence.ts +186 -2
  125. package/src/session/index.ts +5 -6
  126. package/src/session/session.ts +30 -56
  127. package/src/session/types.ts +8 -13
  128. package/src/shell/session-bootstrap.ts +155 -81
  129. package/src/ui/WordReviewEditor.tsx +520 -12
  130. package/src/ui/editor-shell-view.tsx +14 -4
  131. package/src/ui/editor-surface-controller.tsx +5 -3
  132. package/src/ui/headless/selection-tool-resolver.ts +1 -2
  133. package/src/ui/presence-overlay-lane.ts +130 -0
  134. package/src/ui/ui-controller-factory.ts +17 -0
  135. package/src/ui-tailwind/chrome/build-context-menu-entries.ts +5 -1
  136. package/src/ui-tailwind/chrome/editor-action-registry.ts +105 -5
  137. package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +7 -0
  138. package/src/ui-tailwind/chrome/layer-debug-contracts.ts +208 -0
  139. package/src/ui-tailwind/chrome/resolve-target-kind.ts +13 -0
  140. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +11 -3
  141. package/src/ui-tailwind/chrome/tw-command-palette.tsx +36 -6
  142. package/src/ui-tailwind/chrome/tw-context-menu.tsx +6 -1
  143. package/src/ui-tailwind/chrome/tw-display-mode-selector.tsx +42 -109
  144. package/src/ui-tailwind/chrome/tw-inline-find-bar.tsx +26 -6
  145. package/src/ui-tailwind/chrome/tw-navigation-command-bar.tsx +328 -0
  146. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +8 -4
  147. package/src/ui-tailwind/chrome/tw-runtime-repl-dialog.tsx +129 -1
  148. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +19 -5
  149. package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +5 -1
  150. package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +28 -12
  151. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +30 -3
  152. package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +116 -10
  153. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +223 -94
  154. package/src/ui-tailwind/chrome-overlay/tw-presence-overlay-lane.tsx +157 -0
  155. package/src/ui-tailwind/chrome-overlay/tw-review-overlay-lane-markers.tsx +259 -0
  156. package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +5 -2
  157. package/src/ui-tailwind/chrome-overlay/tw-substrate-overlay-lanes.tsx +314 -0
  158. package/src/ui-tailwind/debug/README.md +4 -1
  159. package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +272 -0
  160. package/src/ui-tailwind/debug/layer11-word-field-matrix-evidence.ts +160 -0
  161. package/src/ui-tailwind/editor-surface/perf-probe.ts +14 -215
  162. package/src/ui-tailwind/editor-surface/pm-decorations.ts +42 -0
  163. package/src/ui-tailwind/editor-surface/pm-position-map.ts +38 -2
  164. package/src/ui-tailwind/editor-surface/pm-schema.ts +14 -4
  165. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +34 -5
  166. package/src/ui-tailwind/editor-surface/runtime-decoration-plugin.ts +9 -19
  167. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -2
  168. package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +145 -0
  169. package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +16 -11
  170. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +8 -10
  171. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +3 -0
  172. package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +4 -2
  173. package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +60 -20
  174. package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +16 -11
  175. package/src/ui-tailwind/review/tw-health-panel.tsx +36 -17
  176. package/src/ui-tailwind/review/tw-review-rail.tsx +7 -4
  177. package/src/ui-tailwind/review-workspace/diagnostics-visibility.ts +44 -0
  178. package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +11 -0
  179. package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +16 -1
  180. package/src/ui-tailwind/review-workspace/types.ts +26 -12
  181. package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +40 -11
  182. package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +2 -1
  183. package/src/ui-tailwind/review-workspace/use-page-markers.ts +15 -26
  184. package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +35 -18
  185. package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +41 -32
  186. package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +2 -1
  187. package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +2 -1
  188. package/src/ui-tailwind/status/tw-status-bar.tsx +6 -5
  189. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +52 -80
  190. package/src/ui-tailwind/toolbar/tw-shell-header.tsx +12 -48
  191. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +9 -4
  192. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +328 -361
  193. package/src/ui-tailwind/tw-review-workspace.tsx +152 -286
@@ -0,0 +1,221 @@
1
+ const SHA256_INITIAL_STATE = [
2
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
3
+ 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
4
+ ] as const;
5
+
6
+ const SHA256_CONSTANTS = [
7
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
8
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
9
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
10
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
11
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
12
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
13
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
14
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
15
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
16
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
17
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
18
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
19
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
20
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
21
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
22
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
23
+ ] as const;
24
+
25
+ const HEX_DIGITS = "0123456789abcdef";
26
+
27
+ /**
28
+ * Synchronous, dependency-free SHA-256 for Layer 02 evidence refs.
29
+ *
30
+ * This intentionally does not import `node:crypto` or Web Crypto:
31
+ * `src/model/**` must stay browser-safe and independent of host APIs, while
32
+ * `collectCanonicalLayoutInputs()` is synchronous. Web Crypto is async, and a
33
+ * Node-only import would leak a server runtime dependency into the canonical
34
+ * model bundle. Keep this module small and covered by Node crypto vectors.
35
+ */
36
+ export function sha256TextHex(text: string): string {
37
+ return sha256Hex(utf8Bytes(text));
38
+ }
39
+
40
+ export function sha256Hex(bytes: Uint8Array): string {
41
+ const padded = padSha256Message(bytes);
42
+ const schedule = new Uint32Array(64);
43
+ let state0: number = SHA256_INITIAL_STATE[0];
44
+ let state1: number = SHA256_INITIAL_STATE[1];
45
+ let state2: number = SHA256_INITIAL_STATE[2];
46
+ let state3: number = SHA256_INITIAL_STATE[3];
47
+ let state4: number = SHA256_INITIAL_STATE[4];
48
+ let state5: number = SHA256_INITIAL_STATE[5];
49
+ let state6: number = SHA256_INITIAL_STATE[6];
50
+ let state7: number = SHA256_INITIAL_STATE[7];
51
+
52
+ for (let chunkOffset = 0; chunkOffset < padded.byteLength; chunkOffset += 64) {
53
+ for (let index = 0; index < 16; index += 1) {
54
+ const wordOffset = chunkOffset + index * 4;
55
+ schedule[index] =
56
+ (((padded[wordOffset] ?? 0) << 24) |
57
+ ((padded[wordOffset + 1] ?? 0) << 16) |
58
+ ((padded[wordOffset + 2] ?? 0) << 8) |
59
+ (padded[wordOffset + 3] ?? 0)) >>>
60
+ 0;
61
+ }
62
+
63
+ for (let index = 16; index < 64; index += 1) {
64
+ schedule[index] = add32(
65
+ lowerSigma1(schedule[index - 2] ?? 0),
66
+ schedule[index - 7] ?? 0,
67
+ lowerSigma0(schedule[index - 15] ?? 0),
68
+ schedule[index - 16] ?? 0,
69
+ );
70
+ }
71
+
72
+ let a = state0;
73
+ let b = state1;
74
+ let c = state2;
75
+ let d = state3;
76
+ let e = state4;
77
+ let f = state5;
78
+ let g = state6;
79
+ let h = state7;
80
+
81
+ for (let index = 0; index < 64; index += 1) {
82
+ const t1 = add32(
83
+ h,
84
+ upperSigma1(e),
85
+ choose(e, f, g),
86
+ SHA256_CONSTANTS[index] ?? 0,
87
+ schedule[index] ?? 0,
88
+ );
89
+ const t2 = add32(upperSigma0(a), majority(a, b, c));
90
+
91
+ h = g;
92
+ g = f;
93
+ f = e;
94
+ e = add32(d, t1);
95
+ d = c;
96
+ c = b;
97
+ b = a;
98
+ a = add32(t1, t2);
99
+ }
100
+
101
+ state0 = add32(state0, a);
102
+ state1 = add32(state1, b);
103
+ state2 = add32(state2, c);
104
+ state3 = add32(state3, d);
105
+ state4 = add32(state4, e);
106
+ state5 = add32(state5, f);
107
+ state6 = add32(state6, g);
108
+ state7 = add32(state7, h);
109
+ }
110
+
111
+ return wordsToHex(state0, state1, state2, state3, state4, state5, state6, state7);
112
+ }
113
+
114
+ function utf8Bytes(text: string): Uint8Array {
115
+ const bytes: number[] = [];
116
+ for (let index = 0; index < text.length; index += 1) {
117
+ let codePoint = text.codePointAt(index) ?? 0;
118
+ if (codePoint > 0xffff) index += 1;
119
+ if (codePoint <= 0x7f) {
120
+ bytes.push(codePoint);
121
+ } else if (codePoint <= 0x7ff) {
122
+ bytes.push(0xc0 | (codePoint >>> 6), 0x80 | (codePoint & 0x3f));
123
+ } else if (codePoint <= 0xffff) {
124
+ bytes.push(
125
+ 0xe0 | (codePoint >>> 12),
126
+ 0x80 | ((codePoint >>> 6) & 0x3f),
127
+ 0x80 | (codePoint & 0x3f),
128
+ );
129
+ } else {
130
+ bytes.push(
131
+ 0xf0 | (codePoint >>> 18),
132
+ 0x80 | ((codePoint >>> 12) & 0x3f),
133
+ 0x80 | ((codePoint >>> 6) & 0x3f),
134
+ 0x80 | (codePoint & 0x3f),
135
+ );
136
+ }
137
+ }
138
+ return new Uint8Array(bytes);
139
+ }
140
+
141
+ function padSha256Message(bytes: Uint8Array): Uint8Array {
142
+ const bitLength = bytes.byteLength * 8;
143
+ const paddedLength = Math.ceil((bytes.byteLength + 9) / 64) * 64;
144
+ const padded = new Uint8Array(paddedLength);
145
+ padded.set(bytes);
146
+ padded[bytes.byteLength] = 0x80;
147
+
148
+ const lengthOffset = paddedLength - 8;
149
+ for (let index = 0; index < 8; index += 1) {
150
+ const shift = (7 - index) * 8;
151
+ padded[lengthOffset + index] = Math.floor(bitLength / 2 ** shift) & 0xff;
152
+ }
153
+
154
+ return padded;
155
+ }
156
+
157
+ function rotateRight(value: number, shift: number): number {
158
+ return ((value >>> shift) | (value << (32 - shift))) >>> 0;
159
+ }
160
+
161
+ function add32(...values: number[]): number {
162
+ let total = 0;
163
+ for (const value of values) {
164
+ total = (total + value) >>> 0;
165
+ }
166
+ return total;
167
+ }
168
+
169
+ function wordsToHex(
170
+ word0: number,
171
+ word1: number,
172
+ word2: number,
173
+ word3: number,
174
+ word4: number,
175
+ word5: number,
176
+ word6: number,
177
+ word7: number,
178
+ ): string {
179
+ return (
180
+ wordToHex(word0) +
181
+ wordToHex(word1) +
182
+ wordToHex(word2) +
183
+ wordToHex(word3) +
184
+ wordToHex(word4) +
185
+ wordToHex(word5) +
186
+ wordToHex(word6) +
187
+ wordToHex(word7)
188
+ );
189
+ }
190
+
191
+ function wordToHex(word: number): string {
192
+ let hex = "";
193
+ for (let shift = 28; shift >= 0; shift -= 4) {
194
+ hex += HEX_DIGITS[(word >>> shift) & 0xf];
195
+ }
196
+ return hex;
197
+ }
198
+
199
+ function choose(x: number, y: number, z: number): number {
200
+ return ((x & y) ^ (~x & z)) >>> 0;
201
+ }
202
+
203
+ function majority(x: number, y: number, z: number): number {
204
+ return ((x & y) ^ (x & z) ^ (y & z)) >>> 0;
205
+ }
206
+
207
+ function upperSigma0(value: number): number {
208
+ return (rotateRight(value, 2) ^ rotateRight(value, 13) ^ rotateRight(value, 22)) >>> 0;
209
+ }
210
+
211
+ function upperSigma1(value: number): number {
212
+ return (rotateRight(value, 6) ^ rotateRight(value, 11) ^ rotateRight(value, 25)) >>> 0;
213
+ }
214
+
215
+ function lowerSigma0(value: number): number {
216
+ return (rotateRight(value, 7) ^ rotateRight(value, 18) ^ (value >>> 3)) >>> 0;
217
+ }
218
+
219
+ function lowerSigma1(value: number): number {
220
+ return (rotateRight(value, 17) ^ rotateRight(value, 19) ^ (value >>> 10)) >>> 0;
221
+ }
@@ -2,6 +2,8 @@ import type {
2
2
  AnchorGeometry,
3
3
  BlockNode,
4
4
  CanonicalDocument,
5
+ CanonicalFieldEvidenceRef,
6
+ CanonicalSourceRef,
5
7
  DrawingFrameNode,
6
8
  FieldFamily,
7
9
  FieldRefreshStatus,
@@ -9,12 +11,15 @@ import type {
9
11
  HeaderFooterVariant,
10
12
  ImageNode,
11
13
  InlineNode,
14
+ ParagraphNode,
12
15
  SectionProperties,
13
16
  TableCellNode,
14
17
  TableNode,
15
18
  TableRowNode,
16
19
  TocRegion,
20
+ ParagraphStyleNumberingReference,
17
21
  } from "./canonical-document.ts";
22
+ import { sha256TextHex } from "./canonical-hash.ts";
18
23
 
19
24
  export const MAIN_STORY_KEY = "main";
20
25
 
@@ -39,16 +44,19 @@ export interface CanonicalStoryIdentity {
39
44
  readonly noteId?: string;
40
45
  }
41
46
 
42
- export type CanonicalFieldRegionKind = "field" | "toc-region";
47
+ export type CanonicalFieldRegionKind = "field" | "toc-region" | "hyperlink-field";
43
48
 
44
49
  export interface CanonicalFieldRegionIdentity {
45
50
  readonly regionId: string;
51
+ readonly canonicalFieldId: string;
46
52
  readonly kind: CanonicalFieldRegionKind;
47
53
  readonly storyKey: string;
48
54
  readonly fieldIndex: number;
49
55
  readonly fieldFamily: FieldFamily;
50
56
  readonly refreshStatus: FieldRefreshStatus;
51
57
  readonly paragraphIndex: number;
58
+ readonly sourceRef?: CanonicalSourceRef;
59
+ readonly fieldEvidence?: CanonicalFieldEvidenceRef;
52
60
  readonly tocId?: string;
53
61
  readonly sourcePath?: string;
54
62
  readonly parentKind?: TocRegion["parentKind"];
@@ -61,15 +69,33 @@ export interface CanonicalFieldRegionIdentity {
61
69
  export interface CanonicalLayoutInputs {
62
70
  readonly stories: readonly CanonicalStoryIdentity[];
63
71
  readonly fieldRegions: readonly CanonicalFieldRegionIdentity[];
72
+ readonly numbering: readonly CanonicalNumberingLayoutInput[];
64
73
  readonly tables: readonly CanonicalTableLayoutInput[];
65
74
  readonly anchors: readonly CanonicalAnchorLayoutInput[];
66
75
  readonly scopes: readonly CanonicalScopeLayoutInput[];
67
76
  }
68
77
 
78
+ export interface CanonicalNumberingLayoutInput {
79
+ readonly numberingKey: string;
80
+ readonly storyKey: string;
81
+ readonly blockPath: string;
82
+ readonly paragraphIndex: number;
83
+ readonly sourceRef?: CanonicalSourceRef;
84
+ readonly numberingSourceRef?: CanonicalSourceRef;
85
+ readonly numberingOrigin?: "paragraph" | "paragraph-style";
86
+ readonly paragraphStyleId?: string;
87
+ readonly numberingInstanceId: string;
88
+ readonly numberingInstanceSourceRef?: CanonicalSourceRef;
89
+ readonly abstractNumberingId?: string;
90
+ readonly abstractNumberingSourceRef?: CanonicalSourceRef;
91
+ readonly level: number;
92
+ }
93
+
69
94
  export interface CanonicalTableLayoutInput {
70
95
  readonly tableKey: string;
71
96
  readonly storyKey: string;
72
97
  readonly blockPath: string;
98
+ readonly sourceRef?: CanonicalSourceRef;
73
99
  readonly rowCount: number;
74
100
  readonly columnCount: number;
75
101
  readonly gridColumns: readonly number[];
@@ -141,6 +167,7 @@ export interface CanonicalAnchorLayoutInput {
141
167
  readonly storyKey: string;
142
168
  readonly blockPath: string;
143
169
  readonly inlinePath: string;
170
+ readonly sourceRef?: CanonicalSourceRef;
144
171
  readonly objectKind: CanonicalAnchorObjectKind;
145
172
  readonly editPosture: CanonicalAnchorEditPosture;
146
173
  readonly display?: "inline" | "floating";
@@ -218,6 +245,7 @@ export function collectCanonicalLayoutInputs(
218
245
  return {
219
246
  stories: collectCanonicalStoryIdentities(doc),
220
247
  fieldRegions: collectCanonicalFieldRegionIdentities(doc),
248
+ numbering: collectCanonicalNumberingLayoutInputs(doc, blockContexts),
221
249
  tables: collectCanonicalTableLayoutInputs(blockContexts),
222
250
  anchors: collectCanonicalAnchorLayoutInputs(doc, blockContexts),
223
251
  scopes: collectCanonicalScopeLayoutInputs(blockContexts),
@@ -319,14 +347,89 @@ export function collectCanonicalStoryIdentities(
319
347
  return stories;
320
348
  }
321
349
 
350
+ export function collectCanonicalNumberingLayoutInputs(
351
+ doc: CanonicalDocument,
352
+ contexts: readonly CanonicalStoryBlockContext[] = collectStoryBlockContexts(doc),
353
+ ): CanonicalNumberingLayoutInput[] {
354
+ const inputs: CanonicalNumberingLayoutInput[] = [];
355
+
356
+ for (const context of contexts) {
357
+ walkBlocks(context.blocks, context.storyKey, context.basePath, {
358
+ paragraph(paragraph, blockPath) {
359
+ const styleNumbering =
360
+ paragraph.numbering === undefined && paragraph.styleId !== undefined
361
+ ? resolveParagraphStyleNumbering(doc, paragraph.styleId)
362
+ : undefined;
363
+ const numbering = paragraph.numbering ?? styleNumbering;
364
+ if (numbering === undefined) {
365
+ return;
366
+ }
367
+ const instance = doc.numbering.instances[numbering.numberingInstanceId];
368
+ const abstractDefinition =
369
+ instance?.abstractNumberingId === undefined
370
+ ? undefined
371
+ : doc.numbering.abstractDefinitions[instance.abstractNumberingId];
372
+ inputs.push({
373
+ numberingKey: `${context.storyKey}:${blockPath}:numbering`,
374
+ storyKey: context.storyKey,
375
+ blockPath,
376
+ paragraphIndex: blockIndexFromPath(blockPath),
377
+ ...(paragraph.sourceRef !== undefined ? { sourceRef: paragraph.sourceRef } : {}),
378
+ ...(paragraph.numbering?.sourceRef !== undefined
379
+ ? { numberingSourceRef: paragraph.numbering.sourceRef }
380
+ : {}),
381
+ numberingOrigin: paragraph.numbering === undefined ? "paragraph-style" : "paragraph",
382
+ ...(paragraph.numbering === undefined && paragraph.styleId !== undefined
383
+ ? { paragraphStyleId: paragraph.styleId }
384
+ : {}),
385
+ numberingInstanceId: numbering.numberingInstanceId,
386
+ ...(instance?.sourceRef !== undefined
387
+ ? { numberingInstanceSourceRef: instance.sourceRef }
388
+ : {}),
389
+ ...(instance?.abstractNumberingId !== undefined
390
+ ? { abstractNumberingId: instance.abstractNumberingId }
391
+ : {}),
392
+ ...(abstractDefinition?.sourceRef !== undefined
393
+ ? { abstractNumberingSourceRef: abstractDefinition.sourceRef }
394
+ : {}),
395
+ level: numbering.level ?? 0,
396
+ });
397
+ },
398
+ });
399
+ }
400
+
401
+ return inputs;
402
+ }
403
+
404
+ function resolveParagraphStyleNumbering(
405
+ doc: CanonicalDocument,
406
+ styleId: string,
407
+ ): ParagraphStyleNumberingReference | undefined {
408
+ const visited = new Set<string>();
409
+ let currentStyleId: string | undefined = styleId;
410
+
411
+ while (currentStyleId !== undefined && !visited.has(currentStyleId)) {
412
+ visited.add(currentStyleId);
413
+ const style: CanonicalDocument["styles"]["paragraphs"][string] | undefined =
414
+ doc.styles.paragraphs[currentStyleId];
415
+ if (style === undefined) {
416
+ return undefined;
417
+ }
418
+ if (style.numbering !== undefined) {
419
+ return style.numbering;
420
+ }
421
+ currentStyleId = style.basedOn;
422
+ }
423
+
424
+ return undefined;
425
+ }
426
+
322
427
  export function collectCanonicalFieldRegionIdentities(
323
428
  doc: CanonicalDocument,
324
429
  ): CanonicalFieldRegionIdentity[] {
325
430
  const registry = doc.fieldRegistry;
326
- if (!registry) return [];
327
-
328
431
  const byFieldIndex = new Map(
329
- [...registry.supported, ...registry.preserveOnly].map((entry) => [
432
+ [...(registry?.supported ?? []), ...(registry?.preserveOnly ?? [])].map((entry) => [
330
433
  entry.fieldIndex,
331
434
  entry,
332
435
  ]),
@@ -334,23 +437,47 @@ export function collectCanonicalFieldRegionIdentities(
334
437
  const regions: CanonicalFieldRegionIdentity[] = [];
335
438
 
336
439
  for (const entry of byFieldIndex.values()) {
440
+ const canonicalFieldId =
441
+ entry.canonicalFieldId ??
442
+ createCanonicalFieldId({
443
+ fieldIndex: entry.fieldIndex,
444
+ storyKey: entry.storyKey,
445
+ sourceRef: entry.sourceRef,
446
+ });
337
447
  regions.push({
338
448
  regionId: `field:${entry.fieldIndex}`,
449
+ canonicalFieldId,
339
450
  kind: "field",
340
451
  storyKey: entry.storyKey ?? MAIN_STORY_KEY,
341
452
  fieldIndex: entry.fieldIndex,
342
453
  fieldFamily: entry.fieldFamily,
343
454
  refreshStatus: entry.refreshStatus,
344
455
  paragraphIndex: entry.paragraphIndex,
456
+ ...(entry.sourceRef !== undefined ? { sourceRef: entry.sourceRef } : {}),
457
+ fieldEvidence: createFieldEvidenceRef({
458
+ instruction: entry.instruction,
459
+ result: entry.displayText,
460
+ sourceRef: entry.sourceRef,
461
+ locked: entry.locked,
462
+ dirty: entry.dirty,
463
+ }),
345
464
  });
346
465
  }
347
466
 
348
- for (const toc of registry.tocRegions ?? []) {
467
+ for (const toc of registry?.tocRegions ?? []) {
349
468
  const field = byFieldIndex.get(toc.sourceFieldIndex);
469
+ const storyKey = field?.storyKey ?? MAIN_STORY_KEY;
350
470
  regions.push({
351
471
  regionId: `toc:${toc.tocId}`,
472
+ canonicalFieldId:
473
+ field?.canonicalFieldId ??
474
+ createCanonicalFieldId({
475
+ fieldIndex: toc.sourceFieldIndex,
476
+ storyKey,
477
+ sourceRef: field?.sourceRef,
478
+ }),
352
479
  kind: "toc-region",
353
- storyKey: field?.storyKey ?? MAIN_STORY_KEY,
480
+ storyKey,
354
481
  fieldIndex: toc.sourceFieldIndex,
355
482
  fieldFamily: field?.fieldFamily ?? "TOC",
356
483
  refreshStatus: field?.refreshStatus ?? "stale",
@@ -365,9 +492,102 @@ export function collectCanonicalFieldRegionIdentities(
365
492
  });
366
493
  }
367
494
 
495
+ regions.push(...collectHyperlinkFieldRegionIdentities(doc, regions.length));
496
+
497
+ return regions;
498
+ }
499
+
500
+ function collectHyperlinkFieldRegionIdentities(
501
+ doc: CanonicalDocument,
502
+ startIndex: number,
503
+ ): CanonicalFieldRegionIdentity[] {
504
+ const regions: CanonicalFieldRegionIdentity[] = [];
505
+ for (const context of collectStoryBlockContexts(doc)) {
506
+ walkBlocks(context.blocks, context.storyKey, context.basePath, {
507
+ inline(inline, blockPath, inlinePath) {
508
+ if (inline.type !== "hyperlink" || inline.fieldCarrier === undefined) {
509
+ return;
510
+ }
511
+ const evidence = createFieldEvidenceRef({
512
+ sourceRef: inline.fieldCarrier.sourceRef ?? inline.sourceRef,
513
+ instructionHash: inline.fieldCarrier.instructionHash,
514
+ resultHash: inline.fieldCarrier.resultHash,
515
+ locked: inline.fieldCarrier.locked,
516
+ dirty: inline.fieldCarrier.dirty,
517
+ });
518
+ const sourceRef = evidence.sourceRef;
519
+ const fieldIndex = startIndex + regions.length;
520
+ const canonicalFieldId = createCanonicalFieldId({
521
+ fieldIndex,
522
+ storyKey: context.storyKey,
523
+ sourceRef,
524
+ });
525
+ regions.push({
526
+ regionId: `hyperlink-field:${sourceRef?.sourceId ?? `${context.storyKey}:${inlinePath}`}`,
527
+ canonicalFieldId,
528
+ kind: "hyperlink-field",
529
+ storyKey: context.storyKey,
530
+ fieldIndex,
531
+ fieldFamily: inline.fieldCarrier.fieldFamily ?? "HYPERLINK",
532
+ refreshStatus: inline.fieldCarrier.refreshStatus ?? "preserve-only",
533
+ paragraphIndex: blockIndexFromPath(blockPath),
534
+ ...(sourceRef !== undefined ? { sourceRef } : {}),
535
+ fieldEvidence: evidence,
536
+ });
537
+ },
538
+ });
539
+ }
368
540
  return regions;
369
541
  }
370
542
 
543
+ function createFieldEvidenceRef(input: {
544
+ readonly instruction?: string;
545
+ readonly result?: string;
546
+ readonly instructionHash?: string;
547
+ readonly resultHash?: string;
548
+ readonly sourceRef?: CanonicalSourceRef;
549
+ readonly locked?: boolean;
550
+ readonly dirty?: boolean;
551
+ }): CanonicalFieldEvidenceRef {
552
+ return {
553
+ ...(input.sourceRef !== undefined ? { sourceRef: input.sourceRef } : {}),
554
+ ...(input.instructionHash !== undefined
555
+ ? { instructionHash: input.instructionHash }
556
+ : input.instruction !== undefined
557
+ ? { instructionHash: hashText(input.instruction) }
558
+ : {}),
559
+ ...(input.resultHash !== undefined
560
+ ? { resultHash: input.resultHash }
561
+ : input.result !== undefined
562
+ ? { resultHash: hashText(input.result) }
563
+ : {}),
564
+ ...(input.locked !== undefined ? { locked: input.locked } : {}),
565
+ ...(input.dirty !== undefined ? { dirty: input.dirty } : {}),
566
+ };
567
+ }
568
+
569
+ function hashText(text: string): string {
570
+ return sha256TextHex(text);
571
+ }
572
+
573
+ export function createCanonicalFieldId(input: {
574
+ readonly fieldIndex: number;
575
+ readonly storyKey?: string | undefined;
576
+ readonly sourceRef?: CanonicalSourceRef | undefined;
577
+ }): string {
578
+ const sourceId = input.sourceRef?.sourceId;
579
+ if (sourceId !== undefined && sourceId.length > 0) {
580
+ return `canonical-field:source:${sourceId}`;
581
+ }
582
+ return `canonical-field:story:${input.storyKey ?? MAIN_STORY_KEY}:field:${input.fieldIndex}`;
583
+ }
584
+
585
+ function blockIndexFromPath(blockPath: string): number {
586
+ const matches = [...blockPath.matchAll(/\/block\[(\d+)\]/g)];
587
+ const last = matches.at(-1);
588
+ return last ? Number.parseInt(last[1]!, 10) : 0;
589
+ }
590
+
371
591
  export function collectCanonicalTableLayoutInputs(
372
592
  contexts: readonly CanonicalStoryBlockContext[],
373
593
  ): CanonicalTableLayoutInput[] {
@@ -473,6 +693,7 @@ export function collectStoryBlockContexts(
473
693
  }
474
694
 
475
695
  interface BlockWalkVisitor {
696
+ readonly paragraph?: (paragraph: ParagraphNode, blockPath: string) => void;
476
697
  readonly table?: (table: TableNode, blockPath: string) => void;
477
698
  readonly inline?: (
478
699
  inline: InlineNode,
@@ -512,6 +733,7 @@ function walkBlocks(
512
733
  }
513
734
 
514
735
  if (block.type === "paragraph") {
736
+ visitor.paragraph?.(block, blockPath);
515
737
  walkInlines(block.children, storyKey, blockPath, `${blockPath}/inline`, visitor);
516
738
  continue;
517
739
  }
@@ -568,6 +790,7 @@ function projectTableLayoutInput(
568
790
  tableKey,
569
791
  storyKey,
570
792
  blockPath,
793
+ ...(table.sourceRef !== undefined ? { sourceRef: table.sourceRef } : {}),
571
794
  rowCount: table.rows.length,
572
795
  columnCount: table.gridColumns.length,
573
796
  gridColumns: [...table.gridColumns],
@@ -673,11 +896,13 @@ function projectDrawingFrameAnchor(
673
896
  const content = node.content;
674
897
  const shapeBlocks = content.type === "shape" ? content.txbxBlocks : undefined;
675
898
  const objectKind = drawingContentKind(content);
899
+ const sourceRef = node.sourceRef ?? drawingContentSourceRef(content);
676
900
  return {
677
901
  objectKey: `${storyKey}:${inlinePath}`,
678
902
  storyKey,
679
903
  blockPath,
680
904
  inlinePath,
905
+ ...(sourceRef !== undefined ? { sourceRef } : {}),
681
906
  objectKind,
682
907
  editPosture: drawingEditPosture(node),
683
908
  display: anchor.display,
@@ -753,12 +978,26 @@ function projectLegacyImageAnchor(
753
978
  ? { allowOverlap: node.floating.allowOverlap }
754
979
  : {}),
755
980
  mediaId: node.mediaId,
981
+ ...(node.sourceRef !== undefined ? { sourceRef: node.sourceRef } : {}),
756
982
  ...(media?.packagePartName !== undefined ? { packagePartName: media.packagePartName } : {}),
757
983
  ...(media?.relationshipId !== undefined ? { relationshipId: media.relationshipId } : {}),
758
984
  ...(node.placementXml !== undefined ? { rawXmlPreserved: true } : {}),
759
985
  };
760
986
  }
761
987
 
988
+ function preserveOnlySourceRef(sourceId: string | undefined): CanonicalSourceRef | undefined {
989
+ return sourceId === undefined ? undefined : { sourceId };
990
+ }
991
+
992
+ function drawingContentSourceRef(
993
+ content: DrawingFrameNode["content"],
994
+ ): CanonicalSourceRef | undefined {
995
+ if ("preserveOnlyObject" in content) {
996
+ return preserveOnlySourceRef(content.preserveOnlyObject?.sourceId);
997
+ }
998
+ return undefined;
999
+ }
1000
+
762
1001
  function normalizeFloatingAxis(
763
1002
  axis: NonNullable<ImageNode["floating"]>["horizontalPosition"],
764
1003
  ): AnchorGeometry["positionH"] {
@@ -65,6 +65,7 @@ export type {
65
65
  RuntimeLayoutDivergenceKind,
66
66
  RuntimeLayoutDivergence,
67
67
  RuntimePageFrame,
68
+ RuntimePageFrameCompleteness,
68
69
  RuntimePageLocalStoryInstance,
69
70
  RuntimeResolvedStoryField,
70
71
  RuntimeStoryAnchoredObject,