documentation-hub 5.7.2

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 (271) hide show
  1. package/.eslintrc.json +43 -0
  2. package/.github/workflows/build.yml +64 -0
  3. package/.github/workflows/ci.yml +39 -0
  4. package/.vscode/extensions.json +3 -0
  5. package/Current.md +97 -0
  6. package/DocHub_Image.png +0 -0
  7. package/README.md +666 -0
  8. package/USER_GUIDE.md +1173 -0
  9. package/Updater.md +311 -0
  10. package/build/256x256.png +0 -0
  11. package/build/512x512.png +0 -0
  12. package/build/app-update.yml +4 -0
  13. package/build/create-icon.js +208 -0
  14. package/build/icon.ico +0 -0
  15. package/build/icon.png +0 -0
  16. package/build/icon_1024x1024.png +0 -0
  17. package/dist/assets/Analytics-BpsG9895.js +1 -0
  18. package/dist/assets/Card-IAZin8kp.js +1 -0
  19. package/dist/assets/CurrentSession-B-rFkHvf.js +12 -0
  20. package/dist/assets/Dashboard-C_5gMb0q.js +1 -0
  21. package/dist/assets/Documents-CqZ25axS.js +1 -0
  22. package/dist/assets/Input-l89xwXBi.js +1 -0
  23. package/dist/assets/Reporting-DqdHJY_a.js +1 -0
  24. package/dist/assets/Search-XNbu5z_3.js +1 -0
  25. package/dist/assets/SessionManager-lH9hZfzH.js +1 -0
  26. package/dist/assets/Sessions-ClZOPYNc.js +1 -0
  27. package/dist/assets/Settings-DUEHGURa.js +11 -0
  28. package/dist/assets/index-8xUe8ptc.js +24 -0
  29. package/dist/assets/index-RYyJqF7O.css +1 -0
  30. package/dist/assets/path-BkOl0AGO.js +1 -0
  31. package/dist/assets/promises-ID_B9S-h.js +1 -0
  32. package/dist/assets/urlHelpers-TvgahX0r.js +1 -0
  33. package/dist/assets/useToast-yRSO1dkm.js +1 -0
  34. package/dist/assets/vendor-charts-RkGK5ROP.js +36 -0
  35. package/dist/assets/vendor-db-l0sNRNKZ.js +1 -0
  36. package/dist/assets/vendor-react-BVZ_anCF.js +4 -0
  37. package/dist/assets/vendor-search-Dw8P0qyA.js +1 -0
  38. package/dist/assets/vendor-ui-BU7NfluV.js +53 -0
  39. package/dist/electron/PowerAutomateApiService-LfW09ZGr.js +147 -0
  40. package/dist/electron/main-CXkNtyv-.js +19789 -0
  41. package/dist/electron/main.js +5 -0
  42. package/dist/electron/preload.js +1 -0
  43. package/dist/icon.png +0 -0
  44. package/dist/index.html +27 -0
  45. package/docs/CODEBASE_ANALYSIS_REPORT.md +309 -0
  46. package/docs/DEBUG_LOGGING_GUIDE.md +244 -0
  47. package/docs/README.md +115 -0
  48. package/docs/TOC_WIRING_GUIDE.md +344 -0
  49. package/docs/analysis/Bullet_Symbol_Bug_Analysis.md +136 -0
  50. package/docs/analysis/DOCXMLATER_ANALYSIS_SUMMARY.txt +169 -0
  51. package/docs/analysis/Document_Processing_Issues_Analysis.md +704 -0
  52. package/docs/analysis/FIELD_PRESERVATION_ANALYSIS.md +1200 -0
  53. package/docs/analysis/INDENTATION_PRESERVE_ANALYSIS.md +181 -0
  54. package/docs/analysis/INDENTATION_PRESERVE_IMPLEMENTATION.md +207 -0
  55. package/docs/analysis/List_Implementation.md +206 -0
  56. package/docs/analysis/List_Implementation_Accuracy_Report.md +366 -0
  57. package/docs/analysis/PROCESSING_OPTIONS_UI_UPDATES.md +220 -0
  58. package/docs/analysis/RefactorStyles.md +852 -0
  59. package/docs/analysis/STYLE_PARAMETER_ENHANCEMENT.md +143 -0
  60. package/docs/analysis/docxmlater-comparison-todo-2025-11-13.md +636 -0
  61. package/docs/analysis/docxmlater-implementation-analysis-2025-11-13.md +340 -0
  62. package/docs/analysis/docxmlater-template_ui-integration-analysis.md +263 -0
  63. package/docs/analysis/github-issues-to-create.md +237 -0
  64. package/docs/api/API_README.md +538 -0
  65. package/docs/api/API_REFERENCE.md +751 -0
  66. package/docs/api/TYPE_DEFINITIONS.md +869 -0
  67. package/docs/architecture/FONT_EMBEDDING_GUIDE.md +318 -0
  68. package/docs/architecture/docxmlater-functions-and-structure.md +726 -0
  69. package/docs/docxmlater-readme.md +1341 -0
  70. package/docs/fixes/EXECUTION_LOG_TEST_BASE.md +573 -0
  71. package/docs/fixes/HYPERLINK_TEXT_SANITIZATION.md +253 -0
  72. package/docs/fixes/README.md +37 -0
  73. package/docs/github-issues/issue-1-body.md +125 -0
  74. package/docs/github-issues/issue-10-body.md +850 -0
  75. package/docs/github-issues/issue-2-body.md +200 -0
  76. package/docs/github-issues/issue-3-body.md +270 -0
  77. package/docs/github-issues/issue-4-body.md +169 -0
  78. package/docs/github-issues/issue-5-body.md +173 -0
  79. package/docs/github-issues/issue-6-body.md +158 -0
  80. package/docs/github-issues/issue-7-body.md +171 -0
  81. package/docs/github-issues/issue-8-body.md +407 -0
  82. package/docs/github-issues/issue-9-body.md +515 -0
  83. package/docs/github-issues/issue-tracker.md +274 -0
  84. package/docs/github-issues/predictive-analysis-2025-10-18.md +2131 -0
  85. package/docs/implementation/List_Framework_Refactor_Plan.md +336 -0
  86. package/docs/implementation/PRIMARY_TEXT_COLOR_FEATURE.md +217 -0
  87. package/docs/implementation/RELEASE_PLAN_v2.1.0.md +362 -0
  88. package/docs/implementation/RefactorStyles.md +588 -0
  89. package/docs/implementation/implement-plan.md +489 -0
  90. package/docs/implementation/missing-helpers-implementation.md +391 -0
  91. package/docs/implementation/refactor-plan.md +520 -0
  92. package/docs/implementation/session-implementation-complete.md +233 -0
  93. package/docs/implementation/session-management-plan.md +250 -0
  94. package/docs/setup-checklist.md +77 -0
  95. package/docs/versions/changelog.md +345 -0
  96. package/electron/customUpdater.ts +656 -0
  97. package/electron/main.ts +2441 -0
  98. package/electron/memoryConfig.ts +187 -0
  99. package/electron/preload.ts +394 -0
  100. package/electron/proxyConfig.ts +340 -0
  101. package/electron/services/BackupService.ts +452 -0
  102. package/electron/services/DictionaryService.ts +402 -0
  103. package/electron/services/LocalDictionaryLookupService.ts +147 -0
  104. package/electron/services/PowerAutomateApiService.ts +231 -0
  105. package/electron/services/SharePointSyncService.ts +474 -0
  106. package/electron/windowsCertStore.ts +427 -0
  107. package/electron/zscalerConfig.ts +381 -0
  108. package/eslint.config.js +92 -0
  109. package/jest.config.js +52 -0
  110. package/package.json +214 -0
  111. package/postcss.config.mjs +6 -0
  112. package/public/icon.png +0 -0
  113. package/publish-release.ps1 +5 -0
  114. package/renovate.json +30 -0
  115. package/src/App.tsx +216 -0
  116. package/src/__mocks__/p-limit.js +12 -0
  117. package/src/__mocks__/styleMock.js +1 -0
  118. package/src/components/common/BugReportButton.tsx +44 -0
  119. package/src/components/common/BugReportDialog.tsx +193 -0
  120. package/src/components/common/Button.tsx +153 -0
  121. package/src/components/common/Card.tsx +86 -0
  122. package/src/components/common/ColorPickerDialog.tsx +177 -0
  123. package/src/components/common/ConfirmDialog.tsx +96 -0
  124. package/src/components/common/DebugConsole.tsx +275 -0
  125. package/src/components/common/EmptyState.tsx +183 -0
  126. package/src/components/common/ErrorBoundary.tsx +98 -0
  127. package/src/components/common/ErrorDetailsDialog.tsx +153 -0
  128. package/src/components/common/ErrorFallback.tsx +218 -0
  129. package/src/components/common/Input.tsx +109 -0
  130. package/src/components/common/Skeleton.tsx +184 -0
  131. package/src/components/common/SplashScreen.tsx +81 -0
  132. package/src/components/common/Toast.tsx +155 -0
  133. package/src/components/common/Tooltip.tsx +79 -0
  134. package/src/components/common/UpdateNotification.tsx +320 -0
  135. package/src/components/comparison/ComparisonWindow.tsx +374 -0
  136. package/src/components/comparison/SideBySideDiff.tsx +486 -0
  137. package/src/components/comparison/index.ts +8 -0
  138. package/src/components/document/DocumentUploader.tsx +288 -0
  139. package/src/components/document/HyperlinkPreview.tsx +430 -0
  140. package/src/components/document/HyperlinkService.md +1484 -0
  141. package/src/components/document/Hyperlink_Technical_Documentation.md +496 -0
  142. package/src/components/document/InlineChangesView.tsx +707 -0
  143. package/src/components/document/ProcessingProgress.tsx +303 -0
  144. package/src/components/document/ProcessingResults.tsx +256 -0
  145. package/src/components/document/TrackedChangesDetail.tsx +530 -0
  146. package/src/components/document/TrackedChangesPanel.tsx +546 -0
  147. package/src/components/document/VirtualDocumentList.tsx +240 -0
  148. package/src/components/editor/DocumentEditor.tsx +723 -0
  149. package/src/components/editor/DocumentEditorModal.tsx +640 -0
  150. package/src/components/editor/EditorQuickActions.tsx +502 -0
  151. package/src/components/editor/EditorToolbar.tsx +312 -0
  152. package/src/components/editor/TableEditor.tsx +926 -0
  153. package/src/components/editor/index.ts +18 -0
  154. package/src/components/layout/Header.tsx +190 -0
  155. package/src/components/layout/Sidebar.tsx +313 -0
  156. package/src/components/layout/TitleBar.tsx +190 -0
  157. package/src/components/navigation/CommandPalette.tsx +233 -0
  158. package/src/components/navigation/KeyboardShortcutsModal.tsx +173 -0
  159. package/src/components/sessions/ChangeItem.tsx +408 -0
  160. package/src/components/sessions/ChangeViewer.tsx +1155 -0
  161. package/src/components/sessions/DocumentComparisonModal.tsx +314 -0
  162. package/src/components/sessions/ProcessingOptions.tsx +297 -0
  163. package/src/components/sessions/ReplacementsTab.tsx +438 -0
  164. package/src/components/sessions/RevisionHandlingOptions.tsx +87 -0
  165. package/src/components/sessions/SessionManager.tsx +188 -0
  166. package/src/components/sessions/StylesEditor.tsx +1335 -0
  167. package/src/components/sessions/TabContainer.tsx +151 -0
  168. package/src/components/sessions/VirtualSessionList.tsx +157 -0
  169. package/src/components/sessions/sessionToProcessorManager.tsx +420 -0
  170. package/src/components/settings/CertificateManager.tsx +410 -0
  171. package/src/components/settings/SegmentedControl.tsx +88 -0
  172. package/src/components/settings/SettingRow.tsx +52 -0
  173. package/src/contexts/GlobalStatsContext.tsx +396 -0
  174. package/src/contexts/SessionContext.tsx +2129 -0
  175. package/src/contexts/ThemeContext.tsx +428 -0
  176. package/src/contexts/UserSettingsContext.tsx +290 -0
  177. package/src/contexts/__tests__/GlobalStatsContext.test.tsx +390 -0
  178. package/src/global.d.ts +273 -0
  179. package/src/hooks/useDocumentQueue.tsx +210 -0
  180. package/src/hooks/useToast.tsx +55 -0
  181. package/src/main.tsx +10 -0
  182. package/src/pages/Analytics.tsx +386 -0
  183. package/src/pages/CurrentSession.tsx +1174 -0
  184. package/src/pages/Dashboard.tsx +319 -0
  185. package/src/pages/Documents.tsx +317 -0
  186. package/src/pages/Projects.tsx +250 -0
  187. package/src/pages/Reporting.tsx +386 -0
  188. package/src/pages/Search.tsx +349 -0
  189. package/src/pages/Sessions.tsx +285 -0
  190. package/src/pages/Settings.tsx +2662 -0
  191. package/src/services/HyperlinkService.ts +1085 -0
  192. package/src/services/document/DocXMLaterProcessor.ts +617 -0
  193. package/src/services/document/DocumentProcessingComparison.ts +856 -0
  194. package/src/services/document/DocumentSnapshotService.ts +575 -0
  195. package/src/services/document/WordDocumentProcessor.ts +10509 -0
  196. package/src/services/document/__tests__/DocXMLaterProcessor.hyperlinks.test.md +311 -0
  197. package/src/services/document/__tests__/WordDocumentProcessor.integration.test.ts +515 -0
  198. package/src/services/document/__tests__/WordDocumentProcessor.test.ts +812 -0
  199. package/src/services/document/blanklines/BlankLineManager.ts +658 -0
  200. package/src/services/document/blanklines/__tests__/paragraphChecks.test.ts +281 -0
  201. package/src/services/document/blanklines/helpers/blankLineInsertion.ts +87 -0
  202. package/src/services/document/blanklines/helpers/blankLineSnapshot.ts +251 -0
  203. package/src/services/document/blanklines/helpers/clearCustom.ts +121 -0
  204. package/src/services/document/blanklines/helpers/contextChecks.ts +117 -0
  205. package/src/services/document/blanklines/helpers/imageChecks.ts +51 -0
  206. package/src/services/document/blanklines/helpers/paragraphChecks.ts +236 -0
  207. package/src/services/document/blanklines/helpers/removeBlanksBetweenListItems.ts +91 -0
  208. package/src/services/document/blanklines/helpers/removeTrailingBlanks.ts +35 -0
  209. package/src/services/document/blanklines/helpers/tableGuards.ts +21 -0
  210. package/src/services/document/blanklines/index.ts +67 -0
  211. package/src/services/document/blanklines/rules/additionRules.ts +337 -0
  212. package/src/services/document/blanklines/rules/indentationRules.ts +317 -0
  213. package/src/services/document/blanklines/rules/removalRules.ts +362 -0
  214. package/src/services/document/blanklines/rules/ruleTypes.ts +92 -0
  215. package/src/services/document/blanklines/types.ts +29 -0
  216. package/src/services/document/helpers/ImageBorderCropper.ts +377 -0
  217. package/src/services/document/helpers/__tests__/whitespace.test.ts +272 -0
  218. package/src/services/document/helpers/whitespace.ts +117 -0
  219. package/src/services/document/list/ListNormalizer.ts +947 -0
  220. package/src/services/document/list/index.ts +45 -0
  221. package/src/services/document/list/list-detection.ts +275 -0
  222. package/src/services/document/list/list-types.ts +162 -0
  223. package/src/services/document/processors/HyperlinkProcessor.ts +370 -0
  224. package/src/services/document/processors/ListProcessor.ts +257 -0
  225. package/src/services/document/processors/StructureProcessor.ts +176 -0
  226. package/src/services/document/processors/StyleProcessor.ts +389 -0
  227. package/src/services/document/processors/TableProcessor.ts +2238 -0
  228. package/src/services/document/processors/__tests__/HyperlinkProcessor.test.ts +314 -0
  229. package/src/services/document/processors/__tests__/ListProcessor.test.ts +291 -0
  230. package/src/services/document/processors/__tests__/StructureProcessor.test.ts +257 -0
  231. package/src/services/document/processors/__tests__/TableProcessor.hlp-tips-bullets.test.ts +459 -0
  232. package/src/services/document/processors/__tests__/TableProcessor.test.ts +1604 -0
  233. package/src/services/document/processors/index.ts +28 -0
  234. package/src/services/document/types/docx-processing.ts +310 -0
  235. package/src/services/editor/EditorActionHandlers.ts +901 -0
  236. package/src/services/editor/index.ts +13 -0
  237. package/src/setupTests.ts +47 -0
  238. package/src/styles/global.css +782 -0
  239. package/src/types/backup.ts +132 -0
  240. package/src/types/dictionary.ts +125 -0
  241. package/src/types/document-processing.ts +331 -0
  242. package/src/types/docxmlater-augments.d.ts +142 -0
  243. package/src/types/editor.ts +280 -0
  244. package/src/types/electron.ts +340 -0
  245. package/src/types/globalStats.ts +155 -0
  246. package/src/types/hyperlink.ts +471 -0
  247. package/src/types/operations.ts +354 -0
  248. package/src/types/session.ts +427 -0
  249. package/src/types/settings.ts +112 -0
  250. package/src/utils/MemoryMonitor.ts +248 -0
  251. package/src/utils/cn.ts +6 -0
  252. package/src/utils/colorConvert.ts +306 -0
  253. package/src/utils/diffUtils.ts +347 -0
  254. package/src/utils/documentUtils.ts +202 -0
  255. package/src/utils/electronGuard.ts +62 -0
  256. package/src/utils/indexedDB.ts +915 -0
  257. package/src/utils/logger.ts +717 -0
  258. package/src/utils/pathSecurity.ts +232 -0
  259. package/src/utils/pathValidator.ts +236 -0
  260. package/src/utils/processingTimeEstimator.ts +153 -0
  261. package/src/utils/safeJsonParse.ts +62 -0
  262. package/src/utils/textSanitizer.ts +162 -0
  263. package/src/utils/urlHelpers.ts +304 -0
  264. package/src/utils/urlPatterns.ts +198 -0
  265. package/src/utils/urlSanitizer.ts +152 -0
  266. package/src/vite-env.d.ts +11 -0
  267. package/tsconfig.electron.json +19 -0
  268. package/tsconfig.json +36 -0
  269. package/tsconfig.node.json +12 -0
  270. package/typedoc.json +45 -0
  271. package/vite.config.ts +152 -0
