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,410 @@
1
+ import { useState, useEffect, useRef } from 'react';
2
+ import {
3
+ Card,
4
+ CardContent,
5
+ CardDescription,
6
+ CardHeader,
7
+ CardTitle,
8
+ } from '@/components/common/Card';
9
+ import { Button } from '@/components/common/Button';
10
+ import { Input } from '@/components/common/Input';
11
+ import { AlertCircle, CheckCircle2, Upload, Shield, Download, Info, Trash2 } from 'lucide-react';
12
+ import logger from '@/utils/logger';
13
+
14
+ interface Certificate {
15
+ path: string;
16
+ name: string;
17
+ issuer?: string;
18
+ subject?: string;
19
+ validUntil?: string;
20
+ isActive: boolean;
21
+ isZscaler?: boolean;
22
+ }
23
+
24
+ export function CertificateManager() {
25
+ const [certificates, setCertificates] = useState<Certificate[]>([]);
26
+ const [isZscalerDetected, setIsZscalerDetected] = useState(false);
27
+ const [certificateStatus, setCertificateStatus] = useState<
28
+ 'checking' | 'configured' | 'not-configured' | 'error'
29
+ >('checking');
30
+ const [importStatus, setImportStatus] = useState<{
31
+ type: 'success' | 'error' | 'info' | null;
32
+ message: string;
33
+ }>({ type: null, message: '' });
34
+ const [currentCertPath, setCurrentCertPath] = useState<string>('');
35
+
36
+ // Timeout refs for cleanup
37
+ const importTimeoutRef = useRef<NodeJS.Timeout | null>(null);
38
+ const detectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
39
+ const testTimeoutRef = useRef<NodeJS.Timeout | null>(null);
40
+
41
+ useEffect(() => {
42
+ checkCertificateStatus();
43
+ loadExistingCertificates();
44
+
45
+ // Listen for certificate configuration from main process
46
+ const handleCertificateConfigured = (_event: unknown, data: { certPath: string }) => {
47
+ setCertificateStatus('configured');
48
+ setCurrentCertPath(data.certPath);
49
+ loadExistingCertificates();
50
+ };
51
+
52
+ window.electronAPI.on('certificate-configured', handleCertificateConfigured);
53
+
54
+ return () => {
55
+ window.electronAPI.removeListener('certificate-configured', handleCertificateConfigured);
56
+ };
57
+ }, []);
58
+
59
+ // Cleanup all timeouts on unmount
60
+ useEffect(() => {
61
+ return () => {
62
+ if (importTimeoutRef.current) {
63
+ clearTimeout(importTimeoutRef.current);
64
+ }
65
+ if (detectTimeoutRef.current) {
66
+ clearTimeout(detectTimeoutRef.current);
67
+ }
68
+ if (testTimeoutRef.current) {
69
+ clearTimeout(testTimeoutRef.current);
70
+ }
71
+ };
72
+ }, []);
73
+
74
+ const checkCertificateStatus = async () => {
75
+ try {
76
+ // Check if Zscaler is detected
77
+ const zscalerStatus = await window.electronAPI.checkZscalerStatus();
78
+ setIsZscalerDetected(zscalerStatus.detected);
79
+
80
+ // Check current certificate configuration
81
+ const certPath = await window.electronAPI.getCertificatePath();
82
+ if (certPath) {
83
+ setCertificateStatus('configured');
84
+ setCurrentCertPath(certPath);
85
+ } else {
86
+ setCertificateStatus('not-configured');
87
+ }
88
+ } catch (_error) {
89
+ logger.error('Error checking certificate status:', _error);
90
+ setCertificateStatus('error');
91
+ }
92
+ };
93
+
94
+ const loadExistingCertificates = async () => {
95
+ try {
96
+ const certPaths = await window.electronAPI.getInstalledCertificates();
97
+ const certs: Certificate[] = certPaths.map((path: string) => ({
98
+ path,
99
+ name: path.split('/').pop() || path,
100
+ isActive: path === currentCertPath,
101
+ }));
102
+ setCertificates(certs);
103
+ } catch (_error) {
104
+ logger.error('Error loading certificates:', _error);
105
+ }
106
+ };
107
+
108
+ const handleImportCertificate = async () => {
109
+ try {
110
+ const result = await window.electronAPI.importCertificate();
111
+
112
+ if (result.success) {
113
+ setImportStatus({
114
+ type: 'success',
115
+ message: `Certificate imported successfully: ${result.name}`,
116
+ });
117
+ setCertificateStatus('configured');
118
+ loadExistingCertificates();
119
+ } else {
120
+ setImportStatus({
121
+ type: 'error',
122
+ message: result.error || 'Failed to import certificate',
123
+ });
124
+ }
125
+ } catch (_error) {
126
+ setImportStatus({
127
+ type: 'error',
128
+ message: 'Error importing certificate',
129
+ });
130
+ }
131
+
132
+ // Clear status after 5 seconds
133
+ if (importTimeoutRef.current) {
134
+ clearTimeout(importTimeoutRef.current);
135
+ }
136
+ importTimeoutRef.current = setTimeout(() => {
137
+ setImportStatus({ type: null, message: '' });
138
+ importTimeoutRef.current = null;
139
+ }, 5000);
140
+ };
141
+
142
+ const handleExportFromBrowser = async () => {
143
+ try {
144
+ // Open instructions in browser
145
+ await window.electronAPI.openExternal(
146
+ 'https://github.com/ItMeDiaTech/Documentation_Hub/wiki/Export-Certificate-From-Browser'
147
+ );
148
+ } catch (_error) {
149
+ logger.error('Error opening browser guide:', _error);
150
+ }
151
+ };
152
+
153
+ const handleAutoDetect = async () => {
154
+ setImportStatus({ type: 'info', message: 'Searching for certificates in Windows store...' });
155
+
156
+ try {
157
+ const result = await window.electronAPI.autoDetectCertificates();
158
+
159
+ if (result.success) {
160
+ setImportStatus({
161
+ type: 'success',
162
+ message: `Found and configured ${result.count} certificate(s)`,
163
+ });
164
+ setCertificateStatus('configured');
165
+ loadExistingCertificates();
166
+ } else {
167
+ setImportStatus({
168
+ type: 'error',
169
+ message: 'No Zscaler certificates found. You may need to export manually.',
170
+ });
171
+ }
172
+ } catch (_error) {
173
+ setImportStatus({
174
+ type: 'error',
175
+ message: 'Error detecting certificates',
176
+ });
177
+ }
178
+
179
+ if (detectTimeoutRef.current) {
180
+ clearTimeout(detectTimeoutRef.current);
181
+ }
182
+ detectTimeoutRef.current = setTimeout(() => {
183
+ setImportStatus({ type: null, message: '' });
184
+ detectTimeoutRef.current = null;
185
+ }, 5000);
186
+ };
187
+
188
+ const handleRemoveCertificate = async (certPath: string) => {
189
+ try {
190
+ const result = await window.electronAPI.removeCertificate(certPath);
191
+ if (result.success) {
192
+ loadExistingCertificates();
193
+ }
194
+ } catch (_error) {
195
+ logger.error('Error removing certificate:', _error);
196
+ }
197
+ };
198
+
199
+ const handleTestConnection = async () => {
200
+ setImportStatus({ type: 'info', message: 'Testing connection to GitHub...' });
201
+
202
+ try {
203
+ const result = await window.electronAPI.testGitHubConnection();
204
+
205
+ if (result.success) {
206
+ setImportStatus({
207
+ type: 'success',
208
+ message: 'Connection successful! Updates should work now.',
209
+ });
210
+ } else {
211
+ setImportStatus({
212
+ type: 'error',
213
+ message: `Connection failed: ${result.error}`,
214
+ });
215
+ }
216
+ } catch (_error) {
217
+ setImportStatus({
218
+ type: 'error',
219
+ message: 'Error testing connection',
220
+ });
221
+ }
222
+
223
+ if (testTimeoutRef.current) {
224
+ clearTimeout(testTimeoutRef.current);
225
+ }
226
+ testTimeoutRef.current = setTimeout(() => {
227
+ setImportStatus({ type: null, message: '' });
228
+ testTimeoutRef.current = null;
229
+ }, 5000);
230
+ };
231
+
232
+ return (
233
+ <div className="space-y-4">
234
+ {/* Status Banner */}
235
+ {isZscalerDetected && (
236
+ <Card className="border-yellow-500 bg-yellow-50 dark:bg-yellow-950/20">
237
+ <CardHeader>
238
+ <div className="flex items-center gap-2">
239
+ <Shield className="h-5 w-5 text-yellow-600" />
240
+ <CardTitle className="text-lg">Zscaler Detected</CardTitle>
241
+ </div>
242
+ <CardDescription>
243
+ Zscaler is performing SSL inspection on your network. Certificate configuration is
244
+ required for automatic updates.
245
+ </CardDescription>
246
+ </CardHeader>
247
+ </Card>
248
+ )}
249
+
250
+ {/* Certificate Status */}
251
+ <Card>
252
+ <CardHeader>
253
+ <CardTitle>Certificate Configuration</CardTitle>
254
+ <CardDescription>
255
+ Manage certificates for secure connections through corporate networks
256
+ </CardDescription>
257
+ </CardHeader>
258
+ <CardContent className="space-y-4">
259
+ {/* Current Status */}
260
+ <div className="flex items-center justify-between p-4 rounded-lg bg-muted">
261
+ <div className="flex items-center gap-3">
262
+ {certificateStatus === 'configured' ? (
263
+ <CheckCircle2 className="h-5 w-5 text-green-600" />
264
+ ) : (
265
+ <AlertCircle className="h-5 w-5 text-yellow-600" />
266
+ )}
267
+ <div>
268
+ <p className="font-medium">
269
+ {certificateStatus === 'configured'
270
+ ? 'Certificate Configured'
271
+ : 'Certificate Not Configured'}
272
+ </p>
273
+ {currentCertPath && (
274
+ <p className="text-sm text-muted-foreground">{currentCertPath}</p>
275
+ )}
276
+ </div>
277
+ </div>
278
+ <Button variant="outline" size="sm" onClick={handleTestConnection}>
279
+ Test Connection
280
+ </Button>
281
+ </div>
282
+
283
+ {/* Import Status Message */}
284
+ {importStatus.type && (
285
+ <div
286
+ className={`p-3 rounded-lg flex items-center gap-2 ${
287
+ importStatus.type === 'success'
288
+ ? 'bg-green-50 text-green-700 dark:bg-green-950/20'
289
+ : importStatus.type === 'error'
290
+ ? 'bg-red-50 text-red-700 dark:bg-red-950/20'
291
+ : 'bg-blue-50 text-blue-700 dark:bg-blue-950/20'
292
+ }`}
293
+ >
294
+ <Info className="h-4 w-4" />
295
+ <span className="text-sm">{importStatus.message}</span>
296
+ </div>
297
+ )}
298
+
299
+ {/* Actions */}
300
+ <div className="flex gap-2">
301
+ <Button onClick={handleAutoDetect} variant="default" className="flex-1">
302
+ <Shield className="mr-2 h-4 w-4" />
303
+ Auto-Detect Certificates
304
+ </Button>
305
+ <Button onClick={handleImportCertificate} variant="outline" className="flex-1">
306
+ <Upload className="mr-2 h-4 w-4" />
307
+ Import Certificate
308
+ </Button>
309
+ </div>
310
+
311
+ <Button onClick={handleExportFromBrowser} variant="ghost" className="w-full">
312
+ <Download className="mr-2 h-4 w-4" />
313
+ How to Export Certificate from Browser
314
+ </Button>
315
+ </CardContent>
316
+ </Card>
317
+
318
+ {/* Installed Certificates */}
319
+ {certificates.length > 0 && (
320
+ <Card>
321
+ <CardHeader>
322
+ <CardTitle>Installed Certificates</CardTitle>
323
+ <CardDescription>Certificates currently available to the application</CardDescription>
324
+ </CardHeader>
325
+ <CardContent>
326
+ <div className="space-y-2">
327
+ {certificates.map((cert, index) => (
328
+ <div
329
+ key={index}
330
+ className="flex items-center justify-between p-3 rounded-lg border"
331
+ >
332
+ <div className="flex-1">
333
+ <div className="flex items-center gap-2">
334
+ <p className="font-medium">{cert.name}</p>
335
+ {cert.isZscaler && (
336
+ <span className="text-xs px-2 py-0.5 rounded bg-yellow-100 text-yellow-700 dark:bg-yellow-900 dark:text-yellow-300">
337
+ Zscaler
338
+ </span>
339
+ )}
340
+ {cert.isActive && (
341
+ <span className="text-xs px-2 py-0.5 rounded bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300">
342
+ Active
343
+ </span>
344
+ )}
345
+ </div>
346
+ {cert.issuer && (
347
+ <p className="text-sm text-muted-foreground">Issuer: {cert.issuer}</p>
348
+ )}
349
+ {cert.validUntil && (
350
+ <p className="text-sm text-muted-foreground">
351
+ Valid until: {cert.validUntil}
352
+ </p>
353
+ )}
354
+ </div>
355
+ <Button
356
+ onClick={() => handleRemoveCertificate(cert.path)}
357
+ variant="ghost"
358
+ size="icon"
359
+ >
360
+ <Trash2 className="h-4 w-4" />
361
+ </Button>
362
+ </div>
363
+ ))}
364
+ </div>
365
+ </CardContent>
366
+ </Card>
367
+ )}
368
+
369
+ {/* Help Section */}
370
+ <Card>
371
+ <CardHeader>
372
+ <CardTitle>Troubleshooting Guide</CardTitle>
373
+ </CardHeader>
374
+ <CardContent>
375
+ <div className="space-y-3 text-sm">
376
+ <div>
377
+ <p className="font-medium mb-1">If auto-detect doesn't work:</p>
378
+ <ol className="list-decimal list-inside space-y-1 text-muted-foreground ml-2">
379
+ <li>Open Chrome or Edge and navigate to https://github.com</li>
380
+ <li>Click the padlock icon in the address bar</li>
381
+ <li>Select "Connection is secure" → "Certificate is valid"</li>
382
+ <li>Go to the "Details" tab and select the root certificate</li>
383
+ <li>Export as Base64 encoded .CER or .PEM file</li>
384
+ <li>Use the "Import Certificate" button to import the file</li>
385
+ </ol>
386
+ </div>
387
+
388
+ <div>
389
+ <p className="font-medium mb-1">Common certificate names:</p>
390
+ <ul className="list-disc list-inside space-y-1 text-muted-foreground ml-2">
391
+ <li>Zscaler Root CA</li>
392
+ <li>Zscaler Intermediate Root CA</li>
393
+ <li>Your Company Root CA</li>
394
+ <li>Corporate Proxy Certificate</li>
395
+ </ul>
396
+ </div>
397
+
398
+ <div className="mt-4 p-3 rounded-lg bg-muted">
399
+ <p className="text-xs text-muted-foreground">
400
+ <strong>Note:</strong> If you continue to have issues, contact your IT department
401
+ and request that *.github.com and *.githubusercontent.com be bypassed from SSL
402
+ inspection.
403
+ </p>
404
+ </div>
405
+ </div>
406
+ </CardContent>
407
+ </Card>
408
+ </div>
409
+ );
410
+ }
@@ -0,0 +1,88 @@
1
+ import { motion } from 'framer-motion';
2
+ import { cn } from '@/utils/cn';
3
+ import type { LucideIcon } from 'lucide-react';
4
+
5
+ interface SegmentedControlOption<T extends string> {
6
+ value: T;
7
+ label: string;
8
+ icon?: LucideIcon;
9
+ }
10
+
11
+ interface SegmentedControlProps<T extends string> {
12
+ value: T;
13
+ onValueChange: (value: T) => void;
14
+ options: SegmentedControlOption<T>[];
15
+ className?: string;
16
+ size?: 'sm' | 'md' | 'lg';
17
+ }
18
+
19
+ export function SegmentedControl<T extends string>({
20
+ value,
21
+ onValueChange,
22
+ options,
23
+ className,
24
+ size = 'md',
25
+ }: SegmentedControlProps<T>) {
26
+ const sizeStyles = {
27
+ sm: 'p-0.5 text-xs',
28
+ md: 'p-1 text-sm',
29
+ lg: 'p-1.5 text-base',
30
+ };
31
+
32
+ const buttonSizeStyles = {
33
+ sm: 'px-2.5 py-1',
34
+ md: 'px-4 py-2',
35
+ lg: 'px-5 py-2.5',
36
+ };
37
+
38
+ const iconSizeStyles = {
39
+ sm: 'w-3.5 h-3.5',
40
+ md: 'w-4 h-4',
41
+ lg: 'w-5 h-5',
42
+ };
43
+
44
+ return (
45
+ <div
46
+ role="radiogroup"
47
+ className={cn(
48
+ 'inline-flex items-center rounded-lg bg-muted/50 border border-border',
49
+ sizeStyles[size],
50
+ className
51
+ )}
52
+ >
53
+ {options.map((option) => {
54
+ const isSelected = value === option.value;
55
+ const Icon = option.icon;
56
+
57
+ return (
58
+ <button
59
+ key={option.value}
60
+ type="button"
61
+ role="radio"
62
+ aria-checked={isSelected}
63
+ onClick={() => onValueChange(option.value)}
64
+ className={cn(
65
+ 'relative flex items-center justify-center gap-2 rounded-md font-medium transition-colors',
66
+ buttonSizeStyles[size],
67
+ isSelected
68
+ ? 'text-foreground'
69
+ : 'text-muted-foreground hover:text-foreground'
70
+ )}
71
+ >
72
+ {isSelected && (
73
+ <motion.div
74
+ layoutId="segmented-indicator"
75
+ className="absolute inset-0 bg-background rounded-md shadow-sm border border-border/50"
76
+ transition={{ type: 'spring', stiffness: 400, damping: 30 }}
77
+ />
78
+ )}
79
+ <span className="relative flex items-center gap-2">
80
+ {Icon && <Icon className={iconSizeStyles[size]} />}
81
+ <span>{option.label}</span>
82
+ </span>
83
+ </button>
84
+ );
85
+ })}
86
+ </div>
87
+ );
88
+ }
@@ -0,0 +1,52 @@
1
+ import { cn } from '@/utils/cn';
2
+ import type { LucideIcon } from 'lucide-react';
3
+ import type { ReactNode } from 'react';
4
+
5
+ interface SettingRowProps {
6
+ icon?: LucideIcon;
7
+ title: string;
8
+ description?: string;
9
+ badge?: string;
10
+ children: ReactNode;
11
+ className?: string;
12
+ }
13
+
14
+ export function SettingRow({
15
+ icon: Icon,
16
+ title,
17
+ description,
18
+ badge,
19
+ children,
20
+ className,
21
+ }: SettingRowProps) {
22
+ return (
23
+ <div
24
+ className={cn(
25
+ 'flex items-center justify-between gap-4 py-3',
26
+ className
27
+ )}
28
+ >
29
+ <div className="flex items-start gap-3 flex-1 min-w-0">
30
+ {Icon && (
31
+ <div className="p-2 rounded-lg bg-muted shrink-0 mt-0.5">
32
+ <Icon className="w-4 h-4 text-muted-foreground" />
33
+ </div>
34
+ )}
35
+ <div className="flex-1 min-w-0">
36
+ <div className="flex items-center gap-2">
37
+ <span className="text-sm font-medium">{title}</span>
38
+ {badge && (
39
+ <span className="px-1.5 py-0.5 text-[10px] font-medium rounded bg-muted text-muted-foreground uppercase tracking-wide">
40
+ {badge}
41
+ </span>
42
+ )}
43
+ </div>
44
+ {description && (
45
+ <p className="text-xs text-muted-foreground mt-0.5">{description}</p>
46
+ )}
47
+ </div>
48
+ </div>
49
+ <div className="shrink-0">{children}</div>
50
+ </div>
51
+ );
52
+ }