@beyondwork/docx-react-component 1.0.66 → 1.0.69

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 (384) hide show
  1. package/README.md +75 -931
  2. package/package.json +26 -27
  3. package/src/api/anchor-conversion.ts +43 -0
  4. package/src/api/editor-state-types.ts +2 -1
  5. package/src/api/public-types.ts +504 -101
  6. package/src/api/session-state.ts +4 -0
  7. package/src/api/v3/README.md +91 -0
  8. package/src/api/v3/_create.ts +146 -0
  9. package/src/api/v3/_layer-metadata.ts +362 -0
  10. package/src/api/v3/_mocks.ts +84 -0
  11. package/src/api/v3/_runtime-handle.ts +162 -0
  12. package/src/api/v3/_ux-response.ts +73 -0
  13. package/src/api/v3/ai/_metadata-audit.ts +225 -0
  14. package/src/api/v3/ai/attach.ts +235 -0
  15. package/src/api/v3/ai/bundle.ts +132 -0
  16. package/src/api/v3/ai/explain.ts +144 -0
  17. package/src/api/v3/ai/export.ts +54 -0
  18. package/src/api/v3/ai/inspect.ts +118 -0
  19. package/src/api/v3/ai/policy.ts +77 -0
  20. package/src/api/v3/ai/replacement.ts +341 -0
  21. package/src/api/v3/ai/resolve.ts +133 -0
  22. package/src/api/v3/index.ts +79 -0
  23. package/src/api/v3/runtime/chart.ts +310 -0
  24. package/src/api/v3/runtime/clipboard.ts +81 -0
  25. package/src/api/v3/runtime/collab.ts +331 -0
  26. package/src/api/v3/runtime/content.ts +236 -0
  27. package/src/api/v3/runtime/document.ts +282 -0
  28. package/src/api/v3/runtime/formatting.ts +186 -0
  29. package/src/api/v3/runtime/geometry.ts +349 -0
  30. package/src/api/v3/runtime/layout.ts +108 -0
  31. package/src/api/v3/runtime/review.ts +129 -0
  32. package/src/api/v3/runtime/search.ts +74 -0
  33. package/src/api/v3/runtime/table.ts +63 -0
  34. package/src/api/v3/runtime/workflow.ts +434 -0
  35. package/src/api/v3/ui/_context.ts +86 -0
  36. package/src/api/v3/ui/_create.ts +65 -0
  37. package/src/api/v3/ui/_types.ts +520 -0
  38. package/src/api/v3/ui/chrome-composition.ts +342 -0
  39. package/src/{ui-tailwind/chrome → api/v3/ui}/chrome-preset-model.ts +11 -1
  40. package/src/api/v3/ui/chrome.ts +476 -0
  41. package/src/api/v3/ui/debug.ts +124 -0
  42. package/src/api/v3/ui/index.ts +64 -0
  43. package/src/api/v3/ui/overlays-visibility.ts +170 -0
  44. package/src/api/v3/ui/overlays.ts +427 -0
  45. package/src/api/v3/ui/scope.ts +71 -0
  46. package/src/api/v3/ui/session.ts +100 -0
  47. package/src/api/v3/ui/surface.ts +170 -0
  48. package/src/api/v3/ui/viewport.ts +303 -0
  49. package/src/core/commands/index.ts +28 -6
  50. package/src/core/commands/list-commands.ts +3 -2
  51. package/src/core/commands/section-layout-commands.ts +9 -8
  52. package/src/core/schema/text-schema.ts +16 -0
  53. package/src/core/selection/mapping.ts +33 -72
  54. package/src/core/state/editor-state.ts +96 -189
  55. package/src/index.ts +23 -4
  56. package/src/io/chart-preview-resolver.ts +1 -1
  57. package/src/io/docx-session.ts +36 -4795
  58. package/src/io/export/build-app-properties-xml.ts +1 -1
  59. package/src/io/export/serialize-comments.ts +1 -1
  60. package/src/io/export/serialize-headers-footers.ts +6 -1
  61. package/src/io/export/serialize-main-document.ts +45 -0
  62. package/src/io/export/serialize-run-formatting.ts +17 -2
  63. package/src/io/export/twip.ts +1 -1
  64. package/src/io/normalize/normalize-text.ts +27 -20
  65. package/src/io/ooxml/chart/parse-series.ts +1 -1
  66. package/src/io/ooxml/chart/resolve-color.ts +2 -2
  67. package/src/io/ooxml/chart/types.ts +1 -1
  68. package/src/io/ooxml/classify-embedding.ts +83 -33
  69. package/src/io/ooxml/parse-fill.ts +1 -1
  70. package/src/io/ooxml/parse-main-document.ts +71 -1
  71. package/src/io/ooxml/parse-object.ts +14 -10
  72. package/src/io/ooxml/parse-run-formatting.ts +47 -1
  73. package/src/io/ooxml/property-grab-bag.ts +2 -2
  74. package/src/io/ooxml/units.ts +11 -0
  75. package/src/io/ooxml/workflow-payload.ts +282 -7
  76. package/src/model/anchor.ts +85 -0
  77. package/src/model/canonical-document.ts +351 -15
  78. package/src/model/chart-types.ts +1 -1
  79. package/src/model/layout/index.ts +83 -0
  80. package/src/model/layout/page-graph-types.ts +181 -0
  81. package/src/model/layout/page-layout-snapshot.ts +105 -0
  82. package/src/model/layout/resolved-layout-types.ts +47 -0
  83. package/src/model/layout/runtime-page-graph-types.ts +102 -0
  84. package/src/model/paragraph-scope-ids.ts +72 -0
  85. package/src/model/review/comment-types.ts +112 -0
  86. package/src/model/review/index.ts +2 -0
  87. package/src/model/review/revision-types.ts +215 -0
  88. package/src/model/snapshot.ts +32 -0
  89. package/src/review/store/comment-store.ts +21 -47
  90. package/src/review/store/revision-types.ts +40 -198
  91. package/src/runtime/collab/base-doc-fingerprint.ts +6 -1
  92. package/src/runtime/collab/runtime-collab-sync.ts +13 -3
  93. package/src/runtime/collab-session.ts +1 -1
  94. package/src/runtime/debug/build-debug-inspector-snapshot.ts +686 -0
  95. package/src/runtime/debug/event-ring-buffer.ts +64 -0
  96. package/src/runtime/debug/probability-sampler.ts +18 -0
  97. package/src/runtime/debug/runtime-debug-facet.ts +67 -0
  98. package/src/runtime/debug/stage-tokens.ts +31 -0
  99. package/src/runtime/debug/telemetry-bus.ts +271 -0
  100. package/src/runtime/debug/types.ts +275 -0
  101. package/src/runtime/debug/wrap-ref-for-telemetry.ts +118 -0
  102. package/src/runtime/document-layout.ts +8 -6
  103. package/src/runtime/document-runtime.ts +843 -1141
  104. package/src/runtime/document-search.ts +1 -1
  105. package/src/runtime/edit-ops/index.ts +1 -1
  106. package/src/runtime/external-send-runtime.ts +1 -1
  107. package/src/runtime/formatting/document-lookup.ts +235 -0
  108. package/src/runtime/formatting/field/registry.ts +41 -0
  109. package/src/runtime/{field-resolver.ts → formatting/field/resolver.ts} +27 -2
  110. package/src/runtime/formatting/font-resolution.ts +83 -0
  111. package/src/runtime/formatting/formatting-context.ts +903 -0
  112. package/src/runtime/formatting/formatting-types.ts +157 -0
  113. package/src/runtime/{hyperlink-color-resolver.ts → formatting/hyperlink-color.ts} +2 -2
  114. package/src/runtime/formatting/index.ts +125 -0
  115. package/src/runtime/{resolved-numbering-geometry.ts → formatting/numbering/geometry.ts} +1 -1
  116. package/src/runtime/{numbering-prefix.ts → formatting/numbering/prefix.ts} +170 -3
  117. package/src/runtime/formatting/paragraph-style-resolver.ts +92 -0
  118. package/src/runtime/formatting/projector.ts +75 -0
  119. package/src/runtime/formatting/resolve-effective.ts +407 -0
  120. package/src/runtime/formatting/revision-display.ts +105 -0
  121. package/src/runtime/{paragraph-style-resolver.ts → formatting/style-cascade.ts} +84 -141
  122. package/src/runtime/{table-style-resolver.ts → formatting/table-style-resolver.ts} +1 -1
  123. package/src/runtime/formatting/telemetry-bridge.ts +106 -0
  124. package/src/runtime/{theme-color-resolver.ts → formatting/theme-color.ts} +2 -30
  125. package/src/runtime/geometry/caret-geometry.ts +164 -0
  126. package/src/runtime/geometry/geometry-facet.ts +364 -0
  127. package/src/runtime/geometry/geometry-types.ts +256 -0
  128. package/src/runtime/geometry/hit-test.ts +125 -0
  129. package/src/runtime/geometry/index.ts +71 -0
  130. package/src/runtime/geometry/inert-geometry-facet.ts +43 -0
  131. package/src/runtime/geometry/invalidation.ts +35 -0
  132. package/src/runtime/geometry/object-handles.ts +77 -0
  133. package/src/runtime/geometry/overlay-rects.ts +85 -0
  134. package/src/runtime/geometry/project-anchors.ts +100 -0
  135. package/src/runtime/geometry/project-fragments.ts +216 -0
  136. package/src/runtime/geometry/projector.ts +129 -0
  137. package/src/runtime/geometry/replacement-envelope.ts +130 -0
  138. package/src/runtime/geometry/viewport.ts +218 -0
  139. package/src/runtime/layout/compat-input-ledger.ts +211 -0
  140. package/src/runtime/layout/index.ts +6 -1
  141. package/src/runtime/layout/inert-layout-facet.ts +12 -7
  142. package/src/runtime/layout/layout-engine-instance.ts +189 -11
  143. package/src/runtime/layout/layout-engine-version.ts +450 -1
  144. package/src/runtime/layout/layout-facet-types.ts +60 -0
  145. package/src/runtime/layout/layout-measurement-provider.ts +13 -0
  146. package/src/runtime/layout/measurement-backend-canvas.ts +14 -2
  147. package/src/runtime/layout/measurement-backend-empirical.ts +23 -4
  148. package/src/runtime/layout/page-graph.ts +62 -209
  149. package/src/runtime/layout/page-story-resolver.ts +7 -12
  150. package/src/runtime/layout/paginated-layout-engine.ts +186 -11
  151. package/src/runtime/layout/project-block-fragments.ts +11 -0
  152. package/src/runtime/layout/projector.ts +90 -0
  153. package/src/runtime/layout/public-facet.ts +187 -442
  154. package/src/runtime/layout/resolved-formatting-state.ts +158 -26
  155. package/src/runtime/layout/table-render-plan.ts +1 -1
  156. package/src/runtime/prerender/cache-envelope.ts +6 -1
  157. package/src/runtime/prerender/prerender-document.ts +18 -23
  158. package/src/runtime/render/decoration-resolver.ts +1 -1
  159. package/src/runtime/render/render-frame-types.ts +20 -0
  160. package/src/runtime/render/render-kernel.ts +94 -25
  161. package/src/runtime/scopes/_formatting-seam.ts +262 -0
  162. package/src/runtime/scopes/_scope-dependencies.ts +49 -0
  163. package/src/runtime/scopes/action-validation.ts +356 -0
  164. package/src/runtime/scopes/attach-explanation.ts +102 -0
  165. package/src/runtime/scopes/audit-bundle.ts +71 -0
  166. package/src/runtime/scopes/compile-scope-bundle.ts +163 -0
  167. package/src/runtime/scopes/compile-scope.ts +262 -0
  168. package/src/runtime/scopes/compiler-service.ts +431 -0
  169. package/src/runtime/scopes/create-issue.ts +107 -0
  170. package/src/runtime/scopes/enumerate-scopes.ts +543 -0
  171. package/src/runtime/scopes/evidence.ts +233 -0
  172. package/src/runtime/scopes/index.ts +150 -0
  173. package/src/runtime/scopes/position-map.ts +214 -0
  174. package/src/runtime/scopes/preservation-boundary.ts +91 -0
  175. package/src/runtime/scopes/projector.ts +49 -0
  176. package/src/runtime/scopes/replaceability.ts +87 -0
  177. package/src/runtime/scopes/replacement/apply.ts +228 -0
  178. package/src/runtime/scopes/replacement/compile.ts +59 -0
  179. package/src/runtime/scopes/replacement/propose.ts +42 -0
  180. package/src/runtime/scopes/resolve-reference.ts +347 -0
  181. package/src/runtime/scopes/review-bundle.ts +141 -0
  182. package/src/runtime/scopes/scope-kinds/_paragraph-text.ts +57 -0
  183. package/src/runtime/scopes/scope-kinds/_table-text.ts +42 -0
  184. package/src/runtime/scopes/scope-kinds/comment-thread.ts +59 -0
  185. package/src/runtime/scopes/scope-kinds/field.ts +65 -0
  186. package/src/runtime/scopes/scope-kinds/heading.ts +84 -0
  187. package/src/runtime/scopes/scope-kinds/list-item.ts +77 -0
  188. package/src/runtime/scopes/scope-kinds/paragraph.ts +182 -0
  189. package/src/runtime/scopes/scope-kinds/revision.ts +62 -0
  190. package/src/runtime/scopes/scope-kinds/table-cell.ts +57 -0
  191. package/src/runtime/scopes/scope-kinds/table-row.ts +61 -0
  192. package/src/runtime/scopes/scope-kinds/table.ts +55 -0
  193. package/src/runtime/scopes/scope-range.ts +208 -0
  194. package/src/runtime/scopes/semantic-scope-types.ts +454 -0
  195. package/src/runtime/scopes/workflow-overlap.ts +92 -0
  196. package/src/runtime/selection/index.ts +1 -1
  197. package/src/runtime/structure-ops/fragment-insert.ts +1 -1
  198. package/src/runtime/structure-ops/index.ts +1 -1
  199. package/src/runtime/surface-projection.ts +232 -262
  200. package/src/runtime/units.ts +4 -2
  201. package/src/runtime/workflow/coordinator.ts +1348 -0
  202. package/src/runtime/workflow/derived-scope-resolver.ts +125 -0
  203. package/src/runtime/workflow/index.ts +25 -0
  204. package/src/runtime/workflow/markup-mode-policy.ts +98 -0
  205. package/src/runtime/{workflow-markup.ts → workflow/markup.ts} +6 -6
  206. package/src/runtime/workflow/metadata-persistence.ts +306 -0
  207. package/src/runtime/workflow/metadata-writer.ts +123 -0
  208. package/src/runtime/workflow/overlay-store.ts +690 -0
  209. package/src/runtime/workflow/projector.ts +127 -0
  210. package/src/runtime/{query-scopes.ts → workflow/query-scopes.ts} +3 -3
  211. package/src/runtime/{workflow-rail-segments.ts → workflow/rail/compose.ts} +60 -165
  212. package/src/runtime/workflow/rail/types.ts +198 -0
  213. package/src/runtime/workflow/scope-rail-composer.ts +39 -0
  214. package/src/runtime/{scope-resolver.ts → workflow/scope-resolver.ts} +3 -3
  215. package/src/runtime/workflow/scope-writer.ts +188 -0
  216. package/src/runtime/{tamper-gate.ts → workflow/tamper-gate.ts} +1 -1
  217. package/src/runtime/workflow/visibility-policy.ts +129 -0
  218. package/src/session/_sync-legacy.ts +66 -0
  219. package/src/session/export/embedded-reconstitute.ts +104 -0
  220. package/src/session/export/export-diagnostics.ts +85 -0
  221. package/src/session/export/export-validation.ts +110 -0
  222. package/src/session/export/index.ts +34 -0
  223. package/src/session/export/preservation-reattach.ts +30 -0
  224. package/src/session/export/serialize-dispatch.ts +165 -0
  225. package/src/session/export/stateful-export-pipeline.ts +432 -0
  226. package/src/session/export/stateful-export.ts +684 -0
  227. package/src/session/import/canonical-assembly.ts +227 -0
  228. package/src/session/import/diagnostics-session.ts +54 -0
  229. package/src/session/import/embedded-discovery.ts +225 -0
  230. package/src/session/import/embedded-offload.ts +337 -0
  231. package/src/session/import/import-diagnostics.ts +69 -0
  232. package/src/session/import/loader-types.ts +313 -0
  233. package/src/session/import/loader.ts +1834 -0
  234. package/src/session/import/normalize.ts +195 -0
  235. package/src/session/import/package-parts.ts +217 -0
  236. package/src/session/import/package-read.ts +195 -0
  237. package/src/session/import/parse-orchestration.ts +105 -0
  238. package/src/session/import/part-constants.ts +70 -0
  239. package/src/session/import/part-discovery.ts +94 -0
  240. package/src/session/import/preservation-index.ts +46 -0
  241. package/src/{runtime/read-only-diagnostics-runtime.ts → session/import/read-only-diagnostics.ts} +24 -3
  242. package/src/session/import/review-import.ts +508 -0
  243. package/src/session/import/styles-consolidation.ts +281 -0
  244. package/src/session/import/workflow-scope-import.ts +256 -0
  245. package/src/session/index.ts +37 -0
  246. package/src/session/session-state.ts +69 -0
  247. package/src/session/session.ts +532 -0
  248. package/src/session/shared/protection.ts +228 -0
  249. package/src/session/shared/session-utils.ts +82 -0
  250. package/src/session/types.ts +499 -0
  251. package/src/shell/chart-snapshots.ts +96 -0
  252. package/src/shell/media-previews.ts +85 -0
  253. package/src/shell/overlay-anchor-bridge.ts +53 -0
  254. package/src/shell/paste-adapter.ts +23 -0
  255. package/src/shell/ref-commands.ts +1697 -0
  256. package/src/shell/ref-utilities.ts +48 -0
  257. package/src/shell/search.ts +51 -0
  258. package/src/{ui/editor-runtime-boundary.ts → shell/session-bootstrap.ts} +243 -67
  259. package/src/shell/ui-subscriber-channels.ts +81 -0
  260. package/src/shell/use-collab-sync.ts +116 -0
  261. package/src/ui/WordReviewEditor.tsx +496 -2051
  262. package/src/ui/editor-shell-view.tsx +30 -1
  263. package/src/ui/editor-surface-controller.tsx +49 -1
  264. package/src/ui/headless/revision-decoration-model.ts +83 -0
  265. package/src/{ui-tailwind/chrome → ui/headless}/role-action-sets.ts +1 -1
  266. package/src/ui/headless/scoped-chrome-policy.ts +2 -2
  267. package/src/ui/headless/selection-tool-context.ts +1 -1
  268. package/src/ui/headless/selection-tool-resolver.ts +1 -1
  269. package/src/ui/runtime-shortcut-dispatch.ts +46 -1
  270. package/src/ui/ui-controller-factory.ts +221 -0
  271. package/src/ui-tailwind/chart/ChartSurface.tsx +2 -2
  272. package/src/ui-tailwind/chart/layout/legend-layout.ts +1 -1
  273. package/src/ui-tailwind/chart/layout/plot-area.ts +2 -2
  274. package/src/ui-tailwind/chart/layout/title-layout.ts +1 -1
  275. package/src/ui-tailwind/chart/render/area.tsx +3 -3
  276. package/src/ui-tailwind/chart/render/bar-column.tsx +3 -3
  277. package/src/ui-tailwind/chart/render/bubble.tsx +3 -3
  278. package/src/ui-tailwind/chart/render/combo.tsx +2 -2
  279. package/src/ui-tailwind/chart/render/data-labels.tsx +2 -2
  280. package/src/ui-tailwind/chart/render/font-metrics.ts +2 -2
  281. package/src/ui-tailwind/chart/render/line.tsx +3 -3
  282. package/src/ui-tailwind/chart/render/pie.tsx +6 -6
  283. package/src/ui-tailwind/chart/render/scatter.tsx +3 -3
  284. package/src/ui-tailwind/chart/render/svg-primitives.ts +3 -3
  285. package/src/ui-tailwind/chart/render/unsupported.tsx +2 -2
  286. package/src/ui-tailwind/chrome/build-context-menu-entries.ts +88 -0
  287. package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +1 -1
  288. package/src/ui-tailwind/chrome/collab-send-to-supplier-button.tsx +1 -1
  289. package/src/ui-tailwind/chrome/collab-tamper-banner.tsx +1 -1
  290. package/src/ui-tailwind/chrome/collab-top-nav-container.tsx +1 -1
  291. package/src/ui-tailwind/chrome/editor-action-registry.ts +553 -0
  292. package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +182 -0
  293. package/src/ui-tailwind/chrome/local-surface-arbiter.ts +534 -0
  294. package/src/ui-tailwind/chrome/resolve-target-kind.ts +226 -0
  295. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +38 -4
  296. package/src/ui-tailwind/chrome/tw-context-band.tsx +125 -0
  297. package/src/ui-tailwind/chrome/tw-context-menu-portal.tsx +248 -0
  298. package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +42 -1
  299. package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +8 -7
  300. package/src/ui-tailwind/chrome/tw-selection-tool-blocked.tsx +38 -4
  301. package/src/ui-tailwind/chrome/tw-selection-tool-comment.tsx +104 -6
  302. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +66 -7
  303. package/src/ui-tailwind/chrome/tw-selection-tool-workflow.tsx +54 -8
  304. package/src/ui-tailwind/chrome/tw-shortcut-hint.tsx +7 -1
  305. package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +33 -0
  306. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +78 -1
  307. package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +16 -8
  308. package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +276 -0
  309. package/src/ui-tailwind/chrome/use-context-menu-controller.ts +201 -0
  310. package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +1 -1
  311. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +22 -4
  312. package/src/ui-tailwind/chrome-overlay/tw-comment-balloon-layer.tsx +1 -1
  313. package/src/ui-tailwind/chrome-overlay/tw-locked-block-layer.tsx +1 -1
  314. package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +11 -5
  315. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +197 -3
  316. package/src/ui-tailwind/chrome-overlay/tw-revision-margin-bar-layer.tsx +1 -1
  317. package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +35 -6
  318. package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +24 -16
  319. package/src/ui-tailwind/chrome-overlay/tw-table-continuation-header.tsx +1 -1
  320. package/src/ui-tailwind/debug/README.md +57 -0
  321. package/src/ui-tailwind/debug/index.ts +3 -0
  322. package/src/ui-tailwind/debug/tw-debug-overlay.tsx +186 -0
  323. package/src/ui-tailwind/debug/tw-debug-presentation.tsx +80 -0
  324. package/src/ui-tailwind/debug/tw-debug-top-bar.tsx +83 -0
  325. package/src/ui-tailwind/editor-surface/chart-node-view.tsx +2 -2
  326. package/src/ui-tailwind/editor-surface/float-wrap-resolver.ts +1 -1
  327. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +135 -10
  328. package/src/ui-tailwind/editor-surface/pm-decorations.ts +40 -13
  329. package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +1 -1
  330. package/src/ui-tailwind/editor-surface/pm-schema.ts +1 -1
  331. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +3 -3
  332. package/src/ui-tailwind/editor-surface/predicted-tag-preflight.ts +1 -1
  333. package/src/ui-tailwind/editor-surface/remote-cursor-plugin.ts +2 -2
  334. package/src/ui-tailwind/editor-surface/scroll-anchor.ts +91 -9
  335. package/src/ui-tailwind/editor-surface/shape-renderer.ts +1 -1
  336. package/src/ui-tailwind/editor-surface/surface-layer.ts +1 -1
  337. package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +1 -1
  338. package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +23 -6
  339. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +132 -22
  340. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +1 -1
  341. package/src/ui-tailwind/index.ts +0 -5
  342. package/src/ui-tailwind/overlay-anchor-bridge-context.tsx +33 -0
  343. package/src/ui-tailwind/page-stack/floating-image-overlay-model.ts +66 -29
  344. package/src/ui-tailwind/page-stack/tw-floating-image-layer.tsx +25 -2
  345. package/src/ui-tailwind/review/comment-markdown-renderer.tsx +15 -0
  346. package/src/ui-tailwind/review/tw-review-rail.tsx +92 -4
  347. package/src/ui-tailwind/review/tw-workflow-tab.tsx +1 -1
  348. package/src/ui-tailwind/review-workspace/page-chrome.ts +210 -0
  349. package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +101 -0
  350. package/src/ui-tailwind/review-workspace/paragraph-layout.ts +115 -0
  351. package/src/ui-tailwind/review-workspace/selection-toolbar-placement.ts +97 -0
  352. package/src/ui-tailwind/review-workspace/tw-review-workspace-navigator.tsx +130 -0
  353. package/src/ui-tailwind/review-workspace/tw-review-workspace-page-toolbar.tsx +240 -0
  354. package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +59 -0
  355. package/src/ui-tailwind/review-workspace/types.ts +408 -0
  356. package/src/ui-tailwind/review-workspace/use-chrome-policy.ts +104 -0
  357. package/src/ui-tailwind/review-workspace/use-derived-view-state.ts +151 -0
  358. package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +70 -0
  359. package/src/ui-tailwind/review-workspace/use-grabbed-segment-offsets.ts +40 -0
  360. package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +55 -0
  361. package/src/ui-tailwind/review-workspace/use-page-markers.ts +130 -0
  362. package/src/ui-tailwind/review-workspace/use-pm-surface-capture.ts +60 -0
  363. package/src/ui-tailwind/review-workspace/use-review-rail-state.ts +63 -0
  364. package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +170 -0
  365. package/src/ui-tailwind/review-workspace/use-scroll-root-capture.ts +28 -0
  366. package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +113 -0
  367. package/src/ui-tailwind/review-workspace/use-shell-selection-anchor-bridge.ts +120 -0
  368. package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +55 -0
  369. package/src/ui-tailwind/review-workspace/use-viewport-dimensions.ts +43 -0
  370. package/src/ui-tailwind/review-workspace/use-workspace-arbiter.ts +25 -0
  371. package/src/ui-tailwind/review-workspace/use-workspace-composition.ts +86 -0
  372. package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +150 -0
  373. package/src/ui-tailwind/theme/editor-theme.css +25 -0
  374. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +2 -2
  375. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +61 -98
  376. package/src/ui-tailwind/tw-review-workspace.tsx +521 -1802
  377. package/src/ui-tailwind/ui-api-context.tsx +43 -0
  378. package/src/ui-tailwind/ui-shell-channels-context.tsx +49 -0
  379. package/src/validation/compatibility-engine.ts +6 -6
  380. package/src/runtime/styles-cascade.ts +0 -33
  381. package/src/ui-tailwind/chrome/tw-mode-dock.tsx +0 -85
  382. /package/src/runtime/{page-number-format.ts → formatting/field/page-number-format.ts} +0 -0
  383. /package/src/runtime/{ai-action-policy.ts → workflow/ai-action-policy.ts} +0 -0
  384. /package/src/runtime/{scope-tag-registry.ts → workflow/scope-tag-registry.ts} +0 -0
