documentation-hub 5.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/.eslintrc.json +43 -0
  2. package/.github/workflows/build.yml +64 -0
  3. package/.github/workflows/ci.yml +39 -0
  4. package/.vscode/extensions.json +3 -0
  5. package/Current.md +97 -0
  6. package/DocHub_Image.png +0 -0
  7. package/README.md +666 -0
  8. package/USER_GUIDE.md +1173 -0
  9. package/Updater.md +311 -0
  10. package/build/256x256.png +0 -0
  11. package/build/512x512.png +0 -0
  12. package/build/app-update.yml +4 -0
  13. package/build/create-icon.js +208 -0
  14. package/build/icon.ico +0 -0
  15. package/build/icon.png +0 -0
  16. package/build/icon_1024x1024.png +0 -0
  17. package/dist/assets/Analytics-BpsG9895.js +1 -0
  18. package/dist/assets/Card-IAZin8kp.js +1 -0
  19. package/dist/assets/CurrentSession-B-rFkHvf.js +12 -0
  20. package/dist/assets/Dashboard-C_5gMb0q.js +1 -0
  21. package/dist/assets/Documents-CqZ25axS.js +1 -0
  22. package/dist/assets/Input-l89xwXBi.js +1 -0
  23. package/dist/assets/Reporting-DqdHJY_a.js +1 -0
  24. package/dist/assets/Search-XNbu5z_3.js +1 -0
  25. package/dist/assets/SessionManager-lH9hZfzH.js +1 -0
  26. package/dist/assets/Sessions-ClZOPYNc.js +1 -0
  27. package/dist/assets/Settings-DUEHGURa.js +11 -0
  28. package/dist/assets/index-8xUe8ptc.js +24 -0
  29. package/dist/assets/index-RYyJqF7O.css +1 -0
  30. package/dist/assets/path-BkOl0AGO.js +1 -0
  31. package/dist/assets/promises-ID_B9S-h.js +1 -0
  32. package/dist/assets/urlHelpers-TvgahX0r.js +1 -0
  33. package/dist/assets/useToast-yRSO1dkm.js +1 -0
  34. package/dist/assets/vendor-charts-RkGK5ROP.js +36 -0
  35. package/dist/assets/vendor-db-l0sNRNKZ.js +1 -0
  36. package/dist/assets/vendor-react-BVZ_anCF.js +4 -0
  37. package/dist/assets/vendor-search-Dw8P0qyA.js +1 -0
  38. package/dist/assets/vendor-ui-BU7NfluV.js +53 -0
  39. package/dist/electron/PowerAutomateApiService-LfW09ZGr.js +147 -0
  40. package/dist/electron/main-CXkNtyv-.js +19789 -0
  41. package/dist/electron/main.js +5 -0
  42. package/dist/electron/preload.js +1 -0
  43. package/dist/icon.png +0 -0
  44. package/dist/index.html +27 -0
  45. package/docs/CODEBASE_ANALYSIS_REPORT.md +309 -0
  46. package/docs/DEBUG_LOGGING_GUIDE.md +244 -0
  47. package/docs/README.md +115 -0
  48. package/docs/TOC_WIRING_GUIDE.md +344 -0
  49. package/docs/analysis/Bullet_Symbol_Bug_Analysis.md +136 -0
  50. package/docs/analysis/DOCXMLATER_ANALYSIS_SUMMARY.txt +169 -0
  51. package/docs/analysis/Document_Processing_Issues_Analysis.md +704 -0
  52. package/docs/analysis/FIELD_PRESERVATION_ANALYSIS.md +1200 -0
  53. package/docs/analysis/INDENTATION_PRESERVE_ANALYSIS.md +181 -0
  54. package/docs/analysis/INDENTATION_PRESERVE_IMPLEMENTATION.md +207 -0
  55. package/docs/analysis/List_Implementation.md +206 -0
  56. package/docs/analysis/List_Implementation_Accuracy_Report.md +366 -0
  57. package/docs/analysis/PROCESSING_OPTIONS_UI_UPDATES.md +220 -0
  58. package/docs/analysis/RefactorStyles.md +852 -0
  59. package/docs/analysis/STYLE_PARAMETER_ENHANCEMENT.md +143 -0
  60. package/docs/analysis/docxmlater-comparison-todo-2025-11-13.md +636 -0
  61. package/docs/analysis/docxmlater-implementation-analysis-2025-11-13.md +340 -0
  62. package/docs/analysis/docxmlater-template_ui-integration-analysis.md +263 -0
  63. package/docs/analysis/github-issues-to-create.md +237 -0
  64. package/docs/api/API_README.md +538 -0
  65. package/docs/api/API_REFERENCE.md +751 -0
  66. package/docs/api/TYPE_DEFINITIONS.md +869 -0
  67. package/docs/architecture/FONT_EMBEDDING_GUIDE.md +318 -0
  68. package/docs/architecture/docxmlater-functions-and-structure.md +726 -0
  69. package/docs/docxmlater-readme.md +1341 -0
  70. package/docs/fixes/EXECUTION_LOG_TEST_BASE.md +573 -0
  71. package/docs/fixes/HYPERLINK_TEXT_SANITIZATION.md +253 -0
  72. package/docs/fixes/README.md +37 -0
  73. package/docs/github-issues/issue-1-body.md +125 -0
  74. package/docs/github-issues/issue-10-body.md +850 -0
  75. package/docs/github-issues/issue-2-body.md +200 -0
  76. package/docs/github-issues/issue-3-body.md +270 -0
  77. package/docs/github-issues/issue-4-body.md +169 -0
  78. package/docs/github-issues/issue-5-body.md +173 -0
  79. package/docs/github-issues/issue-6-body.md +158 -0
  80. package/docs/github-issues/issue-7-body.md +171 -0
  81. package/docs/github-issues/issue-8-body.md +407 -0
  82. package/docs/github-issues/issue-9-body.md +515 -0
  83. package/docs/github-issues/issue-tracker.md +274 -0
  84. package/docs/github-issues/predictive-analysis-2025-10-18.md +2131 -0
  85. package/docs/implementation/List_Framework_Refactor_Plan.md +336 -0
  86. package/docs/implementation/PRIMARY_TEXT_COLOR_FEATURE.md +217 -0
  87. package/docs/implementation/RELEASE_PLAN_v2.1.0.md +362 -0
  88. package/docs/implementation/RefactorStyles.md +588 -0
  89. package/docs/implementation/implement-plan.md +489 -0
  90. package/docs/implementation/missing-helpers-implementation.md +391 -0
  91. package/docs/implementation/refactor-plan.md +520 -0
  92. package/docs/implementation/session-implementation-complete.md +233 -0
  93. package/docs/implementation/session-management-plan.md +250 -0
  94. package/docs/setup-checklist.md +77 -0
  95. package/docs/versions/changelog.md +345 -0
  96. package/electron/customUpdater.ts +656 -0
  97. package/electron/main.ts +2441 -0
  98. package/electron/memoryConfig.ts +187 -0
  99. package/electron/preload.ts +394 -0
  100. package/electron/proxyConfig.ts +340 -0
  101. package/electron/services/BackupService.ts +452 -0
  102. package/electron/services/DictionaryService.ts +402 -0
  103. package/electron/services/LocalDictionaryLookupService.ts +147 -0
  104. package/electron/services/PowerAutomateApiService.ts +231 -0
  105. package/electron/services/SharePointSyncService.ts +474 -0
  106. package/electron/windowsCertStore.ts +427 -0
  107. package/electron/zscalerConfig.ts +381 -0
  108. package/eslint.config.js +92 -0
  109. package/jest.config.js +52 -0
  110. package/package.json +214 -0
  111. package/postcss.config.mjs +6 -0
  112. package/public/icon.png +0 -0
  113. package/publish-release.ps1 +5 -0
  114. package/renovate.json +30 -0
  115. package/src/App.tsx +216 -0
  116. package/src/__mocks__/p-limit.js +12 -0
  117. package/src/__mocks__/styleMock.js +1 -0
  118. package/src/components/common/BugReportButton.tsx +44 -0
  119. package/src/components/common/BugReportDialog.tsx +193 -0
  120. package/src/components/common/Button.tsx +153 -0
  121. package/src/components/common/Card.tsx +86 -0
  122. package/src/components/common/ColorPickerDialog.tsx +177 -0
  123. package/src/components/common/ConfirmDialog.tsx +96 -0
  124. package/src/components/common/DebugConsole.tsx +275 -0
  125. package/src/components/common/EmptyState.tsx +183 -0
  126. package/src/components/common/ErrorBoundary.tsx +98 -0
  127. package/src/components/common/ErrorDetailsDialog.tsx +153 -0
  128. package/src/components/common/ErrorFallback.tsx +218 -0
  129. package/src/components/common/Input.tsx +109 -0
  130. package/src/components/common/Skeleton.tsx +184 -0
  131. package/src/components/common/SplashScreen.tsx +81 -0
  132. package/src/components/common/Toast.tsx +155 -0
  133. package/src/components/common/Tooltip.tsx +79 -0
  134. package/src/components/common/UpdateNotification.tsx +320 -0
  135. package/src/components/comparison/ComparisonWindow.tsx +374 -0
  136. package/src/components/comparison/SideBySideDiff.tsx +486 -0
  137. package/src/components/comparison/index.ts +8 -0
  138. package/src/components/document/DocumentUploader.tsx +288 -0
  139. package/src/components/document/HyperlinkPreview.tsx +430 -0
  140. package/src/components/document/HyperlinkService.md +1484 -0
  141. package/src/components/document/Hyperlink_Technical_Documentation.md +496 -0
  142. package/src/components/document/InlineChangesView.tsx +707 -0
  143. package/src/components/document/ProcessingProgress.tsx +303 -0
  144. package/src/components/document/ProcessingResults.tsx +256 -0
  145. package/src/components/document/TrackedChangesDetail.tsx +530 -0
  146. package/src/components/document/TrackedChangesPanel.tsx +546 -0
  147. package/src/components/document/VirtualDocumentList.tsx +240 -0
  148. package/src/components/editor/DocumentEditor.tsx +723 -0
  149. package/src/components/editor/DocumentEditorModal.tsx +640 -0
  150. package/src/components/editor/EditorQuickActions.tsx +502 -0
  151. package/src/components/editor/EditorToolbar.tsx +312 -0
  152. package/src/components/editor/TableEditor.tsx +926 -0
  153. package/src/components/editor/index.ts +18 -0
  154. package/src/components/layout/Header.tsx +190 -0
  155. package/src/components/layout/Sidebar.tsx +313 -0
  156. package/src/components/layout/TitleBar.tsx +190 -0
  157. package/src/components/navigation/CommandPalette.tsx +233 -0
  158. package/src/components/navigation/KeyboardShortcutsModal.tsx +173 -0
  159. package/src/components/sessions/ChangeItem.tsx +408 -0
  160. package/src/components/sessions/ChangeViewer.tsx +1155 -0
  161. package/src/components/sessions/DocumentComparisonModal.tsx +314 -0
  162. package/src/components/sessions/ProcessingOptions.tsx +297 -0
  163. package/src/components/sessions/ReplacementsTab.tsx +438 -0
  164. package/src/components/sessions/RevisionHandlingOptions.tsx +87 -0
  165. package/src/components/sessions/SessionManager.tsx +188 -0
  166. package/src/components/sessions/StylesEditor.tsx +1335 -0
  167. package/src/components/sessions/TabContainer.tsx +151 -0
  168. package/src/components/sessions/VirtualSessionList.tsx +157 -0
  169. package/src/components/sessions/sessionToProcessorManager.tsx +420 -0
  170. package/src/components/settings/CertificateManager.tsx +410 -0
  171. package/src/components/settings/SegmentedControl.tsx +88 -0
  172. package/src/components/settings/SettingRow.tsx +52 -0
  173. package/src/contexts/GlobalStatsContext.tsx +396 -0
  174. package/src/contexts/SessionContext.tsx +2129 -0
  175. package/src/contexts/ThemeContext.tsx +428 -0
  176. package/src/contexts/UserSettingsContext.tsx +290 -0
  177. package/src/contexts/__tests__/GlobalStatsContext.test.tsx +390 -0
  178. package/src/global.d.ts +273 -0
  179. package/src/hooks/useDocumentQueue.tsx +210 -0
  180. package/src/hooks/useToast.tsx +55 -0
  181. package/src/main.tsx +10 -0
  182. package/src/pages/Analytics.tsx +386 -0
  183. package/src/pages/CurrentSession.tsx +1174 -0
  184. package/src/pages/Dashboard.tsx +319 -0
  185. package/src/pages/Documents.tsx +317 -0
  186. package/src/pages/Projects.tsx +250 -0
  187. package/src/pages/Reporting.tsx +386 -0
  188. package/src/pages/Search.tsx +349 -0
  189. package/src/pages/Sessions.tsx +285 -0
  190. package/src/pages/Settings.tsx +2662 -0
  191. package/src/services/HyperlinkService.ts +1085 -0
  192. package/src/services/document/DocXMLaterProcessor.ts +617 -0
  193. package/src/services/document/DocumentProcessingComparison.ts +856 -0
  194. package/src/services/document/DocumentSnapshotService.ts +575 -0
  195. package/src/services/document/WordDocumentProcessor.ts +10509 -0
  196. package/src/services/document/__tests__/DocXMLaterProcessor.hyperlinks.test.md +311 -0
  197. package/src/services/document/__tests__/WordDocumentProcessor.integration.test.ts +515 -0
  198. package/src/services/document/__tests__/WordDocumentProcessor.test.ts +812 -0
  199. package/src/services/document/blanklines/BlankLineManager.ts +658 -0
  200. package/src/services/document/blanklines/__tests__/paragraphChecks.test.ts +281 -0
  201. package/src/services/document/blanklines/helpers/blankLineInsertion.ts +87 -0
  202. package/src/services/document/blanklines/helpers/blankLineSnapshot.ts +251 -0
  203. package/src/services/document/blanklines/helpers/clearCustom.ts +121 -0
  204. package/src/services/document/blanklines/helpers/contextChecks.ts +117 -0
  205. package/src/services/document/blanklines/helpers/imageChecks.ts +51 -0
  206. package/src/services/document/blanklines/helpers/paragraphChecks.ts +236 -0
  207. package/src/services/document/blanklines/helpers/removeBlanksBetweenListItems.ts +91 -0
  208. package/src/services/document/blanklines/helpers/removeTrailingBlanks.ts +35 -0
  209. package/src/services/document/blanklines/helpers/tableGuards.ts +21 -0
  210. package/src/services/document/blanklines/index.ts +67 -0
  211. package/src/services/document/blanklines/rules/additionRules.ts +337 -0
  212. package/src/services/document/blanklines/rules/indentationRules.ts +317 -0
  213. package/src/services/document/blanklines/rules/removalRules.ts +362 -0
  214. package/src/services/document/blanklines/rules/ruleTypes.ts +92 -0
  215. package/src/services/document/blanklines/types.ts +29 -0
  216. package/src/services/document/helpers/ImageBorderCropper.ts +377 -0
  217. package/src/services/document/helpers/__tests__/whitespace.test.ts +272 -0
  218. package/src/services/document/helpers/whitespace.ts +117 -0
  219. package/src/services/document/list/ListNormalizer.ts +947 -0
  220. package/src/services/document/list/index.ts +45 -0
  221. package/src/services/document/list/list-detection.ts +275 -0
  222. package/src/services/document/list/list-types.ts +162 -0
  223. package/src/services/document/processors/HyperlinkProcessor.ts +370 -0
  224. package/src/services/document/processors/ListProcessor.ts +257 -0
  225. package/src/services/document/processors/StructureProcessor.ts +176 -0
  226. package/src/services/document/processors/StyleProcessor.ts +389 -0
  227. package/src/services/document/processors/TableProcessor.ts +2238 -0
  228. package/src/services/document/processors/__tests__/HyperlinkProcessor.test.ts +314 -0
  229. package/src/services/document/processors/__tests__/ListProcessor.test.ts +291 -0
  230. package/src/services/document/processors/__tests__/StructureProcessor.test.ts +257 -0
  231. package/src/services/document/processors/__tests__/TableProcessor.hlp-tips-bullets.test.ts +459 -0
  232. package/src/services/document/processors/__tests__/TableProcessor.test.ts +1604 -0
  233. package/src/services/document/processors/index.ts +28 -0
  234. package/src/services/document/types/docx-processing.ts +310 -0
  235. package/src/services/editor/EditorActionHandlers.ts +901 -0
  236. package/src/services/editor/index.ts +13 -0
  237. package/src/setupTests.ts +47 -0
  238. package/src/styles/global.css +782 -0
  239. package/src/types/backup.ts +132 -0
  240. package/src/types/dictionary.ts +125 -0
  241. package/src/types/document-processing.ts +331 -0
  242. package/src/types/docxmlater-augments.d.ts +142 -0
  243. package/src/types/editor.ts +280 -0
  244. package/src/types/electron.ts +340 -0
  245. package/src/types/globalStats.ts +155 -0
  246. package/src/types/hyperlink.ts +471 -0
  247. package/src/types/operations.ts +354 -0
  248. package/src/types/session.ts +427 -0
  249. package/src/types/settings.ts +112 -0
  250. package/src/utils/MemoryMonitor.ts +248 -0
  251. package/src/utils/cn.ts +6 -0
  252. package/src/utils/colorConvert.ts +306 -0
  253. package/src/utils/diffUtils.ts +347 -0
  254. package/src/utils/documentUtils.ts +202 -0
  255. package/src/utils/electronGuard.ts +62 -0
  256. package/src/utils/indexedDB.ts +915 -0
  257. package/src/utils/logger.ts +717 -0
  258. package/src/utils/pathSecurity.ts +232 -0
  259. package/src/utils/pathValidator.ts +236 -0
  260. package/src/utils/processingTimeEstimator.ts +153 -0
  261. package/src/utils/safeJsonParse.ts +62 -0
  262. package/src/utils/textSanitizer.ts +162 -0
  263. package/src/utils/urlHelpers.ts +304 -0
  264. package/src/utils/urlPatterns.ts +198 -0
  265. package/src/utils/urlSanitizer.ts +152 -0
  266. package/src/vite-env.d.ts +11 -0
  267. package/tsconfig.electron.json +19 -0
  268. package/tsconfig.json +36 -0
  269. package/tsconfig.node.json +12 -0
  270. package/typedoc.json +45 -0
  271. package/vite.config.ts +152 -0
