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
package/Updater.md ADDED
@@ -0,0 +1,311 @@
1
+ # Implementing delta updates in Electron with electron-builder and electron-updater
2
+
3
+ Delta updates work out-of-the-box with your stack—**blockmap files are generated automatically** for NSIS targets, and electron-updater uses them by default. The key is ensuring proper configuration for auto-restart and handling your corporate network scenario. Here's everything you need for a complete implementation.
4
+
5
+ ## How blockmap-based delta updates work
6
+
7
+ When electron-builder creates an NSIS installer, it automatically generates a `.blockmap` file alongside the `.exe`. This file contains checksums of content-defined chunks created using a rolling hash algorithm. During updates, electron-updater downloads both the old and new blockmap files, compares them, and only downloads the changed blocks via HTTP Range requests. A typical update might show: **"Full: 66,133 KB, To download: 7,785 KB (12%)"**—demonstrating significant bandwidth savings.
8
+
9
+ Three files are generated for each build: the installer (`App Setup 1.0.0.exe`), its blockmap (`App Setup 1.0.0.exe.blockmap`), and the metadata file (`latest.yml`). All three must be published to GitHub Releases for delta updates to function.
10
+
11
+ ## Package.json build configuration
12
+
13
+ No special configuration is required to enable delta updates—they're on by default. However, your `artifactName` must include the version to ensure proper blockmap URL construction:
14
+
15
+ ```json
16
+ {
17
+ "name": "your-app",
18
+ "version": "1.0.0",
19
+ "build": {
20
+ "appId": "com.yourcompany.app",
21
+ "productName": "Your App",
22
+ "win": {
23
+ "target": "nsis",
24
+ "icon": "build/icon.ico"
25
+ },
26
+ "nsis": {
27
+ "oneClick": true,
28
+ "perMachine": false,
29
+ "artifactName": "${productName} Setup ${version}.${ext}",
30
+ "deleteAppDataOnUninstall": false
31
+ },
32
+ "publish": {
33
+ "provider": "github",
34
+ "owner": "your-username",
35
+ "repo": "your-repo",
36
+ "releaseType": "release"
37
+ }
38
+ },
39
+ "dependencies": {
40
+ "electron-updater": "^6.7.2",
41
+ "electron-log": "^5.0.0"
42
+ },
43
+ "devDependencies": {
44
+ "electron": "^28.0.0",
45
+ "electron-builder": "^26.3.0"
46
+ }
47
+ }
48
+ ```
49
+
50
+ To explicitly disable delta updates (if needed), add `"differentialPackage": false` under the `nsis` key, or set `autoUpdater.disableDifferentialDownload = true` at runtime.
51
+
52
+ ## Complete auto-update implementation with silent restart
53
+
54
+ Here's a production-ready implementation that handles your Zscaler/corporate network scenario with automatic silent restart:
55
+
56
+ ```typescript
57
+ // updater.ts
58
+ import { NsisUpdater, UpdateInfo, ProgressInfo } from 'electron-updater';
59
+ import { app, session, BrowserWindow } from 'electron';
60
+ import log from 'electron-log';
61
+
62
+ export class AutoUpdateManager {
63
+ private autoUpdater: NsisUpdater;
64
+ private mainWindow: BrowserWindow | null = null;
65
+
66
+ constructor(mainWindow?: BrowserWindow) {
67
+ this.mainWindow = mainWindow || null;
68
+
69
+ // Use NsisUpdater directly for custom configuration
70
+ this.autoUpdater = new NsisUpdater({
71
+ provider: 'github',
72
+ owner: 'your-username',
73
+ repo: 'your-repo'
74
+ });
75
+
76
+ this.configureUpdater();
77
+ this.setupCorporateNetworkHandling();
78
+ this.setupEventHandlers();
79
+ }
80
+
81
+ private configureUpdater(): void {
82
+ // Attach logger for debugging
83
+ this.autoUpdater.logger = log;
84
+ log.transports.file.level = 'debug';
85
+
86
+ // Core configuration
87
+ this.autoUpdater.autoDownload = true; // Auto-download when found
88
+ this.autoUpdater.autoInstallOnAppQuit = true; // Install on quit as fallback
89
+ this.autoUpdater.autoRunAppAfterInstall = true; // Restart after install
90
+
91
+ // Keep differential downloads enabled (default)
92
+ this.autoUpdater.disableDifferentialDownload = false;
93
+ }
94
+
95
+ private setupCorporateNetworkHandling(): void {
96
+ // Handle Zscaler/corporate proxy certificate interception
97
+ session.defaultSession.setCertificateVerifyProc((request, callback) => {
98
+ const { hostname, certificate, verificationResult } = request;
99
+
100
+ log.debug('Certificate verification:', {
101
+ hostname,
102
+ issuer: certificate.issuerName,
103
+ result: verificationResult
104
+ });
105
+
106
+ // Accept known corporate CA certificates (Zscaler, etc.)
107
+ const trustedIssuers = ['Zscaler', 'YourCorpCA'];
108
+ const isTrustedCorporate = trustedIssuers.some(
109
+ issuer => certificate.issuerName.includes(issuer)
110
+ );
111
+
112
+ if (verificationResult === 'net::OK' || isTrustedCorporate) {
113
+ callback(0); // Accept
114
+ } else {
115
+ log.warn('Rejecting untrusted certificate:', certificate.issuerName);
116
+ callback(-2); // Reject
117
+ }
118
+ });
119
+ }
120
+
121
+ private setupEventHandlers(): void {
122
+ this.autoUpdater.on('checking-for-update', () => {
123
+ log.info('Checking for update...');
124
+ this.sendStatusToRenderer('checking');
125
+ });
126
+
127
+ this.autoUpdater.on('update-available', (info: UpdateInfo) => {
128
+ log.info(`Update available: ${info.version}`);
129
+ this.sendStatusToRenderer('available', info);
130
+ });
131
+
132
+ this.autoUpdater.on('update-not-available', (info: UpdateInfo) => {
133
+ log.info('Application is up to date');
134
+ this.sendStatusToRenderer('not-available', info);
135
+ });
136
+
137
+ this.autoUpdater.on('download-progress', (progress: ProgressInfo) => {
138
+ const message = `Downloaded ${progress.percent.toFixed(1)}% (${this.formatBytes(progress.transferred)}/${this.formatBytes(progress.total)})`;
139
+ log.info(message);
140
+ this.sendStatusToRenderer('downloading', progress);
141
+ });
142
+
143
+ this.autoUpdater.on('update-downloaded', (info: UpdateInfo) => {
144
+ log.info(`Update downloaded: ${info.version}`);
145
+ this.sendStatusToRenderer('downloaded', info);
146
+
147
+ // Trigger silent automatic restart
148
+ this.installAndRestart();
149
+ });
150
+
151
+ this.autoUpdater.on('error', (error: Error) => {
152
+ log.error('Auto-updater error:', error);
153
+ this.sendStatusToRenderer('error', { message: error.message });
154
+ });
155
+ }
156
+
157
+ private installAndRestart(): void {
158
+ log.info('Installing update and restarting...');
159
+
160
+ setImmediate(() => {
161
+ // Remove listeners that might prevent quit
162
+ app.removeAllListeners('window-all-closed');
163
+
164
+ // quitAndInstall(isSilent, isForceRunAfter)
165
+ // isSilent=true: No installer UI shown
166
+ // isForceRunAfter=true: App restarts after silent install
167
+ this.autoUpdater.quitAndInstall(true, true);
168
+ });
169
+ }
170
+
171
+ private sendStatusToRenderer(status: string, data?: any): void {
172
+ if (this.mainWindow && !this.mainWindow.isDestroyed()) {
173
+ this.mainWindow.webContents.send('update-status', { status, data });
174
+ }
175
+ }
176
+
177
+ private formatBytes(bytes: number): string {
178
+ const mb = bytes / (1024 * 1024);
179
+ return `${mb.toFixed(1)} MB`;
180
+ }
181
+
182
+ public async checkForUpdates(): Promise<void> {
183
+ // Skip check on first run (Squirrel issue)
184
+ if (process.argv.includes('--squirrel-firstrun')) {
185
+ log.info('Skipping update check on first run');
186
+ return;
187
+ }
188
+
189
+ try {
190
+ await this.autoUpdater.checkForUpdates();
191
+ } catch (error) {
192
+ log.error('Update check failed:', error);
193
+ }
194
+ }
195
+
196
+ public startScheduledChecks(intervalMs: number = 4 * 60 * 60 * 1000): void {
197
+ // Check every 4 hours by default
198
+ setInterval(() => this.checkForUpdates(), intervalMs);
199
+ }
200
+ }
201
+ ```
202
+
203
+ ## Main process integration
204
+
205
+ ```typescript
206
+ // main.ts
207
+ import { app, BrowserWindow } from 'electron';
208
+ import { AutoUpdateManager } from './updater';
209
+
210
+ let mainWindow: BrowserWindow | null = null;
211
+ let updateManager: AutoUpdateManager;
212
+
213
+ app.whenReady().then(() => {
214
+ mainWindow = new BrowserWindow({
215
+ width: 1200,
216
+ height: 800,
217
+ webPreferences: {
218
+ nodeIntegration: false,
219
+ contextIsolation: true,
220
+ preload: path.join(__dirname, 'preload.js')
221
+ }
222
+ });
223
+
224
+ // Initialize updater after window is ready
225
+ updateManager = new AutoUpdateManager(mainWindow);
226
+
227
+ // Delay initial check to avoid startup conflicts
228
+ setTimeout(() => {
229
+ updateManager.checkForUpdates();
230
+ }, 10000);
231
+
232
+ // Start periodic checks
233
+ updateManager.startScheduledChecks();
234
+ });
235
+ ```
236
+
237
+ ## Testing delta updates locally
238
+
239
+ Create a `dev-app-update.yml` file in your project root to test updates against a local server:
240
+
241
+ ```yaml
242
+ provider: generic
243
+ url: http://localhost:8080/updates/
244
+ channel: latest
245
+ ```
246
+
247
+ Set up a simple local update server:
248
+
249
+ ```javascript
250
+ // local-server.js
251
+ const express = require('express');
252
+ const path = require('path');
253
+ const app = express();
254
+
255
+ // Enable range requests for differential downloads
256
+ app.use('/updates', express.static(path.join(__dirname, 'dist'), {
257
+ acceptRanges: true
258
+ }));
259
+
260
+ app.listen(8080, () => console.log('Update server: http://localhost:8080'));
261
+ ```
262
+
263
+ Modify your updater to use the dev config during development:
264
+
265
+ ```typescript
266
+ import path from 'path';
267
+
268
+ // In your updater constructor, add:
269
+ if (process.env.NODE_ENV === 'development' || !app.isPackaged) {
270
+ this.autoUpdater.forceDevUpdateConfig = true;
271
+ this.autoUpdater.updateConfigPath = path.join(__dirname, '../dev-app-update.yml');
272
+ }
273
+ ```
274
+
275
+ **Testing workflow:**
276
+ 1. Build version 1.0.0: `npm run build`
277
+ 2. Copy dist files to your server directory
278
+ 3. Install and run version 1.0.0
279
+ 4. Bump version to 1.0.1 and rebuild
280
+ 5. Copy new dist files (including `.blockmap`) to server
281
+ 6. The running app should detect and delta-download the update
282
+
283
+ ## Verifying differential downloads actually work
284
+
285
+ Enable debug logging and look for these key log entries that confirm delta updates:
286
+
287
+ ```
288
+ [info] Download block maps (old: ".../1.0.0.exe.blockmap", new: ".../1.0.1.exe.blockmap")
289
+ [info] File has 367 changed blocks
290
+ [info] Full: 113,665.86 KB, To download: 7,785.66 KB (7%)
291
+ ```
292
+
293
+ If you see **"Cannot download differentially, fallback to full download"**, check that:
294
+ - Both `.blockmap` files are accessible (current and new version)
295
+ - Your server supports HTTP Range requests
296
+ - The SHA512 checksums in `latest.yml` match the actual files
297
+ - The updater cache isn't corrupted (clear `%AppData%\Local\{app-name}-updater`)
298
+
299
+ ## Troubleshooting common issues
300
+
301
+ **Blockmap 404 errors on GitHub**: Ensure you publish both the `.exe` and `.exe.blockmap` files to the same release. When using `electron-builder --publish always`, this happens automatically.
302
+
303
+ **Certificate errors behind Zscaler**: The `setCertificateVerifyProc` handler in the implementation above addresses this. Add your corporate CA's issuer name to the `trustedIssuers` array.
304
+
305
+ **Silent install not restarting**: The `quitAndInstall(true, true)` call with both parameters set to `true` forces silent install with auto-restart. Ensure `app.removeAllListeners('window-all-closed')` is called before `quitAndInstall` to prevent quit interruption.
306
+
307
+ **First-run update check fails**: Windows Squirrel holds a file lock during initial installation. Always check for `--squirrel-firstrun` in process arguments and skip the update check on first launch, as shown in the implementation.
308
+
309
+ ## Conclusion
310
+
311
+ The electron-builder + electron-updater stack provides robust delta update support with minimal configuration. Key takeaways: blockmaps are generated automatically for NSIS targets, `quitAndInstall(true, true)` enables silent auto-restart on Windows, and corporate proxy scenarios require explicit certificate handling via `setCertificateVerifyProc`. For local testing, combine `dev-app-update.yml` with `forceDevUpdateConfig = true` and a local HTTP server with range request support. Monitor your logs for the "changed blocks" message to confirm differential downloads are working—you should see download sizes of **5-20% of the full installer** for typical application updates.
Binary file
Binary file
@@ -0,0 +1,4 @@
1
+ provider: github
2
+ owner: ItMeDiaTech
3
+ repo: Documentation_Hub
4
+ releaseType: release
@@ -0,0 +1,208 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { createCanvas, loadImage } = require('canvas');
4
+
5
+ // Convert PNG to ICO format for Windows
6
+ function createICOFromPNG(pngPath, icoPath, sizes = [16, 32, 48, 256]) {
7
+ return new Promise(async (resolve, reject) => {
8
+ try {
9
+ const sourceImage = await loadImage(pngPath);
10
+ const iconData = [];
11
+
12
+ // Generate each size
13
+ for (const size of sizes) {
14
+ const canvas = createCanvas(size, size);
15
+ const ctx = canvas.getContext('2d');
16
+
17
+ // Enable high-quality image scaling
18
+ ctx.imageSmoothingEnabled = true;
19
+ ctx.imageSmoothingQuality = 'high';
20
+
21
+ // Draw resized image
22
+ ctx.drawImage(sourceImage, 0, 0, size, size);
23
+
24
+ // Get RGBA pixel data
25
+ const imageData = ctx.getImageData(0, 0, size, size);
26
+ const pixels = Buffer.alloc(size * size * 4);
27
+
28
+ // Convert RGBA to BGRA and flip vertically (BMP stores rows bottom-to-top)
29
+ const rowBytes = size * 4;
30
+ for (let y = 0; y < size; y++) {
31
+ const srcRow = y * rowBytes;
32
+ const dstRow = (size - 1 - y) * rowBytes;
33
+ for (let x = 0; x < size; x++) {
34
+ const srcIdx = srcRow + x * 4;
35
+ const dstIdx = dstRow + x * 4;
36
+ pixels[dstIdx] = imageData.data[srcIdx + 2]; // B
37
+ pixels[dstIdx + 1] = imageData.data[srcIdx + 1]; // G
38
+ pixels[dstIdx + 2] = imageData.data[srcIdx]; // R
39
+ pixels[dstIdx + 3] = imageData.data[srcIdx + 3]; // A
40
+ }
41
+ }
42
+
43
+ iconData.push({ size, data: pixels, imageData });
44
+ }
45
+
46
+ // ICO header
47
+ const header = Buffer.alloc(6);
48
+ header.writeUInt16LE(0, 0); // Reserved
49
+ header.writeUInt16LE(1, 2); // Type (1 = ICO)
50
+ header.writeUInt16LE(iconData.length, 4); // Number of images
51
+
52
+ // Calculate offsets
53
+ let offset = 6 + (16 * iconData.length);
54
+ const directories = [];
55
+ const images = [];
56
+
57
+ iconData.forEach(icon => {
58
+ // Directory entry
59
+ const dir = Buffer.alloc(16);
60
+ dir[0] = icon.size < 256 ? icon.size : 0; // Width
61
+ dir[1] = icon.size < 256 ? icon.size : 0; // Height
62
+ dir[2] = 0; // Color palette
63
+ dir[3] = 0; // Reserved
64
+ dir.writeUInt16LE(1, 4); // Color planes
65
+ dir.writeUInt16LE(32, 6); // Bits per pixel
66
+
67
+ // BMP data size = header(40) + pixels + mask (DWORD-aligned rows)
68
+ const maskRowSize = Math.ceil(Math.ceil(icon.size / 8) / 4) * 4;
69
+ const bmpSize = 40 + icon.data.length + (maskRowSize * icon.size);
70
+ dir.writeUInt32LE(bmpSize, 8);
71
+ dir.writeUInt32LE(offset, 12);
72
+
73
+ directories.push(dir);
74
+
75
+ // BMP header
76
+ const bmpHeader = Buffer.alloc(40);
77
+ bmpHeader.writeInt32LE(40, 0); // Header size
78
+ bmpHeader.writeInt32LE(icon.size, 4); // Width
79
+ bmpHeader.writeInt32LE(icon.size * 2, 8); // Height (doubled for ICO)
80
+ bmpHeader.writeUInt16LE(1, 12); // Planes
81
+ bmpHeader.writeUInt16LE(32, 14); // Bits per pixel
82
+ bmpHeader.writeUInt32LE(0, 16); // Compression
83
+ bmpHeader.writeUInt32LE(icon.data.length, 20); // Image size
84
+
85
+ // Generate AND mask from alpha channel
86
+ // ICO AND mask: 1 = transparent, 0 = opaque
87
+ // Rows must be DWORD (4-byte) aligned
88
+ const rowSize = Math.ceil(icon.size / 8);
89
+ const alignedRowSize = Math.ceil(rowSize / 4) * 4;
90
+ const mask = Buffer.alloc(alignedRowSize * icon.size, 0);
91
+
92
+ // BMP stores rows bottom-up, so we need to flip vertically
93
+ for (let y = 0; y < icon.size; y++) {
94
+ const srcRow = icon.size - 1 - y; // Flip vertically for BMP format
95
+ for (let x = 0; x < icon.size; x++) {
96
+ const pixelIndex = (srcRow * icon.size + x) * 4;
97
+ const alpha = icon.imageData.data[pixelIndex + 3];
98
+
99
+ // If pixel is transparent (alpha < 128), set mask bit to 1
100
+ if (alpha < 128) {
101
+ const byteIndex = y * alignedRowSize + Math.floor(x / 8);
102
+ const bitIndex = 7 - (x % 8); // MSB first within each byte
103
+ mask[byteIndex] |= (1 << bitIndex);
104
+ }
105
+ }
106
+ }
107
+
108
+ images.push(Buffer.concat([bmpHeader, icon.data, mask]));
109
+ offset += bmpSize;
110
+ });
111
+
112
+ // Combine all parts
113
+ const ico = Buffer.concat([header, ...directories, ...images]);
114
+ fs.writeFileSync(icoPath, ico);
115
+ console.log(`✓ Created ${path.basename(icoPath)}: ${fs.statSync(icoPath).size.toLocaleString()} bytes`);
116
+ resolve();
117
+ } catch (error) {
118
+ reject(error);
119
+ }
120
+ });
121
+ }
122
+
123
+ // Resize PNG image to specified size
124
+ async function resizePNG(sourcePath, targetPath, targetSize) {
125
+ try {
126
+ const sourceImage = await loadImage(sourcePath);
127
+ const canvas = createCanvas(targetSize, targetSize);
128
+ const ctx = canvas.getContext('2d');
129
+
130
+ // Enable high-quality image scaling
131
+ ctx.imageSmoothingEnabled = true;
132
+ ctx.imageSmoothingQuality = 'high';
133
+
134
+ // Draw resized image
135
+ ctx.drawImage(sourceImage, 0, 0, targetSize, targetSize);
136
+
137
+ // Save as PNG
138
+ const buffer = canvas.toBuffer('image/png');
139
+ fs.writeFileSync(targetPath, buffer);
140
+ console.log(`✓ Created ${path.basename(targetPath)}: ${targetSize}x${targetSize} (${fs.statSync(targetPath).size.toLocaleString()} bytes)`);
141
+ } catch (error) {
142
+ console.error(`✗ Error creating ${path.basename(targetPath)}:`, error.message);
143
+ throw error;
144
+ }
145
+ }
146
+
147
+ // Main execution
148
+ async function generateIcons() {
149
+ console.log('='.repeat(60));
150
+ console.log('Generating application icons from icon_1024x1024.png');
151
+ console.log('='.repeat(60));
152
+
153
+ try {
154
+ // Determine if we're running from the build directory or root
155
+ const isInBuildDir = __dirname.endsWith('build');
156
+ const rootDir = isInBuildDir ? path.join(__dirname, '..') : __dirname;
157
+ const buildDir = path.join(rootDir, 'build');
158
+ const sourcePath = path.join(buildDir, 'icon_1024x1024.png');
159
+
160
+ // Verify source file exists
161
+ if (!fs.existsSync(sourcePath)) {
162
+ throw new Error(`Source file not found: ${sourcePath}`);
163
+ }
164
+
165
+ console.log(`\nSource: ${path.basename(sourcePath)}`);
166
+ console.log(`Build directory: ${buildDir}`);
167
+
168
+ // Ensure build directory exists
169
+ if (!fs.existsSync(buildDir)) {
170
+ fs.mkdirSync(buildDir, { recursive: true });
171
+ console.log('✓ Created build directory');
172
+ }
173
+
174
+ console.log('\nGenerating icon files...\n');
175
+
176
+ // Copy original as base icon.png (electron-builder will use this)
177
+ const baseIconPath = path.join(buildDir, 'icon.png');
178
+ fs.copyFileSync(sourcePath, baseIconPath);
179
+ console.log(`+ Copied source to icon.png: 1024x1024`);
180
+
181
+ // Generate standard sizes
182
+ await resizePNG(sourcePath, path.join(buildDir, '512x512.png'), 512);
183
+ await resizePNG(sourcePath, path.join(buildDir, '256x256.png'), 256);
184
+
185
+ // Generate Windows ICO file
186
+ console.log('\nGenerating Windows ICO file...\n');
187
+ await createICOFromPNG(sourcePath, path.join(buildDir, 'icon.ico'));
188
+
189
+ console.log('\n' + '='.repeat(60));
190
+ console.log('✅ Icons generated successfully!');
191
+ console.log('='.repeat(60));
192
+ console.log('\nGenerated files:');
193
+ console.log(' - icon.png (1024x1024) - Base icon for electron-builder');
194
+ console.log(' - 512x512.png - Linux icon');
195
+ console.log(' - 256x256.png - Linux icon');
196
+ console.log(' - icon.ico - Windows application icon');
197
+ console.log('\nElectron-builder will auto-generate:');
198
+ console.log(' - .icns for macOS from icon.png');
199
+ console.log(' - Additional Windows sizes if needed');
200
+ console.log('');
201
+ } catch (error) {
202
+ console.error('\n❌ Error generating icons:', error.message);
203
+ process.exit(1);
204
+ }
205
+ }
206
+
207
+ // Run the script
208
+ generateIcons();
package/build/icon.ico ADDED
Binary file
package/build/icon.png ADDED
Binary file
Binary file
@@ -0,0 +1 @@
1
+ import{a6 as I,a7 as K,a8 as B,r as U,j as e,m as n,l as S,ad as R,aY as W,aZ as E,ab as T}from"./vendor-ui-BU7NfluV.js";import{r as i}from"./vendor-react-BVZ_anCF.js";import{C as o,a as c,b as p,c as u,d as j}from"./Card-IAZin8kp.js";import{a as V,c as M,B as Y}from"./index-8xUe8ptc.js";import{R as y,L,C as v,X as g,Y as k,T as b,a as f,b as H,B as $,d as A}from"./vendor-charts-RkGK5ROP.js";const G={hidden:{opacity:0},visible:{opacity:1,transition:{staggerChildren:.1}}},d={hidden:{opacity:0,y:20},visible:{opacity:1,y:0,transition:{duration:.5}}},_=i.memo(function(){const[r,F]=i.useState("daily"),[t,N]=i.useState(null),{stats:l,getDailyHistory:C,getWeeklyHistory:D,getMonthlyHistory:w}=V(),m=i.useCallback(a=>{a?.activePayload?.[0]?.payload&&N(a.activePayload[0].payload)},[]),h=i.useCallback(()=>{N(null)},[]),x=i.useMemo(()=>r==="daily"?[...C(30)].reverse().map(s=>({date:new Date(s.date).toLocaleDateString("en-US",{month:"short",day:"numeric"}),Documents:s.documentsProcessed,Hyperlinks:s.hyperlinksChecked,Feedback:s.feedbackImported,"Time (min)":s.timeSaved})):r==="weekly"?[...D(12)].reverse().map(s=>({date:`${new Date(s.weekStart).toLocaleDateString("en-US",{month:"short",day:"numeric"})} - ${new Date(s.weekEnd).toLocaleDateString("en-US",{month:"short",day:"numeric"})}`,Documents:s.documentsProcessed,Hyperlinks:s.hyperlinksChecked,Feedback:s.feedbackImported,"Time (min)":s.timeSaved})):[...w(12)].reverse().map(s=>({date:new Date(s.month+"-01").toLocaleDateString("en-US",{month:"short",year:"numeric"}),Documents:s.documentsProcessed,Hyperlinks:s.hyperlinksChecked,Feedback:s.feedbackImported,"Time (min)":s.timeSaved})),[r,C,D,w]),P=[{value:"daily",label:"Daily",icon:R,description:"Last 30 days"},{value:"weekly",label:"Weekly",icon:W,description:"Last 12 weeks"},{value:"monthly",label:"Monthly",icon:E,description:"Last 12 months"}],z=[{title:"Documents Processed",value:l.allTime.documentsProcessed,icon:I,color:"text-green-500",bgColor:"bg-green-500/10"},{title:"Hyperlinks Checked",value:l.allTime.hyperlinksChecked,icon:K,color:"text-blue-500",bgColor:"bg-blue-500/10"},{title:"Feedback Imported",value:l.allTime.feedbackImported,icon:B,color:"text-purple-500",bgColor:"bg-purple-500/10"},{title:"Time Saved",value:`${l.allTime.timeSaved}m`,icon:U,color:"text-orange-500",bgColor:"bg-orange-500/10"}];return e.jsxs(n.div,{variants:G,initial:"hidden",animate:"visible",className:"p-6 space-y-6 max-w-[1400px] mx-auto",children:[e.jsxs("div",{className:"sticky top-0 z-10 bg-background -mx-6 px-6 pb-4 border-b border-border/50",children:[e.jsx(n.div,{variants:d,className:"flex items-center justify-between pt-0 pb-4",children:e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsxs("div",{children:[e.jsxs("h1",{className:"text-3xl font-bold mb-1 flex items-center gap-2",children:[e.jsx(S,{className:"w-8 h-8"}),"Analytics"]}),e.jsx("p",{className:"text-muted-foreground",children:"Track performance and insights"})]}),e.jsx("div",{className:"h-12 w-px bg-border"}),t?e.jsxs("div",{className:"text-base font-medium flex items-center gap-4",children:[e.jsxs("span",{className:"text-muted-foreground",children:[t.date,":"]}),t.Documents!==void 0&&e.jsxs("span",{className:"text-green-500",children:["Docs: ",t.Documents]}),t.Hyperlinks!==void 0&&e.jsxs("span",{className:"text-blue-500",children:["Links: ",t.Hyperlinks]}),t.Feedback!==void 0&&e.jsxs("span",{className:"text-purple-500",children:["Feedback: ",t.Feedback]}),t["Time (min)"]!==void 0&&e.jsxs("span",{className:"text-orange-500",children:["Time: ",t["Time (min)"],"m"]})]}):e.jsx("span",{className:"text-muted-foreground italic text-sm",children:"Hover over charts for details"})]})}),e.jsx(n.div,{variants:d,className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4",children:z.map(a=>{const s=a.icon;return e.jsx(o,{className:"border-border/50",children:e.jsx(c,{className:"pt-6",children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-sm text-foreground mb-1",children:a.title}),e.jsx("p",{className:"text-2xl font-bold",children:a.value})]}),e.jsx("div",{className:M("p-3 rounded-lg",a.bgColor),children:e.jsx(s,{className:M("w-6 h-6",a.color)})})]})})},a.title)})})]}),e.jsx(n.div,{variants:d,className:"flex gap-2",children:P.map(a=>{const s=a.icon;return e.jsxs(Y,{variant:r===a.value?"default":"outline",onClick:()=>F(a.value),className:"flex-1 gap-2",children:[e.jsx(s,{className:"w-4 h-4"}),e.jsxs("div",{className:"flex flex-col items-start",children:[e.jsx("span",{className:"font-semibold",children:a.label}),e.jsx("span",{className:"text-xs opacity-70",children:a.description})]})]},a.value)})}),e.jsxs(n.div,{variants:d,className:"grid grid-cols-1 gap-6",children:[e.jsxs(o,{children:[e.jsxs(p,{children:[e.jsxs(u,{className:"flex items-center gap-2",children:[e.jsx(T,{className:"w-5 h-5"}),"Documents Processed Over Time"]}),e.jsxs(j,{children:["Track document processing trends across"," ",r==="daily"?"days":r==="weekly"?"weeks":"months"]})]}),e.jsx(c,{children:e.jsx(y,{width:"100%",height:300,children:e.jsxs(L,{data:x,onMouseMove:m,onMouseLeave:h,children:[e.jsx(v,{strokeDasharray:"3 3",className:"stroke-border"}),e.jsx(g,{dataKey:"date",tick:{fontSize:12,fill:"var(--color-foreground)"},angle:-45,textAnchor:"end",height:80}),e.jsx(k,{tick:{fontSize:12,fill:"var(--color-foreground)"}}),e.jsx(b,{content:()=>null}),e.jsx(f,{}),e.jsx(H,{type:"monotone",dataKey:"Documents",stroke:"hsl(142, 76%, 36%)",strokeWidth:2,dot:{r:4}})]})})})]}),e.jsxs(o,{children:[e.jsxs(p,{children:[e.jsxs(u,{className:"flex items-center gap-2",children:[e.jsx(T,{className:"w-5 h-5"}),"Hyperlinks Checked Over Time"]}),e.jsxs(j,{children:["Track hyperlink validation trends across"," ",r==="daily"?"days":r==="weekly"?"weeks":"months"]})]}),e.jsx(c,{children:e.jsx(y,{width:"100%",height:300,children:e.jsxs(L,{data:x,onMouseMove:m,onMouseLeave:h,children:[e.jsx(v,{strokeDasharray:"3 3",className:"stroke-border"}),e.jsx(g,{dataKey:"date",tick:{fontSize:12,fill:"var(--color-foreground)"},angle:-45,textAnchor:"end",height:80}),e.jsx(k,{tick:{fontSize:12,fill:"var(--color-foreground)"}}),e.jsx(b,{content:()=>null}),e.jsx(f,{}),e.jsx(H,{type:"monotone",dataKey:"Hyperlinks",stroke:"hsl(221, 83%, 53%)",strokeWidth:2,dot:{r:4}})]})})})]}),e.jsxs(o,{children:[e.jsxs(p,{children:[e.jsxs(u,{className:"flex items-center gap-2",children:[e.jsx(S,{className:"w-5 h-5"}),"Activity Breakdown"]}),e.jsx(j,{children:"Compare different metrics side by side"})]}),e.jsx(c,{children:e.jsx(y,{width:"100%",height:300,children:e.jsxs($,{data:x,onMouseMove:m,onMouseLeave:h,children:[e.jsx(v,{strokeDasharray:"3 3",className:"stroke-border"}),e.jsx(g,{dataKey:"date",tick:{fontSize:12,fill:"var(--color-foreground)"},angle:-45,textAnchor:"end",height:80}),e.jsx(k,{tick:{fontSize:12,fill:"var(--color-foreground)"}}),e.jsx(b,{content:()=>null}),e.jsx(f,{}),e.jsx(A,{dataKey:"Feedback",fill:"hsl(280, 100%, 70%)"}),e.jsx(A,{dataKey:"Time (min)",fill:"hsl(25, 95%, 53%)"})]})})})]})]})]})});export{_ as Analytics};
@@ -0,0 +1 @@
1
+ import{j as s,m as c}from"./vendor-ui-BU7NfluV.js";import{r as o}from"./vendor-react-BVZ_anCF.js";import{c as d}from"./index-8xUe8ptc.js";const m=o.forwardRef(({className:r,variant:e="default",interactive:a=!1,children:t,...i},n)=>{const l={default:"bg-card text-card-foreground shadow-xs",bordered:"border border-border bg-card text-card-foreground",ghost:"bg-transparent",glass:"glass border border-white/10 shadow-lg"};return s.jsx(c.div,{ref:n,className:d("rounded-lg",l[e],a&&"cursor-pointer transition-shadow duration-200 hover:shadow-md",r),whileHover:a?{scale:1.02}:void 0,whileTap:a?{scale:.98}:void 0,transition:{duration:.2},...i,children:t})});m.displayName="Card";const f=o.forwardRef(({className:r,...e},a)=>s.jsx("div",{ref:a,className:d("flex flex-col space-y-1.5 p-6",r),...e}));f.displayName="CardHeader";const p=o.forwardRef(({className:r,...e},a)=>s.jsx("h3",{ref:a,className:d("text-2xl font-semibold leading-none tracking-tight",r),...e}));p.displayName="CardTitle";const x=o.forwardRef(({className:r,...e},a)=>s.jsx("p",{ref:a,className:d("text-sm text-muted-foreground",r),...e}));x.displayName="CardDescription";const C=o.forwardRef(({className:r,...e},a)=>s.jsx("div",{ref:a,className:d("p-6 pt-0",r),...e}));C.displayName="CardContent";const g=o.forwardRef(({className:r,...e},a)=>s.jsx("div",{ref:a,className:d("flex items-center p-6 pt-0",r),...e}));g.displayName="CardFooter";export{m as C,C as a,f as b,p as c,x as d};