@@ -7,7 +7,7 @@
7
7
  * content type.
8
8
  *
9
9
  * Required by:
10
- * docs/plans/lane-3-layout-engine-ooxml-fidelity.md §2 A.5
10
+ * CLAUDE.md (lane status table) §2 A.5
11
11
  *
12
12
  * Schema shape (ECMA-376 Part 1, Office Extended properties):
13
13
  * <Properties xmlns="…/extended-properties" xmlns:vt="…/docPropsVTypes">
@@ -23,7 +23,7 @@ interface XmlTextNode {
23
23
 
24
24
  type XmlNode = XmlElementNode | XmlTextNode;
25
25
 
26
- interface ParagraphBoundaryMap {
26
+ export interface ParagraphBoundaryMap {
27
27
  paragraphIndex: number;
28
28
  start: number;
29
29
  end: number;
@@ -32,11 +32,16 @@ const V_NS = `xmlns:v="urn:schemas-microsoft-com:vml"`;
32
32
  const WP_NS = `xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"`;
33
33
  const WPS_NS = `xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape"`;
34
34
  const A_NS = `xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"`;
35
+ // DrawingML pic namespace — required at the part root because inline-image
36
+ // serialization and preserved rawXml both emit pic:pic / pic:spPr elements.
37
+ // Word tolerates undefined prefixes; LibreOffice strict-parses and rejects
38
+ // ("Namespace prefix pic on spPr is not defined", word/footer1.xml).
39
+ const PIC_NS = `xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"`;
35
40
  const W10_NS = `xmlns:w10="urn:schemas-microsoft-com:office:word"`;
36
41
  const W14_NS = `xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml"`;
37
42
  const W15_NS = `xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml"`;
38
43
  const WP14_NS = `xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing"`;
39
- const EXTENDED_ROOT_ATTRS = `${MC_NS} ${O_NS} ${V_NS} ${WP_NS} ${WPS_NS} ${A_NS} ${W10_NS} ${W14_NS} ${W15_NS} ${WP14_NS} mc:Ignorable="wps wp14 w14 w15"`;
44
+ const EXTENDED_ROOT_ATTRS = `${MC_NS} ${O_NS} ${V_NS} ${WP_NS} ${WPS_NS} ${A_NS} ${PIC_NS} ${W10_NS} ${W14_NS} ${W15_NS} ${WP14_NS} mc:Ignorable="wps wp14 w14 w15"`;
40
45
 
41
46
  /**
42
47
  * Serialize a HeaderDocument into a headerN.xml string.
@@ -102,12 +102,28 @@ interface ParagraphSerializationResult {
102
102
  boundary: RevisionParagraphBoundary;
103
103
  }
104
104
 
105
+ type SerializeTelemetryBus = import("../../runtime/debug/telemetry-bus.ts").TelemetryBus;
106
+ let activeSerializeTelemetryBus: SerializeTelemetryBus | undefined;
107
+
108
+ export function setActiveSerializeTelemetryBus(bus: SerializeTelemetryBus | undefined): void {
109
+ activeSerializeTelemetryBus = bus;
110
+ }
111
+
112
+ function serializePerformanceNow(): number {
113
+ if (typeof performance !== "undefined" && typeof performance.now === "function") {
114
+ return performance.now();
115
+ }
116
+ return Date.now();
117
+ }
118
+
105
119
  export function serializeMainDocument(
106
120
  content: DocumentRootNode,
107
121
  preservation: PreservationStore = { opaqueFragments: {}, packageParts: {} },
108
122
  existingRelationships: readonly OpcRelationship[] = [],
109
123
  options: SerializeMainDocumentOptions = {},
110
124
  ): SerializedMainDocument {
125
+ const bus = activeSerializeTelemetryBus;
126
+ const started = bus?.isEnabled("serialize") ? serializePerformanceNow() : 0;
111
127
  const nextRelationshipIndex =
112
128
  existingRelationships.reduce((maxIndex, relationship) => {
113
129
  const match = /^rIdHyperlink(\d+)$/.exec(relationship.id);
@@ -267,6 +283,26 @@ export function serializeMainDocument(
267
283
  offsetParagraphBoundary(boundary, prefix.length),
268
284
  );
269
285
 
286
+ if (bus?.isEnabled("serialize")) {
287
+ const blockKindCounts: Record<string, number> = Object.create(null);
288
+ for (const node of content.children) {
289
+ const kind = (node as { type?: string }).type ?? "unknown";
290
+ blockKindCounts[kind] = (blockKindCounts[kind] ?? 0) + 1;
291
+ }
292
+ bus.emit({
293
+ channel: "serialize",
294
+ type: "serialize.main_document.completed",
295
+ t: 0,
296
+ payload: {
297
+ blockCount: content.children.length,
298
+ blockKindCounts,
299
+ documentXmlBytes: documentXml.length,
300
+ relationshipCount: state.relationships.length,
301
+ ms: serializePerformanceNow() - started,
302
+ },
303
+ });
304
+ }
305
+
270
306
  return {
271
307
  documentXml,
272
308
  relationships: state.relationships,
@@ -1664,9 +1700,18 @@ function serializeDocumentAttributes(
1664
1700
  }
1665
1701
  }
1666
1702
 
1703
+ // DrawingML picture namespace — always declare at the document root. Inline-
1704
+ // image serialization (wp:inline…xmlns:pic=…) locally scopes the pic prefix,
1705
+ // so fresh images are self-contained. But preserved rawXml (opaque_block /
1706
+ // complex objects / headers-footers round-trip) may emit pic:pic / pic:spPr
1707
+ // relying on inherited-scope. Without xmlns:pic at the root, LibreOffice
1708
+ // strict-parses and rejects with "Namespace prefix pic on spPr is not
1709
+ // defined". Declared before the `attributes` spread so a source-document
1710
+ // override (should one exist) wins.
1667
1711
  const merged: Record<string, string> = {
1668
1712
  "xmlns:r": ns.r,
1669
1713
  "xmlns:w": ns.w,
1714
+ "xmlns:pic": "http://schemas.openxmlformats.org/drawingml/2006/picture",
1670
1715
  ...extensionXmlns,
1671
1716
  ...(attributes ?? {}),
1672
1717
  };
@@ -30,13 +30,28 @@ export function buildRunPropertiesXml(rPr: CanonicalRunFormatting | undefined):
30
30
  // 1. rStyle
31
31
  if (rPr.characterStyleId) parts.push(`<w:rStyle w:val="${escXml(rPr.characterStyleId)}"/>`);
32
32
 
33
- // 2. rFonts
34
- if (rPr.fontFamilyAscii || rPr.fontFamilyHAnsi || rPr.fontFamilyEastAsia || rPr.fontFamilyCs) {
33
+ // 2. rFonts — concrete names + ECMA-376 §17.3.2.26 theme-slot attrs.
34
+ // The complex-script theme attr is emitted as `w:cstheme` (all
35
+ // lowercase) to match Word's output.
36
+ if (
37
+ rPr.fontFamilyAscii ||
38
+ rPr.fontFamilyHAnsi ||
39
+ rPr.fontFamilyEastAsia ||
40
+ rPr.fontFamilyCs ||
41
+ rPr.asciiTheme ||
42
+ rPr.hAnsiTheme ||
43
+ rPr.eastAsiaTheme ||
44
+ rPr.csTheme
45
+ ) {
35
46
  const attrs: string[] = [];
36
47
  if (rPr.fontFamilyAscii) attrs.push(`w:ascii="${escXml(rPr.fontFamilyAscii)}"`);
37
48
  if (rPr.fontFamilyHAnsi) attrs.push(`w:hAnsi="${escXml(rPr.fontFamilyHAnsi)}"`);
38
49
  if (rPr.fontFamilyEastAsia) attrs.push(`w:eastAsia="${escXml(rPr.fontFamilyEastAsia)}"`);
39
50
  if (rPr.fontFamilyCs) attrs.push(`w:cs="${escXml(rPr.fontFamilyCs)}"`);
51
+ if (rPr.asciiTheme) attrs.push(`w:asciiTheme="${escXml(rPr.asciiTheme)}"`);
52
+ if (rPr.hAnsiTheme) attrs.push(`w:hAnsiTheme="${escXml(rPr.hAnsiTheme)}"`);
53
+ if (rPr.eastAsiaTheme) attrs.push(`w:eastAsiaTheme="${escXml(rPr.eastAsiaTheme)}"`);
54
+ if (rPr.csTheme) attrs.push(`w:cstheme="${escXml(rPr.csTheme)}"`);
40
55
  parts.push(`<w:rFonts ${attrs.join(" ")}/>`);
41
56
  }
42
57
 
@@ -18,7 +18,7 @@
18
18
  * requireTwip(value) — mandatory numeric: throws on non-finite input.
19
19
  *
20
20
  * Source:
21
- * docs/plans/lane-3-layout-engine-ooxml-fidelity.md §2 A.2
21
+ * CLAUDE.md (lane status table) §2 A.2
22
22
  */
23
23
 
24
24
  /**
@@ -4,7 +4,6 @@ import type {
4
4
  CustomXmlNode,
5
5
  DiagnosticStore,
6
6
  DocumentRootNode,
7
- FieldRegistry,
8
7
  InlineNode,
9
8
  MediaCatalog,
10
9
  OpaqueBlockNode,
@@ -37,21 +36,30 @@ import type {
37
36
  ParsedTableCellNode,
38
37
  ParsedTableRowNode,
39
38
  } from "../ooxml/parse-main-document.ts";
40
- import { classifyFieldInstruction, buildFieldRegistry } from "../ooxml/parse-fields.ts";
39
+ import { classifyFieldInstruction } from "../ooxml/parse-fields.ts";
41
40
  import {
42
41
  type LoadScheduler,
43
42
  nowMs,
44
43
  shouldYield,
45
44
  } from "../load-scheduler.ts";
46
45
 
46
+ /**
47
+ * Output of `normalizeParsedTextDocument`. Carries content + media +
48
+ * preservation + diagnostics + the ambient section-properties rider,
49
+ * and nothing else. The canonical `fieldRegistry` is NOT produced here
50
+ * because it requires the fully-assembled styles catalog + subParts;
51
+ * it is constructed inside `createImportedCanonicalDocument` (session
52
+ * layer) with all three inputs available. Tests that previously
53
+ * checked `normalized.fieldRegistry` call `buildFieldRegistry` from
54
+ * `src/io/ooxml/parse-fields.ts` directly — that is the canonical
55
+ * field-registry factory.
56
+ */
47
57
  export interface NormalizedTextDocument {
48
58
  content: DocumentRootNode;
49
59
  media: MediaCatalog;
50
60
  preservation: PreservationStore;
51
61
  diagnostics: DiagnosticStore;
52
62
  finalSectionProperties?: ParsedMainDocument["finalSectionProperties"];
53
- /** Package-backed field registry built during normalization. */
54
- fieldRegistry?: FieldRegistry;
55
63
  }
56
64
 
57
65
  interface NormalizationState {
@@ -67,7 +75,6 @@ interface NormalizationState {
67
75
  export function normalizeParsedTextDocument(
68
76
  document: ParsedMainDocument,
69
77
  packagePartName = "/word/document.xml",
70
- options?: { styles?: import("../../model/canonical-document.ts").StylesCatalog },
71
78
  ): NormalizedTextDocument {
72
79
  const state: NormalizationState = {
73
80
  nextFragmentIndex: 1,
@@ -103,13 +110,6 @@ export function normalizeParsedTextDocument(
103
110
 
104
111
  const content: DocumentRootNode = { type: "doc", children };
105
112
 
106
- // Build the field registry from normalized content.
107
- // When styles are available, the registry includes full TOC heading resolution.
108
- // Without styles, it still catalogs all field instances for the supported/preserve-only partition.
109
- const styles = options?.styles ?? { paragraphs: {}, characters: {}, tables: {} };
110
- const fieldRegistry = buildFieldRegistry({ content, styles });
111
- const hasFields = fieldRegistry.supported.length > 0 || fieldRegistry.preserveOnly.length > 0;
112
-
113
113
  return {
114
114
  content,
115
115
  media: state.media,
@@ -118,7 +118,6 @@ export function normalizeParsedTextDocument(
118
118
  ...(document.finalSectionProperties !== undefined
119
119
  ? { finalSectionProperties: document.finalSectionProperties }
120
120
  : {}),
121
- ...(hasFields ? { fieldRegistry } : {}),
122
121
  };
123
122
  }
124
123
 
@@ -138,7 +137,6 @@ export async function normalizeParsedTextDocumentAsync(
138
137
  document: ParsedMainDocument,
139
138
  packagePartName = "/word/document.xml",
140
139
  scheduler: LoadScheduler,
141
- options?: { styles?: import("../../model/canonical-document.ts").StylesCatalog },
142
140
  ): Promise<NormalizedTextDocument> {
143
141
  const state: NormalizationState = {
144
142
  nextFragmentIndex: 1,
@@ -184,10 +182,6 @@ export async function normalizeParsedTextDocumentAsync(
184
182
 
185
183
  const content: DocumentRootNode = { type: "doc", children };
186
184
 
187
- const styles = options?.styles ?? { paragraphs: {}, characters: {}, tables: {} };
188
- const fieldRegistry = buildFieldRegistry({ content, styles });
189
- const hasFields = fieldRegistry.supported.length > 0 || fieldRegistry.preserveOnly.length > 0;
190
-
191
185
  return {
192
186
  content,
193
187
  media: state.media,
@@ -196,7 +190,6 @@ export async function normalizeParsedTextDocumentAsync(
196
190
  ...(document.finalSectionProperties !== undefined
197
191
  ? { finalSectionProperties: document.finalSectionProperties }
198
192
  : {}),
199
- ...(hasFields ? { fieldRegistry } : {}),
200
193
  };
201
194
  }
202
195
 
@@ -560,6 +553,18 @@ function normalizeInlineChildren(
560
553
  bookmarkId: node.bookmarkId,
561
554
  });
562
555
  break;
556
+ case "scope_marker_start":
557
+ normalized.push({
558
+ type: "scope_marker_start",
559
+ scopeId: node.scopeId,
560
+ });
561
+ break;
562
+ case "scope_marker_end":
563
+ normalized.push({
564
+ type: "scope_marker_end",
565
+ scopeId: node.scopeId,
566
+ });
567
+ break;
563
568
  case "footnote_ref":
564
569
  normalized.push({
565
570
  type: "footnote_ref",
@@ -672,7 +677,7 @@ function normalizeDrawingFrameNode(
672
677
  * surface renderer can resolve `previewMediaId` → `previewSrc` the same
673
678
  * way it does for inline images. Chart/SmartArt nodes weren't previously
674
679
  * registered because only image nodes walked through `normalizeImageNode`.
675
- * Needed by docs/plans/lane-5-charts.md Stage 0 (real mc:Fallback bitmaps) and
680
+ * Needed by CLAUDE.md (lane status table) Stage 0 (real mc:Fallback bitmaps) and
676
681
  * Stage 0B (synthesized previews from the demo harness decorator) —
677
682
  * without this, previewMediaId sits in the canonical model but
678
683
  * `canonicalDocument.media.items` has no corresponding MediaItem and
@@ -850,6 +855,8 @@ function measureNormalizedInlineDisplayLength(nodes: InlineNode[]): number {
850
855
  return total + measureNormalizedInlineDisplayLength(node.children);
851
856
  case "bookmark_start":
852
857
  case "bookmark_end":
858
+ case "scope_marker_start":
859
+ case "scope_marker_end":
853
860
  return total;
854
861
  default:
855
862
  return total + 1;
@@ -22,7 +22,7 @@ import {
22
22
  } from "../xml-attr-helpers.ts";
23
23
  import type { XmlElementNode } from "../xml-element.ts";
24
24
  import { parseXml } from "../xml-parser.ts";
25
- import { GRADIENT_STOP_UNITS } from "../../../runtime/units.ts";
25
+ import { GRADIENT_STOP_UNITS } from "../units.ts";
26
26
 
27
27
  import type {
28
28
  BubbleSeries,
@@ -2,7 +2,7 @@
2
2
  * Resolve a `ColorRef` into a concrete sRGB hex string.
3
3
  *
4
4
  * Implements the second layer of the OOXML chart-color cascade
5
- * (docs/plans/lane-5-charts.md § 4 Task 2.2): scheme-color and
5
+ * (CLAUDE.md (lane status table) § 4 Task 2.2): scheme-color and
6
6
  * themeLinked references are looked up in `ResolvedTheme.colors`, and
7
7
  * OOXML color modifiers (`lumMod`, `lumOff`, `shade`, `tint`, `satMod`,
8
8
  * `hueMod`) are applied in declaration order via HSL color math per
@@ -19,7 +19,7 @@
19
19
 
20
20
  import type { ColorMod, ColorRef } from "./types.ts";
21
21
  import type { ResolvedTheme } from "../../../model/canonical-document.ts";
22
- import { GRADIENT_STOP_UNITS } from "../../../runtime/units.ts";
22
+ import { GRADIENT_STOP_UNITS } from "../units.ts";
23
23
 
24
24
  const FALLBACK_COLOR = "#808080";
25
25
 
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Re-export shim. The canonical chart type definitions live in
5
5
  * `src/model/chart-types.ts` — that location avoids the former
6
- * `model → io` backward dependency (see `docs/plans/architecture-lane.md`
6
+ * `model → io` backward dependency (see `CLAUDE.md (lane status table)`
7
7
  * §F1). io/ooxml/chart and ui-tailwind/chart consumers can continue
8
8
  * importing from this path unchanged.
9
9
  */
@@ -10,11 +10,16 @@
10
10
  * no side-effects that break the editor. Parser proceeds as today.
11
11
  *
12
12
  * - **offloadable** — a native OOXML package (Word sub-doc, Excel
13
- * workbook) that a host-side storage adapter could extract and
14
- * reconstitute. In the hotfix, `offloadable` is treated as
15
- * `store-only` (there is no adapter yet); the opaque-fragment path
16
- * preserves bytes + XML for round-trip. Graduates when refactor/01
17
- * Step 7 lands a `hostAdapter.storeEmbeddedDocument?` callback.
13
+ * workbook, PowerPoint presentation) that a host-side storage
14
+ * adapter extracts at load (`storeEmbeddedDocument`) and
15
+ * reconstitutes at export (`loadEmbeddedDocument`). Refactor/01
16
+ * Step 7 activated this classification; the offload pipeline lives
17
+ * at `src/session/import/embedded-offload.ts` +
18
+ * `src/session/export/embedded-reconstitute.ts`. When the host
19
+ * does NOT implement the adapter methods, `offloadable` manifests
20
+ * fall through to the opaque-fragment preservation path — the
21
+ * bytes still round-trip byte-identical, they just stay on the
22
+ * parent package.
18
23
  *
19
24
  * - **store-only** — complex binary or undigestible content (PDF,
20
25
  * legacy binary Office, package-embedded docx with icon aspect,
@@ -45,38 +50,54 @@
45
50
  * template variants), it is store-only even if ProgID + content-type
46
51
  * were both missing. Weak signal but matches file-type intent.
47
52
  *
48
- * Why no `offloadable` return value today: the host-adapter callback
49
- * pair (`storeEmbeddedDocument?` + `loadEmbeddedDocument?`) that makes
50
- * `offloadable` meaningful lands as refactor/01 Step 7. Until then,
51
- * what would be `offloadable` is folded into `store-only` — the
52
- * opaque path preserves bytes + XML for the eventual offload.
53
+ * Classification split (refactor/01 Step 7, 2026-04-22):
54
+ * - Native OOXML ProgIDs `offloadable`:
55
+ * Word.Document.*, Word.DocumentMacroEnabled.*, Word.Template.*,
56
+ * Excel.Sheet.*, Excel.SheetMacroEnabled.*, Excel.Worksheet.*,
57
+ * Excel.Chart.*, Excel.ChartMacroEnabled.*,
58
+ * PowerPoint.Slide.*, PowerPoint.SlideMacroEnabled.*,
59
+ * PowerPoint.Show.*, PowerPoint.ShowMacroEnabled.*,
60
+ * PowerPoint.Document.*, PowerPoint.Template.*
61
+ * - Binary / opaque ProgIDs → `store-only`:
62
+ * Excel.SheetBinaryMacroEnabled.* (.xlsb),
63
+ * AcroExch.Document.* (PDF), exact `Package` / `Packager.Package`
64
+ * - Content-type override: native OOXML content types →
65
+ * `offloadable`; `application/pdf` → `store-only`.
66
+ * - Target-path fallback: `.docx/.docm/.dotx/.dotm` +
67
+ * `.xlsx/.xlsm/.xltx/.xltm` + `.pptx/.pptm/.potx/.potm` →
68
+ * `offloadable`; `.xlsb` / `.pdf` → `store-only`.
53
69
  *
54
70
  * See `docs/architecture/01-package-session.md` §P8 for the full
55
71
  * contract.
56
72
  */
57
73
 
58
- const STORE_ONLY_PROGID_PREFIXES: readonly string[] = [
59
- // Nested Word documents — primary real-world crash source (CCEP
60
- // "EU & Global IT Services Agreement.docx" type).
74
+ const OFFLOADABLE_PROGID_PREFIXES: readonly string[] = [
75
+ // Native OOXML Word documents — extractable to host storage with
76
+ // the Step 7 adapter, reconstituted byte-identical at export.
61
77
  "Word.Document.",
62
78
  "Word.DocumentMacroEnabled.",
63
79
  "Word.Template.",
64
- // Spreadsheet embeddings. Excel.Sheet.12 + Excel.Worksheet.12 are
65
- // the common ProgIDs; Excel.Chart.* less so.
80
+ // Native OOXML Excel formats (.xlsx / .xlsm / .xltx / .xltm).
81
+ // `SheetBinaryMacroEnabled` is .xlsb binary, not OOXML — and
82
+ // lives in the store-only list below.
66
83
  "Excel.Sheet.",
67
84
  "Excel.SheetMacroEnabled.",
68
- "Excel.SheetBinaryMacroEnabled.",
69
85
  "Excel.Worksheet.",
70
86
  "Excel.Chart.",
71
87
  "Excel.ChartMacroEnabled.",
72
- // Presentations.
88
+ // Native OOXML presentations (.pptx / .pptm / .potx / .potm).
73
89
  "PowerPoint.Slide.",
74
90
  "PowerPoint.SlideMacroEnabled.",
75
91
  "PowerPoint.Show.",
76
92
  "PowerPoint.ShowMacroEnabled.",
77
93
  "PowerPoint.Document.",
78
94
  "PowerPoint.Template.",
79
- // PDF via Adobe Acrobat.
95
+ ];
96
+
97
+ const STORE_ONLY_PROGID_PREFIXES: readonly string[] = [
98
+ // Excel's legacy binary format — not OOXML, can't be offloaded.
99
+ "Excel.SheetBinaryMacroEnabled.",
100
+ // PDF via Adobe Acrobat — binary, store-only.
80
101
  "AcroExch.Document.",
81
102
  ];
82
103
 
@@ -87,20 +108,24 @@ const STORE_ONLY_PROGID_EXACT: ReadonlySet<string> = new Set([
87
108
  ]);
88
109
 
89
110
  /**
90
- * Content-type patterns that force store-only regardless of ProgID.
91
- * These are checked against the relationship target's content-type as
92
- * declared in `[Content_Types].xml` overrides.
111
+ * Content-type patterns that mark native-OOXML embeddings eligible
112
+ * for offload when the host adapter opts in.
93
113
  */
94
- const STORE_ONLY_CONTENT_TYPES: readonly string[] = [
95
- // Package-embedded Word document (relationships/package type on a
96
- // word/embeddings/*.docx part). Catches the CCEP case even if the
97
- // ProgID is missing or atypical.
114
+ const OFFLOADABLE_CONTENT_TYPES: readonly string[] = [
98
115
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
99
116
  "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
100
117
  "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
101
118
  "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
102
119
  "application/vnd.openxmlformats-officedocument.presentationml.presentation",
103
120
  "application/vnd.openxmlformats-officedocument.presentationml.template",
121
+ ];
122
+
123
+ /**
124
+ * Content-type patterns that force store-only — binary formats that
125
+ * cannot be re-processed by this codebase and must round-trip as
126
+ * opaque bytes.
127
+ */
128
+ const STORE_ONLY_CONTENT_TYPES: readonly string[] = [
104
129
  "application/pdf",
105
130
  ];
106
131
 
@@ -138,14 +163,22 @@ export function classifyEmbedding(
138
163
  const contentType = input.contentType?.trim().toLowerCase() ?? "";
139
164
  const targetPath = input.targetPath?.toLowerCase() ?? "";
140
165
 
141
- // Exact ProgID match.
166
+ // Exact ProgID match — ambiguous `Package` / `Packager.Package`
167
+ // stays store-only (fail-closed: no signal for native OOXML so
168
+ // host storage extraction isn't safe).
142
169
  if (progId && STORE_ONLY_PROGID_EXACT.has(progId)) {
143
170
  return "store-only";
144
171
  }
145
172
 
146
- // ProgID prefix match. Handles Word.Document.12, Word.Document.14,
147
- // Word.DocumentMacroEnabled.12, Excel.Sheet.12, AcroExch.Document.7, etc.
173
+ // ProgID prefix match check offloadable families first so
174
+ // native-OOXML embeddings are promoted ahead of the binary
175
+ // fall-through list.
148
176
  if (progId) {
177
+ for (const prefix of OFFLOADABLE_PROGID_PREFIXES) {
178
+ if (progId.startsWith(prefix)) {
179
+ return "offloadable";
180
+ }
181
+ }
149
182
  for (const prefix of STORE_ONLY_PROGID_PREFIXES) {
150
183
  if (progId.startsWith(prefix)) {
151
184
  return "store-only";
@@ -153,8 +186,15 @@ export function classifyEmbedding(
153
186
  }
154
187
  }
155
188
 
156
- // Content-type override.
189
+ // Content-type override — native-OOXML content types lift to
190
+ // offloadable regardless of ProgID; binary content types stay
191
+ // store-only.
157
192
  if (contentType) {
193
+ for (const ct of OFFLOADABLE_CONTENT_TYPES) {
194
+ if (contentType === ct || contentType.startsWith(`${ct};`)) {
195
+ return "offloadable";
196
+ }
197
+ }
158
198
  for (const ct of STORE_ONLY_CONTENT_TYPES) {
159
199
  if (contentType === ct || contentType.startsWith(`${ct};`)) {
160
200
  return "store-only";
@@ -163,13 +203,21 @@ export function classifyEmbedding(
163
203
  }
164
204
 
165
205
  // Target-path fallback when we have no progId + no content-type.
166
- // A `.docx` / `.xlsx` / `.pptx` / `.pdf` extension in the embeddings
167
- // folder is a strong signal of a package payload.
206
+ // A native-OOXML extension in the embeddings folder is a strong
207
+ // signal of an offloadable package payload. `.xlsb` / `.pdf` stay
208
+ // store-only.
168
209
  if (targetPath) {
169
- const storeOnlyExtensions = [".docx", ".docm", ".dotx", ".dotm",
210
+ const offloadableExtensions = [
211
+ ".docx", ".docm", ".dotx", ".dotm",
170
212
  ".xlsx", ".xlsm", ".xltx", ".xltm",
171
213
  ".pptx", ".pptm", ".potx", ".potm",
172
- ".pdf"];
214
+ ];
215
+ for (const ext of offloadableExtensions) {
216
+ if (targetPath.endsWith(ext)) {
217
+ return "offloadable";
218
+ }
219
+ }
220
+ const storeOnlyExtensions = [".xlsb", ".pdf"];
173
221
  for (const ext of storeOnlyExtensions) {
174
222
  if (targetPath.endsWith(ext)) {
175
223
  return "store-only";
@@ -187,7 +235,9 @@ export function classifyEmbedding(
187
235
  * Exposed for tests. Not part of the public API.
188
236
  */
189
237
  export const __internal = {
238
+ OFFLOADABLE_PROGID_PREFIXES,
190
239
  STORE_ONLY_PROGID_PREFIXES,
191
240
  STORE_ONLY_PROGID_EXACT,
241
+ OFFLOADABLE_CONTENT_TYPES,
192
242
  STORE_ONLY_CONTENT_TYPES,
193
243
  } as const;
@@ -17,7 +17,7 @@
17
17
  * four fill families.
18
18
  */
19
19
 
20
- import { GRADIENT_STOP_UNITS } from "../../runtime/units.ts";
20
+ import { GRADIENT_STOP_UNITS } from "./units.ts";
21
21
  import { localName } from "./xml-attr-helpers.ts";
22
22
 
23
23
  export interface XmlElementNode {
@@ -284,6 +284,8 @@ export type ParsedInlineNode =
284
284
  | ParsedOleEmbedInlineNode
285
285
  | ParsedBookmarkStartInlineNode
286
286
  | ParsedBookmarkEndInlineNode
287
+ | ParsedScopeMarkerStartInlineNode
288
+ | ParsedScopeMarkerEndInlineNode
287
289
  | ParsedFootnoteRefInlineNode
288
290
  | ParsedFieldInlineNode
289
291
  | ParsedPermStartInlineNode
@@ -425,6 +427,23 @@ export interface ParsedBookmarkEndInlineNode {
425
427
  rawXml: string;
426
428
  }
427
429
 
430
+ /**
431
+ * Scope-marker inline nodes — produced by `rewriteScopeMarkerBookmarks`
432
+ * post-parse after it sees the `bw:scope:` bookmark prefix. These survive
433
+ * the normalization pass and land in the canonical document as
434
+ * `ScopeMarkerStartNode` / `ScopeMarkerEndNode`. See
435
+ * `src/io/ooxml/parse-scope-markers.ts` for the prefix contract.
436
+ */
437
+ export interface ParsedScopeMarkerStartInlineNode {
438
+ type: "scope_marker_start";
439
+ scopeId: string;
440
+ }
441
+
442
+ export interface ParsedScopeMarkerEndInlineNode {
443
+ type: "scope_marker_end";
444
+ scopeId: string;
445
+ }
446
+
428
447
  export interface ParsedFootnoteRefInlineNode {
429
448
  type: "footnote_ref";
430
449
  noteId: string;
@@ -618,6 +637,20 @@ const HYPERLINK_RELATIONSHIP_TYPE =
618
637
  */
619
638
  let activeChartPartLookup: ChartPartLookup | undefined;
620
639
 
640
+ /**
641
+ * Phase 1 telemetry — optional bus installed by the runtime before a load.
642
+ * Populated via `setActiveParseTelemetryBus(bus)` from
643
+ * `src/io/docx-session.ts`. Emits one `parse.main_document.completed`
644
+ * event per call with element counts. Re-entrancy invariant matches
645
+ * `activeChartPartLookup` above.
646
+ */
647
+ type ParseTelemetryBus = import("../../runtime/debug/telemetry-bus.ts").TelemetryBus;
648
+ let activeParseTelemetryBus: ParseTelemetryBus | undefined;
649
+
650
+ export function setActiveParseTelemetryBus(bus: ParseTelemetryBus | undefined): void {
651
+ activeParseTelemetryBus = bus;
652
+ }
653
+
621
654
  export function parseMainDocumentXml(
622
655
  xml: string,
623
656
  relationships: readonly OpcRelationship[] = [],
@@ -626,13 +659,50 @@ export function parseMainDocumentXml(
626
659
  chartPartLookup?: ChartPartLookup,
627
660
  ): ParsedMainDocument {
628
661
  activeChartPartLookup = chartPartLookup;
662
+ const bus = activeParseTelemetryBus;
663
+ const started = bus?.isEnabled("parse") ? performanceNow() : 0;
629
664
  try {
630
- return parseMainDocumentXmlInner(xml, relationships, mediaParts, sourcePartPath);
665
+ const result = parseMainDocumentXmlInner(xml, relationships, mediaParts, sourcePartPath);
666
+ if (bus?.isEnabled("parse")) {
667
+ emitParseSummary(bus, result, sourcePartPath, performanceNow() - started);
668
+ }
669
+ return result;
631
670
  } finally {
632
671
  activeChartPartLookup = undefined;
633
672
  }
634
673
  }
635
674
 
675
+ function performanceNow(): number {
676
+ if (typeof performance !== "undefined" && typeof performance.now === "function") {
677
+ return performance.now();
678
+ }
679
+ return Date.now();
680
+ }
681
+
682
+ function emitParseSummary(
683
+ bus: ParseTelemetryBus,
684
+ result: ParsedMainDocument,
685
+ partPath: string,
686
+ ms: number,
687
+ ): void {
688
+ const counts: Record<string, number> = Object.create(null);
689
+ for (const block of result.blocks) {
690
+ const kind = (block as { type?: string }).type ?? "unknown";
691
+ counts[kind] = (counts[kind] ?? 0) + 1;
692
+ }
693
+ bus.emit({
694
+ channel: "parse",
695
+ type: "parse.main_document.completed",
696
+ t: 0,
697
+ payload: {
698
+ partPath,
699
+ blockCount: result.blocks.length,
700
+ blockKindCounts: counts,
701
+ ms,
702
+ },
703
+ });
704
+ }
705
+
636
706
  /**
637
707
  * CO4.5 — Parse a raw `<w:sdt>` XML fragment in isolation, with full drawing-
638
708
  * frame support. Used by `parse-picture-sdt.ts` and tests.