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,18 @@
1
+ /**
2
+ * Editor Components - Document editing and tracked changes
3
+ *
4
+ * Exports:
5
+ * - DocumentEditorModal: Fullscreen modal for document editing
6
+ * - EditorToolbar: Toolbar with save, undo/redo, formatting buttons
7
+ * - EditorQuickActions: Sidebar with quick action buttons
8
+ * - DocumentEditor: Main content editable document editor
9
+ * - TableEditor: Full table editing component with cell selection
10
+ */
11
+
12
+ export { DocumentEditorModal } from './DocumentEditorModal';
13
+ export { EditorToolbar } from './EditorToolbar';
14
+ export { EditorQuickActions } from './EditorQuickActions';
15
+ export { DocumentEditor } from './DocumentEditor';
16
+ export type { DocumentEditorRef, DocumentEditorProps } from './DocumentEditor';
17
+ export { TableEditor } from './TableEditor';
18
+ export type { TableEditorRef, TableEditorProps } from './TableEditor';
@@ -0,0 +1,190 @@
1
+ import { ChevronRight, Zap, Moon, Sun, Clock, Monitor } from 'lucide-react';
2
+ import { useTheme } from '@/contexts/ThemeContext';
3
+ import { cn } from '@/utils/cn';
4
+ import { useState, useEffect, memo, useCallback, useMemo, useRef } from 'react';
5
+ import { useLocation, useNavigate } from 'react-router-dom';
6
+ import { SimpleTooltip } from '@/components/common/Tooltip';
7
+
8
+ const pathToTitle: Record<string, string> = {
9
+ '/': 'Dashboard',
10
+ '/projects': 'Projects',
11
+ '/analytics': 'Analytics',
12
+ '/team': 'Team',
13
+ '/documents': 'Documents',
14
+ '/plugins': 'Plugins',
15
+ '/notifications': 'Notifications',
16
+ '/search': 'Search',
17
+ '/profile': 'Profile',
18
+ '/settings': 'Settings',
19
+ '/sessions': 'Sessions',
20
+ };
21
+
22
+ const pathDescriptions: Record<string, string> = {
23
+ '/': 'Manage your document processing sessions',
24
+ '/projects': 'View and manage your projects',
25
+ '/analytics': 'Track performance and insights',
26
+ '/team': 'Collaborate with your team members',
27
+ '/documents': 'Browse and manage documents',
28
+ '/plugins': 'Extend functionality with plugins',
29
+ '/notifications': 'View your notifications',
30
+ '/search': 'Search across your workspace',
31
+ '/profile': 'Manage your profile',
32
+ '/settings': 'Manage your account and application preferences',
33
+ '/sessions': 'View and manage all sessions',
34
+ };
35
+
36
+ export const Header = memo(function Header({ onCommandPalette }: { onCommandPalette: () => void }) {
37
+ const { theme, setTheme } = useTheme();
38
+ const [showThemeMenu, setShowThemeMenu] = useState(false);
39
+ const [currentTime, setCurrentTime] = useState(new Date());
40
+ const location = useLocation();
41
+ const navigate = useNavigate();
42
+
43
+ useEffect(() => {
44
+ const timer = setInterval(() => {
45
+ setCurrentTime(new Date());
46
+ }, 1000);
47
+
48
+ return () => clearInterval(timer);
49
+ }, []);
50
+
51
+ const formatTime = useCallback((date: Date) => {
52
+ return date.toLocaleTimeString('en-US', {
53
+ hour: '2-digit',
54
+ minute: '2-digit',
55
+ hour12: true,
56
+ });
57
+ }, []);
58
+
59
+ const breadcrumbs = useMemo(() => {
60
+ const paths = location.pathname.split('/').filter(Boolean);
61
+ const result = [];
62
+ let currentPath = '';
63
+
64
+ for (const path of paths) {
65
+ currentPath += `/${path}`;
66
+ result.push({
67
+ label: pathToTitle[currentPath] || path,
68
+ path: currentPath,
69
+ });
70
+ }
71
+
72
+ if (result.length === 0) {
73
+ result.push({ label: 'Dashboard', path: '/' });
74
+ }
75
+
76
+ return result;
77
+ }, [location.pathname]);
78
+
79
+ const currentPath = location.pathname;
80
+ const description = pathDescriptions[currentPath] || '';
81
+
82
+ const toggleThemeMenu = useCallback(() => {
83
+ setShowThemeMenu((prev) => !prev);
84
+ }, []);
85
+
86
+ const handleThemeChange = useCallback(
87
+ (value: 'light' | 'dark' | 'system') => {
88
+ setTheme(value);
89
+ setShowThemeMenu(false);
90
+ },
91
+ [setTheme]
92
+ );
93
+
94
+ return (
95
+ <header className="header-bg border-b border-border bg-background/50 backdrop-blur-xl px-6 py-2">
96
+ <div className="flex items-center justify-between">
97
+ <div className="flex flex-col gap-0.5">
98
+ <div className="flex items-center gap-2">
99
+ {breadcrumbs.map((crumb, index) => (
100
+ <div key={crumb.path} className="flex items-center gap-2">
101
+ {index > 0 && <ChevronRight className="w-4 h-4 text-muted-foreground" />}
102
+ <button
103
+ onClick={() => index < breadcrumbs.length - 1 && navigate(crumb.path)}
104
+ className={cn(
105
+ 'text-base font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 rounded-sm',
106
+ index === breadcrumbs.length - 1
107
+ ? 'text-foreground cursor-default pointer-events-none'
108
+ : 'text-muted-foreground hover:text-foreground cursor-pointer'
109
+ )}
110
+ disabled={index === breadcrumbs.length - 1}
111
+ aria-current={index === breadcrumbs.length - 1 ? 'page' : undefined}
112
+ >
113
+ {crumb.label}
114
+ </button>
115
+ </div>
116
+ ))}
117
+ </div>
118
+ {description && <p className="text-sm text-muted-foreground">{description}</p>}
119
+ </div>
120
+
121
+ <div className="flex items-center gap-3">
122
+ <div className="clock-widget flex items-center gap-2 px-3 py-1.5 rounded-md bg-muted/50 text-sm">
123
+ <Clock className="w-4 h-4" />
124
+ <span className="font-mono">{formatTime(currentTime)}</span>
125
+ </div>
126
+
127
+ <div className="w-px h-6 bg-border" />
128
+
129
+ <SimpleTooltip content="Quick actions (Ctrl+K)">
130
+ <button
131
+ onClick={onCommandPalette}
132
+ className={cn(
133
+ 'p-2 rounded-md',
134
+ 'hover:bg-accent hover:text-accent-foreground transition-colors',
135
+ 'focus:outline-none focus-visible:ring-2 focus-visible:ring-ring'
136
+ )}
137
+ aria-label="Quick actions (Ctrl+K)"
138
+ >
139
+ <Zap className="w-4 h-4" />
140
+ </button>
141
+ </SimpleTooltip>
142
+
143
+ <SimpleTooltip content={`Theme: ${theme === 'light' ? 'Light' : theme === 'dark' ? 'Dark' : 'System'}`}>
144
+ <div className="relative">
145
+ <button
146
+ onClick={toggleThemeMenu}
147
+ className={cn(
148
+ 'p-2 rounded-md',
149
+ 'hover:bg-accent hover:text-accent-foreground transition-colors',
150
+ 'focus:outline-none focus-visible:ring-2 focus-visible:ring-ring'
151
+ )}
152
+ aria-label="Theme switcher"
153
+ >
154
+ {theme === 'light' && <Sun className="w-4 h-4" />}
155
+ {theme === 'dark' && <Moon className="w-4 h-4" />}
156
+ {theme === 'system' && <Monitor className="w-4 h-4" />}
157
+ </button>
158
+
159
+ {showThemeMenu && (
160
+ <>
161
+ <div className="fixed inset-0 z-10" onClick={() => setShowThemeMenu(false)} />
162
+ <div className="absolute right-0 top-full mt-2 w-36 rounded-md border border-border bg-popover p-1 shadow-md z-20">
163
+ {[
164
+ { value: 'light' as const, icon: Sun, label: 'Light' },
165
+ { value: 'dark' as const, icon: Moon, label: 'Dark' },
166
+ { value: 'system' as const, icon: Monitor, label: 'System' },
167
+ ].map(({ value, icon: Icon, label }) => (
168
+ <button
169
+ key={value}
170
+ onClick={() => handleThemeChange(value)}
171
+ className={cn(
172
+ 'w-full flex items-center gap-2 px-2 py-1.5 rounded text-sm',
173
+ 'hover:bg-accent hover:text-accent-foreground transition-colors',
174
+ theme === value && 'bg-accent text-accent-foreground'
175
+ )}
176
+ >
177
+ <Icon className="w-3 h-3" />
178
+ <span>{label}</span>
179
+ </button>
180
+ ))}
181
+ </div>
182
+ </>
183
+ )}
184
+ </div>
185
+ </SimpleTooltip>
186
+ </div>
187
+ </div>
188
+ </header>
189
+ );
190
+ });
@@ -0,0 +1,313 @@
1
+ import { useState, useEffect, useRef, memo, useCallback, useMemo } from 'react';
2
+ import { motion, AnimatePresence } from 'framer-motion';
3
+ import {
4
+ Home,
5
+ FolderOpen,
6
+ Settings,
7
+ ChevronRight,
8
+ BarChart3,
9
+ FileText,
10
+ X,
11
+ Circle,
12
+ Keyboard,
13
+ Mail,
14
+ } from 'lucide-react';
15
+ import { cn } from '@/utils/cn';
16
+ import { useNavigate, useLocation } from 'react-router-dom';
17
+ import { useSession } from '@/contexts/SessionContext';
18
+ import { SimpleTooltip } from '@/components/common/Tooltip';
19
+ import iconPng from '/icon.png';
20
+
21
+ interface NavItem {
22
+ id: string;
23
+ label: string;
24
+ icon: React.ElementType;
25
+ path: string;
26
+ badge?: number;
27
+ indented?: boolean;
28
+ closeable?: boolean;
29
+ onClose?: () => void;
30
+ shortcut?: string;
31
+ }
32
+
33
+ interface NavSection {
34
+ title?: string;
35
+ items: NavItem[];
36
+ }
37
+
38
+ export const Sidebar = memo(function Sidebar() {
39
+ const [collapsed, setCollapsed] = useState(false);
40
+ const [logoClickCount, setLogoClickCount] = useState(0);
41
+ const clickTimerRef = useRef<NodeJS.Timeout | null>(null);
42
+ const navigate = useNavigate();
43
+ const location = useLocation();
44
+ const { sessions, activeSessions, closeSession } = useSession();
45
+
46
+ // Reset click count after 2 seconds of inactivity
47
+ useEffect(() => {
48
+ if (logoClickCount > 0) {
49
+ if (clickTimerRef.current) {
50
+ clearTimeout(clickTimerRef.current);
51
+ }
52
+ clickTimerRef.current = setTimeout(() => {
53
+ setLogoClickCount(0);
54
+ }, 2000);
55
+ }
56
+ return () => {
57
+ if (clickTimerRef.current) {
58
+ clearTimeout(clickTimerRef.current);
59
+ }
60
+ };
61
+ }, [logoClickCount]);
62
+
63
+ // Ctrl+Q to toggle sidebar
64
+ useEffect(() => {
65
+ const handleKeyDown = (e: KeyboardEvent) => {
66
+ if ((e.ctrlKey || e.metaKey) && e.key === 'q') {
67
+ e.preventDefault();
68
+ setCollapsed((prev) => !prev);
69
+ }
70
+ };
71
+ window.addEventListener('keydown', handleKeyDown);
72
+ return () => window.removeEventListener('keydown', handleKeyDown);
73
+ }, []);
74
+
75
+ const handleLogoClick = useCallback(() => {
76
+ const newCount = logoClickCount + 1;
77
+ setLogoClickCount(newCount);
78
+
79
+ if (newCount === 5) {
80
+ // Easter egg: open dev tools!
81
+ window.electronAPI.openDevTools();
82
+ setLogoClickCount(0);
83
+ }
84
+ }, [logoClickCount]);
85
+
86
+ const handleNavClick = useCallback(
87
+ (path: string) => {
88
+ navigate(path);
89
+ },
90
+ [navigate]
91
+ );
92
+
93
+ // Build navigation sections with dynamic sessions
94
+ const navSections = useMemo(() => {
95
+ const sections: NavSection[] = [];
96
+
97
+ // Main navigation section
98
+ const mainItems: NavItem[] = [
99
+ { id: 'home', label: 'Dashboard', icon: Home, path: '/', shortcut: 'Ctrl+1' },
100
+ ];
101
+
102
+ // Add active sessions under Dashboard
103
+ activeSessions.forEach((session) => {
104
+ mainItems.push({
105
+ id: `session-${session.id}`,
106
+ label: session.name,
107
+ icon: Circle,
108
+ path: `/session/${session.id}`,
109
+ indented: true,
110
+ closeable: true,
111
+ onClose: () => closeSession(session.id),
112
+ });
113
+ });
114
+
115
+ sections.push({ items: mainItems });
116
+
117
+ // Workspace section
118
+ sections.push({
119
+ title: 'Workspace',
120
+ items: [
121
+ { id: 'sessions', label: 'Sessions', icon: FolderOpen, path: '/sessions', shortcut: 'Ctrl+2' },
122
+ { id: 'analytics', label: 'Analytics', icon: BarChart3, path: '/analytics', shortcut: 'Ctrl+3' },
123
+ { id: 'documents', label: 'Documents', icon: FileText, path: '/documents', shortcut: 'Ctrl+4' },
124
+ { id: 'reporting', label: 'Reporting', icon: Mail, path: '/reporting', shortcut: 'Ctrl+5' },
125
+ ],
126
+ });
127
+
128
+ return sections;
129
+ }, [activeSessions, closeSession]);
130
+
131
+ const bottomItems = useMemo<NavItem[]>(
132
+ () => [
133
+ { id: 'settings', label: 'Settings', icon: Settings, path: '/settings', shortcut: 'Ctrl+,' },
134
+ ],
135
+ []
136
+ );
137
+
138
+ const renderNavItem = (item: NavItem) => {
139
+ const Icon = item.icon;
140
+ const isActive = location.pathname === item.path;
141
+
142
+ const buttonElement = (
143
+ <div
144
+ className={cn(
145
+ 'relative group',
146
+ item.indented && !collapsed && 'ml-4',
147
+ item.indented && collapsed && 'ml-0'
148
+ )}
149
+ >
150
+ <button
151
+ type="button"
152
+ onClick={() => handleNavClick(item.path)}
153
+ className={cn(
154
+ 'w-full flex items-center gap-3 px-3 py-2 rounded-lg transition-all duration-200',
155
+ 'hover:bg-accent hover:text-accent-foreground',
156
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
157
+ isActive && 'bg-primary text-primary-foreground hover:bg-primary/90',
158
+ collapsed && 'justify-center'
159
+ )}
160
+ >
161
+ <div className="flex items-center gap-3 flex-1 pointer-events-none">
162
+ {item.indented && !collapsed && (
163
+ <div className="w-4 h-4 flex items-center justify-center">
164
+ <Circle className="w-2 h-2" />
165
+ </div>
166
+ )}
167
+ {(!item.indented || collapsed) && (
168
+ <Icon className={cn('w-4 h-4 flex-shrink-0', collapsed && 'w-5 h-5')} />
169
+ )}
170
+ <AnimatePresence mode="wait">
171
+ {!collapsed && (
172
+ <motion.span
173
+ initial={{ opacity: 0, width: 0 }}
174
+ animate={{ opacity: 1, width: 'auto' }}
175
+ exit={{ opacity: 0, width: 0 }}
176
+ transition={{ duration: 0.2 }}
177
+ className="text-sm font-medium whitespace-nowrap overflow-hidden flex-1 text-left"
178
+ >
179
+ {item.label}
180
+ </motion.span>
181
+ )}
182
+ </AnimatePresence>
183
+ </div>
184
+ {item.closeable && !collapsed && (
185
+ <div
186
+ role="button"
187
+ tabIndex={0}
188
+ onClick={(e) => {
189
+ e.stopPropagation();
190
+ item.onClose?.();
191
+ }}
192
+ onKeyDown={(e) => {
193
+ if (e.key === 'Enter' || e.key === ' ') {
194
+ e.preventDefault();
195
+ e.stopPropagation();
196
+ item.onClose?.();
197
+ }
198
+ }}
199
+ className="opacity-0 group-hover:opacity-100 transition-opacity p-1 rounded hover:bg-background/20 pointer-events-none group-hover:pointer-events-auto"
200
+ >
201
+ <X className="w-3 h-3" />
202
+ </div>
203
+ )}
204
+ {item.badge && !collapsed && (
205
+ <span className="ml-auto px-1.5 py-0.5 text-xs bg-destructive text-destructive-foreground rounded-full">
206
+ {item.badge}
207
+ </span>
208
+ )}
209
+ {item.badge && collapsed && (
210
+ <span className="absolute top-1 right-1 w-2 h-2 bg-destructive rounded-full" />
211
+ )}
212
+ </button>
213
+ </div>
214
+ );
215
+
216
+ // Wrap with tooltip when collapsed (for non-indented items)
217
+ if (collapsed && !item.indented) {
218
+ return (
219
+ <SimpleTooltip key={item.id} content={item.label} side="right">
220
+ {buttonElement}
221
+ </SimpleTooltip>
222
+ );
223
+ }
224
+
225
+ return <div key={item.id}>{buttonElement}</div>;
226
+ };
227
+
228
+ return (
229
+ <motion.aside
230
+ initial={{ width: 240 }}
231
+ animate={{ width: collapsed ? 64 : 240 }}
232
+ transition={{ duration: 0.3, ease: 'easeInOut' }}
233
+ className="sidebar-bg h-full bg-background/50 backdrop-blur-xl border-r border-border flex flex-col relative"
234
+ >
235
+ <div className="p-4 flex items-center justify-between">
236
+ <AnimatePresence mode="wait">
237
+ {!collapsed && (
238
+ <motion.div
239
+ initial={{ opacity: 0 }}
240
+ animate={{ opacity: 1 }}
241
+ exit={{ opacity: 0 }}
242
+ className="flex items-center gap-2"
243
+ >
244
+ <motion.div
245
+ onClick={handleLogoClick}
246
+ whileTap={{ scale: 0.9 }}
247
+ className="w-8 h-8 rounded-lg cursor-pointer hover:opacity-90 transition-opacity overflow-hidden"
248
+ >
249
+ <img src={iconPng} alt="DocHub" className="w-full h-full" />
250
+ </motion.div>
251
+ <span className="font-semibold text-sm">DocHub</span>
252
+ </motion.div>
253
+ )}
254
+ </AnimatePresence>
255
+ {collapsed && (
256
+ <motion.div
257
+ onClick={handleLogoClick}
258
+ whileTap={{ scale: 0.9 }}
259
+ className="w-8 h-8 rounded-lg cursor-pointer hover:opacity-90 transition-opacity overflow-hidden"
260
+ >
261
+ <img src={iconPng} alt="DocHub" className="w-full h-full" />
262
+ </motion.div>
263
+ )}
264
+ </div>
265
+
266
+ <div className="mx-4 mb-3 h-px bg-border/50" />
267
+
268
+ <nav className="flex-1 px-3 pb-3 overflow-y-auto">
269
+ <div className="space-y-4">
270
+ {navSections.map((section, sectionIndex) => (
271
+ <div key={section.title || sectionIndex}>
272
+ {section.title && !collapsed && (
273
+ <div className="px-3 mb-2">
274
+ <span className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/70">
275
+ {section.title}
276
+ </span>
277
+ </div>
278
+ )}
279
+ {section.title && collapsed && (
280
+ <div className="mx-auto my-2 w-6 h-px bg-border/50" />
281
+ )}
282
+ <div className="space-y-1">{section.items.map(renderNavItem)}</div>
283
+ </div>
284
+ ))}
285
+ </div>
286
+ </nav>
287
+
288
+ <div className="px-3 pb-3 space-y-1 border-t border-border pt-3">
289
+ {bottomItems.map(renderNavItem)}
290
+ </div>
291
+
292
+ <SimpleTooltip content={collapsed ? 'Expand sidebar (Ctrl+Q)' : 'Collapse sidebar (Ctrl+Q)'} side="right">
293
+ <motion.button
294
+ onClick={() => setCollapsed(!collapsed)}
295
+ whileHover={{ scale: 1.1 }}
296
+ whileTap={{ scale: 0.9 }}
297
+ className={cn(
298
+ 'absolute -right-4 top-1/2 -translate-y-1/2 w-8 h-8 rounded-full',
299
+ 'bg-background border-2 border-border',
300
+ 'hover:border-primary hover:shadow-lg transition-all',
301
+ 'flex items-center justify-center',
302
+ 'focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',
303
+ 'group'
304
+ )}
305
+ >
306
+ <motion.div animate={{ rotate: collapsed ? 0 : 180 }} transition={{ duration: 0.3 }}>
307
+ <ChevronRight className="w-4 h-4 text-muted-foreground group-hover:text-primary transition-colors" />
308
+ </motion.div>
309
+ </motion.button>
310
+ </SimpleTooltip>
311
+ </motion.aside>
312
+ );
313
+ });