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.
- package/.eslintrc.json +43 -0
- package/.github/workflows/build.yml +64 -0
- package/.github/workflows/ci.yml +39 -0
- package/.vscode/extensions.json +3 -0
- package/Current.md +97 -0
- package/DocHub_Image.png +0 -0
- package/README.md +666 -0
- package/USER_GUIDE.md +1173 -0
- package/Updater.md +311 -0
- package/build/256x256.png +0 -0
- package/build/512x512.png +0 -0
- package/build/app-update.yml +4 -0
- package/build/create-icon.js +208 -0
- package/build/icon.ico +0 -0
- package/build/icon.png +0 -0
- package/build/icon_1024x1024.png +0 -0
- package/dist/assets/Analytics-BpsG9895.js +1 -0
- package/dist/assets/Card-IAZin8kp.js +1 -0
- package/dist/assets/CurrentSession-B-rFkHvf.js +12 -0
- package/dist/assets/Dashboard-C_5gMb0q.js +1 -0
- package/dist/assets/Documents-CqZ25axS.js +1 -0
- package/dist/assets/Input-l89xwXBi.js +1 -0
- package/dist/assets/Reporting-DqdHJY_a.js +1 -0
- package/dist/assets/Search-XNbu5z_3.js +1 -0
- package/dist/assets/SessionManager-lH9hZfzH.js +1 -0
- package/dist/assets/Sessions-ClZOPYNc.js +1 -0
- package/dist/assets/Settings-DUEHGURa.js +11 -0
- package/dist/assets/index-8xUe8ptc.js +24 -0
- package/dist/assets/index-RYyJqF7O.css +1 -0
- package/dist/assets/path-BkOl0AGO.js +1 -0
- package/dist/assets/promises-ID_B9S-h.js +1 -0
- package/dist/assets/urlHelpers-TvgahX0r.js +1 -0
- package/dist/assets/useToast-yRSO1dkm.js +1 -0
- package/dist/assets/vendor-charts-RkGK5ROP.js +36 -0
- package/dist/assets/vendor-db-l0sNRNKZ.js +1 -0
- package/dist/assets/vendor-react-BVZ_anCF.js +4 -0
- package/dist/assets/vendor-search-Dw8P0qyA.js +1 -0
- package/dist/assets/vendor-ui-BU7NfluV.js +53 -0
- package/dist/electron/PowerAutomateApiService-LfW09ZGr.js +147 -0
- package/dist/electron/main-CXkNtyv-.js +19789 -0
- package/dist/electron/main.js +5 -0
- package/dist/electron/preload.js +1 -0
- package/dist/icon.png +0 -0
- package/dist/index.html +27 -0
- package/docs/CODEBASE_ANALYSIS_REPORT.md +309 -0
- package/docs/DEBUG_LOGGING_GUIDE.md +244 -0
- package/docs/README.md +115 -0
- package/docs/TOC_WIRING_GUIDE.md +344 -0
- package/docs/analysis/Bullet_Symbol_Bug_Analysis.md +136 -0
- package/docs/analysis/DOCXMLATER_ANALYSIS_SUMMARY.txt +169 -0
- package/docs/analysis/Document_Processing_Issues_Analysis.md +704 -0
- package/docs/analysis/FIELD_PRESERVATION_ANALYSIS.md +1200 -0
- package/docs/analysis/INDENTATION_PRESERVE_ANALYSIS.md +181 -0
- package/docs/analysis/INDENTATION_PRESERVE_IMPLEMENTATION.md +207 -0
- package/docs/analysis/List_Implementation.md +206 -0
- package/docs/analysis/List_Implementation_Accuracy_Report.md +366 -0
- package/docs/analysis/PROCESSING_OPTIONS_UI_UPDATES.md +220 -0
- package/docs/analysis/RefactorStyles.md +852 -0
- package/docs/analysis/STYLE_PARAMETER_ENHANCEMENT.md +143 -0
- package/docs/analysis/docxmlater-comparison-todo-2025-11-13.md +636 -0
- package/docs/analysis/docxmlater-implementation-analysis-2025-11-13.md +340 -0
- package/docs/analysis/docxmlater-template_ui-integration-analysis.md +263 -0
- package/docs/analysis/github-issues-to-create.md +237 -0
- package/docs/api/API_README.md +538 -0
- package/docs/api/API_REFERENCE.md +751 -0
- package/docs/api/TYPE_DEFINITIONS.md +869 -0
- package/docs/architecture/FONT_EMBEDDING_GUIDE.md +318 -0
- package/docs/architecture/docxmlater-functions-and-structure.md +726 -0
- package/docs/docxmlater-readme.md +1341 -0
- package/docs/fixes/EXECUTION_LOG_TEST_BASE.md +573 -0
- package/docs/fixes/HYPERLINK_TEXT_SANITIZATION.md +253 -0
- package/docs/fixes/README.md +37 -0
- package/docs/github-issues/issue-1-body.md +125 -0
- package/docs/github-issues/issue-10-body.md +850 -0
- package/docs/github-issues/issue-2-body.md +200 -0
- package/docs/github-issues/issue-3-body.md +270 -0
- package/docs/github-issues/issue-4-body.md +169 -0
- package/docs/github-issues/issue-5-body.md +173 -0
- package/docs/github-issues/issue-6-body.md +158 -0
- package/docs/github-issues/issue-7-body.md +171 -0
- package/docs/github-issues/issue-8-body.md +407 -0
- package/docs/github-issues/issue-9-body.md +515 -0
- package/docs/github-issues/issue-tracker.md +274 -0
- package/docs/github-issues/predictive-analysis-2025-10-18.md +2131 -0
- package/docs/implementation/List_Framework_Refactor_Plan.md +336 -0
- package/docs/implementation/PRIMARY_TEXT_COLOR_FEATURE.md +217 -0
- package/docs/implementation/RELEASE_PLAN_v2.1.0.md +362 -0
- package/docs/implementation/RefactorStyles.md +588 -0
- package/docs/implementation/implement-plan.md +489 -0
- package/docs/implementation/missing-helpers-implementation.md +391 -0
- package/docs/implementation/refactor-plan.md +520 -0
- package/docs/implementation/session-implementation-complete.md +233 -0
- package/docs/implementation/session-management-plan.md +250 -0
- package/docs/setup-checklist.md +77 -0
- package/docs/versions/changelog.md +345 -0
- package/electron/customUpdater.ts +656 -0
- package/electron/main.ts +2441 -0
- package/electron/memoryConfig.ts +187 -0
- package/electron/preload.ts +394 -0
- package/electron/proxyConfig.ts +340 -0
- package/electron/services/BackupService.ts +452 -0
- package/electron/services/DictionaryService.ts +402 -0
- package/electron/services/LocalDictionaryLookupService.ts +147 -0
- package/electron/services/PowerAutomateApiService.ts +231 -0
- package/electron/services/SharePointSyncService.ts +474 -0
- package/electron/windowsCertStore.ts +427 -0
- package/electron/zscalerConfig.ts +381 -0
- package/eslint.config.js +92 -0
- package/jest.config.js +52 -0
- package/package.json +214 -0
- package/postcss.config.mjs +6 -0
- package/public/icon.png +0 -0
- package/publish-release.ps1 +5 -0
- package/renovate.json +30 -0
- package/src/App.tsx +216 -0
- package/src/__mocks__/p-limit.js +12 -0
- package/src/__mocks__/styleMock.js +1 -0
- package/src/components/common/BugReportButton.tsx +44 -0
- package/src/components/common/BugReportDialog.tsx +193 -0
- package/src/components/common/Button.tsx +153 -0
- package/src/components/common/Card.tsx +86 -0
- package/src/components/common/ColorPickerDialog.tsx +177 -0
- package/src/components/common/ConfirmDialog.tsx +96 -0
- package/src/components/common/DebugConsole.tsx +275 -0
- package/src/components/common/EmptyState.tsx +183 -0
- package/src/components/common/ErrorBoundary.tsx +98 -0
- package/src/components/common/ErrorDetailsDialog.tsx +153 -0
- package/src/components/common/ErrorFallback.tsx +218 -0
- package/src/components/common/Input.tsx +109 -0
- package/src/components/common/Skeleton.tsx +184 -0
- package/src/components/common/SplashScreen.tsx +81 -0
- package/src/components/common/Toast.tsx +155 -0
- package/src/components/common/Tooltip.tsx +79 -0
- package/src/components/common/UpdateNotification.tsx +320 -0
- package/src/components/comparison/ComparisonWindow.tsx +374 -0
- package/src/components/comparison/SideBySideDiff.tsx +486 -0
- package/src/components/comparison/index.ts +8 -0
- package/src/components/document/DocumentUploader.tsx +288 -0
- package/src/components/document/HyperlinkPreview.tsx +430 -0
- package/src/components/document/HyperlinkService.md +1484 -0
- package/src/components/document/Hyperlink_Technical_Documentation.md +496 -0
- package/src/components/document/InlineChangesView.tsx +707 -0
- package/src/components/document/ProcessingProgress.tsx +303 -0
- package/src/components/document/ProcessingResults.tsx +256 -0
- package/src/components/document/TrackedChangesDetail.tsx +530 -0
- package/src/components/document/TrackedChangesPanel.tsx +546 -0
- package/src/components/document/VirtualDocumentList.tsx +240 -0
- package/src/components/editor/DocumentEditor.tsx +723 -0
- package/src/components/editor/DocumentEditorModal.tsx +640 -0
- package/src/components/editor/EditorQuickActions.tsx +502 -0
- package/src/components/editor/EditorToolbar.tsx +312 -0
- package/src/components/editor/TableEditor.tsx +926 -0
- package/src/components/editor/index.ts +18 -0
- package/src/components/layout/Header.tsx +190 -0
- package/src/components/layout/Sidebar.tsx +313 -0
- package/src/components/layout/TitleBar.tsx +190 -0
- package/src/components/navigation/CommandPalette.tsx +233 -0
- package/src/components/navigation/KeyboardShortcutsModal.tsx +173 -0
- package/src/components/sessions/ChangeItem.tsx +408 -0
- package/src/components/sessions/ChangeViewer.tsx +1155 -0
- package/src/components/sessions/DocumentComparisonModal.tsx +314 -0
- package/src/components/sessions/ProcessingOptions.tsx +297 -0
- package/src/components/sessions/ReplacementsTab.tsx +438 -0
- package/src/components/sessions/RevisionHandlingOptions.tsx +87 -0
- package/src/components/sessions/SessionManager.tsx +188 -0
- package/src/components/sessions/StylesEditor.tsx +1335 -0
- package/src/components/sessions/TabContainer.tsx +151 -0
- package/src/components/sessions/VirtualSessionList.tsx +157 -0
- package/src/components/sessions/sessionToProcessorManager.tsx +420 -0
- package/src/components/settings/CertificateManager.tsx +410 -0
- package/src/components/settings/SegmentedControl.tsx +88 -0
- package/src/components/settings/SettingRow.tsx +52 -0
- package/src/contexts/GlobalStatsContext.tsx +396 -0
- package/src/contexts/SessionContext.tsx +2129 -0
- package/src/contexts/ThemeContext.tsx +428 -0
- package/src/contexts/UserSettingsContext.tsx +290 -0
- package/src/contexts/__tests__/GlobalStatsContext.test.tsx +390 -0
- package/src/global.d.ts +273 -0
- package/src/hooks/useDocumentQueue.tsx +210 -0
- package/src/hooks/useToast.tsx +55 -0
- package/src/main.tsx +10 -0
- package/src/pages/Analytics.tsx +386 -0
- package/src/pages/CurrentSession.tsx +1174 -0
- package/src/pages/Dashboard.tsx +319 -0
- package/src/pages/Documents.tsx +317 -0
- package/src/pages/Projects.tsx +250 -0
- package/src/pages/Reporting.tsx +386 -0
- package/src/pages/Search.tsx +349 -0
- package/src/pages/Sessions.tsx +285 -0
- package/src/pages/Settings.tsx +2662 -0
- package/src/services/HyperlinkService.ts +1085 -0
- package/src/services/document/DocXMLaterProcessor.ts +617 -0
- package/src/services/document/DocumentProcessingComparison.ts +856 -0
- package/src/services/document/DocumentSnapshotService.ts +575 -0
- package/src/services/document/WordDocumentProcessor.ts +10509 -0
- package/src/services/document/__tests__/DocXMLaterProcessor.hyperlinks.test.md +311 -0
- package/src/services/document/__tests__/WordDocumentProcessor.integration.test.ts +515 -0
- package/src/services/document/__tests__/WordDocumentProcessor.test.ts +812 -0
- package/src/services/document/blanklines/BlankLineManager.ts +658 -0
- package/src/services/document/blanklines/__tests__/paragraphChecks.test.ts +281 -0
- package/src/services/document/blanklines/helpers/blankLineInsertion.ts +87 -0
- package/src/services/document/blanklines/helpers/blankLineSnapshot.ts +251 -0
- package/src/services/document/blanklines/helpers/clearCustom.ts +121 -0
- package/src/services/document/blanklines/helpers/contextChecks.ts +117 -0
- package/src/services/document/blanklines/helpers/imageChecks.ts +51 -0
- package/src/services/document/blanklines/helpers/paragraphChecks.ts +236 -0
- package/src/services/document/blanklines/helpers/removeBlanksBetweenListItems.ts +91 -0
- package/src/services/document/blanklines/helpers/removeTrailingBlanks.ts +35 -0
- package/src/services/document/blanklines/helpers/tableGuards.ts +21 -0
- package/src/services/document/blanklines/index.ts +67 -0
- package/src/services/document/blanklines/rules/additionRules.ts +337 -0
- package/src/services/document/blanklines/rules/indentationRules.ts +317 -0
- package/src/services/document/blanklines/rules/removalRules.ts +362 -0
- package/src/services/document/blanklines/rules/ruleTypes.ts +92 -0
- package/src/services/document/blanklines/types.ts +29 -0
- package/src/services/document/helpers/ImageBorderCropper.ts +377 -0
- package/src/services/document/helpers/__tests__/whitespace.test.ts +272 -0
- package/src/services/document/helpers/whitespace.ts +117 -0
- package/src/services/document/list/ListNormalizer.ts +947 -0
- package/src/services/document/list/index.ts +45 -0
- package/src/services/document/list/list-detection.ts +275 -0
- package/src/services/document/list/list-types.ts +162 -0
- package/src/services/document/processors/HyperlinkProcessor.ts +370 -0
- package/src/services/document/processors/ListProcessor.ts +257 -0
- package/src/services/document/processors/StructureProcessor.ts +176 -0
- package/src/services/document/processors/StyleProcessor.ts +389 -0
- package/src/services/document/processors/TableProcessor.ts +2238 -0
- package/src/services/document/processors/__tests__/HyperlinkProcessor.test.ts +314 -0
- package/src/services/document/processors/__tests__/ListProcessor.test.ts +291 -0
- package/src/services/document/processors/__tests__/StructureProcessor.test.ts +257 -0
- package/src/services/document/processors/__tests__/TableProcessor.hlp-tips-bullets.test.ts +459 -0
- package/src/services/document/processors/__tests__/TableProcessor.test.ts +1604 -0
- package/src/services/document/processors/index.ts +28 -0
- package/src/services/document/types/docx-processing.ts +310 -0
- package/src/services/editor/EditorActionHandlers.ts +901 -0
- package/src/services/editor/index.ts +13 -0
- package/src/setupTests.ts +47 -0
- package/src/styles/global.css +782 -0
- package/src/types/backup.ts +132 -0
- package/src/types/dictionary.ts +125 -0
- package/src/types/document-processing.ts +331 -0
- package/src/types/docxmlater-augments.d.ts +142 -0
- package/src/types/editor.ts +280 -0
- package/src/types/electron.ts +340 -0
- package/src/types/globalStats.ts +155 -0
- package/src/types/hyperlink.ts +471 -0
- package/src/types/operations.ts +354 -0
- package/src/types/session.ts +427 -0
- package/src/types/settings.ts +112 -0
- package/src/utils/MemoryMonitor.ts +248 -0
- package/src/utils/cn.ts +6 -0
- package/src/utils/colorConvert.ts +306 -0
- package/src/utils/diffUtils.ts +347 -0
- package/src/utils/documentUtils.ts +202 -0
- package/src/utils/electronGuard.ts +62 -0
- package/src/utils/indexedDB.ts +915 -0
- package/src/utils/logger.ts +717 -0
- package/src/utils/pathSecurity.ts +232 -0
- package/src/utils/pathValidator.ts +236 -0
- package/src/utils/processingTimeEstimator.ts +153 -0
- package/src/utils/safeJsonParse.ts +62 -0
- package/src/utils/textSanitizer.ts +162 -0
- package/src/utils/urlHelpers.ts +304 -0
- package/src/utils/urlPatterns.ts +198 -0
- package/src/utils/urlSanitizer.ts +152 -0
- package/src/vite-env.d.ts +11 -0
- package/tsconfig.electron.json +19 -0
- package/tsconfig.json +36 -0
- package/tsconfig.node.json +12 -0
- package/typedoc.json +45 -0
- package/vite.config.ts +152 -0
|
@@ -0,0 +1,901 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EditorActionHandlers - Service for handling editor quick actions
|
|
3
|
+
*
|
|
4
|
+
* Provides implementations for all quick actions that manipulate documents
|
|
5
|
+
* using docxmlater APIs.
|
|
6
|
+
*
|
|
7
|
+
* Categories:
|
|
8
|
+
* - Text formatting (bold, italic, underline, etc.)
|
|
9
|
+
* - Paragraph styles (headings, normal, list paragraph)
|
|
10
|
+
* - Table operations (rows, columns, merge, split, shading)
|
|
11
|
+
* - Hyperlinks (insert, remove)
|
|
12
|
+
* - Tracked changes (accept all, reject all)
|
|
13
|
+
* - Structure (page break)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { Document, Paragraph, Table, Run } from 'docxmlater';
|
|
17
|
+
import type { QuickActionId, EditorSelection, CellSelection } from '@/types/editor';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Result of an editor action
|
|
21
|
+
*/
|
|
22
|
+
export interface ActionResult {
|
|
23
|
+
success: boolean;
|
|
24
|
+
error?: string;
|
|
25
|
+
description: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Context for editor actions
|
|
30
|
+
*/
|
|
31
|
+
export interface ActionContext {
|
|
32
|
+
document: Document;
|
|
33
|
+
selection: EditorSelection | null;
|
|
34
|
+
tableSelection: CellSelection | null;
|
|
35
|
+
selectedTableIndex: number | null;
|
|
36
|
+
tableShadingSettings?: {
|
|
37
|
+
header2Shading: string;
|
|
38
|
+
otherShading: string;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Handler function type
|
|
44
|
+
*/
|
|
45
|
+
type ActionHandler = (context: ActionContext) => Promise<ActionResult>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Helper to get paragraph from document by index
|
|
49
|
+
*/
|
|
50
|
+
function getParagraphAtIndex(doc: Document, index: number): Paragraph | null {
|
|
51
|
+
const elements = doc.getBodyElements();
|
|
52
|
+
const element = elements[index];
|
|
53
|
+
if (element && (element as any).type === 'paragraph') {
|
|
54
|
+
return element as Paragraph;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Helper to get table from document by index
|
|
61
|
+
*/
|
|
62
|
+
function getTableAtIndex(doc: Document, index: number): Table | null {
|
|
63
|
+
const elements = doc.getBodyElements();
|
|
64
|
+
const element = elements[index];
|
|
65
|
+
if (element && (element as any).type === 'table') {
|
|
66
|
+
return element as Table;
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Map of action handlers by action ID
|
|
73
|
+
*/
|
|
74
|
+
const actionHandlers: Partial<Record<QuickActionId, ActionHandler>> = {
|
|
75
|
+
// Text Formatting
|
|
76
|
+
bold: async (ctx) => {
|
|
77
|
+
if (!ctx.selection) {
|
|
78
|
+
return { success: false, error: 'No text selected', description: 'Toggle bold' };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const paragraph = getParagraphAtIndex(ctx.document, ctx.selection.paragraphIndex);
|
|
83
|
+
if (!paragraph) {
|
|
84
|
+
return { success: false, error: 'Paragraph not found', description: 'Toggle bold' };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const runs = paragraph.getRuns();
|
|
88
|
+
|
|
89
|
+
// Toggle bold on selected runs
|
|
90
|
+
for (
|
|
91
|
+
let i = ctx.selection.runStartIndex;
|
|
92
|
+
i <= ctx.selection.runEndIndex && i < runs.length;
|
|
93
|
+
i++
|
|
94
|
+
) {
|
|
95
|
+
const run = runs[i];
|
|
96
|
+
const formatting = run.getFormatting();
|
|
97
|
+
const currentBold = formatting.bold || false;
|
|
98
|
+
run.setBold(!currentBold);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return { success: true, description: 'Toggle bold formatting' };
|
|
102
|
+
} catch (error) {
|
|
103
|
+
return {
|
|
104
|
+
success: false,
|
|
105
|
+
error: error instanceof Error ? error.message : 'Failed to toggle bold',
|
|
106
|
+
description: 'Toggle bold',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
italic: async (ctx) => {
|
|
112
|
+
if (!ctx.selection) {
|
|
113
|
+
return { success: false, error: 'No text selected', description: 'Toggle italic' };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const paragraph = getParagraphAtIndex(ctx.document, ctx.selection.paragraphIndex);
|
|
118
|
+
if (!paragraph) {
|
|
119
|
+
return { success: false, error: 'Paragraph not found', description: 'Toggle italic' };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const runs = paragraph.getRuns();
|
|
123
|
+
|
|
124
|
+
for (
|
|
125
|
+
let i = ctx.selection.runStartIndex;
|
|
126
|
+
i <= ctx.selection.runEndIndex && i < runs.length;
|
|
127
|
+
i++
|
|
128
|
+
) {
|
|
129
|
+
const run = runs[i];
|
|
130
|
+
const formatting = run.getFormatting();
|
|
131
|
+
const currentItalic = formatting.italic || false;
|
|
132
|
+
run.setItalic(!currentItalic);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return { success: true, description: 'Toggle italic formatting' };
|
|
136
|
+
} catch (error) {
|
|
137
|
+
return {
|
|
138
|
+
success: false,
|
|
139
|
+
error: error instanceof Error ? error.message : 'Failed to toggle italic',
|
|
140
|
+
description: 'Toggle italic',
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
underline: async (ctx) => {
|
|
146
|
+
if (!ctx.selection) {
|
|
147
|
+
return { success: false, error: 'No text selected', description: 'Toggle underline' };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const paragraph = getParagraphAtIndex(ctx.document, ctx.selection.paragraphIndex);
|
|
152
|
+
if (!paragraph) {
|
|
153
|
+
return { success: false, error: 'Paragraph not found', description: 'Toggle underline' };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const runs = paragraph.getRuns();
|
|
157
|
+
|
|
158
|
+
for (
|
|
159
|
+
let i = ctx.selection.runStartIndex;
|
|
160
|
+
i <= ctx.selection.runEndIndex && i < runs.length;
|
|
161
|
+
i++
|
|
162
|
+
) {
|
|
163
|
+
const run = runs[i];
|
|
164
|
+
const formatting = run.getFormatting();
|
|
165
|
+
const currentUnderline = formatting.underline;
|
|
166
|
+
run.setUnderline(currentUnderline ? undefined : 'single');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return { success: true, description: 'Toggle underline formatting' };
|
|
170
|
+
} catch (error) {
|
|
171
|
+
return {
|
|
172
|
+
success: false,
|
|
173
|
+
error: error instanceof Error ? error.message : 'Failed to toggle underline',
|
|
174
|
+
description: 'Toggle underline',
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
'clear-formatting': async (ctx) => {
|
|
180
|
+
if (!ctx.selection) {
|
|
181
|
+
return {
|
|
182
|
+
success: false,
|
|
183
|
+
error: 'No text selected',
|
|
184
|
+
description: 'Clear formatting',
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
const paragraph = getParagraphAtIndex(ctx.document, ctx.selection.paragraphIndex);
|
|
190
|
+
if (!paragraph) {
|
|
191
|
+
return { success: false, error: 'Paragraph not found', description: 'Clear formatting' };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const runs = paragraph.getRuns();
|
|
195
|
+
|
|
196
|
+
for (
|
|
197
|
+
let i = ctx.selection.runStartIndex;
|
|
198
|
+
i <= ctx.selection.runEndIndex && i < runs.length;
|
|
199
|
+
i++
|
|
200
|
+
) {
|
|
201
|
+
const run = runs[i];
|
|
202
|
+
run.setBold(false);
|
|
203
|
+
run.setItalic(false);
|
|
204
|
+
run.setUnderline(undefined);
|
|
205
|
+
run.setStrike(false);
|
|
206
|
+
// Color and highlight would need their own clear methods if available
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return { success: true, description: 'Clear all formatting from selection' };
|
|
210
|
+
} catch (error) {
|
|
211
|
+
return {
|
|
212
|
+
success: false,
|
|
213
|
+
error: error instanceof Error ? error.message : 'Failed to clear formatting',
|
|
214
|
+
description: 'Clear formatting',
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
// Paragraph Styles
|
|
220
|
+
'style-heading1': async (ctx) => {
|
|
221
|
+
if (!ctx.selection) {
|
|
222
|
+
return { success: false, error: 'No paragraph selected', description: 'Apply Heading 1' };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
const paragraph = getParagraphAtIndex(ctx.document, ctx.selection.paragraphIndex);
|
|
227
|
+
if (!paragraph) {
|
|
228
|
+
return { success: false, error: 'Paragraph not found', description: 'Apply Heading 1' };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
paragraph.setStyle('Heading1');
|
|
232
|
+
return { success: true, description: 'Apply Heading 1 style' };
|
|
233
|
+
} catch (error) {
|
|
234
|
+
return {
|
|
235
|
+
success: false,
|
|
236
|
+
error: error instanceof Error ? error.message : 'Failed to apply style',
|
|
237
|
+
description: 'Apply Heading 1',
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
'style-heading2': async (ctx) => {
|
|
243
|
+
if (!ctx.selection) {
|
|
244
|
+
return { success: false, error: 'No paragraph selected', description: 'Apply Heading 2' };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
const paragraph = getParagraphAtIndex(ctx.document, ctx.selection.paragraphIndex);
|
|
249
|
+
if (!paragraph) {
|
|
250
|
+
return { success: false, error: 'Paragraph not found', description: 'Apply Heading 2' };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
paragraph.setStyle('Heading2');
|
|
254
|
+
return { success: true, description: 'Apply Heading 2 style' };
|
|
255
|
+
} catch (error) {
|
|
256
|
+
return {
|
|
257
|
+
success: false,
|
|
258
|
+
error: error instanceof Error ? error.message : 'Failed to apply style',
|
|
259
|
+
description: 'Apply Heading 2',
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
'style-normal': async (ctx) => {
|
|
265
|
+
if (!ctx.selection) {
|
|
266
|
+
return { success: false, error: 'No paragraph selected', description: 'Apply Normal' };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
const paragraph = getParagraphAtIndex(ctx.document, ctx.selection.paragraphIndex);
|
|
271
|
+
if (!paragraph) {
|
|
272
|
+
return { success: false, error: 'Paragraph not found', description: 'Apply Normal' };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
paragraph.setStyle('Normal');
|
|
276
|
+
return { success: true, description: 'Apply Normal style' };
|
|
277
|
+
} catch (error) {
|
|
278
|
+
return {
|
|
279
|
+
success: false,
|
|
280
|
+
error: error instanceof Error ? error.message : 'Failed to apply style',
|
|
281
|
+
description: 'Apply Normal',
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
'style-list-paragraph': async (ctx) => {
|
|
287
|
+
if (!ctx.selection) {
|
|
288
|
+
return {
|
|
289
|
+
success: false,
|
|
290
|
+
error: 'No paragraph selected',
|
|
291
|
+
description: 'Apply List Paragraph',
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
const paragraph = getParagraphAtIndex(ctx.document, ctx.selection.paragraphIndex);
|
|
297
|
+
if (!paragraph) {
|
|
298
|
+
return { success: false, error: 'Paragraph not found', description: 'Apply List Paragraph' };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
paragraph.setStyle('ListParagraph');
|
|
302
|
+
return { success: true, description: 'Apply List Paragraph style' };
|
|
303
|
+
} catch (error) {
|
|
304
|
+
return {
|
|
305
|
+
success: false,
|
|
306
|
+
error: error instanceof Error ? error.message : 'Failed to apply style',
|
|
307
|
+
description: 'Apply List Paragraph',
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
// Table Shading
|
|
313
|
+
'apply-h2-shading': async (ctx) => {
|
|
314
|
+
if (!ctx.tableSelection || ctx.selectedTableIndex === null) {
|
|
315
|
+
return { success: false, error: 'No table cells selected', description: 'Apply H2 shading' };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (!ctx.tableShadingSettings) {
|
|
319
|
+
return { success: false, error: 'No shading settings', description: 'Apply H2 shading' };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
try {
|
|
323
|
+
const table = getTableAtIndex(ctx.document, ctx.selectedTableIndex);
|
|
324
|
+
if (!table) {
|
|
325
|
+
return { success: false, error: 'Table not found', description: 'Apply H2 shading' };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const rows = table.getRows();
|
|
329
|
+
const { startRow, endRow, startCol, endCol } = ctx.tableSelection;
|
|
330
|
+
|
|
331
|
+
const minRow = Math.min(startRow, endRow);
|
|
332
|
+
const maxRow = Math.max(startRow, endRow);
|
|
333
|
+
const minCol = Math.min(startCol, endCol);
|
|
334
|
+
const maxCol = Math.max(startCol, endCol);
|
|
335
|
+
|
|
336
|
+
for (let r = minRow; r <= maxRow && r < rows.length; r++) {
|
|
337
|
+
const cells = rows[r].getCells();
|
|
338
|
+
for (let c = minCol; c <= maxCol && c < cells.length; c++) {
|
|
339
|
+
cells[c].setShading({ fill: ctx.tableShadingSettings.header2Shading });
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return { success: true, description: 'Apply Header 2 shading' };
|
|
344
|
+
} catch (error) {
|
|
345
|
+
return {
|
|
346
|
+
success: false,
|
|
347
|
+
error: error instanceof Error ? error.message : 'Failed to apply shading',
|
|
348
|
+
description: 'Apply H2 shading',
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
|
|
353
|
+
'apply-other-shading': async (ctx) => {
|
|
354
|
+
if (!ctx.tableSelection || ctx.selectedTableIndex === null) {
|
|
355
|
+
return {
|
|
356
|
+
success: false,
|
|
357
|
+
error: 'No table cells selected',
|
|
358
|
+
description: 'Apply other shading',
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (!ctx.tableShadingSettings) {
|
|
363
|
+
return { success: false, error: 'No shading settings', description: 'Apply other shading' };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
try {
|
|
367
|
+
const table = getTableAtIndex(ctx.document, ctx.selectedTableIndex);
|
|
368
|
+
if (!table) {
|
|
369
|
+
return { success: false, error: 'Table not found', description: 'Apply other shading' };
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const rows = table.getRows();
|
|
373
|
+
const { startRow, endRow, startCol, endCol } = ctx.tableSelection;
|
|
374
|
+
|
|
375
|
+
const minRow = Math.min(startRow, endRow);
|
|
376
|
+
const maxRow = Math.max(startRow, endRow);
|
|
377
|
+
const minCol = Math.min(startCol, endCol);
|
|
378
|
+
const maxCol = Math.max(startCol, endCol);
|
|
379
|
+
|
|
380
|
+
for (let r = minRow; r <= maxRow && r < rows.length; r++) {
|
|
381
|
+
const cells = rows[r].getCells();
|
|
382
|
+
for (let c = minCol; c <= maxCol && c < cells.length; c++) {
|
|
383
|
+
cells[c].setShading({ fill: ctx.tableShadingSettings.otherShading });
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return { success: true, description: 'Apply other shading' };
|
|
388
|
+
} catch (error) {
|
|
389
|
+
return {
|
|
390
|
+
success: false,
|
|
391
|
+
error: error instanceof Error ? error.message : 'Failed to apply shading',
|
|
392
|
+
description: 'Apply other shading',
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
// Table Row Operations
|
|
398
|
+
'table-add-row-above': async (ctx) => {
|
|
399
|
+
if (!ctx.tableSelection || ctx.selectedTableIndex === null) {
|
|
400
|
+
return { success: false, error: 'No table selected', description: 'Insert row above' };
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
try {
|
|
404
|
+
const table = getTableAtIndex(ctx.document, ctx.selectedTableIndex);
|
|
405
|
+
if (!table) {
|
|
406
|
+
return { success: false, error: 'Table not found', description: 'Insert row above' };
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const targetRow = Math.min(ctx.tableSelection.startRow, ctx.tableSelection.endRow);
|
|
410
|
+
table.insertRow(targetRow);
|
|
411
|
+
|
|
412
|
+
return { success: true, description: 'Insert row above selection' };
|
|
413
|
+
} catch (error) {
|
|
414
|
+
return {
|
|
415
|
+
success: false,
|
|
416
|
+
error: error instanceof Error ? error.message : 'Failed to insert row',
|
|
417
|
+
description: 'Insert row above',
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
|
|
422
|
+
'table-add-row-below': async (ctx) => {
|
|
423
|
+
if (!ctx.tableSelection || ctx.selectedTableIndex === null) {
|
|
424
|
+
return { success: false, error: 'No table selected', description: 'Insert row below' };
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
try {
|
|
428
|
+
const table = getTableAtIndex(ctx.document, ctx.selectedTableIndex);
|
|
429
|
+
if (!table) {
|
|
430
|
+
return { success: false, error: 'Table not found', description: 'Insert row below' };
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const targetRow = Math.max(ctx.tableSelection.startRow, ctx.tableSelection.endRow) + 1;
|
|
434
|
+
table.insertRow(targetRow);
|
|
435
|
+
|
|
436
|
+
return { success: true, description: 'Insert row below selection' };
|
|
437
|
+
} catch (error) {
|
|
438
|
+
return {
|
|
439
|
+
success: false,
|
|
440
|
+
error: error instanceof Error ? error.message : 'Failed to insert row',
|
|
441
|
+
description: 'Insert row below',
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
|
|
446
|
+
'table-delete-row': async (ctx) => {
|
|
447
|
+
if (!ctx.tableSelection || ctx.selectedTableIndex === null) {
|
|
448
|
+
return { success: false, error: 'No table selected', description: 'Delete row' };
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
try {
|
|
452
|
+
const table = getTableAtIndex(ctx.document, ctx.selectedTableIndex);
|
|
453
|
+
if (!table) {
|
|
454
|
+
return { success: false, error: 'Table not found', description: 'Delete row' };
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const rows = table.getRows();
|
|
458
|
+
if (rows.length <= 1) {
|
|
459
|
+
return { success: false, error: 'Cannot delete last row', description: 'Delete row' };
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const minRow = Math.min(ctx.tableSelection.startRow, ctx.tableSelection.endRow);
|
|
463
|
+
const maxRow = Math.max(ctx.tableSelection.startRow, ctx.tableSelection.endRow);
|
|
464
|
+
|
|
465
|
+
// Delete rows from bottom to top to maintain indices
|
|
466
|
+
for (let r = maxRow; r >= minRow; r--) {
|
|
467
|
+
table.removeRow(r);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return { success: true, description: 'Delete selected rows' };
|
|
471
|
+
} catch (error) {
|
|
472
|
+
return {
|
|
473
|
+
success: false,
|
|
474
|
+
error: error instanceof Error ? error.message : 'Failed to delete row',
|
|
475
|
+
description: 'Delete row',
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
|
|
480
|
+
// Table Column Operations
|
|
481
|
+
'table-add-col-left': async (ctx) => {
|
|
482
|
+
if (!ctx.tableSelection || ctx.selectedTableIndex === null) {
|
|
483
|
+
return { success: false, error: 'No table selected', description: 'Insert column left' };
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
try {
|
|
487
|
+
const table = getTableAtIndex(ctx.document, ctx.selectedTableIndex);
|
|
488
|
+
if (!table) {
|
|
489
|
+
return { success: false, error: 'Table not found', description: 'Insert column left' };
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const targetCol = Math.min(ctx.tableSelection.startCol, ctx.tableSelection.endCol);
|
|
493
|
+
table.addColumn(targetCol);
|
|
494
|
+
|
|
495
|
+
return { success: true, description: 'Insert column to the left' };
|
|
496
|
+
} catch (error) {
|
|
497
|
+
return {
|
|
498
|
+
success: false,
|
|
499
|
+
error: error instanceof Error ? error.message : 'Failed to insert column',
|
|
500
|
+
description: 'Insert column left',
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
},
|
|
504
|
+
|
|
505
|
+
'table-add-col-right': async (ctx) => {
|
|
506
|
+
if (!ctx.tableSelection || ctx.selectedTableIndex === null) {
|
|
507
|
+
return { success: false, error: 'No table selected', description: 'Insert column right' };
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
try {
|
|
511
|
+
const table = getTableAtIndex(ctx.document, ctx.selectedTableIndex);
|
|
512
|
+
if (!table) {
|
|
513
|
+
return { success: false, error: 'Table not found', description: 'Insert column right' };
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const targetCol = Math.max(ctx.tableSelection.startCol, ctx.tableSelection.endCol) + 1;
|
|
517
|
+
table.addColumn(targetCol);
|
|
518
|
+
|
|
519
|
+
return { success: true, description: 'Insert column to the right' };
|
|
520
|
+
} catch (error) {
|
|
521
|
+
return {
|
|
522
|
+
success: false,
|
|
523
|
+
error: error instanceof Error ? error.message : 'Failed to insert column',
|
|
524
|
+
description: 'Insert column right',
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
|
|
529
|
+
'table-delete-col': async (ctx) => {
|
|
530
|
+
if (!ctx.tableSelection || ctx.selectedTableIndex === null) {
|
|
531
|
+
return { success: false, error: 'No table selected', description: 'Delete column' };
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
try {
|
|
535
|
+
const table = getTableAtIndex(ctx.document, ctx.selectedTableIndex);
|
|
536
|
+
if (!table) {
|
|
537
|
+
return { success: false, error: 'Table not found', description: 'Delete column' };
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const rows = table.getRows();
|
|
541
|
+
if (rows.length > 0 && rows[0].getCells().length <= 1) {
|
|
542
|
+
return { success: false, error: 'Cannot delete last column', description: 'Delete column' };
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const minCol = Math.min(ctx.tableSelection.startCol, ctx.tableSelection.endCol);
|
|
546
|
+
const maxCol = Math.max(ctx.tableSelection.startCol, ctx.tableSelection.endCol);
|
|
547
|
+
|
|
548
|
+
// Delete columns from right to left to maintain indices
|
|
549
|
+
for (let c = maxCol; c >= minCol; c--) {
|
|
550
|
+
table.removeColumn(c);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return { success: true, description: 'Delete selected columns' };
|
|
554
|
+
} catch (error) {
|
|
555
|
+
return {
|
|
556
|
+
success: false,
|
|
557
|
+
error: error instanceof Error ? error.message : 'Failed to delete column',
|
|
558
|
+
description: 'Delete column',
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
|
|
563
|
+
// Cell Operations
|
|
564
|
+
'table-merge-cells': async (ctx) => {
|
|
565
|
+
if (!ctx.tableSelection || ctx.selectedTableIndex === null) {
|
|
566
|
+
return { success: false, error: 'No cells selected', description: 'Merge cells' };
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
try {
|
|
570
|
+
const table = getTableAtIndex(ctx.document, ctx.selectedTableIndex);
|
|
571
|
+
if (!table) {
|
|
572
|
+
return { success: false, error: 'Table not found', description: 'Merge cells' };
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const { startRow, endRow, startCol, endCol } = ctx.tableSelection;
|
|
576
|
+
|
|
577
|
+
table.mergeCells(
|
|
578
|
+
Math.min(startRow, endRow),
|
|
579
|
+
Math.min(startCol, endCol),
|
|
580
|
+
Math.max(startRow, endRow),
|
|
581
|
+
Math.max(startCol, endCol)
|
|
582
|
+
);
|
|
583
|
+
|
|
584
|
+
return { success: true, description: 'Merge selected cells' };
|
|
585
|
+
} catch (error) {
|
|
586
|
+
return {
|
|
587
|
+
success: false,
|
|
588
|
+
error: error instanceof Error ? error.message : 'Failed to merge cells',
|
|
589
|
+
description: 'Merge cells',
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
},
|
|
593
|
+
|
|
594
|
+
'table-split-cell': async (_ctx) => {
|
|
595
|
+
// Note: docxmlater may not have a direct split cell API
|
|
596
|
+
return {
|
|
597
|
+
success: false,
|
|
598
|
+
error: 'Split cell not yet implemented',
|
|
599
|
+
description: 'Split cell',
|
|
600
|
+
};
|
|
601
|
+
},
|
|
602
|
+
|
|
603
|
+
'table-vertical-align': async (ctx) => {
|
|
604
|
+
if (!ctx.tableSelection || ctx.selectedTableIndex === null) {
|
|
605
|
+
return { success: false, error: 'No cells selected', description: 'Set vertical alignment' };
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
try {
|
|
609
|
+
const table = getTableAtIndex(ctx.document, ctx.selectedTableIndex);
|
|
610
|
+
if (!table) {
|
|
611
|
+
return { success: false, error: 'Table not found', description: 'Set vertical alignment' };
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const rows = table.getRows();
|
|
615
|
+
const { startRow, endRow, startCol, endCol } = ctx.tableSelection;
|
|
616
|
+
|
|
617
|
+
const minRow = Math.min(startRow, endRow);
|
|
618
|
+
const maxRow = Math.max(startRow, endRow);
|
|
619
|
+
const minCol = Math.min(startCol, endCol);
|
|
620
|
+
const maxCol = Math.max(startCol, endCol);
|
|
621
|
+
|
|
622
|
+
for (let r = minRow; r <= maxRow && r < rows.length; r++) {
|
|
623
|
+
const cells = rows[r].getCells();
|
|
624
|
+
for (let c = minCol; c <= maxCol && c < cells.length; c++) {
|
|
625
|
+
cells[c].setVerticalAlignment('center');
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
return { success: true, description: 'Set vertical alignment to center' };
|
|
630
|
+
} catch (error) {
|
|
631
|
+
return {
|
|
632
|
+
success: false,
|
|
633
|
+
error: error instanceof Error ? error.message : 'Failed to set alignment',
|
|
634
|
+
description: 'Set vertical alignment',
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
},
|
|
638
|
+
|
|
639
|
+
'table-cell-borders': async (_ctx) => {
|
|
640
|
+
return {
|
|
641
|
+
success: false,
|
|
642
|
+
error: 'Cell borders dialog not yet implemented',
|
|
643
|
+
description: 'Configure cell borders',
|
|
644
|
+
};
|
|
645
|
+
},
|
|
646
|
+
|
|
647
|
+
'table-cell-shading': async (_ctx) => {
|
|
648
|
+
return {
|
|
649
|
+
success: false,
|
|
650
|
+
error: 'Cell shading dialog not yet implemented',
|
|
651
|
+
description: 'Configure cell shading',
|
|
652
|
+
};
|
|
653
|
+
},
|
|
654
|
+
|
|
655
|
+
// Structure
|
|
656
|
+
'page-break': async (ctx) => {
|
|
657
|
+
if (!ctx.selection) {
|
|
658
|
+
return { success: false, error: 'No position selected', description: 'Insert page break' };
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
try {
|
|
662
|
+
const paragraph = getParagraphAtIndex(ctx.document, ctx.selection.paragraphIndex);
|
|
663
|
+
if (!paragraph) {
|
|
664
|
+
return { success: false, error: 'Paragraph not found', description: 'Insert page break' };
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
paragraph.setPageBreakBefore(true);
|
|
668
|
+
|
|
669
|
+
return { success: true, description: 'Insert page break before paragraph' };
|
|
670
|
+
} catch (error) {
|
|
671
|
+
return {
|
|
672
|
+
success: false,
|
|
673
|
+
error: error instanceof Error ? error.message : 'Failed to insert page break',
|
|
674
|
+
description: 'Insert page break',
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
},
|
|
678
|
+
|
|
679
|
+
'find-replace': async (_ctx) => {
|
|
680
|
+
return {
|
|
681
|
+
success: false,
|
|
682
|
+
error: 'Find & Replace dialog not yet implemented',
|
|
683
|
+
description: 'Open Find & Replace',
|
|
684
|
+
};
|
|
685
|
+
},
|
|
686
|
+
|
|
687
|
+
// Hyperlinks
|
|
688
|
+
'insert-hyperlink': async (_ctx) => {
|
|
689
|
+
return {
|
|
690
|
+
success: false,
|
|
691
|
+
error: 'Insert hyperlink dialog not yet implemented',
|
|
692
|
+
description: 'Insert hyperlink',
|
|
693
|
+
};
|
|
694
|
+
},
|
|
695
|
+
|
|
696
|
+
'remove-hyperlink': async (ctx) => {
|
|
697
|
+
if (!ctx.selection) {
|
|
698
|
+
return { success: false, error: 'No text selected', description: 'Remove hyperlink' };
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
try {
|
|
702
|
+
const paragraphs = ctx.document.getAllParagraphs();
|
|
703
|
+
const para = paragraphs[ctx.selection.paragraphIndex];
|
|
704
|
+
if (!para) {
|
|
705
|
+
return { success: false, error: 'Paragraph not found', description: 'Remove hyperlink' };
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// Get paragraph content to find hyperlinks
|
|
709
|
+
const content = para.getContent();
|
|
710
|
+
let removedCount = 0;
|
|
711
|
+
|
|
712
|
+
// Find and remove hyperlinks in the paragraph
|
|
713
|
+
for (const item of content) {
|
|
714
|
+
// Check if item is a hyperlink using duck typing
|
|
715
|
+
if (item && typeof (item as any).getUrl === 'function') {
|
|
716
|
+
const hyperlink = item as any;
|
|
717
|
+
const hyperlinkText = hyperlink.getText() || '';
|
|
718
|
+
|
|
719
|
+
// Convert hyperlink to plain text run
|
|
720
|
+
// Create a new run with the hyperlink's text
|
|
721
|
+
if (typeof hyperlink.convertToRun === 'function') {
|
|
722
|
+
// Use docxmlater's built-in conversion if available
|
|
723
|
+
hyperlink.convertToRun();
|
|
724
|
+
removedCount++;
|
|
725
|
+
} else if (typeof para.replaceContent === 'function') {
|
|
726
|
+
// Alternative: replace hyperlink with a text run
|
|
727
|
+
const Run = (await import('docxmlater')).Run;
|
|
728
|
+
const newRun = Run.create(hyperlinkText);
|
|
729
|
+
para.replaceContent(hyperlink, [newRun]);
|
|
730
|
+
removedCount++;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
if (removedCount > 0) {
|
|
736
|
+
return { success: true, description: `Removed ${removedCount} hyperlink(s)` };
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
return {
|
|
740
|
+
success: false,
|
|
741
|
+
error: 'No hyperlinks found in selection',
|
|
742
|
+
description: 'Remove hyperlink',
|
|
743
|
+
};
|
|
744
|
+
} catch (error) {
|
|
745
|
+
return {
|
|
746
|
+
success: false,
|
|
747
|
+
error: error instanceof Error ? error.message : 'Failed to remove hyperlink',
|
|
748
|
+
description: 'Remove hyperlink',
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
},
|
|
752
|
+
|
|
753
|
+
// Tracked Changes
|
|
754
|
+
'accept-all-changes': async (ctx) => {
|
|
755
|
+
try {
|
|
756
|
+
const revisionManager = (ctx.document as any).getRevisionManager?.();
|
|
757
|
+
|
|
758
|
+
if (revisionManager) {
|
|
759
|
+
revisionManager.acceptAll?.();
|
|
760
|
+
return { success: true, description: 'Accept all tracked changes' };
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
return {
|
|
764
|
+
success: false,
|
|
765
|
+
error: 'No revision manager available',
|
|
766
|
+
description: 'Accept all changes',
|
|
767
|
+
};
|
|
768
|
+
} catch (error) {
|
|
769
|
+
return {
|
|
770
|
+
success: false,
|
|
771
|
+
error: error instanceof Error ? error.message : 'Failed to accept changes',
|
|
772
|
+
description: 'Accept all changes',
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
},
|
|
776
|
+
|
|
777
|
+
'reject-all-changes': async (ctx) => {
|
|
778
|
+
try {
|
|
779
|
+
const revisionManager = (ctx.document as any).getRevisionManager?.();
|
|
780
|
+
|
|
781
|
+
if (revisionManager) {
|
|
782
|
+
revisionManager.rejectAll?.();
|
|
783
|
+
return { success: true, description: 'Reject all tracked changes' };
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
return {
|
|
787
|
+
success: false,
|
|
788
|
+
error: 'No revision manager available',
|
|
789
|
+
description: 'Reject all changes',
|
|
790
|
+
};
|
|
791
|
+
} catch (error) {
|
|
792
|
+
return {
|
|
793
|
+
success: false,
|
|
794
|
+
error: error instanceof Error ? error.message : 'Failed to reject changes',
|
|
795
|
+
description: 'Reject all changes',
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
},
|
|
799
|
+
};
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Execute an editor action
|
|
803
|
+
*/
|
|
804
|
+
export async function executeAction(
|
|
805
|
+
actionId: QuickActionId,
|
|
806
|
+
context: ActionContext
|
|
807
|
+
): Promise<ActionResult> {
|
|
808
|
+
const handler = actionHandlers[actionId];
|
|
809
|
+
|
|
810
|
+
if (!handler) {
|
|
811
|
+
return {
|
|
812
|
+
success: false,
|
|
813
|
+
error: `No handler for action: ${actionId}`,
|
|
814
|
+
description: `Execute ${actionId}`,
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
return handler(context);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Check if an action is available given the current context
|
|
823
|
+
*/
|
|
824
|
+
export function isActionAvailable(
|
|
825
|
+
actionId: QuickActionId,
|
|
826
|
+
context: Omit<ActionContext, 'document'>
|
|
827
|
+
): boolean {
|
|
828
|
+
// Text formatting actions require text selection
|
|
829
|
+
const textFormatActions: QuickActionId[] = [
|
|
830
|
+
'bold',
|
|
831
|
+
'italic',
|
|
832
|
+
'underline',
|
|
833
|
+
'clear-formatting',
|
|
834
|
+
];
|
|
835
|
+
|
|
836
|
+
if (textFormatActions.includes(actionId)) {
|
|
837
|
+
return context.selection !== null;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Paragraph style actions require paragraph selection
|
|
841
|
+
const paragraphStyleActions: QuickActionId[] = [
|
|
842
|
+
'style-heading1',
|
|
843
|
+
'style-heading2',
|
|
844
|
+
'style-normal',
|
|
845
|
+
'style-list-paragraph',
|
|
846
|
+
'page-break',
|
|
847
|
+
];
|
|
848
|
+
|
|
849
|
+
if (paragraphStyleActions.includes(actionId)) {
|
|
850
|
+
return context.selection !== null;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// Table actions require table selection
|
|
854
|
+
const tableActions: QuickActionId[] = [
|
|
855
|
+
'apply-h2-shading',
|
|
856
|
+
'apply-other-shading',
|
|
857
|
+
'table-add-row-above',
|
|
858
|
+
'table-add-row-below',
|
|
859
|
+
'table-delete-row',
|
|
860
|
+
'table-add-col-left',
|
|
861
|
+
'table-add-col-right',
|
|
862
|
+
'table-delete-col',
|
|
863
|
+
'table-merge-cells',
|
|
864
|
+
'table-split-cell',
|
|
865
|
+
'table-vertical-align',
|
|
866
|
+
'table-cell-borders',
|
|
867
|
+
'table-cell-shading',
|
|
868
|
+
];
|
|
869
|
+
|
|
870
|
+
if (tableActions.includes(actionId)) {
|
|
871
|
+
return context.tableSelection !== null && context.selectedTableIndex !== null;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// Shading actions also require shading settings
|
|
875
|
+
if (actionId === 'apply-h2-shading' || actionId === 'apply-other-shading') {
|
|
876
|
+
return (
|
|
877
|
+
context.tableSelection !== null &&
|
|
878
|
+
context.selectedTableIndex !== null &&
|
|
879
|
+
context.tableShadingSettings !== undefined
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// Global actions are always available
|
|
884
|
+
const globalActions: QuickActionId[] = [
|
|
885
|
+
'find-replace',
|
|
886
|
+
'accept-all-changes',
|
|
887
|
+
'reject-all-changes',
|
|
888
|
+
];
|
|
889
|
+
|
|
890
|
+
if (globalActions.includes(actionId)) {
|
|
891
|
+
return true;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// Default to requiring some selection
|
|
895
|
+
return context.selection !== null || context.tableSelection !== null;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
export default {
|
|
899
|
+
executeAction,
|
|
900
|
+
isActionAvailable,
|
|
901
|
+
};
|