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,438 @@
|
|
|
1
|
+
import { Button } from '@/components/common/Button';
|
|
2
|
+
import { useSession } from '@/contexts/SessionContext';
|
|
3
|
+
import { ReplacementRule } from '@/types/session';
|
|
4
|
+
import { cn } from '@/utils/cn';
|
|
5
|
+
import { validateUrlScheme } from '@/utils/urlHelpers';
|
|
6
|
+
import { AnimatePresence, motion } from 'framer-motion';
|
|
7
|
+
import { Check, Plus, Trash2 } from 'lucide-react';
|
|
8
|
+
import { useEffect, useState } from 'react';
|
|
9
|
+
|
|
10
|
+
interface HyperlinkRule {
|
|
11
|
+
id: string;
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
oldHyperlink: string;
|
|
14
|
+
newContentId: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface TextRule {
|
|
18
|
+
id: string;
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
oldText: string;
|
|
21
|
+
newText: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface ReplacementsTabProps {
|
|
25
|
+
sessionId?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function ReplacementsTab({ sessionId }: ReplacementsTabProps) {
|
|
29
|
+
const { sessions, updateSessionReplacements } = useSession();
|
|
30
|
+
const [replaceHyperlinksEnabled, setReplaceHyperlinksEnabled] = useState(false);
|
|
31
|
+
const [replaceTextEnabled, setReplaceTextEnabled] = useState(false);
|
|
32
|
+
const [hyperlinkRules, setHyperlinkRules] = useState<HyperlinkRule[]>([]);
|
|
33
|
+
const [textRules, setTextRules] = useState<TextRule[]>([]);
|
|
34
|
+
// SECURITY: Track URL validation errors for user feedback
|
|
35
|
+
const [urlValidationErrors, setUrlValidationErrors] = useState<Record<string, string>>({});
|
|
36
|
+
|
|
37
|
+
// Load existing replacements from session on mount
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!sessionId) return;
|
|
40
|
+
|
|
41
|
+
const session = sessions.find((s) => s.id === sessionId);
|
|
42
|
+
if (session?.replacements) {
|
|
43
|
+
// Convert ReplacementRule[] to HyperlinkRule[] and TextRule[]
|
|
44
|
+
const hyperlinks: HyperlinkRule[] = [];
|
|
45
|
+
const texts: TextRule[] = [];
|
|
46
|
+
|
|
47
|
+
session.replacements.forEach((rule) => {
|
|
48
|
+
if (rule.type === 'hyperlink') {
|
|
49
|
+
hyperlinks.push({
|
|
50
|
+
id: rule.id,
|
|
51
|
+
enabled: rule.enabled,
|
|
52
|
+
oldHyperlink: rule.pattern,
|
|
53
|
+
newContentId: rule.replacement,
|
|
54
|
+
});
|
|
55
|
+
} else if (rule.type === 'text') {
|
|
56
|
+
texts.push({
|
|
57
|
+
id: rule.id,
|
|
58
|
+
enabled: rule.enabled,
|
|
59
|
+
oldText: rule.pattern,
|
|
60
|
+
newText: rule.replacement,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
setHyperlinkRules(hyperlinks);
|
|
66
|
+
setTextRules(texts);
|
|
67
|
+
setReplaceHyperlinksEnabled(hyperlinks.some((r) => r.enabled));
|
|
68
|
+
setReplaceTextEnabled(texts.some((r) => r.enabled));
|
|
69
|
+
}
|
|
70
|
+
}, [sessionId, sessions]);
|
|
71
|
+
|
|
72
|
+
// Save changes to session whenever rules change
|
|
73
|
+
const saveRulesToSession = (hyperlinks: HyperlinkRule[], texts: TextRule[]) => {
|
|
74
|
+
if (!sessionId) return;
|
|
75
|
+
|
|
76
|
+
// Convert back to ReplacementRule[]
|
|
77
|
+
const replacements: ReplacementRule[] = [
|
|
78
|
+
...hyperlinks.map((h) => ({
|
|
79
|
+
id: h.id,
|
|
80
|
+
enabled: h.enabled,
|
|
81
|
+
type: 'hyperlink' as const,
|
|
82
|
+
pattern: h.oldHyperlink,
|
|
83
|
+
replacement: h.newContentId,
|
|
84
|
+
})),
|
|
85
|
+
...texts.map((t) => ({
|
|
86
|
+
id: t.id,
|
|
87
|
+
enabled: t.enabled,
|
|
88
|
+
type: 'text' as const,
|
|
89
|
+
pattern: t.oldText,
|
|
90
|
+
replacement: t.newText,
|
|
91
|
+
})),
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
updateSessionReplacements(sessionId, replacements);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const addHyperlinkRule = () => {
|
|
98
|
+
const newRule: HyperlinkRule = {
|
|
99
|
+
id: `hyperlink-${Date.now()}`,
|
|
100
|
+
enabled: true,
|
|
101
|
+
oldHyperlink: '',
|
|
102
|
+
newContentId: '',
|
|
103
|
+
};
|
|
104
|
+
const updatedRules = [...hyperlinkRules, newRule];
|
|
105
|
+
setHyperlinkRules(updatedRules);
|
|
106
|
+
saveRulesToSession(updatedRules, textRules);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const updateHyperlinkRule = (id: string, updates: Partial<HyperlinkRule>) => {
|
|
110
|
+
// SECURITY FIX: Validate URL scheme for newContentId to prevent XSS-like attacks
|
|
111
|
+
if (updates.newContentId !== undefined) {
|
|
112
|
+
const validation = validateUrlScheme(updates.newContentId);
|
|
113
|
+
|
|
114
|
+
if (!validation.valid) {
|
|
115
|
+
// Store validation error to show to user
|
|
116
|
+
setUrlValidationErrors((prev) => ({
|
|
117
|
+
...prev,
|
|
118
|
+
[id]: validation.error || 'Invalid URL',
|
|
119
|
+
}));
|
|
120
|
+
|
|
121
|
+
// Still update the field value (for user to see and correct)
|
|
122
|
+
// but don't save to session until valid
|
|
123
|
+
const updatedRules = hyperlinkRules.map((rule) =>
|
|
124
|
+
rule.id === id ? { ...rule, ...updates } : rule
|
|
125
|
+
);
|
|
126
|
+
setHyperlinkRules(updatedRules);
|
|
127
|
+
return; // Don't save to session with invalid URL
|
|
128
|
+
} else {
|
|
129
|
+
// Clear any previous validation error for this field
|
|
130
|
+
setUrlValidationErrors((prev) => {
|
|
131
|
+
const newErrors = { ...prev };
|
|
132
|
+
delete newErrors[id];
|
|
133
|
+
return newErrors;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// If validation passed or update doesn't include newContentId, proceed normally
|
|
139
|
+
const updatedRules = hyperlinkRules.map((rule) =>
|
|
140
|
+
rule.id === id ? { ...rule, ...updates } : rule
|
|
141
|
+
);
|
|
142
|
+
setHyperlinkRules(updatedRules);
|
|
143
|
+
saveRulesToSession(updatedRules, textRules);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const removeHyperlinkRule = (id: string) => {
|
|
147
|
+
const updatedRules = hyperlinkRules.filter((rule) => rule.id !== id);
|
|
148
|
+
setHyperlinkRules(updatedRules);
|
|
149
|
+
saveRulesToSession(updatedRules, textRules);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const addTextRule = () => {
|
|
153
|
+
const newRule: TextRule = {
|
|
154
|
+
id: `text-${Date.now()}`,
|
|
155
|
+
enabled: true,
|
|
156
|
+
oldText: '',
|
|
157
|
+
newText: '',
|
|
158
|
+
};
|
|
159
|
+
const updatedRules = [...textRules, newRule];
|
|
160
|
+
setTextRules(updatedRules);
|
|
161
|
+
saveRulesToSession(hyperlinkRules, updatedRules);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const updateTextRule = (id: string, updates: Partial<TextRule>) => {
|
|
165
|
+
const updatedRules = textRules.map((rule) => (rule.id === id ? { ...rule, ...updates } : rule));
|
|
166
|
+
setTextRules(updatedRules);
|
|
167
|
+
saveRulesToSession(hyperlinkRules, updatedRules);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const removeTextRule = (id: string) => {
|
|
171
|
+
const updatedRules = textRules.filter((rule) => rule.id !== id);
|
|
172
|
+
setTextRules(updatedRules);
|
|
173
|
+
saveRulesToSession(hyperlinkRules, updatedRules);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<div className="space-y-6">
|
|
178
|
+
{/* Hyperlink Replacements */}
|
|
179
|
+
<div className="space-y-4">
|
|
180
|
+
<div className="flex items-center justify-between">
|
|
181
|
+
<div className="flex items-center gap-3">
|
|
182
|
+
<button
|
|
183
|
+
onClick={() => setReplaceHyperlinksEnabled(!replaceHyperlinksEnabled)}
|
|
184
|
+
className={cn(
|
|
185
|
+
'relative w-12 h-6 rounded-full transition-colors',
|
|
186
|
+
replaceHyperlinksEnabled ? 'bg-primary' : 'bg-muted'
|
|
187
|
+
)}
|
|
188
|
+
>
|
|
189
|
+
<motion.div
|
|
190
|
+
className="absolute top-1 w-4 h-4 bg-white rounded-full shadow-xs"
|
|
191
|
+
animate={{ x: replaceHyperlinksEnabled ? 24 : 2 }}
|
|
192
|
+
transition={{ type: 'spring', stiffness: 500, damping: 30 }}
|
|
193
|
+
/>
|
|
194
|
+
</button>
|
|
195
|
+
<div>
|
|
196
|
+
<h3 className="font-medium">Replace Hyperlinks</h3>
|
|
197
|
+
<p className="text-sm text-muted-foreground">
|
|
198
|
+
Replace hyperlink targets based on rules
|
|
199
|
+
</p>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
<Button
|
|
203
|
+
size="sm"
|
|
204
|
+
variant="outline"
|
|
205
|
+
icon={<Plus className="w-4 h-4" />}
|
|
206
|
+
onClick={addHyperlinkRule}
|
|
207
|
+
disabled={!replaceHyperlinksEnabled}
|
|
208
|
+
>
|
|
209
|
+
Add Rule
|
|
210
|
+
</Button>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
{hyperlinkRules.length > 0 && (
|
|
214
|
+
<div className="border border-border rounded-lg overflow-hidden">
|
|
215
|
+
<table className="w-full">
|
|
216
|
+
<thead className="bg-muted/50">
|
|
217
|
+
<tr>
|
|
218
|
+
<th className="px-4 py-3 text-left text-sm font-medium w-12">Enable</th>
|
|
219
|
+
<th className="px-4 py-3 text-left text-sm font-medium">Old Hyperlink Text</th>
|
|
220
|
+
<th className="px-4 py-3 text-left text-sm font-medium">New Content ID</th>
|
|
221
|
+
<th className="px-4 py-3 text-left text-sm font-medium w-12"></th>
|
|
222
|
+
</tr>
|
|
223
|
+
</thead>
|
|
224
|
+
<tbody>
|
|
225
|
+
<AnimatePresence>
|
|
226
|
+
{hyperlinkRules.map((rule) => (
|
|
227
|
+
<motion.tr
|
|
228
|
+
key={rule.id}
|
|
229
|
+
initial={{ opacity: 0, y: -10 }}
|
|
230
|
+
animate={{ opacity: 1, y: 0 }}
|
|
231
|
+
exit={{ opacity: 0, x: -20 }}
|
|
232
|
+
className={cn(
|
|
233
|
+
'border-t border-border',
|
|
234
|
+
!replaceHyperlinksEnabled && 'opacity-50'
|
|
235
|
+
)}
|
|
236
|
+
>
|
|
237
|
+
<td className="px-4 py-3">
|
|
238
|
+
<label className="flex items-center cursor-pointer">
|
|
239
|
+
<div className="relative">
|
|
240
|
+
<input
|
|
241
|
+
type="checkbox"
|
|
242
|
+
checked={rule.enabled}
|
|
243
|
+
onChange={(e) =>
|
|
244
|
+
updateHyperlinkRule(rule.id, { enabled: e.target.checked })
|
|
245
|
+
}
|
|
246
|
+
disabled={!replaceHyperlinksEnabled}
|
|
247
|
+
className="sr-only"
|
|
248
|
+
/>
|
|
249
|
+
<div
|
|
250
|
+
className={cn(
|
|
251
|
+
'w-5 h-5 rounded border-2 flex items-center justify-center transition-all',
|
|
252
|
+
rule.enabled && replaceHyperlinksEnabled
|
|
253
|
+
? 'bg-primary border-primary checkbox-checked'
|
|
254
|
+
: 'border-border'
|
|
255
|
+
)}
|
|
256
|
+
>
|
|
257
|
+
{rule.enabled && replaceHyperlinksEnabled && (
|
|
258
|
+
<Check className="w-3 h-3 text-primary-foreground checkbox-checkmark" />
|
|
259
|
+
)}
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
</label>
|
|
263
|
+
</td>
|
|
264
|
+
<td className="px-4 py-3">
|
|
265
|
+
<input
|
|
266
|
+
type="text"
|
|
267
|
+
value={rule.oldHyperlink}
|
|
268
|
+
onChange={(e) =>
|
|
269
|
+
updateHyperlinkRule(rule.id, { oldHyperlink: e.target.value })
|
|
270
|
+
}
|
|
271
|
+
disabled={!replaceHyperlinksEnabled}
|
|
272
|
+
placeholder="Enter old hyperlink text"
|
|
273
|
+
className="w-full px-3 py-1.5 text-sm border border-border rounded-md bg-muted/30 focus:bg-background transition-colors disabled:opacity-50"
|
|
274
|
+
/>
|
|
275
|
+
</td>
|
|
276
|
+
<td className="px-4 py-3">
|
|
277
|
+
<div className="space-y-1">
|
|
278
|
+
<input
|
|
279
|
+
type="text"
|
|
280
|
+
value={rule.newContentId}
|
|
281
|
+
onChange={(e) =>
|
|
282
|
+
updateHyperlinkRule(rule.id, { newContentId: e.target.value })
|
|
283
|
+
}
|
|
284
|
+
disabled={!replaceHyperlinksEnabled}
|
|
285
|
+
placeholder="Enter new content ID"
|
|
286
|
+
className={cn(
|
|
287
|
+
'w-full px-3 py-1.5 text-sm border rounded-md bg-muted/30 focus:bg-background transition-colors disabled:opacity-50',
|
|
288
|
+
urlValidationErrors[rule.id]
|
|
289
|
+
? 'border-red-500 focus:border-red-500'
|
|
290
|
+
: 'border-border'
|
|
291
|
+
)}
|
|
292
|
+
/>
|
|
293
|
+
{urlValidationErrors[rule.id] && (
|
|
294
|
+
<p className="text-xs text-red-500 mt-1">
|
|
295
|
+
{urlValidationErrors[rule.id]}
|
|
296
|
+
</p>
|
|
297
|
+
)}
|
|
298
|
+
</div>
|
|
299
|
+
</td>
|
|
300
|
+
<td className="px-4 py-3">
|
|
301
|
+
<button
|
|
302
|
+
onClick={() => removeHyperlinkRule(rule.id)}
|
|
303
|
+
disabled={!replaceHyperlinksEnabled}
|
|
304
|
+
className="p-1.5 text-muted-foreground hover:text-red-500 transition-colors disabled:opacity-50"
|
|
305
|
+
>
|
|
306
|
+
<Trash2 className="w-4 h-4" />
|
|
307
|
+
</button>
|
|
308
|
+
</td>
|
|
309
|
+
</motion.tr>
|
|
310
|
+
))}
|
|
311
|
+
</AnimatePresence>
|
|
312
|
+
</tbody>
|
|
313
|
+
</table>
|
|
314
|
+
</div>
|
|
315
|
+
)}
|
|
316
|
+
</div>
|
|
317
|
+
|
|
318
|
+
{/* Text Replacements */}
|
|
319
|
+
<div className="space-y-4">
|
|
320
|
+
<div className="flex items-center justify-between">
|
|
321
|
+
<div className="flex items-center gap-3">
|
|
322
|
+
<button
|
|
323
|
+
onClick={() => setReplaceTextEnabled(!replaceTextEnabled)}
|
|
324
|
+
className={cn(
|
|
325
|
+
'relative w-12 h-6 rounded-full transition-colors',
|
|
326
|
+
replaceTextEnabled ? 'bg-primary' : 'bg-muted'
|
|
327
|
+
)}
|
|
328
|
+
>
|
|
329
|
+
<motion.div
|
|
330
|
+
className="absolute top-1 w-4 h-4 bg-white rounded-full shadow-xs"
|
|
331
|
+
animate={{ x: replaceTextEnabled ? 24 : 2 }}
|
|
332
|
+
transition={{ type: 'spring', stiffness: 500, damping: 30 }}
|
|
333
|
+
/>
|
|
334
|
+
</button>
|
|
335
|
+
<div>
|
|
336
|
+
<h3 className="font-medium">Replace Text</h3>
|
|
337
|
+
<p className="text-sm text-muted-foreground">Replace text content based on rules</p>
|
|
338
|
+
</div>
|
|
339
|
+
</div>
|
|
340
|
+
<Button
|
|
341
|
+
size="sm"
|
|
342
|
+
variant="outline"
|
|
343
|
+
icon={<Plus className="w-4 h-4" />}
|
|
344
|
+
onClick={addTextRule}
|
|
345
|
+
disabled={!replaceTextEnabled}
|
|
346
|
+
>
|
|
347
|
+
Add Rule
|
|
348
|
+
</Button>
|
|
349
|
+
</div>
|
|
350
|
+
|
|
351
|
+
{textRules.length > 0 && (
|
|
352
|
+
<div className="border border-border rounded-lg overflow-hidden">
|
|
353
|
+
<table className="w-full">
|
|
354
|
+
<thead className="bg-muted/50">
|
|
355
|
+
<tr>
|
|
356
|
+
<th className="px-4 py-3 text-left text-sm font-medium w-12">Enable</th>
|
|
357
|
+
<th className="px-4 py-3 text-left text-sm font-medium">Old Text</th>
|
|
358
|
+
<th className="px-4 py-3 text-left text-sm font-medium">New Text</th>
|
|
359
|
+
<th className="px-4 py-3 text-left text-sm font-medium w-12"></th>
|
|
360
|
+
</tr>
|
|
361
|
+
</thead>
|
|
362
|
+
<tbody>
|
|
363
|
+
<AnimatePresence>
|
|
364
|
+
{textRules.map((rule) => (
|
|
365
|
+
<motion.tr
|
|
366
|
+
key={rule.id}
|
|
367
|
+
initial={{ opacity: 0, y: -10 }}
|
|
368
|
+
animate={{ opacity: 1, y: 0 }}
|
|
369
|
+
exit={{ opacity: 0, x: -20 }}
|
|
370
|
+
className={cn('border-t border-border', !replaceTextEnabled && 'opacity-50')}
|
|
371
|
+
>
|
|
372
|
+
<td className="px-4 py-3">
|
|
373
|
+
<label className="flex items-center cursor-pointer">
|
|
374
|
+
<div className="relative">
|
|
375
|
+
<input
|
|
376
|
+
type="checkbox"
|
|
377
|
+
checked={rule.enabled}
|
|
378
|
+
onChange={(e) =>
|
|
379
|
+
updateTextRule(rule.id, { enabled: e.target.checked })
|
|
380
|
+
}
|
|
381
|
+
disabled={!replaceTextEnabled}
|
|
382
|
+
className="sr-only"
|
|
383
|
+
/>
|
|
384
|
+
<div
|
|
385
|
+
className={cn(
|
|
386
|
+
'w-5 h-5 rounded border-2 flex items-center justify-center transition-all',
|
|
387
|
+
rule.enabled && replaceTextEnabled
|
|
388
|
+
? 'bg-primary border-primary checkbox-checked'
|
|
389
|
+
: 'border-border'
|
|
390
|
+
)}
|
|
391
|
+
>
|
|
392
|
+
{rule.enabled && replaceTextEnabled && (
|
|
393
|
+
<Check className="w-3 h-3 text-primary-foreground checkbox-checkmark" />
|
|
394
|
+
)}
|
|
395
|
+
</div>
|
|
396
|
+
</div>
|
|
397
|
+
</label>
|
|
398
|
+
</td>
|
|
399
|
+
<td className="px-4 py-3">
|
|
400
|
+
<input
|
|
401
|
+
type="text"
|
|
402
|
+
value={rule.oldText}
|
|
403
|
+
onChange={(e) => updateTextRule(rule.id, { oldText: e.target.value })}
|
|
404
|
+
disabled={!replaceTextEnabled}
|
|
405
|
+
placeholder="Enter old text"
|
|
406
|
+
className="w-full px-3 py-1.5 text-sm border border-border rounded-md bg-muted/30 focus:bg-background transition-colors disabled:opacity-50"
|
|
407
|
+
/>
|
|
408
|
+
</td>
|
|
409
|
+
<td className="px-4 py-3">
|
|
410
|
+
<input
|
|
411
|
+
type="text"
|
|
412
|
+
value={rule.newText}
|
|
413
|
+
onChange={(e) => updateTextRule(rule.id, { newText: e.target.value })}
|
|
414
|
+
disabled={!replaceTextEnabled}
|
|
415
|
+
placeholder="Enter new text"
|
|
416
|
+
className="w-full px-3 py-1.5 text-sm border border-border rounded-md bg-muted/30 focus:bg-background transition-colors disabled:opacity-50"
|
|
417
|
+
/>
|
|
418
|
+
</td>
|
|
419
|
+
<td className="px-4 py-3">
|
|
420
|
+
<button
|
|
421
|
+
onClick={() => removeTextRule(rule.id)}
|
|
422
|
+
disabled={!replaceTextEnabled}
|
|
423
|
+
className="p-1.5 text-muted-foreground hover:text-red-500 transition-colors disabled:opacity-50"
|
|
424
|
+
>
|
|
425
|
+
<Trash2 className="w-4 h-4" />
|
|
426
|
+
</button>
|
|
427
|
+
</td>
|
|
428
|
+
</motion.tr>
|
|
429
|
+
))}
|
|
430
|
+
</AnimatePresence>
|
|
431
|
+
</tbody>
|
|
432
|
+
</table>
|
|
433
|
+
</div>
|
|
434
|
+
)}
|
|
435
|
+
</div>
|
|
436
|
+
</div>
|
|
437
|
+
);
|
|
438
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RevisionHandlingOptions - Controls for Word tracked changes handling
|
|
3
|
+
*
|
|
4
|
+
* All DocHub modifications are automatically tracked as Word revisions.
|
|
5
|
+
* This component provides the option to auto-accept revisions for clean output.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { cn } from '@/utils/cn';
|
|
9
|
+
import { Check, FileText, GitBranch } from 'lucide-react';
|
|
10
|
+
|
|
11
|
+
interface RevisionHandlingOptionsProps {
|
|
12
|
+
autoAccept: boolean;
|
|
13
|
+
onAutoAcceptChange: (autoAccept: boolean) => void;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function RevisionHandlingOptions({
|
|
18
|
+
autoAccept,
|
|
19
|
+
onAutoAcceptChange,
|
|
20
|
+
disabled = false,
|
|
21
|
+
}: RevisionHandlingOptionsProps) {
|
|
22
|
+
return (
|
|
23
|
+
<div className="space-y-4">
|
|
24
|
+
<div className="flex items-center gap-2 text-sm">
|
|
25
|
+
<GitBranch className="w-4 h-4 text-muted-foreground" />
|
|
26
|
+
<span className="font-medium">Track Changes</span>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div className="p-3 bg-muted/30 rounded-lg border border-border">
|
|
30
|
+
<div className="flex items-start gap-2">
|
|
31
|
+
<FileText className="w-4 h-4 text-blue-500 mt-0.5 shrink-0" />
|
|
32
|
+
<div>
|
|
33
|
+
<p className="text-sm font-medium">All changes are tracked</p>
|
|
34
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
35
|
+
When enabled, DocHub records all document modifications as Word tracked changes (revisions).
|
|
36
|
+
These changes appear in the Document Changes tab and are visible when opening the file in Microsoft Word.
|
|
37
|
+
</p>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<label
|
|
43
|
+
className={cn(
|
|
44
|
+
'flex items-start gap-3 p-3 rounded-lg border cursor-pointer transition-colors',
|
|
45
|
+
autoAccept
|
|
46
|
+
? 'border-primary bg-primary/5'
|
|
47
|
+
: 'border-border hover:border-primary/50 hover:bg-muted/50',
|
|
48
|
+
disabled && 'opacity-50 cursor-not-allowed'
|
|
49
|
+
)}
|
|
50
|
+
>
|
|
51
|
+
<div className="relative mt-0.5">
|
|
52
|
+
<input
|
|
53
|
+
type="checkbox"
|
|
54
|
+
checked={autoAccept}
|
|
55
|
+
onChange={(e) => onAutoAcceptChange(e.target.checked)}
|
|
56
|
+
disabled={disabled}
|
|
57
|
+
className="sr-only"
|
|
58
|
+
/>
|
|
59
|
+
<div
|
|
60
|
+
className={cn(
|
|
61
|
+
'w-5 h-5 rounded border-2 flex items-center justify-center transition-all',
|
|
62
|
+
autoAccept
|
|
63
|
+
? 'bg-primary border-primary'
|
|
64
|
+
: 'border-muted-foreground/50'
|
|
65
|
+
)}
|
|
66
|
+
>
|
|
67
|
+
{autoAccept && <Check className="w-3 h-3 text-primary-foreground" />}
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div className="flex-1 min-w-0">
|
|
72
|
+
<span className="font-medium text-sm">Auto-Accept Tracked Changes</span>
|
|
73
|
+
<p className="mt-1 text-xs text-muted-foreground leading-relaxed">
|
|
74
|
+
Creates a clean document without visible tracked changes.
|
|
75
|
+
Changes are still recorded in DocHub for review.
|
|
76
|
+
</p>
|
|
77
|
+
</div>
|
|
78
|
+
</label>
|
|
79
|
+
|
|
80
|
+
{autoAccept && (
|
|
81
|
+
<p className="text-xs text-blue-600 dark:text-blue-400">
|
|
82
|
+
Tracked changes will be auto-accepted and not visible in Microsoft Word.
|
|
83
|
+
</p>
|
|
84
|
+
)}
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|