@stll/folio-core 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/docx/headerFooterParser.js +3 -0
  2. package/dist/docx/headerFooterVerbatim.d.ts +19 -0
  3. package/dist/docx/headerFooterVerbatim.js +25 -0
  4. package/dist/docx/index.d.ts +2 -0
  5. package/dist/docx/index.js +2 -0
  6. package/dist/docx/metafileRaster.d.ts +30 -0
  7. package/dist/docx/metafileRaster.js +80 -0
  8. package/dist/docx/paragraphParser.js +5 -1
  9. package/dist/docx/parser.d.ts +12 -2
  10. package/dist/docx/parser.js +31 -1
  11. package/dist/docx/serializer/headerFooterSerializer.js +3 -0
  12. package/dist/docx/styleParser.js +2 -2
  13. package/dist/docx/tableParser.js +2 -3
  14. package/dist/docx/unzip.js +7 -1
  15. package/dist/docx/wrapTypes.d.ts +8 -1
  16. package/dist/docx/wrapTypes.js +0 -8
  17. package/dist/docx/xmlParser.d.ts +7 -1
  18. package/dist/docx/xmlParser.js +17 -1
  19. package/dist/i18n/messages/catalogs.gen.d.ts +2776 -0
  20. package/dist/i18n/messages/catalogs.gen.js +2742 -0
  21. package/dist/i18n/messages/messages.gen.d.ts +166 -0
  22. package/dist/i18n/messages/messages.gen.js +0 -0
  23. package/dist/i18n/messages.d.ts +13 -0
  24. package/dist/i18n/messages.js +39 -0
  25. package/dist/layout-bridge/cellSelectionHighlight.d.ts +18 -0
  26. package/dist/layout-bridge/cellSelectionHighlight.js +36 -0
  27. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +3 -2
  28. package/dist/layout-bridge/convert/headerFooterLayout.js +39 -15
  29. package/dist/layout-bridge/convert/toFlowBlocks.js +1 -0
  30. package/dist/layout-bridge/headerFooterLayout.d.ts +50 -0
  31. package/dist/layout-bridge/headerFooterLayout.js +216 -0
  32. package/dist/layout-bridge/measuring/measureBlocksPipeline.d.ts +33 -0
  33. package/dist/layout-bridge/measuring/measureBlocksPipeline.js +0 -0
  34. package/dist/layout-bridge/sectionColumns.d.ts +11 -0
  35. package/dist/layout-bridge/sectionColumns.js +21 -0
  36. package/dist/layout-bridge/tableInsertHover.d.ts +42 -0
  37. package/dist/layout-bridge/tableInsertHover.js +102 -0
  38. package/dist/layout-engine/index.d.ts +2 -1
  39. package/dist/layout-engine/index.js +2 -1
  40. package/dist/layout-engine/measure/lineBreaks.d.ts +10 -0
  41. package/dist/layout-engine/measure/lineBreaks.js +30 -0
  42. package/dist/layout-engine/measure/measureContainer.js +1 -1
  43. package/dist/layout-engine/measure/measureParagraph.js +63 -22
  44. package/dist/layout-engine/pmPageIndex.d.ts +24 -0
  45. package/dist/layout-engine/pmPageIndex.js +37 -0
  46. package/dist/layout-engine/types.d.ts +2 -1
  47. package/dist/layout-painter/imageLayout.d.ts +101 -0
  48. package/dist/layout-painter/imageLayout.js +161 -0
  49. package/dist/layout-painter/renderPage.d.ts +16 -1
  50. package/dist/layout-painter/renderPage.js +28 -1
  51. package/dist/layout-painter/renderParagraph.js +2 -1
  52. package/dist/layout-painter/sdtBoundary.d.ts +24 -1
  53. package/dist/layout-painter/sdtBoundary.js +40 -1
  54. package/dist/managers/AutoSaveManager.d.ts +89 -0
  55. package/dist/managers/AutoSaveManager.js +279 -0
  56. package/dist/managers/ClipboardManager.d.ts +31 -0
  57. package/dist/managers/ClipboardManager.js +147 -0
  58. package/dist/paged-layout/paragraphFlash.d.ts +17 -0
  59. package/dist/paged-layout/paragraphFlash.js +56 -0
  60. package/dist/paged-layout/paragraphFlashTypes.d.ts +18 -0
  61. package/dist/paged-layout/paragraphFlashTypes.js +0 -0
  62. package/dist/paged-layout/rangeProjection.d.ts +23 -1
  63. package/dist/paged-layout/rangeProjection.js +34 -1
  64. package/dist/prosemirror/cellDragSelection.d.ts +34 -0
  65. package/dist/prosemirror/cellDragSelection.js +69 -0
  66. package/dist/prosemirror/commands/sectionBreak.d.ts +15 -0
  67. package/dist/prosemirror/commands/sectionBreak.js +54 -0
  68. package/dist/prosemirror/commentIdAllocator.d.ts +32 -0
  69. package/dist/prosemirror/commentIdAllocator.js +35 -0
  70. package/dist/prosemirror/commentOps.d.ts +35 -0
  71. package/dist/prosemirror/commentOps.js +104 -0
  72. package/dist/prosemirror/conversion/toProseDoc.js +29 -6
  73. package/dist/prosemirror/extensions/index.d.ts +3 -0
  74. package/dist/prosemirror/extensions/index.js +3 -0
  75. package/dist/prosemirror/imageCommit.d.ts +42 -0
  76. package/dist/prosemirror/imageCommit.js +118 -0
  77. package/dist/prosemirror/paraText.d.ts +26 -0
  78. package/dist/prosemirror/paraText.js +73 -0
  79. package/dist/prosemirror/plugins/templateDirectives.d.ts +21 -1
  80. package/dist/prosemirror/plugins/templateDirectives.js +40 -1
  81. package/dist/prosemirror/queries.d.ts +46 -0
  82. package/dist/prosemirror/queries.js +74 -0
  83. package/dist/prosemirror/styles/styleResolver.d.ts +43 -1
  84. package/dist/prosemirror/styles/styleResolver.js +34 -0
  85. package/dist/prosemirror/tableResize.d.ts +49 -0
  86. package/dist/prosemirror/tableResize.js +162 -0
  87. package/dist/prosemirror/utils/extractTrackedChanges.d.ts +106 -0
  88. package/dist/prosemirror/utils/extractTrackedChanges.js +379 -0
  89. package/dist/prosemirror/utils/visualLineNavigation.d.ts +27 -0
  90. package/dist/prosemirror/utils/visualLineNavigation.js +217 -0
  91. package/dist/style-engine/index.d.ts +2 -2
  92. package/dist/style-engine/styleEngine.d.ts +14 -1
  93. package/dist/style-engine/styleEngine.js +15 -0
  94. package/dist/utils/colorResolver.d.ts +8 -1
  95. package/dist/utils/colorResolver.js +12 -1
  96. package/dist/utils/findVerticalScrollParent.d.ts +20 -0
  97. package/dist/utils/findVerticalScrollParent.js +30 -0
  98. package/dist/utils/fontResolver.d.ts +22 -3
  99. package/dist/utils/fontResolver.js +216 -31
  100. package/dist/utils/headerFooter.js +2 -0
  101. package/package.json +9 -1
