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,81 @@
1
+ /**
2
+ * SplashScreen Component
3
+ * Displays an animated loading screen while the application initializes
4
+ * Provides visual feedback during context provider initialization
5
+ */
6
+
7
+ import { motion } from 'framer-motion';
8
+ import { Loader2 } from 'lucide-react';
9
+
10
+ interface SplashScreenProps {
11
+ message?: string;
12
+ }
13
+
14
+ export function SplashScreen({ message = 'Initializing Documentation Hub...' }: SplashScreenProps) {
15
+ return (
16
+ <div className="fixed inset-0 z-50 flex items-center justify-center bg-background">
17
+ <motion.div
18
+ initial={{ opacity: 0, scale: 0.9 }}
19
+ animate={{ opacity: 1, scale: 1 }}
20
+ transition={{ duration: 0.3 }}
21
+ className="flex flex-col items-center space-y-6"
22
+ >
23
+ {/* Logo/Brand */}
24
+ <motion.div
25
+ animate={{
26
+ scale: [1, 1.05, 1],
27
+ }}
28
+ transition={{
29
+ duration: 2,
30
+ repeat: Infinity,
31
+ ease: 'easeInOut',
32
+ }}
33
+ className="text-4xl font-bold text-primary"
34
+ >
35
+ Documentation Hub
36
+ </motion.div>
37
+
38
+ {/* Loading Spinner */}
39
+ <motion.div
40
+ animate={{ rotate: 360 }}
41
+ transition={{
42
+ duration: 1,
43
+ repeat: Infinity,
44
+ ease: 'linear',
45
+ }}
46
+ >
47
+ <Loader2 className="h-12 w-12 text-primary" />
48
+ </motion.div>
49
+
50
+ {/* Loading Message */}
51
+ <motion.p
52
+ initial={{ opacity: 0 }}
53
+ animate={{ opacity: 1 }}
54
+ transition={{ delay: 0.3 }}
55
+ className="text-sm text-muted-foreground"
56
+ >
57
+ {message}
58
+ </motion.p>
59
+
60
+ {/* Progress Dots */}
61
+ <div className="flex space-x-2">
62
+ {[0, 1, 2].map((i) => (
63
+ <motion.div
64
+ key={i}
65
+ animate={{
66
+ scale: [1, 1.3, 1],
67
+ opacity: [0.3, 1, 0.3],
68
+ }}
69
+ transition={{
70
+ duration: 1.5,
71
+ repeat: Infinity,
72
+ delay: i * 0.2,
73
+ }}
74
+ className="h-2 w-2 rounded-full bg-primary"
75
+ />
76
+ ))}
77
+ </div>
78
+ </motion.div>
79
+ </div>
80
+ );
81
+ }
@@ -0,0 +1,155 @@
1
+ import * as React from 'react';
2
+ import * as ToastPrimitives from '@radix-ui/react-toast';
3
+ import { X, CheckCircle2, AlertCircle, Info } from 'lucide-react';
4
+ import { cn } from '@/utils/cn';
5
+ import { Toast as ToastType } from '@/hooks/useToast';
6
+
7
+ const ToastProvider = ToastPrimitives.Provider;
8
+
9
+ const ToastViewport = React.forwardRef<
10
+ React.ElementRef<typeof ToastPrimitives.Viewport>,
11
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
12
+ >(({ className, ...props }, ref) => (
13
+ <ToastPrimitives.Viewport
14
+ ref={ref}
15
+ className={cn(
16
+ 'fixed bottom-0 right-0 z-[100] flex max-h-screen w-full flex-col gap-1 p-2 sm:max-w-72',
17
+ className
18
+ )}
19
+ {...props}
20
+ />
21
+ ));
22
+ ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
23
+
24
+ const Toast = React.forwardRef<
25
+ React.ElementRef<typeof ToastPrimitives.Root>,
26
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & {
27
+ variant?: 'default' | 'destructive' | 'success';
28
+ }
29
+ >(({ className, variant = 'default', ...props }, ref) => {
30
+ return (
31
+ <ToastPrimitives.Root
32
+ ref={ref}
33
+ className={cn(
34
+ 'group pointer-events-auto relative flex w-full items-center gap-2 overflow-hidden rounded-md border py-2 px-3 shadow-sm transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-bottom-full',
35
+ variant === 'default' && 'border-border/50 bg-background/95 backdrop-blur-sm text-foreground',
36
+ variant === 'destructive' && 'border-red-200 bg-red-50/95 backdrop-blur-sm text-red-800 dark:border-red-800/50 dark:bg-red-950/95 dark:text-red-200',
37
+ variant === 'success' && 'border-green-200 bg-green-50/95 backdrop-blur-sm text-green-800 dark:border-green-800/50 dark:bg-green-950/95 dark:text-green-200',
38
+ className
39
+ )}
40
+ {...props}
41
+ />
42
+ );
43
+ });
44
+ Toast.displayName = ToastPrimitives.Root.displayName;
45
+
46
+ // Status icon component for variants
47
+ const ToastIcon = ({ variant }: { variant?: 'default' | 'destructive' | 'success' }) => {
48
+ if (variant === 'success') {
49
+ return <CheckCircle2 className="h-3.5 w-3.5 shrink-0 text-green-600 dark:text-green-400" />;
50
+ }
51
+ if (variant === 'destructive') {
52
+ return <AlertCircle className="h-3.5 w-3.5 shrink-0 text-red-600 dark:text-red-400" />;
53
+ }
54
+ return <Info className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />;
55
+ };
56
+
57
+ const ToastAction = React.forwardRef<
58
+ React.ElementRef<typeof ToastPrimitives.Action>,
59
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
60
+ >(({ className, ...props }, ref) => (
61
+ <ToastPrimitives.Action
62
+ ref={ref}
63
+ className={cn(
64
+ 'inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive',
65
+ className
66
+ )}
67
+ {...props}
68
+ />
69
+ ));
70
+ ToastAction.displayName = ToastPrimitives.Action.displayName;
71
+
72
+ const ToastClose = React.forwardRef<
73
+ React.ElementRef<typeof ToastPrimitives.Close>,
74
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
75
+ >(({ className, ...props }, ref) => (
76
+ <ToastPrimitives.Close
77
+ ref={ref}
78
+ className={cn(
79
+ 'ml-auto shrink-0 rounded p-0.5 opacity-50 transition-opacity hover:opacity-100 focus:outline-none focus:ring-1 focus:ring-ring',
80
+ className
81
+ )}
82
+ toast-close=""
83
+ {...props}
84
+ >
85
+ <X className="h-3 w-3" />
86
+ </ToastPrimitives.Close>
87
+ ));
88
+ ToastClose.displayName = ToastPrimitives.Close.displayName;
89
+
90
+ const ToastTitle = React.forwardRef<
91
+ React.ElementRef<typeof ToastPrimitives.Title>,
92
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
93
+ >(({ className, ...props }, ref) => (
94
+ <ToastPrimitives.Title ref={ref} className={cn('text-xs font-medium leading-tight', className)} {...props} />
95
+ ));
96
+ ToastTitle.displayName = ToastPrimitives.Title.displayName;
97
+
98
+ const ToastDescription = React.forwardRef<
99
+ React.ElementRef<typeof ToastPrimitives.Description>,
100
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
101
+ >(({ className, ...props }, ref) => (
102
+ <ToastPrimitives.Description
103
+ ref={ref}
104
+ className={cn('text-[11px] opacity-75 leading-tight', className)}
105
+ {...props}
106
+ />
107
+ ));
108
+ ToastDescription.displayName = ToastPrimitives.Description.displayName;
109
+
110
+ type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
111
+
112
+ type ToastActionElement = React.ReactElement<typeof ToastAction>;
113
+
114
+ export {
115
+ type ToastProps,
116
+ type ToastActionElement,
117
+ ToastProvider,
118
+ ToastViewport,
119
+ Toast,
120
+ ToastTitle,
121
+ ToastDescription,
122
+ ToastClose,
123
+ ToastAction,
124
+ ToastIcon,
125
+ };
126
+
127
+ // Toaster component that renders all toasts
128
+ // Minimal design: small, unobtrusive notifications in bottom-right corner
129
+ interface ToasterProps {
130
+ toasts: ToastType[];
131
+ onDismiss: (id: string) => void;
132
+ }
133
+
134
+ export function Toaster({ toasts, onDismiss }: ToasterProps) {
135
+ // Only show the most recent 3 toasts to avoid clutter
136
+ const visibleToasts = toasts.slice(-3);
137
+
138
+ return (
139
+ <ToastProvider swipeDirection="right">
140
+ {visibleToasts.map((toast) => (
141
+ <Toast key={toast.id} variant={toast.variant} duration={toast.duration}>
142
+ <ToastIcon variant={toast.variant} />
143
+ <div className="flex-1 min-w-0">
144
+ <ToastTitle className="truncate">{toast.title}</ToastTitle>
145
+ {toast.description && (
146
+ <ToastDescription className="line-clamp-2">{toast.description}</ToastDescription>
147
+ )}
148
+ </div>
149
+ <ToastClose onClick={() => onDismiss(toast.id)} />
150
+ </Toast>
151
+ ))}
152
+ <ToastViewport />
153
+ </ToastProvider>
154
+ );
155
+ }
@@ -0,0 +1,79 @@
1
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
2
+ import { cn } from '@/utils/cn';
3
+ import { forwardRef } from 'react';
4
+
5
+ const TooltipProvider = TooltipPrimitive.Provider;
6
+
7
+ const Tooltip = TooltipPrimitive.Root;
8
+
9
+ const TooltipTrigger = TooltipPrimitive.Trigger;
10
+
11
+ const TooltipContent = forwardRef<
12
+ React.ElementRef<typeof TooltipPrimitive.Content>,
13
+ React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
14
+ >(({ className, sideOffset = 4, ...props }, ref) => (
15
+ <TooltipPrimitive.Portal>
16
+ <TooltipPrimitive.Content
17
+ ref={ref}
18
+ sideOffset={sideOffset}
19
+ className={cn(
20
+ 'z-50 overflow-hidden rounded-md bg-popover px-3 py-1.5 text-xs text-popover-foreground shadow-md',
21
+ 'animate-in fade-in-0 zoom-in-95',
22
+ 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
23
+ 'data-[side=bottom]:slide-in-from-top-2',
24
+ 'data-[side=left]:slide-in-from-right-2',
25
+ 'data-[side=right]:slide-in-from-left-2',
26
+ 'data-[side=top]:slide-in-from-bottom-2',
27
+ 'border border-border',
28
+ className
29
+ )}
30
+ {...props}
31
+ />
32
+ </TooltipPrimitive.Portal>
33
+ ));
34
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
35
+
36
+ // Convenience wrapper component for simple tooltips
37
+ interface SimpleTooltipProps {
38
+ content: React.ReactNode;
39
+ children: React.ReactNode;
40
+ side?: 'top' | 'right' | 'bottom' | 'left';
41
+ align?: 'start' | 'center' | 'end';
42
+ delayDuration?: number;
43
+ className?: string;
44
+ shortcut?: string;
45
+ }
46
+
47
+ function SimpleTooltip({
48
+ content,
49
+ children,
50
+ side = 'top',
51
+ align = 'center',
52
+ delayDuration = 300,
53
+ className,
54
+ shortcut,
55
+ }: SimpleTooltipProps) {
56
+ return (
57
+ <Tooltip delayDuration={delayDuration}>
58
+ <TooltipTrigger asChild>{children}</TooltipTrigger>
59
+ <TooltipContent side={side} align={align} className={className}>
60
+ <div className="flex items-center gap-2">
61
+ <span>{content}</span>
62
+ {shortcut && (
63
+ <kbd className="px-1.5 py-0.5 text-[10px] font-mono bg-muted rounded border border-border">
64
+ {shortcut}
65
+ </kbd>
66
+ )}
67
+ </div>
68
+ </TooltipContent>
69
+ </Tooltip>
70
+ );
71
+ }
72
+
73
+ export {
74
+ Tooltip,
75
+ TooltipTrigger,
76
+ TooltipContent,
77
+ TooltipProvider,
78
+ SimpleTooltip,
79
+ };
@@ -0,0 +1,320 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { motion, AnimatePresence } from 'framer-motion';
3
+ import {
4
+ Download,
5
+ X,
6
+ FileArchive,
7
+ Info,
8
+ ExternalLink,
9
+ AlertTriangle,
10
+ AlertCircle,
11
+ } from 'lucide-react';
12
+ import { Button } from './Button';
13
+
14
+ export function UpdateNotification() {
15
+ const [isVisible, setIsVisible] = useState(false);
16
+ const [updateInfo, setUpdateInfo] = useState<{ version: string; fallbackUsed?: boolean } | null>(
17
+ null
18
+ );
19
+ const [downloadProgress, setDownloadProgress] = useState(0);
20
+ const [isDownloading, setIsDownloading] = useState(false);
21
+ const [isDownloaded, setIsDownloaded] = useState(false);
22
+ const [isFallbackMode, setIsFallbackMode] = useState(false);
23
+ const [isExtracting, setIsExtracting] = useState(false);
24
+ const [statusMessage, setStatusMessage] = useState<string>('');
25
+ const [downloadError, setDownloadError] = useState(false);
26
+ const [errorCount, setErrorCount] = useState(0);
27
+
28
+ useEffect(() => {
29
+ // Safely check if electronAPI is available (may not be in browser-only mode)
30
+ if (typeof window.electronAPI === 'undefined') {
31
+ console.warn('UpdateNotification: electronAPI not available (running in browser mode?)');
32
+ return;
33
+ }
34
+
35
+ // Listen for update available
36
+ const unsubAvailable = window.electronAPI.onUpdateAvailable(
37
+ (info: { version: string; releaseDate: string; releaseNotes: string }) => {
38
+ setUpdateInfo(info);
39
+ setIsVisible(true);
40
+ }
41
+ );
42
+
43
+ // Listen for download progress
44
+ const unsubProgress = window.electronAPI.onUpdateDownloadProgress(
45
+ (progress: {
46
+ bytesPerSecond: number;
47
+ percent: number;
48
+ transferred: number;
49
+ total: number;
50
+ }) => {
51
+ setDownloadProgress(progress.percent);
52
+ }
53
+ );
54
+
55
+ // Listen for update downloaded
56
+ const unsubDownloaded = window.electronAPI.onUpdateDownloaded(
57
+ (info: { version: string; releaseNotes: string; fallbackUsed?: boolean }) => {
58
+ setIsDownloading(false);
59
+ setIsExtracting(false);
60
+ setIsDownloaded(true);
61
+ setUpdateInfo({ ...info, fallbackUsed: info.fallbackUsed });
62
+ if (info.fallbackUsed) {
63
+ setStatusMessage('Update ready to install (downloaded as compressed archive)');
64
+ }
65
+ }
66
+ );
67
+
68
+ // Listen for errors
69
+ const unsubError = window.electronAPI.onUpdateError((error: { message: string }) => {
70
+ setIsDownloading(false);
71
+ setIsExtracting(false);
72
+ setErrorCount((prev) => prev + 1);
73
+
74
+ // Check for specific error types
75
+ const isCertError =
76
+ error.message?.toLowerCase().includes('certificate') ||
77
+ error.message?.toLowerCase().includes('issuer') ||
78
+ error.message?.toLowerCase().includes('unable to verify');
79
+
80
+ const isMutualTLS =
81
+ error.message?.toLowerCase().includes('econnreset') ||
82
+ error.message?.toLowerCase().includes('connection reset') ||
83
+ error.message?.toLowerCase().includes('mutual');
84
+
85
+ const isProxyError =
86
+ error.message?.toLowerCase().includes('proxy') ||
87
+ error.message?.toLowerCase().includes('firewall') ||
88
+ error.message?.toLowerCase().includes('localhost:8005');
89
+
90
+ // Show error state after multiple failures or if fallback also failed
91
+ if (error.message?.includes('Fallback download failed') || errorCount >= 2) {
92
+ setDownloadError(true);
93
+
94
+ if (isMutualTLS) {
95
+ setStatusMessage(
96
+ 'Enterprise network detected (Mutual TLS required). Your network requires special certificates. Please use manual download or contact IT.'
97
+ );
98
+ } else if (isCertError) {
99
+ setStatusMessage(
100
+ 'Certificate validation failed. Your organization may use custom certificates. Please download manually.'
101
+ );
102
+ } else if (isProxyError) {
103
+ setStatusMessage(
104
+ 'Corporate proxy/firewall blocking download. Please use manual download or check with IT.'
105
+ );
106
+ } else {
107
+ setStatusMessage('Unable to download automatically. Please try manual download.');
108
+ }
109
+ } else if (isMutualTLS) {
110
+ setStatusMessage('Enterprise network detected. Trying PowerShell download method...');
111
+ } else if (isCertError) {
112
+ setStatusMessage('Certificate issue detected. Attempting alternative download method...');
113
+ } else {
114
+ setStatusMessage(`Connection issue: Trying alternative method...`);
115
+ }
116
+ });
117
+
118
+ // Listen for fallback mode activation
119
+ const unsubFallback = window.electronAPI.onUpdateFallbackMode?.((data: { message: string }) => {
120
+ setIsFallbackMode(true);
121
+ setStatusMessage(data.message || 'Using alternative download method...');
122
+ });
123
+
124
+ // Listen for extraction status
125
+ const unsubExtracting = window.electronAPI.onUpdateExtracting?.((data: { message: string }) => {
126
+ setIsExtracting(true);
127
+ setStatusMessage(data.message || 'Extracting update...');
128
+ });
129
+
130
+ // Listen for general status updates
131
+ const unsubStatus = window.electronAPI.onUpdateStatus?.((data: { message: string }) => {
132
+ setStatusMessage(data.message || '');
133
+ });
134
+
135
+ return () => {
136
+ unsubAvailable();
137
+ unsubProgress();
138
+ unsubDownloaded();
139
+ unsubError();
140
+ unsubFallback?.();
141
+ unsubExtracting?.();
142
+ unsubStatus?.();
143
+ };
144
+ }, [errorCount]); // Add errorCount to dependencies to track error state
145
+
146
+ const handleDownload = async () => {
147
+ setIsDownloading(true);
148
+ setDownloadError(false); // Reset error state when retrying
149
+ setErrorCount(0); // Reset error count
150
+ try {
151
+ await window.electronAPI?.downloadUpdate();
152
+ } catch (error) {
153
+ setIsDownloading(false);
154
+ }
155
+ };
156
+
157
+ const handleInstall = () => {
158
+ window.electronAPI?.installUpdate();
159
+ };
160
+
161
+ const handleDismiss = () => {
162
+ setIsVisible(false);
163
+ };
164
+
165
+ const handleManualDownload = async () => {
166
+ try {
167
+ // Use the new IPC handler to open download in system browser
168
+ await window.electronAPI?.openUpdateInBrowser();
169
+ } catch (error) {
170
+ // Fallback to direct window.open if IPC fails
171
+ const releaseUrl = updateInfo?.version
172
+ ? `https://github.com/ItMeDiaTech/Documentation_Hub/releases/tag/v${updateInfo.version}`
173
+ : 'https://github.com/ItMeDiaTech/Documentation_Hub/releases/latest';
174
+ window.open(releaseUrl, '_blank');
175
+ }
176
+ };
177
+
178
+ return (
179
+ <AnimatePresence>
180
+ {isVisible && (
181
+ <motion.div
182
+ initial={{ opacity: 0, y: 50 }}
183
+ animate={{ opacity: 1, y: 0 }}
184
+ exit={{ opacity: 0, y: 50 }}
185
+ className="fixed bottom-4 right-4 z-50"
186
+ >
187
+ <div className="bg-background border border-border rounded-lg shadow-lg p-4 w-96">
188
+ <div className="flex items-start justify-between mb-3">
189
+ <div className="flex items-center gap-2">
190
+ {isFallbackMode ? (
191
+ <FileArchive className="w-5 h-5 text-primary" />
192
+ ) : (
193
+ <AlertCircle className="w-5 h-5 text-primary" />
194
+ )}
195
+ <h3 className="font-semibold">Update Available</h3>
196
+ </div>
197
+ <button
198
+ onClick={handleDismiss}
199
+ className="text-muted-foreground hover:text-foreground transition-colors"
200
+ >
201
+ <X className="w-4 h-4" />
202
+ </button>
203
+ </div>
204
+
205
+ {updateInfo && (
206
+ <p className="text-sm text-muted-foreground mb-4">
207
+ Version {updateInfo.version} is now available
208
+ </p>
209
+ )}
210
+
211
+ {statusMessage && (
212
+ <div className="flex items-center gap-2 mb-3">
213
+ <Info className="w-4 h-4 text-blue-500" />
214
+ <p className="text-xs text-muted-foreground">{statusMessage}</p>
215
+ </div>
216
+ )}
217
+
218
+ {!isDownloading && !isDownloaded && (
219
+ <div className="flex gap-2">
220
+ <Button
221
+ onClick={handleDownload}
222
+ variant="default"
223
+ size="sm"
224
+ className="flex-1"
225
+ icon={<Download className="w-4 h-4" />}
226
+ >
227
+ Download
228
+ </Button>
229
+ <Button onClick={handleDismiss} variant="outline" size="sm">
230
+ Later
231
+ </Button>
232
+ </div>
233
+ )}
234
+
235
+ {isDownloading && (
236
+ <div className="space-y-2">
237
+ <div className="flex items-center justify-between text-sm">
238
+ <span>
239
+ {isExtracting
240
+ ? 'Extracting...'
241
+ : isFallbackMode
242
+ ? 'Downloading compressed update...'
243
+ : 'Downloading...'}
244
+ </span>
245
+ {!isExtracting && (
246
+ <span className="text-muted-foreground">{Math.round(downloadProgress)}%</span>
247
+ )}
248
+ </div>
249
+ <div className="w-full bg-muted rounded-full h-2 overflow-hidden">
250
+ <div
251
+ className={`h-full ${isExtracting ? 'bg-blue-500' : 'bg-primary'} transition-all duration-300`}
252
+ style={{ width: isExtracting ? '100%' : `${downloadProgress}%` }}
253
+ />
254
+ </div>
255
+ {isFallbackMode && !isExtracting && (
256
+ <p className="text-xs text-muted-foreground mt-1">
257
+ Using compressed download to bypass network restrictions
258
+ </p>
259
+ )}
260
+ </div>
261
+ )}
262
+
263
+ {isDownloaded && (
264
+ <div className="flex gap-2">
265
+ <Button
266
+ onClick={handleInstall}
267
+ variant="default"
268
+ size="sm"
269
+ className="flex-1 bg-green-600 hover:bg-green-700"
270
+ >
271
+ Install & Restart
272
+ </Button>
273
+ <Button onClick={handleDismiss} variant="outline" size="sm">
274
+ Later
275
+ </Button>
276
+ </div>
277
+ )}
278
+
279
+ {downloadError && !isDownloading && !isDownloaded && (
280
+ <div className="space-y-3">
281
+ <div className="flex items-center gap-2 text-yellow-600 dark:text-yellow-400">
282
+ <AlertTriangle className="w-4 h-4" />
283
+ <p className="text-xs font-medium">Network Security Issue</p>
284
+ </div>
285
+ {statusMessage && <p className="text-xs text-muted-foreground">{statusMessage}</p>}
286
+ <div className="flex gap-2">
287
+ <Button
288
+ onClick={handleManualDownload}
289
+ variant="default"
290
+ size="sm"
291
+ className="flex-1"
292
+ icon={<ExternalLink className="w-4 h-4" />}
293
+ >
294
+ Download Manually
295
+ </Button>
296
+ <Button
297
+ onClick={handleDownload}
298
+ variant="outline"
299
+ size="sm"
300
+ icon={<Download className="w-4 h-4" />}
301
+ >
302
+ Retry
303
+ </Button>
304
+ </div>
305
+ <div className="bg-muted/50 rounded p-2 text-xs space-y-1">
306
+ <p className="font-medium">Troubleshooting Tips:</p>
307
+ <ul className="list-disc list-inside text-muted-foreground space-y-0.5">
308
+ <li>Disconnect from VPN if connected</li>
309
+ <li>Check with IT about proxy settings</li>
310
+ <li>Use manual download for immediate access</li>
311
+ </ul>
312
+ </div>
313
+ </div>
314
+ )}
315
+ </div>
316
+ </motion.div>
317
+ )}
318
+ </AnimatePresence>
319
+ );
320
+ }