@@ -0,0 +1,67 @@
1
+ /**
2
+ * BlankLineManager module - Rule-based blank line management for DOCX documents.
3
+ *
4
+ * This module manages blank line operations using a rule-based engine that
5
+ * applies explicit removal/addition rules first, then preserves original
6
+ * blank lines where no rule matched.
7
+ */
8
+
9
+ // Main orchestrator
10
+ export { BlankLineManager, blankLineManager } from "./BlankLineManager";
11
+
12
+ // Types
13
+ export type { BlankLineOptions } from "./types";
14
+ export { DEFAULT_BLANK_LINE_OPTIONS } from "./types";
15
+
16
+ // Rule engine types
17
+ export type {
18
+ RuleContext,
19
+ BlankLineRule,
20
+ BlankLineProcessingOptions,
21
+ RuleEngineResult,
22
+ } from "./rules/ruleTypes";
23
+
24
+ // Snapshot system
25
+ export type { BlankLineSnapshot } from "./helpers/blankLineSnapshot";
26
+ export {
27
+ captureBlankLineSnapshot,
28
+ wasOriginallyBlankAtBody,
29
+ wasOriginallyBlankInCell,
30
+ } from "./helpers/blankLineSnapshot";
31
+
32
+ // Rules
33
+ export { removalRules } from "./rules/removalRules";
34
+ export { additionRules } from "./rules/additionRules";
35
+ export { applyIndentationRules, removeSmallIndents } from "./rules/indentationRules";
36
+
37
+ // Helper functions
38
+ export {
39
+ isParagraphBlank,
40
+ startsWithBoldColon,
41
+ isCenteredBoldText,
42
+ isTextOnlyParagraph,
43
+ isTocParagraph,
44
+ } from "./helpers/paragraphChecks";
45
+
46
+ export {
47
+ isImageSmall,
48
+ isSmallImageParagraph,
49
+ getImageRunFromParagraph,
50
+ } from "./helpers/imageChecks";
51
+
52
+ export {
53
+ isWithinListContext,
54
+ isWithinListContextInCell,
55
+ } from "./helpers/contextChecks";
56
+
57
+ export {
58
+ createBlankParagraph,
59
+ insertOrMarkBlankAfter,
60
+ insertOrMarkBlankBefore,
61
+ } from "./helpers/blankLineInsertion";
62
+
63
+ export { clearCustom } from "./helpers/clearCustom";
64
+
65
+ export { removeTrailingBlanksInTableCells } from "./helpers/removeTrailingBlanks";
66
+
67
+ export { removeBlanksBetweenListItems } from "./helpers/removeBlanksBetweenListItems";
@@ -0,0 +1,337 @@
1
+ /**
2
+ * Addition Rules - Define when blank lines must be ADDED or preserved.
3
+ *
4
+ * Addition rules are checked after removal rules. If an addition rule matches
5
+ * a position and no blank exists there, one is added.
6
+ *
7
+ * Each rule's matches() checks the context to determine if a blank line
8
+ * should exist at the position AFTER the current element.
9
+ */
10
+
11
+ import { Paragraph, Table } from "docxmlater";
12
+ import type { BlankLineRule, RuleContext } from "./ruleTypes";
13
+ import { isParagraphBlank, isTocParagraph, startsWithBoldColon, getEffectiveLeftIndent, hasNavigationHyperlink } from "../helpers/paragraphChecks";
14
+ import { getImageRunFromParagraph, isImageSmall, isSmallImageParagraph } from "../helpers/imageChecks";
15
+
16
+ /**
17
+ * Add blank line after Heading 1 style text.
18
+ */
19
+ export const afterHeading1Rule: BlankLineRule = {
20
+ id: "add-after-heading1",
21
+ action: "add",
22
+ scope: "body",
23
+ matches(ctx: RuleContext): boolean {
24
+ if (ctx.scope !== "body") return false;
25
+ if (!(ctx.currentElement instanceof Paragraph)) return false;
26
+
27
+ const style = ctx.currentElement.getStyle();
28
+ if (style !== "Heading1") return false;
29
+ if (ctx.currentElement.getText().trim() === "") return false;
30
+
31
+ // Want a blank AFTER this heading
32
+ return true;
33
+ },
34
+ };
35
+
36
+ /**
37
+ * Add blank line after the Table of Contents.
38
+ * Detects both TOC elements and TOC-styled paragraphs.
39
+ */
40
+ export const afterTocRule: BlankLineRule = {
41
+ id: "add-after-toc",
42
+ action: "add",
43
+ scope: "body",
44
+ matches(ctx: RuleContext): boolean {
45
+ if (ctx.scope !== "body") return false;
46
+ if (!(ctx.currentElement instanceof Paragraph)) return false;
47
+
48
+ // Current must be a TOC paragraph
49
+ if (!isTocParagraph(ctx.currentElement)) return false;
50
+
51
+ // Next must NOT be a TOC paragraph (this is the last TOC entry)
52
+ if (ctx.nextElement instanceof Paragraph && isTocParagraph(ctx.nextElement)) {
53
+ return false;
54
+ }
55
+
56
+ return true;
57
+ },
58
+ };
59
+
60
+ /**
61
+ * Add blank line before the first 1x1 table in the document.
62
+ */
63
+ export const beforeFirst1x1TableRule: BlankLineRule = {
64
+ id: "add-before-first-1x1-table",
65
+ action: "add",
66
+ scope: "body",
67
+ matches(ctx: RuleContext): boolean {
68
+ if (ctx.scope !== "body") return false;
69
+
70
+ // Current element must be a paragraph (the blank would go before the table,
71
+ // but we check from the perspective of "should there be a blank between prev and next")
72
+ // This rule is handled specially in the engine - it checks if nextElement is the first 1x1 table
73
+ if (!(ctx.currentElement instanceof Paragraph)) return false;
74
+
75
+ if (ctx.nextElement instanceof Table) {
76
+ const rowCount = ctx.nextElement.getRowCount();
77
+ const colCount = ctx.nextElement.getColumnCount();
78
+ if (rowCount === 1 && colCount === 1) {
79
+ // Check if this is the first 1x1 table in the document
80
+ const doc = ctx.doc;
81
+ for (let i = 0; i < ctx.currentIndex; i++) {
82
+ const el = doc.getBodyElementAt(i);
83
+ if (el instanceof Table) {
84
+ const r = el.getRowCount();
85
+ const c = el.getColumnCount();
86
+ if (r === 1 && c === 1) {
87
+ return false; // Not the first 1x1 table
88
+ }
89
+ }
90
+ }
91
+ return true; // This is before the first 1x1 table
92
+ }
93
+ }
94
+ return false;
95
+ },
96
+ };
97
+
98
+ /**
99
+ * Add blank line after 1x1 tables.
100
+ */
101
+ export const after1x1TablesRule: BlankLineRule = {
102
+ id: "add-after-1x1-tables",
103
+ action: "add",
104
+ scope: "body",
105
+ matches(ctx: RuleContext): boolean {
106
+ if (ctx.scope !== "body") return false;
107
+ if (!(ctx.currentElement instanceof Table)) return false;
108
+
109
+ const rowCount = ctx.currentElement.getRowCount();
110
+ const colCount = ctx.currentElement.getColumnCount();
111
+
112
+ return rowCount === 1 && colCount === 1;
113
+ },
114
+ };
115
+
116
+ /**
117
+ * Add blank line after tables larger than 1x1.
118
+ */
119
+ export const afterLargeTablesRule: BlankLineRule = {
120
+ id: "add-after-large-tables",
121
+ action: "add",
122
+ scope: "body",
123
+ matches(ctx: RuleContext): boolean {
124
+ if (ctx.scope !== "body") return false;
125
+ if (!(ctx.currentElement instanceof Table)) return false;
126
+
127
+ const rowCount = ctx.currentElement.getRowCount();
128
+ const colCount = ctx.currentElement.getColumnCount();
129
+
130
+ return rowCount > 1 || colCount > 1;
131
+ },
132
+ };
133
+
134
+ /**
135
+ * Add blank line ABOVE bold+colon non-indented paragraphs.
136
+ * Checks ctx.nextElement to see if the upcoming paragraph starts with bold text + colon.
137
+ */
138
+ export const aboveBoldColonNoIndentRule: BlankLineRule = {
139
+ id: "add-above-bold-colon-no-indent",
140
+ action: "add",
141
+ scope: "both",
142
+ matches(ctx: RuleContext): boolean {
143
+ if (!(ctx.nextElement instanceof Paragraph)) return false;
144
+ if (isParagraphBlank(ctx.nextElement)) return false;
145
+ if (!startsWithBoldColon(ctx.nextElement)) return false;
146
+ if (ctx.nextElement.getNumbering()) return false;
147
+
148
+ const indent = ctx.nextElement.getFormatting()?.indentation?.left;
149
+ if (indent && indent > 0) return false;
150
+
151
+ return true;
152
+ },
153
+ };
154
+
155
+ /**
156
+ * Bold+colon with no indentation where next line is NOT indented:
157
+ * add blank after, UNLESS there is a 1x1 table with "Related Document"
158
+ * text within 15 lines above.
159
+ */
160
+ export const boldColonNoIndentAfterRule: BlankLineRule = {
161
+ id: "add-after-bold-colon-no-indent",
162
+ action: "add",
163
+ scope: "both",
164
+ matches(ctx: RuleContext): boolean {
165
+ if (!(ctx.currentElement instanceof Paragraph)) return false;
166
+ if (isParagraphBlank(ctx.currentElement)) return false;
167
+ if (!startsWithBoldColon(ctx.currentElement)) return false;
168
+
169
+ // Must not be a list item
170
+ if (ctx.currentElement.getNumbering()) return false;
171
+
172
+ // Must have no indentation
173
+ const indent = ctx.currentElement.getFormatting()?.indentation?.left;
174
+ if (indent && indent > 0) return false;
175
+
176
+ // Next line must NOT be indented and NOT be a list item
177
+ if (ctx.nextElement instanceof Paragraph) {
178
+ if (ctx.nextElement.getNumbering()) return false;
179
+ const nextIndent = ctx.nextElement.getFormatting()?.indentation?.left;
180
+ if (nextIndent && nextIndent > 0) return false;
181
+ }
182
+
183
+ // Check for "Related Document" 1x1 table within 15 lines above
184
+ if (ctx.scope === "body") {
185
+ const lookbackLimit = Math.max(0, ctx.currentIndex - 15);
186
+ for (let i = ctx.currentIndex - 1; i >= lookbackLimit; i--) {
187
+ const el = ctx.doc.getBodyElementAt(i);
188
+ if (el instanceof Table) {
189
+ const r = el.getRowCount();
190
+ const c = el.getColumnCount();
191
+ if (r === 1 && c === 1) {
192
+ try {
193
+ const cell = el.getCell(0, 0);
194
+ if (cell) {
195
+ const cellText = cell
196
+ .getParagraphs()
197
+ .map((p) => p.getText())
198
+ .join(" ")
199
+ .toLowerCase();
200
+ if (cellText.includes("related document")) {
201
+ return false; // Skip - Related Document table nearby
202
+ }
203
+ }
204
+ } catch {
205
+ // Skip if cell access fails
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
211
+
212
+ return true;
213
+ },
214
+ };
215
+
216
+ /**
217
+ * Add blank line ABOVE navigation hyperlinks (text starts with "Top of" or "Return to").
218
+ */
219
+ export const aboveTopOfDocHyperlinkRule: BlankLineRule = {
220
+ id: "add-above-top-of-doc-hyperlink",
221
+ action: "add",
222
+ scope: "body",
223
+ matches(ctx: RuleContext): boolean {
224
+ if (ctx.scope !== "body") return false;
225
+ if (!(ctx.nextElement instanceof Paragraph)) return false;
226
+ return hasNavigationHyperlink(ctx.nextElement);
227
+ },
228
+ };
229
+
230
+ /**
231
+ * Add blank line after list items ONLY when the next line is NOT indented text.
232
+ */
233
+ export const afterListItemsRule: BlankLineRule = {
234
+ id: "add-after-list-items",
235
+ action: "add",
236
+ scope: "both",
237
+ matches(ctx: RuleContext): boolean {
238
+ if (!(ctx.currentElement instanceof Paragraph)) return false;
239
+ if (!ctx.currentElement.getNumbering()) return false;
240
+
241
+ // This is a list item - check if it's the LAST in its sequence
242
+ if (ctx.nextElement instanceof Paragraph) {
243
+ // If next is also a list item, don't add blank
244
+ if (ctx.nextElement.getNumbering()) return false;
245
+
246
+ // If next is a centered image, always add blank
247
+ if (ctx.nextElement.getAlignment() === "center") {
248
+ const imageRun = getImageRunFromParagraph(ctx.nextElement);
249
+ if (imageRun) return true;
250
+ }
251
+
252
+ // If next is indented text, don't add blank
253
+ // Use getEffectiveLeftIndent to also check style-inherited indentation
254
+ // (e.g., ListParagraph style defines w:left="720" but paragraph may have no direct w:ind)
255
+ const nextIndent = getEffectiveLeftIndent(ctx.nextElement, ctx.doc);
256
+ if (nextIndent > 0) return false;
257
+
258
+ // If next is a small image paragraph with indentation, don't add blank
259
+ if (isSmallImageParagraph(ctx.nextElement)) {
260
+ const imgIndent = getEffectiveLeftIndent(ctx.nextElement, ctx.doc);
261
+ if (imgIndent > 0) return false;
262
+ }
263
+ }
264
+
265
+ // Next is not indented text/list, so we need a blank after this list item
266
+ return true;
267
+ },
268
+ };
269
+
270
+ /**
271
+ * Add blank line above and below images larger than 100x100 pixels.
272
+ * In table cells, do not add blank after if image is the last element.
273
+ */
274
+ export const aboveAndBelowLargeImagesRule: BlankLineRule = {
275
+ id: "add-around-large-images",
276
+ action: "add",
277
+ scope: "both",
278
+ matches(ctx: RuleContext): boolean {
279
+ if (!(ctx.currentElement instanceof Paragraph)) return false;
280
+ if (isParagraphBlank(ctx.currentElement)) return false;
281
+
282
+ const imageRun = getImageRunFromParagraph(ctx.currentElement);
283
+ if (!imageRun) return false;
284
+
285
+ const image = imageRun.getImageElement();
286
+ if (isImageSmall(image)) return false;
287
+
288
+ // Large image - blank needed above and below
289
+ // (The engine will handle checking both directions)
290
+ return true;
291
+ },
292
+ };
293
+
294
+ /**
295
+ * Add blank line above the end-of-document warning/disclaimer.
296
+ * Matches the specific two-line disclaimer:
297
+ * "Not to Be Reproduced or Disclosed to Others Without Prior Written Approval"
298
+ * "ELECTRONIC DATA = OFFICIAL VERSION - PAPER COPY = INFORMATIONAL ONLY"
299
+ */
300
+ export const aboveWarningRule: BlankLineRule = {
301
+ id: "add-above-warning",
302
+ action: "add",
303
+ scope: "body",
304
+ matches(ctx: RuleContext): boolean {
305
+ if (ctx.scope !== "body") return false;
306
+
307
+ // Check if next element is the document disclaimer
308
+ if (!(ctx.nextElement instanceof Paragraph)) return false;
309
+
310
+ const nextText = ctx.nextElement.getText().trim().toLowerCase();
311
+ if (!nextText) return false;
312
+
313
+ const isDisclaimer =
314
+ nextText.includes("not to be reproduced") ||
315
+ nextText.includes("electronic data") ||
316
+ nextText.includes("paper copy = informational only");
317
+
318
+ return isDisclaimer;
319
+ },
320
+ };
321
+
322
+ /**
323
+ * All addition rules in evaluation order.
324
+ */
325
+ export const additionRules: BlankLineRule[] = [
326
+ afterHeading1Rule,
327
+ afterTocRule,
328
+ beforeFirst1x1TableRule,
329
+ after1x1TablesRule,
330
+ afterLargeTablesRule,
331
+ aboveBoldColonNoIndentRule,
332
+ boldColonNoIndentAfterRule,
333
+ aboveTopOfDocHyperlinkRule,
334
+ afterListItemsRule,
335
+ aboveAndBelowLargeImagesRule,
336
+ aboveWarningRule,
337
+ ];