@@ -0,0 +1,217 @@
1
+ import { findVerticalScrollParent } from "../../utils/findVerticalScrollParent.js";
2
+ import { TextSelection } from "prosemirror-state";
3
+ //#region src/prosemirror/utils/visualLineNavigation.ts
4
+ /**
5
+ * Visual line navigation helpers — implements Word/Google-Docs-style
6
+ * ArrowUp / ArrowDown with sticky X across visual lines (not just
7
+ * paragraphs). Lifted from packages/react/src/paged-editor/
8
+ * useVisualLineNavigation.ts so both adapters share the algorithm.
9
+ *
10
+ * Frontend-agnostic: takes a `getContainer: () => HTMLElement | null`
11
+ * callback and a mutable sticky-state object, returns the same
12
+ * function quartet React's hook returns.
13
+ *
14
+ * @packageDocumentation
15
+ * @internal
16
+ */
17
+ const CONTENT_LINE_SELECTOR = ".layout-page-content .layout-line";
18
+ /** @internal */
19
+ function createVisualLineState() {
20
+ return {
21
+ stickyX: null,
22
+ lastVisualLineIndex: -1
23
+ };
24
+ }
25
+ function scrollIntoViewIfNeeded(el) {
26
+ const container = findVerticalScrollParent(el);
27
+ if (!container) return;
28
+ const elRect = el.getBoundingClientRect();
29
+ const containerRect = container.getBoundingClientRect();
30
+ const margin = 40;
31
+ if (elRect.bottom > containerRect.bottom - margin) container.scrollTop += elRect.bottom - containerRect.bottom + margin;
32
+ else if (elRect.top < containerRect.top + margin) container.scrollTop -= containerRect.top - elRect.top + margin;
33
+ }
34
+ /** @internal */
35
+ function getCaretClientX(container, pmPos) {
36
+ const spans = container.querySelectorAll("span[data-pm-start][data-pm-end]");
37
+ for (const span of Array.from(spans)) {
38
+ const spanEl = span;
39
+ const pmStart = Number(spanEl.dataset["pmStart"]);
40
+ const pmEnd = Number(spanEl.dataset["pmEnd"]);
41
+ if (spanEl.classList.contains("layout-run-tab")) {
42
+ if (pmPos >= pmStart && pmPos < pmEnd) return spanEl.getBoundingClientRect().left;
43
+ continue;
44
+ }
45
+ if (pmPos >= pmStart && pmPos <= pmEnd && span.firstChild?.nodeType === Node.TEXT_NODE) {
46
+ const textNode = span.firstChild;
47
+ const charIndex = Math.min(pmPos - pmStart, textNode.length);
48
+ const ownerDoc = spanEl.ownerDocument;
49
+ if (!ownerDoc) continue;
50
+ const range = ownerDoc.createRange();
51
+ range.setStart(textNode, charIndex);
52
+ range.setEnd(textNode, charIndex);
53
+ return range.getBoundingClientRect().left;
54
+ }
55
+ }
56
+ const emptyRuns = container.querySelectorAll(".layout-empty-run");
57
+ for (const emptyRun of Array.from(emptyRuns)) {
58
+ const paragraph = emptyRun.closest(".layout-paragraph");
59
+ if (!paragraph) continue;
60
+ const pmStart = Number(paragraph.dataset["pmStart"]);
61
+ const pmEnd = Number(paragraph.dataset["pmEnd"]);
62
+ if (pmPos >= pmStart && pmPos <= pmEnd) return emptyRun.getBoundingClientRect().left;
63
+ }
64
+ return null;
65
+ }
66
+ /** @internal */
67
+ function findLineElementAtPosition(container, pmPos) {
68
+ const allLines = container.querySelectorAll(CONTENT_LINE_SELECTOR);
69
+ for (const line of Array.from(allLines)) {
70
+ const lineEl = line;
71
+ const spans = lineEl.querySelectorAll("span[data-pm-start][data-pm-end]");
72
+ for (const span of Array.from(spans)) {
73
+ const s = span;
74
+ const start = Number(s.dataset["pmStart"]);
75
+ const end = Number(s.dataset["pmEnd"]);
76
+ if (pmPos >= start && pmPos <= end) return lineEl;
77
+ }
78
+ }
79
+ for (const line of Array.from(allLines)) {
80
+ const lineEl = line;
81
+ const paragraph = lineEl.closest(".layout-paragraph");
82
+ if (!paragraph) continue;
83
+ const pStart = Number(paragraph.dataset["pmStart"]);
84
+ const pEnd = Number(paragraph.dataset["pmEnd"]);
85
+ if (pmPos >= pStart && pmPos <= pEnd) {
86
+ if (paragraph.querySelector(".layout-line") === lineEl) return lineEl;
87
+ }
88
+ }
89
+ return null;
90
+ }
91
+ /** @internal */
92
+ function findPositionOnLineAtClientX(lineEl, clientX) {
93
+ const spans = lineEl.querySelectorAll("span[data-pm-start][data-pm-end]");
94
+ if (spans.length === 0) {
95
+ const paragraph = lineEl.closest(".layout-paragraph");
96
+ if (paragraph?.dataset["pmStart"]) return Number(paragraph.dataset["pmStart"]) + 1;
97
+ return null;
98
+ }
99
+ for (const span of Array.from(spans)) {
100
+ const spanEl = span;
101
+ const rect = spanEl.getBoundingClientRect();
102
+ const pmStart = Number(spanEl.dataset["pmStart"]);
103
+ const pmEnd = Number(spanEl.dataset["pmEnd"]);
104
+ if (spanEl.classList.contains("layout-run-tab")) {
105
+ if (clientX >= rect.left && clientX <= rect.right) return clientX < (rect.left + rect.right) / 2 ? pmStart : pmEnd;
106
+ continue;
107
+ }
108
+ if (clientX >= rect.left && clientX <= rect.right) {
109
+ const textNode = spanEl.firstChild;
110
+ if (!textNode || textNode.nodeType !== Node.TEXT_NODE) return pmStart;
111
+ const text = textNode;
112
+ const ownerDoc = spanEl.ownerDocument;
113
+ if (!ownerDoc) return pmStart;
114
+ let lo = 0;
115
+ let hi = text.length;
116
+ while (lo < hi) {
117
+ const mid = Math.floor((lo + hi) / 2);
118
+ const r = ownerDoc.createRange();
119
+ r.setStart(text, mid);
120
+ r.setEnd(text, mid);
121
+ if (clientX < r.getBoundingClientRect().left) hi = mid;
122
+ else lo = mid + 1;
123
+ }
124
+ if (lo > 0 && lo <= text.length) {
125
+ const r = ownerDoc.createRange();
126
+ r.setStart(text, lo - 1);
127
+ r.setEnd(text, lo - 1);
128
+ const leftX = r.getBoundingClientRect().left;
129
+ r.setStart(text, Math.min(lo, text.length));
130
+ r.setEnd(text, Math.min(lo, text.length));
131
+ const rightX = r.getBoundingClientRect().left;
132
+ if (Math.abs(clientX - leftX) < Math.abs(clientX - rightX)) return pmStart + (lo - 1);
133
+ }
134
+ return pmStart + Math.min(lo, pmEnd - pmStart);
135
+ }
136
+ }
137
+ let closestSpan = null;
138
+ let closestDist = Infinity;
139
+ for (const span of Array.from(spans)) {
140
+ const spanEl = span;
141
+ const rect = spanEl.getBoundingClientRect();
142
+ const dist = clientX < rect.left ? rect.left - clientX : clientX - rect.right;
143
+ if (dist < closestDist) {
144
+ closestDist = dist;
145
+ closestSpan = spanEl;
146
+ }
147
+ }
148
+ if (!closestSpan) return null;
149
+ return clientX < closestSpan.getBoundingClientRect().left ? Number(closestSpan.dataset["pmStart"]) : Number(closestSpan.dataset["pmEnd"]);
150
+ }
151
+ /**
152
+ * Handle PM ArrowUp / ArrowDown with visual-line awareness + sticky
153
+ * X. Returns true if the event was handled and PM should not run
154
+ * its default behaviour. Mutates `state` so consecutive presses
155
+ * keep the same sticky X.
156
+ *
157
+ * @internal
158
+ */
159
+ function handleVisualLineKeyDown(state, view, event, container) {
160
+ if (event.key !== "ArrowUp" && event.key !== "ArrowDown") {
161
+ if ([
162
+ "ArrowLeft",
163
+ "ArrowRight",
164
+ "Home",
165
+ "End"
166
+ ].includes(event.key) || event.key.length === 1 && !event.ctrlKey && !event.metaKey) {
167
+ state.stickyX = null;
168
+ state.lastVisualLineIndex = -1;
169
+ }
170
+ return false;
171
+ }
172
+ if (event.ctrlKey || event.metaKey) {
173
+ state.stickyX = null;
174
+ state.lastVisualLineIndex = -1;
175
+ return false;
176
+ }
177
+ if (!container) return false;
178
+ const allLines = Array.from(container.querySelectorAll(CONTENT_LINE_SELECTOR));
179
+ if (allLines.length === 0) return false;
180
+ const { from, anchor } = view.state.selection;
181
+ if (state.stickyX === null) {
182
+ const clientX = getCaretClientX(container, from);
183
+ if (clientX === null) return false;
184
+ state.stickyX = clientX;
185
+ }
186
+ let currentIndex;
187
+ if (state.lastVisualLineIndex >= 0 && state.lastVisualLineIndex < allLines.length) currentIndex = state.lastVisualLineIndex;
188
+ else {
189
+ const currentLine = findLineElementAtPosition(container, from);
190
+ if (!currentLine) return false;
191
+ currentIndex = allLines.indexOf(currentLine);
192
+ if (currentIndex === -1) return false;
193
+ }
194
+ const targetIndex = event.key === "ArrowUp" ? currentIndex - 1 : currentIndex + 1;
195
+ if (targetIndex < 0 || targetIndex >= allLines.length) {
196
+ state.lastVisualLineIndex = -1;
197
+ return false;
198
+ }
199
+ const targetLine = allLines[targetIndex];
200
+ const newPos = findPositionOnLineAtClientX(targetLine, state.stickyX);
201
+ if (newPos === null) return false;
202
+ state.lastVisualLineIndex = targetIndex;
203
+ const { state: pmState, dispatch } = view;
204
+ const clampedPos = Math.max(0, Math.min(newPos, pmState.doc.content.size));
205
+ try {
206
+ const sel = event.shiftKey ? TextSelection.create(pmState.doc, anchor, clampedPos) : TextSelection.create(pmState.doc, clampedPos);
207
+ dispatch(pmState.tr.setSelection(sel));
208
+ } catch {
209
+ const $newPos = pmState.doc.resolve(clampedPos);
210
+ const sel = event.shiftKey ? TextSelection.between(pmState.doc.resolve(anchor), $newPos) : TextSelection.near($newPos);
211
+ dispatch(pmState.tr.setSelection(sel));
212
+ }
213
+ scrollIntoViewIfNeeded(targetLine);
214
+ return true;
215
+ }
216
+ //#endregion
217
+ export { createVisualLineState, findLineElementAtPosition, findPositionOnLineAtClientX, getCaretClientX, handleVisualLineKeyDown };
@@ -1,3 +1,3 @@
1
- import { ResolvedParagraphStyle } from "../prosemirror/styles/styleResolver.js";
1
+ import { ResolvedParagraphStyle, TableCellParagraphSpacingOverlay } from "../prosemirror/styles/styleResolver.js";
2
2
  import { StyleEngine, StyleEngineCacheStats, StyleEngineOptions, createStyleEngine } from "./styleEngine.js";