@@ -0,0 +1,96 @@
1
+ import * as React from 'react';
2
+ import * as Dialog from '@radix-ui/react-dialog';
3
+ import { Button } from './Button';
4
+ import { X } from 'lucide-react';
5
+ import { cn } from '@/utils/cn';
6
+ import logger from '@/utils/logger';
7
+
8
+ export interface ConfirmDialogProps {
9
+ open: boolean;
10
+ onOpenChange: (open: boolean) => void;
11
+ title: string;
12
+ message: string;
13
+ confirmText?: string;
14
+ cancelText?: string;
15
+ variant?: 'default' | 'destructive';
16
+ onConfirm: () => void | Promise<void>;
17
+ loading?: boolean;
18
+ }
19
+
20
+ export function ConfirmDialog({
21
+ open,
22
+ onOpenChange,
23
+ title,
24
+ message,
25
+ confirmText = 'Confirm',
26
+ cancelText = 'Cancel',
27
+ variant = 'default',
28
+ onConfirm,
29
+ loading = false,
30
+ }: ConfirmDialogProps) {
31
+ const [isProcessing, setIsProcessing] = React.useState(false);
32
+
33
+ const handleConfirm = async () => {
34
+ setIsProcessing(true);
35
+ try {
36
+ await onConfirm();
37
+ onOpenChange(false);
38
+ } catch (error) {
39
+ logger.error('Confirmation action failed:', error);
40
+ } finally {
41
+ setIsProcessing(false);
42
+ }
43
+ };
44
+
45
+ return (
46
+ <Dialog.Root open={open} onOpenChange={onOpenChange}>
47
+ <Dialog.Portal>
48
+ <Dialog.Overlay className="fixed inset-0 bg-black/50 backdrop-blur-xs data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 z-50" />
49
+ <Dialog.Content
50
+ className={cn(
51
+ 'fixed left-[50%] top-[50%] z-50 translate-x-[-50%] translate-y-[-50%]',
52
+ 'w-full max-w-lg rounded-lg border border-border bg-card p-6 shadow-lg',
53
+ 'data-[state=open]:animate-in data-[state=closed]:animate-out',
54
+ 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
55
+ 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
56
+ 'data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%]',
57
+ 'data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]'
58
+ )}
59
+ >
60
+ {/* Header */}
61
+ <div className="flex items-start justify-between mb-4">
62
+ <Dialog.Title className="text-lg font-semibold text-foreground">{title}</Dialog.Title>
63
+ <Dialog.Close className="rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none">
64
+ <X className="h-4 w-4" />
65
+ <span className="sr-only">Close</span>
66
+ </Dialog.Close>
67
+ </div>
68
+
69
+ {/* Message */}
70
+ <Dialog.Description className="text-sm text-muted-foreground mb-6">
71
+ {message}
72
+ </Dialog.Description>
73
+
74
+ {/* Actions */}
75
+ <div className="flex justify-end gap-3">
76
+ <Button
77
+ variant="outline"
78
+ onClick={() => onOpenChange(false)}
79
+ disabled={isProcessing || loading}
80
+ >
81
+ {cancelText}
82
+ </Button>
83
+ <Button
84
+ variant={variant === 'destructive' ? 'destructive' : 'default'}
85
+ onClick={handleConfirm}
86
+ loading={isProcessing || loading}
87
+ disabled={isProcessing || loading}
88
+ >
89
+ {confirmText}
90
+ </Button>
91
+ </div>
92
+ </Dialog.Content>
93
+ </Dialog.Portal>
94
+ </Dialog.Root>
95
+ );
96
+ }
@@ -0,0 +1,275 @@
1
+ import { useState, useEffect, useRef } from 'react';
2
+ import { motion, AnimatePresence } from 'framer-motion';
3
+ import { X, Terminal, AlertTriangle, Network, Shield, Download, Trash2, Copy } from 'lucide-react';
4
+ import { cn } from '@/utils/cn';
5
+
6
+ interface LogEntry {
7
+ id: string;
8
+ timestamp: string;
9
+ type: 'network' | 'cert-error' | 'network-error' | 'tls-error' | 'info' | 'warning';
10
+ message: string;
11
+ details?: any;
12
+ }
13
+
14
+ export function DebugConsole() {
15
+ const [isVisible, setIsVisible] = useState(false);
16
+ const [logs, setLogs] = useState<LogEntry[]>([]);
17
+ const [filter, setFilter] = useState<'all' | 'network' | 'errors'>('all');
18
+ const scrollRef = useRef<HTMLDivElement>(null);
19
+ const [autoScroll, setAutoScroll] = useState(true);
20
+
21
+ useEffect(() => {
22
+ // Listen for debug mode activation (triggered by 5 clicks on logo)
23
+ const handleKeyPress = (e: KeyboardEvent) => {
24
+ // Ctrl+Shift+D to toggle debug console
25
+ if (e.ctrlKey && e.shiftKey && e.key === 'D') {
26
+ setIsVisible(!isVisible);
27
+ }
28
+ };
29
+
30
+ window.addEventListener('keydown', handleKeyPress);
31
+
32
+ // Listen for network requests
33
+ const unsubNetworkRequest = window.electronAPI?.onDebugNetworkRequest?.((data: any) => {
34
+ addLog('network', `${data.method} ${data.url}`, data);
35
+ });
36
+
37
+ // Listen for certificate errors
38
+ const unsubCertError = window.electronAPI?.onDebugCertError?.((data: any) => {
39
+ addLog('cert-error', `Certificate Error: ${data.url}`, data);
40
+ });
41
+
42
+ // Listen for network errors
43
+ const unsubNetworkError = window.electronAPI?.onDebugNetworkError?.((data: any) => {
44
+ addLog('network-error', `Network Error: ${data.error}`, data);
45
+ });
46
+
47
+ // Listen for TLS errors
48
+ const unsubTLSError = window.electronAPI?.onDebugTLSError?.((data: any) => {
49
+ addLog('tls-error', `TLS Error: ${data.message}`, data);
50
+ });
51
+
52
+ // Listen for update status
53
+ const unsubUpdateStatus = window.electronAPI?.onUpdateStatus?.((data: any) => {
54
+ if (data.message?.includes('PowerShell') || data.message?.includes('Mutual TLS')) {
55
+ addLog('info', `Update: ${data.message}`, data);
56
+ }
57
+ });
58
+
59
+ return () => {
60
+ window.removeEventListener('keydown', handleKeyPress);
61
+ unsubNetworkRequest?.();
62
+ unsubCertError?.();
63
+ unsubNetworkError?.();
64
+ unsubTLSError?.();
65
+ unsubUpdateStatus?.();
66
+ };
67
+ }, [isVisible]);
68
+
69
+ useEffect(() => {
70
+ // Auto-scroll to bottom when new logs are added
71
+ if (autoScroll && scrollRef.current) {
72
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
73
+ }
74
+ }, [logs, autoScroll]);
75
+
76
+ const addLog = (type: LogEntry['type'], message: string, details?: any) => {
77
+ const newLog: LogEntry = {
78
+ id: `${Date.now()}-${Math.random()}`,
79
+ timestamp: new Date().toISOString(),
80
+ type,
81
+ message,
82
+ details,
83
+ };
84
+ setLogs((prev) => [...prev.slice(-200), newLog]); // Keep last 200 logs
85
+ };
86
+
87
+ const clearLogs = () => {
88
+ setLogs([]);
89
+ };
90
+
91
+ const exportLogs = () => {
92
+ const logsJson = JSON.stringify(logs, null, 2);
93
+ const blob = new Blob([logsJson], { type: 'application/json' });
94
+ const url = URL.createObjectURL(blob);
95
+ const a = document.createElement('a');
96
+ a.href = url;
97
+ a.download = `debug-logs-${Date.now()}.json`;
98
+ a.click();
99
+ URL.revokeObjectURL(url);
100
+ };
101
+
102
+ const copyLogs = () => {
103
+ const logsText = logs
104
+ .map((log) => `[${log.timestamp}] [${log.type.toUpperCase()}] ${log.message}`)
105
+ .join('\n');
106
+ navigator.clipboard.writeText(logsText);
107
+ };
108
+
109
+ const filteredLogs = logs.filter((log) => {
110
+ if (filter === 'all') return true;
111
+ if (filter === 'network') return log.type === 'network';
112
+ if (filter === 'errors') return log.type.includes('error');
113
+ return true;
114
+ });
115
+
116
+ const getLogIcon = (type: LogEntry['type']) => {
117
+ switch (type) {
118
+ case 'network':
119
+ return <Network className="w-3 h-3" />;
120
+ case 'cert-error':
121
+ return <Shield className="w-3 h-3 text-red-500" />;
122
+ case 'network-error':
123
+ case 'tls-error':
124
+ return <AlertTriangle className="w-3 h-3 text-yellow-500" />;
125
+ default:
126
+ return <Terminal className="w-3 h-3" />;
127
+ }
128
+ };
129
+
130
+ const getLogColor = (type: LogEntry['type']) => {
131
+ switch (type) {
132
+ case 'cert-error':
133
+ case 'tls-error':
134
+ return 'text-red-400';
135
+ case 'network-error':
136
+ return 'text-yellow-400';
137
+ case 'network':
138
+ return 'text-blue-400';
139
+ default:
140
+ return 'text-muted-foreground';
141
+ }
142
+ };
143
+
144
+ return (
145
+ <AnimatePresence>
146
+ {isVisible && (
147
+ <motion.div
148
+ initial={{ opacity: 0, y: 100 }}
149
+ animate={{ opacity: 1, y: 0 }}
150
+ exit={{ opacity: 0, y: 100 }}
151
+ className="fixed bottom-4 right-4 z-[999] w-[600px] h-[400px] bg-background/95 backdrop-blur-xl border border-border rounded-lg shadow-2xl flex flex-col"
152
+ >
153
+ {/* Header */}
154
+ <div className="flex items-center justify-between px-4 py-2 border-b border-border">
155
+ <div className="flex items-center gap-2">
156
+ <Terminal className="w-4 h-4 text-primary" />
157
+ <span className="text-sm font-semibold">Debug Console</span>
158
+ <span className="text-xs text-muted-foreground">({filteredLogs.length} logs)</span>
159
+ </div>
160
+ <div className="flex items-center gap-1">
161
+ {/* Filter buttons */}
162
+ <button
163
+ onClick={() => setFilter('all')}
164
+ className={cn(
165
+ 'px-2 py-1 text-xs rounded transition-colors',
166
+ filter === 'all' ? 'bg-primary text-primary-foreground' : 'hover:bg-muted'
167
+ )}
168
+ >
169
+ All
170
+ </button>
171
+ <button
172
+ onClick={() => setFilter('network')}
173
+ className={cn(
174
+ 'px-2 py-1 text-xs rounded transition-colors',
175
+ filter === 'network' ? 'bg-primary text-primary-foreground' : 'hover:bg-muted'
176
+ )}
177
+ >
178
+ Network
179
+ </button>
180
+ <button
181
+ onClick={() => setFilter('errors')}
182
+ className={cn(
183
+ 'px-2 py-1 text-xs rounded transition-colors',
184
+ filter === 'errors' ? 'bg-primary text-primary-foreground' : 'hover:bg-muted'
185
+ )}
186
+ >
187
+ Errors
188
+ </button>
189
+ <div className="w-px h-4 bg-border mx-1" />
190
+ <button
191
+ onClick={copyLogs}
192
+ className="p-1 hover:bg-muted rounded transition-colors"
193
+ title="Copy logs"
194
+ >
195
+ <Copy className="w-3.5 h-3.5" />
196
+ </button>
197
+ <button
198
+ onClick={exportLogs}
199
+ className="p-1 hover:bg-muted rounded transition-colors"
200
+ title="Export logs"
201
+ >
202
+ <Download className="w-3.5 h-3.5" />
203
+ </button>
204
+ <button
205
+ onClick={clearLogs}
206
+ className="p-1 hover:bg-muted rounded transition-colors"
207
+ title="Clear logs"
208
+ >
209
+ <Trash2 className="w-3.5 h-3.5" />
210
+ </button>
211
+ <button
212
+ onClick={() => setIsVisible(false)}
213
+ className="p-1 hover:bg-muted rounded transition-colors ml-2"
214
+ >
215
+ <X className="w-4 h-4" />
216
+ </button>
217
+ </div>
218
+ </div>
219
+
220
+ {/* Log content */}
221
+ <div
222
+ ref={scrollRef}
223
+ className="flex-1 overflow-y-auto p-2 font-mono text-[11px] space-y-0.5"
224
+ onScroll={(e) => {
225
+ const target = e.target as HTMLDivElement;
226
+ const isAtBottom = target.scrollHeight - target.scrollTop === target.clientHeight;
227
+ setAutoScroll(isAtBottom);
228
+ }}
229
+ >
230
+ {filteredLogs.length === 0 ? (
231
+ <div className="text-center text-muted-foreground py-8">
232
+ No logs to display. Press Ctrl+Shift+D to toggle this console.
233
+ </div>
234
+ ) : (
235
+ filteredLogs.map((log) => (
236
+ <div
237
+ key={log.id}
238
+ className="flex items-start gap-2 hover:bg-muted/30 px-2 py-0.5 rounded"
239
+ >
240
+ <span className="flex-shrink-0 mt-0.5">{getLogIcon(log.type)}</span>
241
+ <span className="text-muted-foreground flex-shrink-0">
242
+ {log.timestamp.split('T')[1].split('.')[0]}
243
+ </span>
244
+ <span className={cn('flex-1 break-all', getLogColor(log.type))}>
245
+ {log.message}
246
+ </span>
247
+ </div>
248
+ ))
249
+ )}
250
+ </div>
251
+
252
+ {/* Footer */}
253
+ <div className="px-4 py-2 border-t border-border flex items-center justify-between">
254
+ <div className="flex items-center gap-2">
255
+ <label className="flex items-center gap-1 text-xs">
256
+ <input
257
+ type="checkbox"
258
+ checked={autoScroll}
259
+ onChange={(e) => setAutoScroll(e.target.checked)}
260
+ className="w-3 h-3"
261
+ />
262
+ Auto-scroll
263
+ </label>
264
+ </div>
265
+ <div className="text-xs text-muted-foreground">
266
+ Press <kbd className="px-1 py-0.5 bg-muted rounded text-[10px]">Ctrl</kbd>+
267
+ <kbd className="px-1 py-0.5 bg-muted rounded text-[10px]">Shift</kbd>+
268
+ <kbd className="px-1 py-0.5 bg-muted rounded text-[10px]">D</kbd> to toggle
269
+ </div>
270
+ </div>
271
+ </motion.div>
272
+ )}
273
+ </AnimatePresence>
274
+ );
275
+ }
@@ -0,0 +1,183 @@
1
+ import { cn } from '@/utils/cn';
2
+ import { motion } from 'framer-motion';
3
+ import type { LucideIcon } from 'lucide-react';
4
+ import { Button } from './Button';
5
+
6
+ interface EmptyStateAction {
7
+ label: string;
8
+ onClick: () => void;
9
+ variant?: 'default' | 'outline' | 'ghost';
10
+ icon?: LucideIcon;
11
+ }
12
+
13
+ interface EmptyStateProps {
14
+ icon?: LucideIcon;
15
+ title: string;
16
+ description?: string;
17
+ primaryAction?: EmptyStateAction;
18
+ secondaryAction?: EmptyStateAction;
19
+ className?: string;
20
+ size?: 'sm' | 'md' | 'lg';
21
+ }
22
+
23
+ export function EmptyState({
24
+ icon: Icon,
25
+ title,
26
+ description,
27
+ primaryAction,
28
+ secondaryAction,
29
+ className,
30
+ size = 'md',
31
+ }: EmptyStateProps) {
32
+ const sizeStyles = {
33
+ sm: {
34
+ container: 'py-8 px-4',
35
+ icon: 'w-10 h-10',
36
+ iconWrapper: 'w-16 h-16',
37
+ title: 'text-base',
38
+ description: 'text-sm',
39
+ },
40
+ md: {
41
+ container: 'py-12 px-6',
42
+ icon: 'w-12 h-12',
43
+ iconWrapper: 'w-20 h-20',
44
+ title: 'text-lg',
45
+ description: 'text-sm',
46
+ },
47
+ lg: {
48
+ container: 'py-16 px-8',
49
+ icon: 'w-14 h-14',
50
+ iconWrapper: 'w-24 h-24',
51
+ title: 'text-xl',
52
+ description: 'text-base',
53
+ },
54
+ };
55
+
56
+ const styles = sizeStyles[size];
57
+
58
+ return (
59
+ <motion.div
60
+ initial={{ opacity: 0, y: 20 }}
61
+ animate={{ opacity: 1, y: 0 }}
62
+ transition={{ duration: 0.3 }}
63
+ className={cn(
64
+ 'flex flex-col items-center justify-center text-center',
65
+ styles.container,
66
+ className
67
+ )}
68
+ >
69
+ {Icon && (
70
+ <motion.div
71
+ initial={{ scale: 0.8 }}
72
+ animate={{ scale: 1 }}
73
+ transition={{ delay: 0.1, type: 'spring', stiffness: 200 }}
74
+ className={cn(
75
+ 'rounded-full bg-muted/50 flex items-center justify-center mb-4',
76
+ styles.iconWrapper
77
+ )}
78
+ >
79
+ <Icon className={cn('text-muted-foreground', styles.icon)} />
80
+ </motion.div>
81
+ )}
82
+
83
+ <motion.h3
84
+ initial={{ opacity: 0 }}
85
+ animate={{ opacity: 1 }}
86
+ transition={{ delay: 0.15 }}
87
+ className={cn('font-semibold text-foreground mb-1', styles.title)}
88
+ >
89
+ {title}
90
+ </motion.h3>
91
+
92
+ {description && (
93
+ <motion.p
94
+ initial={{ opacity: 0 }}
95
+ animate={{ opacity: 1 }}
96
+ transition={{ delay: 0.2 }}
97
+ className={cn(
98
+ 'text-muted-foreground max-w-sm mb-6',
99
+ styles.description
100
+ )}
101
+ >
102
+ {description}
103
+ </motion.p>
104
+ )}
105
+
106
+ {(primaryAction || secondaryAction) && (
107
+ <motion.div
108
+ initial={{ opacity: 0, y: 10 }}
109
+ animate={{ opacity: 1, y: 0 }}
110
+ transition={{ delay: 0.25 }}
111
+ className="flex items-center gap-3"
112
+ >
113
+ {primaryAction && (
114
+ <Button
115
+ onClick={primaryAction.onClick}
116
+ variant={primaryAction.variant || 'default'}
117
+ icon={primaryAction.icon && <primaryAction.icon className="w-4 h-4" />}
118
+ >
119
+ {primaryAction.label}
120
+ </Button>
121
+ )}
122
+ {secondaryAction && (
123
+ <Button
124
+ onClick={secondaryAction.onClick}
125
+ variant={secondaryAction.variant || 'ghost'}
126
+ icon={secondaryAction.icon && <secondaryAction.icon className="w-4 h-4" />}
127
+ >
128
+ {secondaryAction.label}
129
+ </Button>
130
+ )}
131
+ </motion.div>
132
+ )}
133
+ </motion.div>
134
+ );
135
+ }
136
+
137
+ // Preset empty states for common use cases
138
+ export function NoSessionsEmptyState({ onCreateSession }: { onCreateSession: () => void }) {
139
+ return (
140
+ <EmptyState
141
+ title="No sessions yet"
142
+ description="Create your first session to start processing documents"
143
+ primaryAction={{
144
+ label: 'Create Session',
145
+ onClick: onCreateSession,
146
+ }}
147
+ size="lg"
148
+ />
149
+ );
150
+ }
151
+
152
+ export function NoDocumentsEmptyState({ onAddDocuments }: { onAddDocuments: () => void }) {
153
+ return (
154
+ <EmptyState
155
+ title="No documents"
156
+ description="Drop files here or click to add documents to this session"
157
+ primaryAction={{
158
+ label: 'Add Documents',
159
+ onClick: onAddDocuments,
160
+ }}
161
+ />
162
+ );
163
+ }
164
+
165
+ export function NoSearchResultsEmptyState({ query }: { query: string }) {
166
+ return (
167
+ <EmptyState
168
+ title="No results found"
169
+ description={`No documents or sessions match "${query}". Try a different search term.`}
170
+ size="sm"
171
+ />
172
+ );
173
+ }
174
+
175
+ export function NoChangesEmptyState() {
176
+ return (
177
+ <EmptyState
178
+ title="No changes detected"
179
+ description="Process a document to see tracked changes here"
180
+ size="sm"
181
+ />
182
+ );
183
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * React Error Boundary Component
3
+ *
4
+ * Catches JavaScript errors anywhere in the component tree,
5
+ * logs error information, and displays a fallback UI instead
6
+ * of crashing the entire application.
7
+ *
8
+ * Usage:
9
+ * ```tsx
10
+ * <ErrorBoundary>
11
+ * <YourComponent />
12
+ * </ErrorBoundary>
13
+ * ```
14
+ */
15
+
16
+ import React, { Component, ErrorInfo, ReactNode } from 'react';
17
+ import { logger } from '@/utils/logger';
18
+ import { ErrorFallback } from './ErrorFallback';
19
+
20
+ interface Props {
21
+ children: ReactNode;
22
+ fallback?: ReactNode;
23
+ onError?: (error: Error, errorInfo: ErrorInfo) => void;
24
+ }
25
+
26
+ interface State {
27
+ hasError: boolean;
28
+ error: Error | null;
29
+ errorInfo: ErrorInfo | null;
30
+ }
31
+
32
+ export class ErrorBoundary extends Component<Props, State> {
33
+ constructor(props: Props) {
34
+ super(props);
35
+ this.state = {
36
+ hasError: false,
37
+ error: null,
38
+ errorInfo: null,
39
+ };
40
+ }
41
+
42
+ static getDerivedStateFromError(error: Error): Partial<State> {
43
+ // Update state so the next render shows the fallback UI
44
+ return {
45
+ hasError: true,
46
+ error,
47
+ };
48
+ }
49
+
50
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
51
+ // Log error to console and any error reporting service
52
+ logger.error('React Error Boundary caught an error:', {
53
+ error: error.toString(),
54
+ componentStack: errorInfo.componentStack,
55
+ errorInfo,
56
+ });
57
+
58
+ // Update state with error info for display
59
+ this.setState({
60
+ errorInfo,
61
+ });
62
+
63
+ // Call custom error handler if provided
64
+ if (this.props.onError) {
65
+ this.props.onError(error, errorInfo);
66
+ }
67
+ }
68
+
69
+ handleReset = (): void => {
70
+ this.setState({
71
+ hasError: false,
72
+ error: null,
73
+ errorInfo: null,
74
+ });
75
+ };
76
+
77
+ render(): ReactNode {
78
+ if (this.state.hasError) {
79
+ // Custom fallback UI if provided
80
+ if (this.props.fallback) {
81
+ return this.props.fallback;
82
+ }
83
+
84
+ // Default fallback UI
85
+ return (
86
+ <ErrorFallback
87
+ error={this.state.error}
88
+ errorInfo={this.state.errorInfo}
89
+ onReset={this.handleReset}
90
+ />
91
+ );
92
+ }
93
+
94
+ return this.props.children;
95
+ }
96
+ }
97
+
98
+ export default ErrorBoundary;