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,546 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TrackedChangesPanel - Container for tracked changes views
|
|
3
|
+
*
|
|
4
|
+
* Provides tabbed interface for:
|
|
5
|
+
* - Inline view: Word-like tracked changes display
|
|
6
|
+
* - List view: Categorized list of changes (existing ChangeViewer)
|
|
7
|
+
* - Comparison view: Side-by-side pre vs post processing diff
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useState, useMemo, useCallback, useEffect, useRef } from 'react';
|
|
11
|
+
import { motion, AnimatePresence } from 'framer-motion';
|
|
12
|
+
import {
|
|
13
|
+
FileText,
|
|
14
|
+
List,
|
|
15
|
+
Columns,
|
|
16
|
+
ChevronDown,
|
|
17
|
+
ChevronUp,
|
|
18
|
+
ExternalLink,
|
|
19
|
+
Maximize2,
|
|
20
|
+
ChevronLeft,
|
|
21
|
+
ChevronRight,
|
|
22
|
+
RefreshCw,
|
|
23
|
+
ChevronsUpDown,
|
|
24
|
+
ChevronsDownUp,
|
|
25
|
+
Keyboard,
|
|
26
|
+
} from 'lucide-react';
|
|
27
|
+
import { cn } from '@/utils/cn';
|
|
28
|
+
import type { TrackedChangesViewMode, TrackedChangesPanelProps } from '@/types/editor';
|
|
29
|
+
import type { ChangeEntry, UnifiedChange } from '@/types/session';
|
|
30
|
+
import { DocumentSnapshotService } from '@/services/document/DocumentSnapshotService';
|
|
31
|
+
import { InlineChangesView } from './InlineChangesView';
|
|
32
|
+
import { SideBySideDiff } from '../comparison/SideBySideDiff';
|
|
33
|
+
|
|
34
|
+
interface TrackedChangesPanelFullProps extends TrackedChangesPanelProps {
|
|
35
|
+
/** Changes from docxmlater tracked changes */
|
|
36
|
+
wordRevisions?: ChangeEntry[];
|
|
37
|
+
/** Changes from DocHub processing */
|
|
38
|
+
processingChanges?: ChangeEntry[];
|
|
39
|
+
/** Post-processing paragraph text (for comparison) */
|
|
40
|
+
postProcessingText?: string[];
|
|
41
|
+
/** Whether panel is expanded */
|
|
42
|
+
defaultExpanded?: boolean;
|
|
43
|
+
/** Callback when editor is opened */
|
|
44
|
+
onOpenEditor: () => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Tab button component
|
|
49
|
+
*/
|
|
50
|
+
function TabButton({
|
|
51
|
+
active,
|
|
52
|
+
onClick,
|
|
53
|
+
icon: Icon,
|
|
54
|
+
label,
|
|
55
|
+
count,
|
|
56
|
+
}: {
|
|
57
|
+
active: boolean;
|
|
58
|
+
onClick: () => void;
|
|
59
|
+
icon: React.ElementType;
|
|
60
|
+
label: string;
|
|
61
|
+
count?: number;
|
|
62
|
+
}) {
|
|
63
|
+
return (
|
|
64
|
+
<button
|
|
65
|
+
onClick={onClick}
|
|
66
|
+
className={cn(
|
|
67
|
+
'flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-lg transition-colors',
|
|
68
|
+
active
|
|
69
|
+
? 'bg-primary text-primary-foreground'
|
|
70
|
+
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
|
|
71
|
+
)}
|
|
72
|
+
>
|
|
73
|
+
<Icon className="w-4 h-4" />
|
|
74
|
+
<span>{label}</span>
|
|
75
|
+
{count !== undefined && count > 0 && (
|
|
76
|
+
<span
|
|
77
|
+
className={cn(
|
|
78
|
+
'px-1.5 py-0.5 text-xs rounded-full',
|
|
79
|
+
active
|
|
80
|
+
? 'bg-primary-foreground/20 text-primary-foreground'
|
|
81
|
+
: 'bg-muted-foreground/20 text-muted-foreground'
|
|
82
|
+
)}
|
|
83
|
+
>
|
|
84
|
+
{count}
|
|
85
|
+
</span>
|
|
86
|
+
)}
|
|
87
|
+
</button>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Simple list view placeholder (existing ChangeViewer logic)
|
|
94
|
+
*/
|
|
95
|
+
function ListViewContent({ changes }: { changes: ChangeEntry[] }) {
|
|
96
|
+
const grouped = useMemo(() => {
|
|
97
|
+
const groups: Record<string, ChangeEntry[]> = {};
|
|
98
|
+
for (const change of changes) {
|
|
99
|
+
const category = change.category || 'other';
|
|
100
|
+
if (!groups[category]) {
|
|
101
|
+
groups[category] = [];
|
|
102
|
+
}
|
|
103
|
+
groups[category].push(change);
|
|
104
|
+
}
|
|
105
|
+
return groups;
|
|
106
|
+
}, [changes]);
|
|
107
|
+
|
|
108
|
+
const categoryLabels: Record<string, string> = {
|
|
109
|
+
content: 'Content Changes',
|
|
110
|
+
formatting: 'Formatting Changes',
|
|
111
|
+
structural: 'Structural Changes',
|
|
112
|
+
table: 'Table Changes',
|
|
113
|
+
hyperlink: 'Hyperlink Changes',
|
|
114
|
+
other: 'Other Changes',
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const categoryColors: Record<string, string> = {
|
|
118
|
+
content: 'border-l-blue-500',
|
|
119
|
+
formatting: 'border-l-purple-500',
|
|
120
|
+
structural: 'border-l-orange-500',
|
|
121
|
+
table: 'border-l-cyan-500',
|
|
122
|
+
hyperlink: 'border-l-green-500',
|
|
123
|
+
other: 'border-l-gray-500',
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
if (changes.length === 0) {
|
|
127
|
+
return (
|
|
128
|
+
<div className="p-8 text-center text-muted-foreground">
|
|
129
|
+
<List className="w-12 h-12 mx-auto mb-2 opacity-50" />
|
|
130
|
+
<p>No tracked changes</p>
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<div className="divide-y divide-border">
|
|
137
|
+
{Object.entries(grouped).map(([category, categoryChanges]) => (
|
|
138
|
+
<div key={category} className="p-4">
|
|
139
|
+
<h4 className="text-sm font-medium mb-3 flex items-center gap-2">
|
|
140
|
+
<span
|
|
141
|
+
className={cn(
|
|
142
|
+
'w-2 h-2 rounded-full',
|
|
143
|
+
categoryColors[category]?.replace('border-l-', 'bg-') || 'bg-gray-500'
|
|
144
|
+
)}
|
|
145
|
+
/>
|
|
146
|
+
{categoryLabels[category] || category} ({categoryChanges.length})
|
|
147
|
+
</h4>
|
|
148
|
+
<div className="space-y-2">
|
|
149
|
+
{categoryChanges.slice(0, 10).map((change, index) => (
|
|
150
|
+
<div
|
|
151
|
+
key={`${category}-${index}`}
|
|
152
|
+
className={cn(
|
|
153
|
+
'p-3 bg-muted/50 rounded-lg border-l-2',
|
|
154
|
+
categoryColors[category] || 'border-l-gray-500'
|
|
155
|
+
)}
|
|
156
|
+
>
|
|
157
|
+
<p className="text-sm">{change.description}</p>
|
|
158
|
+
{change.content?.before && change.content?.after && (
|
|
159
|
+
<div className="mt-2 text-xs grid grid-cols-2 gap-2">
|
|
160
|
+
<div className="bg-red-50 dark:bg-red-900/20 p-2 rounded">
|
|
161
|
+
<span className="text-red-700 dark:text-red-300 line-through">
|
|
162
|
+
{change.content.before.slice(0, 100)}
|
|
163
|
+
{change.content.before.length > 100 && '...'}
|
|
164
|
+
</span>
|
|
165
|
+
</div>
|
|
166
|
+
<div className="bg-green-50 dark:bg-green-900/20 p-2 rounded">
|
|
167
|
+
<span className="text-green-700 dark:text-green-300">
|
|
168
|
+
{change.content.after.slice(0, 100)}
|
|
169
|
+
{change.content.after.length > 100 && '...'}
|
|
170
|
+
</span>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
)}
|
|
174
|
+
<div className="mt-2 flex items-center gap-3 text-xs text-muted-foreground">
|
|
175
|
+
{change.author && <span>By: {change.author}</span>}
|
|
176
|
+
{change.date && (
|
|
177
|
+
<span>{new Date(change.date).toLocaleDateString()}</span>
|
|
178
|
+
)}
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
))}
|
|
182
|
+
{categoryChanges.length > 10 && (
|
|
183
|
+
<p className="text-xs text-muted-foreground text-center">
|
|
184
|
+
+ {categoryChanges.length - 10} more changes
|
|
185
|
+
</p>
|
|
186
|
+
)}
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
))}
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Main TrackedChangesPanel component
|
|
196
|
+
*/
|
|
197
|
+
export function TrackedChangesPanel({
|
|
198
|
+
sessionId,
|
|
199
|
+
documentId,
|
|
200
|
+
wordRevisions = [],
|
|
201
|
+
processingChanges = [],
|
|
202
|
+
postProcessingText = [],
|
|
203
|
+
defaultExpanded = true,
|
|
204
|
+
onOpenEditor,
|
|
205
|
+
}: TrackedChangesPanelFullProps) {
|
|
206
|
+
const [activeTab, setActiveTab] = useState<TrackedChangesViewMode>('list');
|
|
207
|
+
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
|
|
208
|
+
const [originalText, setOriginalText] = useState<string[]>([]);
|
|
209
|
+
const [isLoadingSnapshot, setIsLoadingSnapshot] = useState(false);
|
|
210
|
+
const [currentChangeIndex, setCurrentChangeIndex] = useState(0);
|
|
211
|
+
const [allParagraphsExpanded, setAllParagraphsExpanded] = useState(true);
|
|
212
|
+
const [showKeyboardHint, setShowKeyboardHint] = useState(false);
|
|
213
|
+
|
|
214
|
+
// Ref for keyboard focus
|
|
215
|
+
const panelRef = useRef<HTMLDivElement>(null);
|
|
216
|
+
|
|
217
|
+
// Combine all changes for total count
|
|
218
|
+
const allChanges = useMemo(() => {
|
|
219
|
+
return [...wordRevisions, ...processingChanges];
|
|
220
|
+
}, [wordRevisions, processingChanges]);
|
|
221
|
+
|
|
222
|
+
// Load snapshot for comparison view
|
|
223
|
+
useEffect(() => {
|
|
224
|
+
if (activeTab === 'comparison' && originalText.length === 0) {
|
|
225
|
+
setIsLoadingSnapshot(true);
|
|
226
|
+
DocumentSnapshotService.getSnapshot(sessionId, documentId)
|
|
227
|
+
.then((snapshot) => {
|
|
228
|
+
if (snapshot) {
|
|
229
|
+
setOriginalText(snapshot.textContent);
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
.catch((error) => {
|
|
233
|
+
console.error('Failed to load snapshot:', error);
|
|
234
|
+
})
|
|
235
|
+
.finally(() => {
|
|
236
|
+
setIsLoadingSnapshot(false);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}, [activeTab, sessionId, documentId, originalText.length]);
|
|
240
|
+
|
|
241
|
+
// Change navigation
|
|
242
|
+
const handlePreviousChange = useCallback(() => {
|
|
243
|
+
setCurrentChangeIndex((prev) => Math.max(0, prev - 1));
|
|
244
|
+
}, []);
|
|
245
|
+
|
|
246
|
+
const handleNextChange = useCallback(() => {
|
|
247
|
+
setCurrentChangeIndex((prev) => Math.min(allChanges.length - 1, prev + 1));
|
|
248
|
+
}, [allChanges.length]);
|
|
249
|
+
|
|
250
|
+
// Toggle all paragraphs expanded/collapsed
|
|
251
|
+
const toggleAllParagraphs = useCallback(() => {
|
|
252
|
+
setAllParagraphsExpanded((prev) => !prev);
|
|
253
|
+
}, []);
|
|
254
|
+
|
|
255
|
+
// Keyboard navigation handler
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
258
|
+
// Only handle if panel is focused or contains focus
|
|
259
|
+
if (!panelRef.current?.contains(document.activeElement) && document.activeElement !== panelRef.current) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Don't handle if user is typing in an input
|
|
264
|
+
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
switch (e.key) {
|
|
269
|
+
case 'j':
|
|
270
|
+
case 'ArrowDown':
|
|
271
|
+
if (!e.ctrlKey && !e.metaKey) {
|
|
272
|
+
e.preventDefault();
|
|
273
|
+
handleNextChange();
|
|
274
|
+
}
|
|
275
|
+
break;
|
|
276
|
+
case 'k':
|
|
277
|
+
case 'ArrowUp':
|
|
278
|
+
if (!e.ctrlKey && !e.metaKey) {
|
|
279
|
+
e.preventDefault();
|
|
280
|
+
handlePreviousChange();
|
|
281
|
+
}
|
|
282
|
+
break;
|
|
283
|
+
case 'e':
|
|
284
|
+
if (!e.ctrlKey && !e.metaKey) {
|
|
285
|
+
e.preventDefault();
|
|
286
|
+
toggleAllParagraphs();
|
|
287
|
+
}
|
|
288
|
+
break;
|
|
289
|
+
case '1':
|
|
290
|
+
if (!e.ctrlKey && !e.metaKey) {
|
|
291
|
+
e.preventDefault();
|
|
292
|
+
setActiveTab('inline');
|
|
293
|
+
}
|
|
294
|
+
break;
|
|
295
|
+
case '2':
|
|
296
|
+
if (!e.ctrlKey && !e.metaKey) {
|
|
297
|
+
e.preventDefault();
|
|
298
|
+
setActiveTab('list');
|
|
299
|
+
}
|
|
300
|
+
break;
|
|
301
|
+
case '3':
|
|
302
|
+
if (!e.ctrlKey && !e.metaKey) {
|
|
303
|
+
e.preventDefault();
|
|
304
|
+
setActiveTab('comparison');
|
|
305
|
+
}
|
|
306
|
+
break;
|
|
307
|
+
case '?':
|
|
308
|
+
e.preventDefault();
|
|
309
|
+
setShowKeyboardHint((prev) => !prev);
|
|
310
|
+
break;
|
|
311
|
+
case 'Escape':
|
|
312
|
+
setShowKeyboardHint(false);
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
318
|
+
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
319
|
+
}, [handleNextChange, handlePreviousChange, toggleAllParagraphs]);
|
|
320
|
+
|
|
321
|
+
return (
|
|
322
|
+
<div
|
|
323
|
+
ref={panelRef}
|
|
324
|
+
tabIndex={0}
|
|
325
|
+
className="bg-card border border-border rounded-lg overflow-hidden focus:outline-none focus:ring-2 focus:ring-primary/50"
|
|
326
|
+
>
|
|
327
|
+
{/* Keyboard Shortcuts Hint Overlay */}
|
|
328
|
+
<AnimatePresence>
|
|
329
|
+
{showKeyboardHint && (
|
|
330
|
+
<motion.div
|
|
331
|
+
initial={{ opacity: 0 }}
|
|
332
|
+
animate={{ opacity: 1 }}
|
|
333
|
+
exit={{ opacity: 0 }}
|
|
334
|
+
className="absolute inset-0 z-50 bg-background/95 backdrop-blur-sm flex items-center justify-center"
|
|
335
|
+
onClick={() => setShowKeyboardHint(false)}
|
|
336
|
+
>
|
|
337
|
+
<div className="bg-card border border-border rounded-lg p-6 shadow-lg max-w-sm">
|
|
338
|
+
<h4 className="font-medium mb-4 flex items-center gap-2">
|
|
339
|
+
<Keyboard className="w-4 h-4" />
|
|
340
|
+
Keyboard Shortcuts
|
|
341
|
+
</h4>
|
|
342
|
+
<div className="space-y-2 text-sm">
|
|
343
|
+
<div className="flex justify-between gap-8">
|
|
344
|
+
<span className="text-muted-foreground">Next change</span>
|
|
345
|
+
<kbd className="px-2 py-0.5 bg-muted rounded text-xs font-mono">j</kbd>
|
|
346
|
+
</div>
|
|
347
|
+
<div className="flex justify-between gap-8">
|
|
348
|
+
<span className="text-muted-foreground">Previous change</span>
|
|
349
|
+
<kbd className="px-2 py-0.5 bg-muted rounded text-xs font-mono">k</kbd>
|
|
350
|
+
</div>
|
|
351
|
+
<div className="flex justify-between gap-8">
|
|
352
|
+
<span className="text-muted-foreground">Expand/Collapse all</span>
|
|
353
|
+
<kbd className="px-2 py-0.5 bg-muted rounded text-xs font-mono">e</kbd>
|
|
354
|
+
</div>
|
|
355
|
+
<div className="flex justify-between gap-8">
|
|
356
|
+
<span className="text-muted-foreground">Inline view</span>
|
|
357
|
+
<kbd className="px-2 py-0.5 bg-muted rounded text-xs font-mono">1</kbd>
|
|
358
|
+
</div>
|
|
359
|
+
<div className="flex justify-between gap-8">
|
|
360
|
+
<span className="text-muted-foreground">List view</span>
|
|
361
|
+
<kbd className="px-2 py-0.5 bg-muted rounded text-xs font-mono">2</kbd>
|
|
362
|
+
</div>
|
|
363
|
+
<div className="flex justify-between gap-8">
|
|
364
|
+
<span className="text-muted-foreground">Comparison view</span>
|
|
365
|
+
<kbd className="px-2 py-0.5 bg-muted rounded text-xs font-mono">3</kbd>
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
<p className="text-xs text-muted-foreground mt-4">
|
|
369
|
+
Press <kbd className="px-1 bg-muted rounded">?</kbd> or <kbd className="px-1 bg-muted rounded">Esc</kbd> to close
|
|
370
|
+
</p>
|
|
371
|
+
</div>
|
|
372
|
+
</motion.div>
|
|
373
|
+
)}
|
|
374
|
+
</AnimatePresence>
|
|
375
|
+
|
|
376
|
+
{/* Header */}
|
|
377
|
+
<div
|
|
378
|
+
className="flex items-center justify-between px-4 py-3 bg-muted/30 cursor-pointer"
|
|
379
|
+
onClick={() => setIsExpanded(!isExpanded)}
|
|
380
|
+
>
|
|
381
|
+
<div className="flex items-center gap-3">
|
|
382
|
+
<FileText className="w-5 h-5 text-primary" />
|
|
383
|
+
<h3 className="font-medium">Tracked Changes</h3>
|
|
384
|
+
{allChanges.length > 0 && (
|
|
385
|
+
<span className="px-2 py-0.5 text-xs bg-primary/10 text-primary rounded-full">
|
|
386
|
+
{allChanges.length} changes
|
|
387
|
+
</span>
|
|
388
|
+
)}
|
|
389
|
+
</div>
|
|
390
|
+
<div className="flex items-center gap-2">
|
|
391
|
+
{/* Keyboard hint button */}
|
|
392
|
+
<button
|
|
393
|
+
onClick={(e) => {
|
|
394
|
+
e.stopPropagation();
|
|
395
|
+
setShowKeyboardHint(true);
|
|
396
|
+
}}
|
|
397
|
+
className="p-1.5 rounded-md hover:bg-muted transition-colors text-muted-foreground"
|
|
398
|
+
title="Keyboard shortcuts (?)"
|
|
399
|
+
>
|
|
400
|
+
<Keyboard className="w-4 h-4" />
|
|
401
|
+
</button>
|
|
402
|
+
<button
|
|
403
|
+
onClick={(e) => {
|
|
404
|
+
e.stopPropagation();
|
|
405
|
+
onOpenEditor();
|
|
406
|
+
}}
|
|
407
|
+
className="p-1.5 rounded-md hover:bg-muted transition-colors"
|
|
408
|
+
title="Open in Editor"
|
|
409
|
+
>
|
|
410
|
+
<Maximize2 className="w-4 h-4" />
|
|
411
|
+
</button>
|
|
412
|
+
{isExpanded ? (
|
|
413
|
+
<ChevronUp className="w-5 h-5 text-muted-foreground" />
|
|
414
|
+
) : (
|
|
415
|
+
<ChevronDown className="w-5 h-5 text-muted-foreground" />
|
|
416
|
+
)}
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
|
|
420
|
+
{/* Content */}
|
|
421
|
+
<AnimatePresence>
|
|
422
|
+
{isExpanded && (
|
|
423
|
+
<motion.div
|
|
424
|
+
initial={{ height: 0, opacity: 0 }}
|
|
425
|
+
animate={{ height: 'auto', opacity: 1 }}
|
|
426
|
+
exit={{ height: 0, opacity: 0 }}
|
|
427
|
+
transition={{ duration: 0.2 }}
|
|
428
|
+
>
|
|
429
|
+
{/* Tab Bar */}
|
|
430
|
+
<div className="flex items-center justify-between px-4 py-2 border-b border-border bg-muted/10">
|
|
431
|
+
<div className="flex items-center gap-1">
|
|
432
|
+
<TabButton
|
|
433
|
+
active={activeTab === 'inline'}
|
|
434
|
+
onClick={() => setActiveTab('inline')}
|
|
435
|
+
icon={FileText}
|
|
436
|
+
label="Inline"
|
|
437
|
+
count={allChanges.length}
|
|
438
|
+
/>
|
|
439
|
+
<TabButton
|
|
440
|
+
active={activeTab === 'list'}
|
|
441
|
+
onClick={() => setActiveTab('list')}
|
|
442
|
+
icon={List}
|
|
443
|
+
label="List"
|
|
444
|
+
count={allChanges.length}
|
|
445
|
+
/>
|
|
446
|
+
<TabButton
|
|
447
|
+
active={activeTab === 'comparison'}
|
|
448
|
+
onClick={() => setActiveTab('comparison')}
|
|
449
|
+
icon={Columns}
|
|
450
|
+
label="Before/After"
|
|
451
|
+
/>
|
|
452
|
+
</div>
|
|
453
|
+
|
|
454
|
+
{/* Controls */}
|
|
455
|
+
{allChanges.length > 0 && activeTab !== 'comparison' && (
|
|
456
|
+
<div className="flex items-center gap-2">
|
|
457
|
+
{/* Expand/Collapse All button (for inline view) */}
|
|
458
|
+
{activeTab === 'inline' && (
|
|
459
|
+
<button
|
|
460
|
+
onClick={toggleAllParagraphs}
|
|
461
|
+
className="p-1.5 rounded hover:bg-muted transition-colors flex items-center gap-1 text-xs text-muted-foreground"
|
|
462
|
+
title={allParagraphsExpanded ? 'Collapse all paragraphs (e)' : 'Expand all paragraphs (e)'}
|
|
463
|
+
>
|
|
464
|
+
{allParagraphsExpanded ? (
|
|
465
|
+
<ChevronsDownUp className="w-4 h-4" />
|
|
466
|
+
) : (
|
|
467
|
+
<ChevronsUpDown className="w-4 h-4" />
|
|
468
|
+
)}
|
|
469
|
+
<span className="hidden sm:inline">
|
|
470
|
+
{allParagraphsExpanded ? 'Collapse' : 'Expand'}
|
|
471
|
+
</span>
|
|
472
|
+
</button>
|
|
473
|
+
)}
|
|
474
|
+
|
|
475
|
+
{/* Separator */}
|
|
476
|
+
{activeTab === 'inline' && (
|
|
477
|
+
<div className="w-px h-4 bg-border" />
|
|
478
|
+
)}
|
|
479
|
+
|
|
480
|
+
{/* Navigation controls */}
|
|
481
|
+
<div className="flex items-center gap-1">
|
|
482
|
+
<button
|
|
483
|
+
onClick={handlePreviousChange}
|
|
484
|
+
disabled={currentChangeIndex === 0}
|
|
485
|
+
className="p-1 rounded hover:bg-muted disabled:opacity-50 disabled:cursor-not-allowed"
|
|
486
|
+
title="Previous change (k)"
|
|
487
|
+
>
|
|
488
|
+
<ChevronLeft className="w-4 h-4" />
|
|
489
|
+
</button>
|
|
490
|
+
<span className="text-xs text-muted-foreground px-2">
|
|
491
|
+
{currentChangeIndex + 1} / {allChanges.length}
|
|
492
|
+
</span>
|
|
493
|
+
<button
|
|
494
|
+
onClick={handleNextChange}
|
|
495
|
+
disabled={currentChangeIndex === allChanges.length - 1}
|
|
496
|
+
className="p-1 rounded hover:bg-muted disabled:opacity-50 disabled:cursor-not-allowed"
|
|
497
|
+
title="Next change (j)"
|
|
498
|
+
>
|
|
499
|
+
<ChevronRight className="w-4 h-4" />
|
|
500
|
+
</button>
|
|
501
|
+
</div>
|
|
502
|
+
</div>
|
|
503
|
+
)}
|
|
504
|
+
</div>
|
|
505
|
+
|
|
506
|
+
{/* Tab Content */}
|
|
507
|
+
<div className="max-h-[500px] overflow-y-auto">
|
|
508
|
+
{activeTab === 'inline' && (
|
|
509
|
+
<InlineChangesView
|
|
510
|
+
changes={allChanges}
|
|
511
|
+
highlightedChangeIndex={currentChangeIndex}
|
|
512
|
+
onChangeClick={(change, index) => setCurrentChangeIndex(index)}
|
|
513
|
+
allExpanded={allParagraphsExpanded}
|
|
514
|
+
/>
|
|
515
|
+
)}
|
|
516
|
+
|
|
517
|
+
{activeTab === 'list' && <ListViewContent changes={allChanges} />}
|
|
518
|
+
|
|
519
|
+
{activeTab === 'comparison' && (
|
|
520
|
+
<>
|
|
521
|
+
{isLoadingSnapshot ? (
|
|
522
|
+
<div className="p-8 text-center text-muted-foreground">
|
|
523
|
+
<RefreshCw className="w-8 h-8 mx-auto mb-2 animate-spin" />
|
|
524
|
+
<p>Loading comparison data...</p>
|
|
525
|
+
</div>
|
|
526
|
+
) : (
|
|
527
|
+
<SideBySideDiff
|
|
528
|
+
originalContent={originalText}
|
|
529
|
+
modifiedContent={postProcessingText}
|
|
530
|
+
syncScroll={true}
|
|
531
|
+
showLineNumbers={true}
|
|
532
|
+
collapseUnchanged={false}
|
|
533
|
+
height={400}
|
|
534
|
+
/>
|
|
535
|
+
)}
|
|
536
|
+
</>
|
|
537
|
+
)}
|
|
538
|
+
</div>
|
|
539
|
+
</motion.div>
|
|
540
|
+
)}
|
|
541
|
+
</AnimatePresence>
|
|
542
|
+
</div>
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export default TrackedChangesPanel;
|