3
- export { type ResolvedParagraphStyle, type StyleEngine, type StyleEngineCacheStats, type StyleEngineOptions, createStyleEngine };
3
+ export { type ResolvedParagraphStyle, type StyleEngine, type StyleEngineCacheStats, type StyleEngineOptions, type TableCellParagraphSpacingOverlay, createStyleEngine };
@@ -1,5 +1,5 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
- import { ResolvedParagraphStyle } from "../prosemirror/styles/styleResolver.js";
2
+ import { ResolvedParagraphStyle, TableCellParagraphSpacingOverlay } from "../prosemirror/styles/styleResolver.js";
3
3
 
4
4
  //#region src/style-engine/styleEngine.d.ts
5
5
  /** Engine construction options. */
@@ -41,6 +41,19 @@ type StyleEngine = {
41
41
  * Returned object is cached and must not be mutated.
42
42
  */
43
43
  resolveParagraphStyle: (styleId: string | undefined | null) => ResolvedParagraphStyle;
44
+ /**
45
+ * Resolve the paragraph cascade for a paragraph inside a table cell,
46
+ * layering the enclosing table style's paragraph-spacing fields between
47
+ * docDefaults and the paragraph's own style chain — see
48
+ * {@link StyleResolver.resolveParagraphStyleInTable}.
49
+ *
50
+ * Pass the *same* `tableParagraphOverlay` object reference for every
51
+ * paragraph governed by the same table style region (folio's table
52
+ * conversion already computes it once per region) — the cache below keys
53
+ * on object identity, so a fresh object per call defeats memoization.
54
+ * Returned object is cached and must not be mutated.
55
+ */
56
+ resolveParagraphStyleInTable: (styleId: string | undefined | null, tableParagraphOverlay: TableCellParagraphSpacingOverlay | undefined) => ResolvedParagraphStyle;
44
57
  /**
45
58
  * Resolve a run/character style with docDefaults applied.
46
59
  * Returns `undefined` when the cascade yields nothing.
@@ -23,6 +23,7 @@ function createStyleEngine(styleDefinitions, options) {
23
23
  const paragraphCache = /* @__PURE__ */ new Map();
24
24
  const runCache = /* @__PURE__ */ new Map();
25
25
  const ownPropsCache = /* @__PURE__ */ new Map();
26
+ let tableOverlayCache = /* @__PURE__ */ new WeakMap();
26
27
  let hits = 0;
27
28
  let misses = 0;
28
29
  const memoize = (cache, key, compute) => {
@@ -40,6 +41,18 @@ function createStyleEngine(styleDefinitions, options) {
40
41
  cache.set(key, { value });
41
42
  return value;
42
43
  };
44
+ const memoizeInTable = (styleId, tableParagraphOverlay) => {
45
+ if (!cacheEnabled) {
46
+ misses += 1;
47
+ return resolver.resolveParagraphStyleInTable(styleId, tableParagraphOverlay);
48
+ }
49
+ let overlayCache = tableOverlayCache.get(tableParagraphOverlay);
50
+ if (!overlayCache) {
51
+ overlayCache = /* @__PURE__ */ new Map();
52
+ tableOverlayCache.set(tableParagraphOverlay, overlayCache);
53
+ }
54
+ return memoize(overlayCache, cacheKey(styleId), () => resolver.resolveParagraphStyleInTable(styleId, tableParagraphOverlay));
55
+ };
43
56
  return {
44
57
  getStyle: (styleId) => resolver.getStyle(styleId),
45
58
  hasStyle: (styleId) => resolver.hasStyle(styleId),
@@ -50,12 +63,14 @@ function createStyleEngine(styleDefinitions, options) {
50
63
  getParagraphStyles: () => resolver.getParagraphStyles(),
51
64
  getTableStyles: () => resolver.getTableStyles(),
52
65
  resolveParagraphStyle: (styleId) => memoize(paragraphCache, cacheKey(styleId), () => resolver.resolveParagraphStyle(styleId)),
66
+ resolveParagraphStyleInTable: (styleId, tableParagraphOverlay) => tableParagraphOverlay === void 0 ? memoize(paragraphCache, cacheKey(styleId), () => resolver.resolveParagraphStyleInTable(styleId, void 0)) : memoizeInTable(styleId, tableParagraphOverlay),
53
67
  resolveRunStyle: (styleId) => memoize(runCache, cacheKey(styleId), () => resolver.resolveRunStyle(styleId)),
54
68
  getRunStyleOwnProperties: (styleId) => memoize(ownPropsCache, cacheKey(styleId), () => resolver.getRunStyleOwnProperties(styleId)),
55
69
  invalidate: () => {
56
70
  paragraphCache.clear();
57
71
  runCache.clear();
58
72
  ownPropsCache.clear();
73
+ tableOverlayCache = /* @__PURE__ */ new WeakMap();
59
74
  hits = 0;
60
75
  misses = 0;
61
76
  },
@@ -16,6 +16,13 @@ declare function resolveColor(color: document_d_exports.ColorValue | undefined |
16
16
  * @param highlight - Highlight color name (e.g., "yellow", "cyan")
17
17
  * @returns CSS color string or empty string for "none"
18
18
  */
19
+ /**
20
+ * Resolve a `ColorValue` to a bare 6-digit hex string (no leading `#`), or
21
+ * `undefined` for an `auto`/absent color. Theme colors resolve through the
22
+ * document theme; explicit `rgb` values are upper-cased. Used by adapters that
23
+ * need a normalized hex (e.g. comparing a cell fill against a swatch).
24
+ */
25
+ declare function resolveColorToHex(color: document_d_exports.ColorValue | undefined | null, theme: document_d_exports.Theme | null | undefined): string | undefined;
19
26
  declare function resolveHighlightColor(highlight: string | undefined): string;
20
27
  /**
21
28
  * Resolve a shading fill or pattern color to CSS
@@ -155,4 +162,4 @@ declare function generateThemeTintShadeMatrix(colorScheme?: document_d_exports.T
155
162
  */
156
163
  declare function colorsEqual(color1: document_d_exports.ColorValue | undefined | null, color2: document_d_exports.ColorValue | undefined | null, theme: document_d_exports.Theme | null | undefined): boolean;
157
164
  //#endregion
158
- export { STANDARD_TEXT_COLORS, ThemeMatrixCell, blendColors, colorsEqual, createRgbColor, createThemeColor, darkenColor, ensureHexPrefix, generateThemeTintShadeMatrix, getContrastingColor, getThemeTintShadeHex, isBlack, isWhite, lightenColor, parseColorString, resolveColor, resolveHighlightColor, resolveHighlightToCss, resolveShadingColor };
165
+ export { STANDARD_TEXT_COLORS, ThemeMatrixCell, blendColors, colorsEqual, createRgbColor, createThemeColor, darkenColor, ensureHexPrefix, generateThemeTintShadeMatrix, getContrastingColor, getThemeTintShadeHex, isBlack, isWhite, lightenColor, parseColorString, resolveColor, resolveColorToHex, resolveHighlightColor, resolveHighlightToCss, resolveShadingColor };
@@ -218,6 +218,17 @@ function resolveColor(color, theme, defaultColor = "000000") {
218
218
  * @param highlight - Highlight color name (e.g., "yellow", "cyan")
219
219
  * @returns CSS color string or empty string for "none"
220
220
  */
221
+ /**
222
+ * Resolve a `ColorValue` to a bare 6-digit hex string (no leading `#`), or
223
+ * `undefined` for an `auto`/absent color. Theme colors resolve through the
224
+ * document theme; explicit `rgb` values are upper-cased. Used by adapters that
225
+ * need a normalized hex (e.g. comparing a cell fill against a swatch).
226
+ */
227
+ function resolveColorToHex(color, theme) {
228
+ if (!color || color.auto) return void 0;
229
+ if (color.themeColor && theme) return resolveColor(color, theme).slice(1);
230
+ if (color.rgb && color.rgb !== "auto") return color.rgb.toUpperCase().replace(/^#/, "");
231
+ }
221
232
  function resolveHighlightColor(highlight) {
222
233
  if (!highlight || highlight === "none") return "";
223
234
  const hex = HIGHLIGHT_COLORS[highlight];
@@ -554,4 +565,4 @@ function colorsEqual(color1, color2, theme) {
554
565
  return resolveColor(color1, theme).toUpperCase() === resolveColor(color2, theme).toUpperCase();
555
566
  }
556
567
  //#endregion
557
- export { STANDARD_TEXT_COLORS, blendColors, colorsEqual, createRgbColor, createThemeColor, darkenColor, ensureHexPrefix, generateThemeTintShadeMatrix, getContrastingColor, getThemeTintShadeHex, isBlack, isWhite, lightenColor, parseColorString, resolveColor, resolveHighlightColor, resolveHighlightToCss, resolveShadingColor };
568
+ export { STANDARD_TEXT_COLORS, blendColors, colorsEqual, createRgbColor, createThemeColor, darkenColor, ensureHexPrefix, generateThemeTintShadeMatrix, getContrastingColor, getThemeTintShadeHex, isBlack, isWhite, lightenColor, parseColorString, resolveColor, resolveColorToHex, resolveHighlightColor, resolveHighlightToCss, resolveShadingColor };
@@ -0,0 +1,20 @@
1
+ //#region src/utils/findVerticalScrollParent.d.ts
2
+ /**
3
+ * Pure DOM helpers — locate the element that vertically scrolls the paginated
4
+ * editor. Shared by scroll-to-position, arrow-key line navigation, and drag
5
+ * auto-scroll so every framework adapter uses the same logic.
6
+ */
7
+ /**
8
+ * First ancestor of `el` with `overflow-y: auto|scroll` and a scrollable
9
+ * overflow height. The walk starts at `el.parentElement` (it does not treat
10
+ * `el` itself as the scroller). Returns `null` when no such ancestor exists
11
+ * before `document.documentElement`.
12
+ */
13
+ declare function findVerticalScrollParent(el: HTMLElement): HTMLElement | null;
14
+ /**
15
+ * Same as {@link findVerticalScrollParent} but falls back to
16
+ * `document.documentElement` so callers always get a valid scroll target.
17
+ */
18
+ declare function findVerticalScrollParentOrRoot(el: HTMLElement): HTMLElement;
19
+ //#endregion
20
+ export { findVerticalScrollParent, findVerticalScrollParentOrRoot };
@@ -0,0 +1,30 @@
1
+ //#region src/utils/findVerticalScrollParent.ts
2
+ /**
3
+ * Pure DOM helpers — locate the element that vertically scrolls the paginated
4
+ * editor. Shared by scroll-to-position, arrow-key line navigation, and drag
5
+ * auto-scroll so every framework adapter uses the same logic.
6
+ */
7
+ /**
8
+ * First ancestor of `el` with `overflow-y: auto|scroll` and a scrollable
9
+ * overflow height. The walk starts at `el.parentElement` (it does not treat
10
+ * `el` itself as the scroller). Returns `null` when no such ancestor exists
11
+ * before `document.documentElement`.
12
+ */
13
+ function findVerticalScrollParent(el) {
14
+ let parent = el.parentElement;
15
+ while (parent && parent !== document.documentElement) {
16
+ const { overflowY } = getComputedStyle(parent);
17
+ if ((overflowY === "auto" || overflowY === "scroll") && parent.scrollHeight > parent.clientHeight + 1) return parent;
18
+ parent = parent.parentElement;
19
+ }
20
+ return null;
21
+ }
22
+ /**
23
+ * Same as {@link findVerticalScrollParent} but falls back to
24
+ * `document.documentElement` so callers always get a valid scroll target.
25
+ */
26
+ function findVerticalScrollParentOrRoot(el) {
27
+ return findVerticalScrollParent(el) ?? document.documentElement;
28
+ }
29
+ //#endregion
30
+ export { findVerticalScrollParent, findVerticalScrollParentOrRoot };
@@ -7,7 +7,7 @@ type ResolvedFont = {
7
7
  /** Google Font name to load (null if no mapping available) */googleFont: string | null; /** CSS font-family value with proper fallback stack */
8
8
  cssFallback: string; /** Original font name from the DOCX */
9
9
  originalFont: string; /** Whether this font has a Google Fonts equivalent */
10
- hasGoogleEquivalent: boolean; /** OS/2 single-line ratio: (usWinAscent + usWinDescent) / unitsPerEm (no external leading) */
10
+ hasGoogleEquivalent: boolean; /** Single-line height ratio (single-line height ÷ font size). See `FontLineHeight`. */
11
11
  singleLineRatio: number;
12
12
  };
13
13
  /**
@@ -15,10 +15,29 @@ type ResolvedFont = {
15
15
  */
16
16
  type FontCategory = "sans-serif" | "serif" | "monospace" | "cursive" | "fantasy" | "system-ui";
17
17
  /**
18
- * Default OS/2 single-line ratio for unmapped fonts.
18
+ * Default single-line ratio for unmapped fonts.
19
19
  * Middle of the common range (1.07–1.27) for standard DOCX fonts.
20
20
  */
21
21
  declare const DEFAULT_SINGLE_LINE_RATIO = 1.15;
22
+ /**
23
+ * True when `family` is a known East-Asian (CJK) typeface — a direct
24
+ * `FONT_MAPPINGS` CJK entry or a romanized alias of one. Used by the measurer
25
+ * to decide whether a run's `w:eastAsia` font can supply the line height for
26
+ * CJK text, or whether Word would font-link to a default East-Asian face
27
+ * instead (see `CJK_FALLBACK_FONT_FAMILY`). Unmapped families return false:
28
+ * we cannot know their metrics, so the caller falls back.
29
+ */
30
+ declare function isCjkFont(family: string): boolean;
31
+ /**
32
+ * Font family whose `singleLineRatio` stands in for Word's default East-Asian
33
+ * face when a CJK-text run declares no usable CJK font (its `w:eastAsia` slot
34
+ * is absent or names a Latin face like "Century"). Word font-links those
35
+ * glyphs to the OS default East-Asian font and takes THAT face's taller line
36
+ * height; MS Mincho carries the measured ratio for it
37
+ * (`JP_MEASURED_LINE_HEIGHT`, ≈1.303). Line-height resolution only — never
38
+ * used for width measurement or painting.
39
+ */
40
+ declare const CJK_FALLBACK_FONT_FAMILY = "MS Mincho";
22
41
  declare function resolveFontFamily(docxFontName: string): ResolvedFont;
23
42
  /**
24
43
  * Resolve a theme font reference to actual font names
@@ -58,4 +77,4 @@ declare function getGoogleFontEquivalent(docxFontName: string): string | null;
58
77
  */
59
78
  declare function hasGoogleFontEquivalent(docxFontName: string): boolean;
60
79
  //#endregion
61
- export { DEFAULT_SINGLE_LINE_RATIO, ResolvedFont, buildFontFamilyString, getGoogleFontEquivalent, getGoogleFontsToLoad, hasGoogleFontEquivalent, resolveFontFamily, resolveThemeFont };
80
+ export { CJK_FALLBACK_FONT_FAMILY, DEFAULT_SINGLE_LINE_RATIO, ResolvedFont, buildFontFamilyString, getGoogleFontEquivalent, getGoogleFontsToLoad, hasGoogleFontEquivalent, isCjkFont, resolveFontFamily, resolveThemeFont };