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,408 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChangeItem - Individual change display component
|
|
3
|
+
*
|
|
4
|
+
* Displays a single unified change with source badge, description,
|
|
5
|
+
* optional diff view, and location context.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { UnifiedChange } from '@/types/session';
|
|
9
|
+
import { cn } from '@/utils/cn';
|
|
10
|
+
import { FileText, User } from 'lucide-react';
|
|
11
|
+
|
|
12
|
+
interface ChangeItemProps {
|
|
13
|
+
change: UnifiedChange;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function ChangeItem({ change }: ChangeItemProps) {
|
|
17
|
+
const hasContent = change.before || change.after;
|
|
18
|
+
const hasDiff = change.before && change.after && change.before !== change.after;
|
|
19
|
+
const isHyperlinkChange = change.category === 'hyperlink' && change.hyperlinkChange;
|
|
20
|
+
const hasPropertyChange = change.propertyChange && change.propertyChange.property;
|
|
21
|
+
const hasGroupedProperties = change.groupedProperties && change.groupedProperties.length > 0;
|
|
22
|
+
|
|
23
|
+
// Detect if this is a combined "Updated" change (has both before and after)
|
|
24
|
+
const isUpdatedChange = hasDiff || change.description?.toLowerCase().startsWith('updated');
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className="group p-3 bg-muted/30 rounded-md hover:bg-muted/50 transition-colors">
|
|
28
|
+
{/* Header Row */}
|
|
29
|
+
<div className="flex items-start justify-between gap-2">
|
|
30
|
+
<div className="flex items-start gap-2 flex-1 min-w-0">
|
|
31
|
+
{/* Source Badge */}
|
|
32
|
+
<SourceBadge source={change.source} />
|
|
33
|
+
|
|
34
|
+
{/* Description */}
|
|
35
|
+
<div className="flex-1 min-w-0">
|
|
36
|
+
<p className="text-sm text-foreground break-words">
|
|
37
|
+
{change.description || 'Change applied'}
|
|
38
|
+
{/* Show Content ID in title for hyperlink changes */}
|
|
39
|
+
{isHyperlinkChange && change.hyperlinkChange?.contentId && (
|
|
40
|
+
<code className="ml-2 bg-primary/10 text-primary px-1.5 py-0.5 rounded text-xs font-medium">
|
|
41
|
+
{extractContentIdSuffix(change.hyperlinkChange.contentId)}
|
|
42
|
+
</code>
|
|
43
|
+
)}
|
|
44
|
+
</p>
|
|
45
|
+
|
|
46
|
+
{/* Affected Text - Show context of what was changed (5-8 words) */}
|
|
47
|
+
{change.affectedText && !change.hyperlinkChange && !isUpdatedChange && (
|
|
48
|
+
<p className="text-xs text-muted-foreground mt-1" title={change.affectedText}>
|
|
49
|
+
Text: "{getContextWords(change.affectedText)}"
|
|
50
|
+
</p>
|
|
51
|
+
)}
|
|
52
|
+
|
|
53
|
+
{/* Author (for Word revisions) */}
|
|
54
|
+
{change.author && (
|
|
55
|
+
<div className="flex items-center gap-1 mt-1 text-xs text-muted-foreground">
|
|
56
|
+
<User className="w-3 h-3" />
|
|
57
|
+
<span>{change.author}</span>
|
|
58
|
+
{change.date && (
|
|
59
|
+
<>
|
|
60
|
+
<span className="mx-1">-</span>
|
|
61
|
+
<span>{formatDate(change.date)}</span>
|
|
62
|
+
</>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
)}
|
|
66
|
+
|
|
67
|
+
{/* Location Context */}
|
|
68
|
+
{change.location?.nearestHeading && (
|
|
69
|
+
<div className="flex items-center gap-1 mt-1 text-xs text-muted-foreground">
|
|
70
|
+
<FileText className="w-3 h-3" />
|
|
71
|
+
<span>Near: {change.location.nearestHeading}</span>
|
|
72
|
+
</div>
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
{/* Count Badge (for consolidated changes) */}
|
|
78
|
+
{change.count && change.count > 1 && (
|
|
79
|
+
<span className="shrink-0 px-2 py-0.5 text-xs font-medium bg-primary/10 text-primary rounded-full">
|
|
80
|
+
{change.count} instances
|
|
81
|
+
</span>
|
|
82
|
+
)}
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
{/* Hyperlink Change View */}
|
|
86
|
+
{isHyperlinkChange ? (
|
|
87
|
+
<HyperlinkChangeView change={change.hyperlinkChange!} />
|
|
88
|
+
) : hasContent ? (
|
|
89
|
+
<div className="mt-2 pl-7">
|
|
90
|
+
{hasDiff ? (
|
|
91
|
+
<UpdatedDiffView before={change.before!} after={change.after!} />
|
|
92
|
+
) : change.before ? (
|
|
93
|
+
<div className="text-xs">
|
|
94
|
+
<span className="text-muted-foreground">Removed: </span>
|
|
95
|
+
<span className="text-red-600 dark:text-red-400 line-through">
|
|
96
|
+
"{getContextWords(change.before)}"
|
|
97
|
+
</span>
|
|
98
|
+
</div>
|
|
99
|
+
) : change.after ? (
|
|
100
|
+
<div className="text-xs">
|
|
101
|
+
<span className="text-muted-foreground">Added: </span>
|
|
102
|
+
<span className="text-green-600 dark:text-green-400">
|
|
103
|
+
"{getContextWords(change.after)}"
|
|
104
|
+
</span>
|
|
105
|
+
</div>
|
|
106
|
+
) : null}
|
|
107
|
+
</div>
|
|
108
|
+
) : null}
|
|
109
|
+
|
|
110
|
+
{/* Property Change View (for single formatting change) */}
|
|
111
|
+
{hasPropertyChange && !hasGroupedProperties && (
|
|
112
|
+
<div className="mt-2 pl-7 text-xs">
|
|
113
|
+
<span className="text-muted-foreground">Property: </span>
|
|
114
|
+
<span className="font-medium">{change.propertyChange!.property}</span>
|
|
115
|
+
{change.affectedText && (
|
|
116
|
+
<span className="text-muted-foreground ml-1" title={change.affectedText}>
|
|
117
|
+
on "{truncateText(change.affectedText, 30)}"
|
|
118
|
+
</span>
|
|
119
|
+
)}
|
|
120
|
+
{(change.propertyChange!.oldValue || change.propertyChange!.newValue) && (
|
|
121
|
+
<span className="ml-2">
|
|
122
|
+
{change.propertyChange!.oldValue && (
|
|
123
|
+
<span className="text-red-600 dark:text-red-400 line-through mr-1">
|
|
124
|
+
{formatPropertyValue(change.propertyChange!.oldValue)}
|
|
125
|
+
</span>
|
|
126
|
+
)}
|
|
127
|
+
<span className="text-muted-foreground mx-1">-></span>
|
|
128
|
+
{change.propertyChange!.newValue && (
|
|
129
|
+
<span className="text-green-600 dark:text-green-400">
|
|
130
|
+
{formatPropertyValue(change.propertyChange!.newValue)}
|
|
131
|
+
</span>
|
|
132
|
+
)}
|
|
133
|
+
</span>
|
|
134
|
+
)}
|
|
135
|
+
</div>
|
|
136
|
+
)}
|
|
137
|
+
|
|
138
|
+
{/* Grouped Property Changes View (multiple formatting changes on same text) */}
|
|
139
|
+
{hasGroupedProperties && (
|
|
140
|
+
<div className="mt-2 pl-7">
|
|
141
|
+
<div className="text-xs text-muted-foreground mb-1">Properties changed:</div>
|
|
142
|
+
<div className="space-y-1">
|
|
143
|
+
{change.groupedProperties!.map((prop, idx) => (
|
|
144
|
+
<div key={idx} className="text-xs flex items-center gap-2">
|
|
145
|
+
<span className="text-muted-foreground">-</span>
|
|
146
|
+
<span className="font-medium min-w-[100px]">{prop.property}:</span>
|
|
147
|
+
{prop.oldValue && (
|
|
148
|
+
<span className="text-red-600 dark:text-red-400 line-through">
|
|
149
|
+
{formatPropertyValue(prop.oldValue)}
|
|
150
|
+
</span>
|
|
151
|
+
)}
|
|
152
|
+
<span className="text-muted-foreground">-></span>
|
|
153
|
+
{prop.newValue && (
|
|
154
|
+
<span className="text-green-600 dark:text-green-400">
|
|
155
|
+
{formatPropertyValue(prop.newValue)}
|
|
156
|
+
</span>
|
|
157
|
+
)}
|
|
158
|
+
</div>
|
|
159
|
+
))}
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
</div>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Sub-components
|
|
168
|
+
|
|
169
|
+
interface SourceBadgeProps {
|
|
170
|
+
source: 'word' | 'processing';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function SourceBadge({ source }: SourceBadgeProps) {
|
|
174
|
+
const isWord = source === 'word';
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<span
|
|
178
|
+
className={cn(
|
|
179
|
+
'shrink-0 px-1.5 py-0.5 text-[10px] font-medium rounded uppercase tracking-wide',
|
|
180
|
+
isWord
|
|
181
|
+
? 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300'
|
|
182
|
+
: 'bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300'
|
|
183
|
+
)}
|
|
184
|
+
>
|
|
185
|
+
{isWord ? 'Word' : 'DocHub'}
|
|
186
|
+
</span>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
interface UpdatedDiffViewProps {
|
|
191
|
+
before: string;
|
|
192
|
+
after: string;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Shows "Updated" change with before/after in a clean format
|
|
197
|
+
*/
|
|
198
|
+
function UpdatedDiffView({ before, after }: UpdatedDiffViewProps) {
|
|
199
|
+
const beforeContext = getContextWords(before);
|
|
200
|
+
const afterContext = getContextWords(after);
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<div className="space-y-1.5 text-xs">
|
|
204
|
+
<div className="flex items-start gap-2">
|
|
205
|
+
<span className="text-muted-foreground shrink-0">From:</span>
|
|
206
|
+
<span className="text-red-600 dark:text-red-400 line-through break-words">
|
|
207
|
+
"{beforeContext}"
|
|
208
|
+
</span>
|
|
209
|
+
</div>
|
|
210
|
+
<div className="flex items-start gap-2">
|
|
211
|
+
<span className="text-muted-foreground shrink-0">To:</span>
|
|
212
|
+
<span className="text-green-600 dark:text-green-400 break-words">
|
|
213
|
+
"{afterContext}"
|
|
214
|
+
</span>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
interface HyperlinkChangeViewProps {
|
|
221
|
+
change: {
|
|
222
|
+
urlBefore?: string;
|
|
223
|
+
urlAfter?: string;
|
|
224
|
+
textBefore?: string;
|
|
225
|
+
textAfter?: string;
|
|
226
|
+
status?: 'updated' | 'not_found' | 'expired';
|
|
227
|
+
contentId?: string;
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function HyperlinkChangeView({ change }: HyperlinkChangeViewProps) {
|
|
232
|
+
const urlChanged = change.urlBefore !== change.urlAfter;
|
|
233
|
+
const textChanged = change.textBefore !== change.textAfter;
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<div className="mt-2 pl-7 space-y-3">
|
|
237
|
+
{/* Content ID - Always shown prominently at top for identification */}
|
|
238
|
+
{change.contentId && (
|
|
239
|
+
<div className="flex items-center gap-2">
|
|
240
|
+
<span className="text-xs font-medium text-muted-foreground">Content ID:</span>
|
|
241
|
+
<code className="bg-primary/10 text-primary px-1.5 py-0.5 rounded text-xs font-medium">
|
|
242
|
+
{change.contentId}
|
|
243
|
+
</code>
|
|
244
|
+
</div>
|
|
245
|
+
)}
|
|
246
|
+
|
|
247
|
+
{/* Status Badge (for not_found or expired) */}
|
|
248
|
+
{change.status && change.status !== 'updated' && (
|
|
249
|
+
<div className="flex items-center gap-2">
|
|
250
|
+
<span
|
|
251
|
+
className={cn(
|
|
252
|
+
'px-2 py-0.5 text-xs font-medium rounded uppercase',
|
|
253
|
+
change.status === 'not_found'
|
|
254
|
+
? 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300'
|
|
255
|
+
: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-300'
|
|
256
|
+
)}
|
|
257
|
+
>
|
|
258
|
+
{change.status === 'not_found' ? 'Source Not Found' : 'Source Expired'}
|
|
259
|
+
</span>
|
|
260
|
+
</div>
|
|
261
|
+
)}
|
|
262
|
+
|
|
263
|
+
{/* What Changed section */}
|
|
264
|
+
{(urlChanged || textChanged) && (
|
|
265
|
+
<div className="space-y-2">
|
|
266
|
+
<span className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
|
267
|
+
What Changed:
|
|
268
|
+
</span>
|
|
269
|
+
|
|
270
|
+
{/* URL Change */}
|
|
271
|
+
{urlChanged && (
|
|
272
|
+
<div className="space-y-1 ml-2">
|
|
273
|
+
<span className="text-xs text-muted-foreground">URL:</span>
|
|
274
|
+
<div className="space-y-1 text-xs font-mono">
|
|
275
|
+
{change.urlBefore && (
|
|
276
|
+
<div className="flex items-start gap-2">
|
|
277
|
+
<span className="shrink-0 w-1 h-full bg-red-500 rounded-full" />
|
|
278
|
+
<span className="text-red-600 dark:text-red-400 line-through break-all">
|
|
279
|
+
{truncateUrl(change.urlBefore)}
|
|
280
|
+
</span>
|
|
281
|
+
</div>
|
|
282
|
+
)}
|
|
283
|
+
{change.urlAfter && (
|
|
284
|
+
<div className="flex items-start gap-2">
|
|
285
|
+
<span className="shrink-0 w-1 h-full bg-green-500 rounded-full" />
|
|
286
|
+
<span className="text-green-600 dark:text-green-400 break-all">
|
|
287
|
+
{truncateUrl(change.urlAfter)}
|
|
288
|
+
</span>
|
|
289
|
+
</div>
|
|
290
|
+
)}
|
|
291
|
+
</div>
|
|
292
|
+
</div>
|
|
293
|
+
)}
|
|
294
|
+
|
|
295
|
+
{/* Text to Display Change */}
|
|
296
|
+
{textChanged && (
|
|
297
|
+
<div className="space-y-1 ml-2">
|
|
298
|
+
<span className="text-xs text-muted-foreground">Text to Display:</span>
|
|
299
|
+
<div className="space-y-1 text-xs font-mono">
|
|
300
|
+
{change.textBefore && (
|
|
301
|
+
<div className="flex items-start gap-2">
|
|
302
|
+
<span className="shrink-0 w-1 h-full bg-red-500 rounded-full" />
|
|
303
|
+
<span className="text-red-600 dark:text-red-400 line-through break-all">
|
|
304
|
+
{truncateText(change.textBefore, 80)}
|
|
305
|
+
</span>
|
|
306
|
+
</div>
|
|
307
|
+
)}
|
|
308
|
+
{change.textAfter && (
|
|
309
|
+
<div className="flex items-start gap-2">
|
|
310
|
+
<span className="shrink-0 w-1 h-full bg-green-500 rounded-full" />
|
|
311
|
+
<span className="text-green-600 dark:text-green-400 break-all">
|
|
312
|
+
{truncateText(change.textAfter, 80)}
|
|
313
|
+
</span>
|
|
314
|
+
</div>
|
|
315
|
+
)}
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
)}
|
|
319
|
+
</div>
|
|
320
|
+
)}
|
|
321
|
+
</div>
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Utility functions
|
|
326
|
+
|
|
327
|
+
function formatDate(date: Date | string): string {
|
|
328
|
+
const d = typeof date === 'string' ? new Date(date) : date;
|
|
329
|
+
return d.toLocaleDateString(undefined, {
|
|
330
|
+
month: 'short',
|
|
331
|
+
day: 'numeric',
|
|
332
|
+
year: 'numeric',
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function truncateText(text: string, maxLength: number): string {
|
|
337
|
+
if (text.length <= maxLength) return text;
|
|
338
|
+
return text.substring(0, maxLength) + '...';
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Gets a context snippet (5-8 words) from text for display
|
|
343
|
+
* Provides meaningful context without overwhelming the UI
|
|
344
|
+
*/
|
|
345
|
+
function getContextWords(text: string): string {
|
|
346
|
+
if (!text) return '';
|
|
347
|
+
|
|
348
|
+
// Clean up whitespace
|
|
349
|
+
const cleaned = text.replace(/\s+/g, ' ').trim();
|
|
350
|
+
if (!cleaned) return '';
|
|
351
|
+
|
|
352
|
+
// Split into words
|
|
353
|
+
const words = cleaned.split(' ').filter((w) => w.length > 0);
|
|
354
|
+
if (words.length === 0) return '';
|
|
355
|
+
|
|
356
|
+
// If 8 words or less, return as-is
|
|
357
|
+
if (words.length <= 8) {
|
|
358
|
+
return cleaned;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Take first 6 words and add ellipsis
|
|
362
|
+
return words.slice(0, 6).join(' ') + '...';
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function truncateUrl(url: string, maxLength: number = 60): string {
|
|
366
|
+
if (url.length <= maxLength) return url;
|
|
367
|
+
try {
|
|
368
|
+
const parsed = new URL(url);
|
|
369
|
+
const domain = parsed.hostname;
|
|
370
|
+
const remaining = maxLength - domain.length - 5;
|
|
371
|
+
return `${domain}/...${url.slice(-Math.max(10, remaining))}`;
|
|
372
|
+
} catch {
|
|
373
|
+
return url.substring(0, maxLength) + '...';
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Extract the numeric suffix from a Content ID for compact display
|
|
379
|
+
* e.g., "TSRC-ABC-123456" -> "123456"
|
|
380
|
+
*/
|
|
381
|
+
function extractContentIdSuffix(contentId: string): string {
|
|
382
|
+
// Try to extract the last numeric/alphanumeric segment
|
|
383
|
+
const parts = contentId.split(/[-_]/);
|
|
384
|
+
const lastPart = parts[parts.length - 1];
|
|
385
|
+
if (lastPart && lastPart.length > 0) {
|
|
386
|
+
return lastPart;
|
|
387
|
+
}
|
|
388
|
+
// Fallback to last 6 characters if no delimiter found
|
|
389
|
+
return contentId.slice(-6);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Format property values for display, handling objects that would show as [object Object]
|
|
394
|
+
*/
|
|
395
|
+
function formatPropertyValue(value: unknown): string {
|
|
396
|
+
if (value === null || value === undefined) return '';
|
|
397
|
+
if (typeof value === 'object') {
|
|
398
|
+
try {
|
|
399
|
+
// For complex objects, show a simplified JSON representation
|
|
400
|
+
const json = JSON.stringify(value);
|
|
401
|
+
// Truncate if too long
|
|
402
|
+
return json.length > 50 ? json.substring(0, 47) + '...' : json;
|
|
403
|
+
} catch {
|
|
404
|
+
return '[complex value]';
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return String(value);
|
|
408
|
+
}
|