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,190 @@
1
+ import { useState, useEffect, useRef } from 'react';
2
+ import { Minus, Square, X, Pin, PinOff } from 'lucide-react';
3
+ import { cn } from '@/utils/cn';
4
+ import { motion, AnimatePresence } from 'framer-motion';
5
+ import { SimpleTooltip, TooltipProvider } from '@/components/common/Tooltip';
6
+
7
+ export function TitleBar() {
8
+ const [isMaximized, setIsMaximized] = useState(false);
9
+ const [isAlwaysOnTop, setIsAlwaysOnTop] = useState(false);
10
+ const [platform, setPlatform] = useState<NodeJS.Platform>('win32');
11
+ const [clickCount, setClickCount] = useState(0);
12
+ const [showDebugToast, setShowDebugToast] = useState(false);
13
+ const resetTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
14
+
15
+ useEffect(() => {
16
+ // Safely check if electronAPI is available (may not be in browser-only mode)
17
+ if (typeof window.electronAPI === 'undefined') {
18
+ console.warn('TitleBar: electronAPI not available (running in browser mode?)');
19
+ return;
20
+ }
21
+
22
+ window.electronAPI.getPlatform().then((p: string) => setPlatform(p as NodeJS.Platform));
23
+ window.electronAPI.isMaximized().then(setIsMaximized);
24
+ window.electronAPI.isAlwaysOnTop().then(setIsAlwaysOnTop);
25
+
26
+ const unsubMaximized = window.electronAPI.onWindowMaximized(() => setIsMaximized(true));
27
+ const unsubUnmaximized = window.electronAPI.onWindowUnmaximized(() => setIsMaximized(false));
28
+ const unsubAlwaysOnTop = window.electronAPI.onAlwaysOnTopChanged((isOnTop: boolean) => setIsAlwaysOnTop(isOnTop));
29
+
30
+ return () => {
31
+ unsubMaximized();
32
+ unsubUnmaximized();
33
+ unsubAlwaysOnTop();
34
+ };
35
+ }, []);
36
+
37
+ const handleMinimize = () => window.electronAPI?.minimizeWindow();
38
+ const handleMaximize = () => window.electronAPI?.maximizeWindow();
39
+ const handleClose = () => window.electronAPI?.closeWindow();
40
+ const handleToggleAlwaysOnTop = () => {
41
+ window.electronAPI?.setAlwaysOnTop(!isAlwaysOnTop);
42
+ };
43
+
44
+ const handleLogoClick = () => {
45
+ // Clear existing timeout
46
+ if (resetTimeoutRef.current) {
47
+ clearTimeout(resetTimeoutRef.current);
48
+ }
49
+
50
+ // Increment click count
51
+ const newCount = clickCount + 1;
52
+ setClickCount(newCount);
53
+
54
+ // Check if we've reached 5 clicks
55
+ if (newCount === 5) {
56
+ // Open dev tools
57
+ window.electronAPI?.openDevTools();
58
+
59
+ // Show toast notification
60
+ setShowDebugToast(true);
61
+ setTimeout(() => setShowDebugToast(false), 3000);
62
+
63
+ // Reset click count
64
+ setClickCount(0);
65
+ } else {
66
+ // Reset count after 2 seconds of inactivity
67
+ resetTimeoutRef.current = setTimeout(() => {
68
+ setClickCount(0);
69
+ }, 2000);
70
+ }
71
+ };
72
+
73
+ // Clean up timeout on unmount
74
+ useEffect(() => {
75
+ return () => {
76
+ if (resetTimeoutRef.current) {
77
+ clearTimeout(resetTimeoutRef.current);
78
+ }
79
+ };
80
+ }, []);
81
+
82
+ const isWindows = platform === 'win32';
83
+ const isMac = platform === 'darwin';
84
+
85
+ return (
86
+ <>
87
+ <div className="h-8 bg-background/80 backdrop-blur-xl border-b border-border flex items-center justify-between drag-region relative">
88
+ <button
89
+ onClick={handleLogoClick}
90
+ className="flex items-center gap-2 px-3 no-drag hover:bg-muted/50 transition-colors"
91
+ >
92
+ <div className="w-4 h-4 rounded bg-primary/20" />
93
+ <span className="text-xs font-medium text-muted-foreground select-none">
94
+ Documentation Hub
95
+ </span>
96
+ </button>
97
+
98
+ {isWindows && (
99
+ <TooltipProvider>
100
+ <div className="flex no-drag">
101
+ <SimpleTooltip content={isAlwaysOnTop ? 'Unpin from top' : 'Pin on top'}>
102
+ <button
103
+ onClick={handleToggleAlwaysOnTop}
104
+ className={cn(
105
+ 'px-3 h-8 hover:bg-muted transition-colors',
106
+ 'focus:outline-none focus-visible:bg-muted',
107
+ isAlwaysOnTop && 'text-primary bg-primary/10'
108
+ )}
109
+ aria-label={isAlwaysOnTop ? 'Unpin from top' : 'Pin on top'}
110
+ >
111
+ {isAlwaysOnTop ? (
112
+ <PinOff className="w-3 h-3" />
113
+ ) : (
114
+ <Pin className="w-3 h-3" />
115
+ )}
116
+ </button>
117
+ </SimpleTooltip>
118
+ <button
119
+ onClick={handleMinimize}
120
+ className={cn(
121
+ 'px-4 h-8 hover:bg-muted transition-colors',
122
+ 'focus:outline-none focus-visible:bg-muted'
123
+ )}
124
+ aria-label="Minimize"
125
+ >
126
+ <Minus className="w-3 h-3" />
127
+ </button>
128
+ <button
129
+ onClick={handleMaximize}
130
+ className={cn(
131
+ 'px-4 h-8 hover:bg-muted transition-colors',
132
+ 'focus:outline-none focus-visible:bg-muted'
133
+ )}
134
+ aria-label={isMaximized ? 'Restore' : 'Maximize'}
135
+ >
136
+ <Square className="w-2.5 h-2.5" />
137
+ </button>
138
+ <button
139
+ onClick={handleClose}
140
+ className={cn(
141
+ 'px-4 h-8 hover:bg-destructive hover:text-destructive-foreground transition-colors',
142
+ 'focus:outline-none focus-visible:bg-destructive focus-visible:text-destructive-foreground'
143
+ )}
144
+ aria-label="Close"
145
+ >
146
+ <X className="w-3.5 h-3.5" />
147
+ </button>
148
+ </div>
149
+ </TooltipProvider>
150
+ )}
151
+
152
+ {isMac && (
153
+ <div className="flex gap-2 px-3 no-drag">
154
+ <button
155
+ onClick={handleClose}
156
+ className="w-3 h-3 rounded-full bg-red-500 hover:bg-red-600 transition-colors"
157
+ aria-label="Close"
158
+ />
159
+ <button
160
+ onClick={handleMinimize}
161
+ className="w-3 h-3 rounded-full bg-yellow-500 hover:bg-yellow-600 transition-colors"
162
+ aria-label="Minimize"
163
+ />
164
+ <button
165
+ onClick={handleMaximize}
166
+ className="w-3 h-3 rounded-full bg-green-500 hover:bg-green-600 transition-colors"
167
+ aria-label="Maximize"
168
+ />
169
+ </div>
170
+ )}
171
+ </div>
172
+
173
+ {/* Debug Mode Toast */}
174
+ <AnimatePresence>
175
+ {showDebugToast && (
176
+ <motion.div
177
+ initial={{ opacity: 0, y: -50 }}
178
+ animate={{ opacity: 1, y: 0 }}
179
+ exit={{ opacity: 0, y: -50 }}
180
+ className="fixed top-12 left-1/2 transform -translate-x-1/2 z-[9999]"
181
+ >
182
+ <div className="bg-primary text-primary-foreground px-4 py-2 rounded-lg shadow-lg flex items-center gap-2">
183
+ <span className="text-sm font-medium">🔧 Debug Mode Activated</span>
184
+ </div>
185
+ </motion.div>
186
+ )}
187
+ </AnimatePresence>
188
+ </>
189
+ );
190
+ }
@@ -0,0 +1,233 @@
1
+ import { useEffect, useState, useCallback, useMemo } from 'react';
2
+ import { Command } from 'cmdk';
3
+ import { motion, AnimatePresence } from 'framer-motion';
4
+ import {
5
+ Search,
6
+ Home,
7
+ FolderOpen,
8
+ Settings,
9
+ User,
10
+ FileText,
11
+ Plug,
12
+ Moon,
13
+ Sun,
14
+ X,
15
+ ArrowRight,
16
+ } from 'lucide-react';
17
+ import { useNavigate } from 'react-router-dom';
18
+ import { useTheme } from '@/contexts/ThemeContext';
19
+ import { cn } from '@/utils/cn';
20
+
21
+ interface CommandItem {
22
+ id: string;
23
+ label: string;
24
+ category: string;
25
+ icon: React.ElementType;
26
+ action: () => void;
27
+ keywords?: string[];
28
+ }
29
+
30
+ export function CommandPalette({
31
+ open,
32
+ onOpenChange,
33
+ }: {
34
+ open: boolean;
35
+ onOpenChange: (open: boolean) => void;
36
+ }) {
37
+ const [search, setSearch] = useState('');
38
+ const navigate = useNavigate();
39
+ const { setTheme } = useTheme();
40
+
41
+ const handleNavigate = useCallback(
42
+ (path: string) => {
43
+ navigate(path);
44
+ onOpenChange(false);
45
+ },
46
+ [navigate, onOpenChange]
47
+ );
48
+
49
+ const items: CommandItem[] = useMemo(
50
+ () => [
51
+ {
52
+ id: 'home',
53
+ label: 'Go to Dashboard',
54
+ category: 'Navigation',
55
+ icon: Home,
56
+ action: () => handleNavigate('/'),
57
+ keywords: ['dashboard', 'home', 'main'],
58
+ },
59
+ {
60
+ id: 'projects',
61
+ label: 'Open Projects',
62
+ category: 'Navigation',
63
+ icon: FolderOpen,
64
+ action: () => handleNavigate('/projects'),
65
+ keywords: ['project', 'folder', 'workspace'],
66
+ },
67
+ {
68
+ id: 'documents',
69
+ label: 'Browse Documents',
70
+ category: 'Navigation',
71
+ icon: FileText,
72
+ action: () => handleNavigate('/documents'),
73
+ keywords: ['docs', 'files', 'text'],
74
+ },
75
+ {
76
+ id: 'plugins',
77
+ label: 'View Plugins',
78
+ category: 'Navigation',
79
+ icon: Plug,
80
+ action: () => handleNavigate('/plugins'),
81
+ keywords: ['plugins', 'extensions', 'addons', 'modules'],
82
+ },
83
+ {
84
+ id: 'profile',
85
+ label: 'View Profile',
86
+ category: 'Account',
87
+ icon: User,
88
+ action: () => handleNavigate('/profile'),
89
+ keywords: ['user', 'account', 'me'],
90
+ },
91
+ {
92
+ id: 'settings',
93
+ label: 'Open Settings',
94
+ category: 'Account',
95
+ icon: Settings,
96
+ action: () => handleNavigate('/settings'),
97
+ keywords: ['preferences', 'config', 'options'],
98
+ },
99
+ {
100
+ id: 'theme-light',
101
+ label: 'Switch to Light Theme',
102
+ category: 'Theme',
103
+ icon: Sun,
104
+ action: () => {
105
+ setTheme('light');
106
+ onOpenChange(false);
107
+ },
108
+ keywords: ['light', 'bright', 'day'],
109
+ },
110
+ {
111
+ id: 'theme-dark',
112
+ label: 'Switch to Dark Theme',
113
+ category: 'Theme',
114
+ icon: Moon,
115
+ action: () => {
116
+ setTheme('dark');
117
+ onOpenChange(false);
118
+ },
119
+ keywords: ['dark', 'night', 'dim'],
120
+ },
121
+ ],
122
+ [handleNavigate, setTheme, onOpenChange]
123
+ );
124
+
125
+ const groupedItems = items.reduce(
126
+ (acc, item) => {
127
+ if (!acc[item.category]) {
128
+ acc[item.category] = [];
129
+ }
130
+ acc[item.category].push(item);
131
+ return acc;
132
+ },
133
+ {} as Record<string, CommandItem[]>
134
+ );
135
+
136
+ useEffect(() => {
137
+ const handleKeyDown = (e: KeyboardEvent) => {
138
+ if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
139
+ e.preventDefault();
140
+ onOpenChange(!open);
141
+ }
142
+ if (e.key === 'Escape') {
143
+ onOpenChange(false);
144
+ }
145
+ };
146
+
147
+ window.addEventListener('keydown', handleKeyDown);
148
+ return () => window.removeEventListener('keydown', handleKeyDown);
149
+ }, [open, onOpenChange]);
150
+
151
+ const handleSelect = useCallback(
152
+ (itemId: string) => {
153
+ const item = items.find((i) => i.id === itemId);
154
+ if (item) {
155
+ item.action();
156
+ }
157
+ },
158
+ [items]
159
+ );
160
+
161
+ return (
162
+ <AnimatePresence>
163
+ {open && (
164
+ <>
165
+ <motion.div
166
+ initial={{ opacity: 0 }}
167
+ animate={{ opacity: 1 }}
168
+ exit={{ opacity: 0 }}
169
+ className="fixed inset-0 bg-background/80 backdrop-blur-xs z-50"
170
+ onClick={() => onOpenChange(false)}
171
+ />
172
+ <motion.div
173
+ initial={{ opacity: 0, scale: 0.95 }}
174
+ animate={{ opacity: 1, scale: 1 }}
175
+ exit={{ opacity: 0, scale: 0.95 }}
176
+ transition={{ duration: 0.15 }}
177
+ className="fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-2xl z-50"
178
+ >
179
+ <Command className="rounded-xl border border-border bg-popover shadow-2xl">
180
+ <div className="flex items-center border-b border-border px-3">
181
+ <Search className="w-4 h-4 text-muted-foreground mr-2 shrink-0" />
182
+ <Command.Input
183
+ value={search}
184
+ onValueChange={setSearch}
185
+ placeholder="Type a command or search..."
186
+ className="flex-1 py-3 text-sm bg-transparent outline-none placeholder:text-muted-foreground"
187
+ />
188
+ <button
189
+ onClick={() => onOpenChange(false)}
190
+ className="p-1 rounded hover:bg-accent"
191
+ aria-label="Close"
192
+ >
193
+ <X className="w-4 h-4" />
194
+ </button>
195
+ </div>
196
+ <Command.List className="max-h-96 overflow-y-auto p-2">
197
+ <Command.Empty className="py-6 text-center text-sm text-muted-foreground">
198
+ No results found.
199
+ </Command.Empty>
200
+ {Object.entries(groupedItems).map(([category, categoryItems]) => (
201
+ <Command.Group key={category} heading={category} className="px-2 pb-2">
202
+ <div className="text-xs text-muted-foreground font-medium mb-1 px-2">
203
+ {category}
204
+ </div>
205
+ {categoryItems.map((item) => {
206
+ const Icon = item.icon;
207
+ return (
208
+ <Command.Item
209
+ key={item.id}
210
+ value={`${item.label} ${item.keywords?.join(' ') || ''}`}
211
+ onSelect={() => handleSelect(item.id)}
212
+ className={cn(
213
+ 'flex items-center gap-3 px-2 py-2 rounded-md cursor-pointer',
214
+ 'hover:bg-accent hover:text-accent-foreground',
215
+ 'aria-selected:bg-accent aria-selected:text-accent-foreground'
216
+ )}
217
+ >
218
+ <Icon className="w-4 h-4 text-muted-foreground" />
219
+ <span className="flex-1">{item.label}</span>
220
+ <ArrowRight className="w-3 h-3 text-muted-foreground" />
221
+ </Command.Item>
222
+ );
223
+ })}
224
+ </Command.Group>
225
+ ))}
226
+ </Command.List>
227
+ </Command>
228
+ </motion.div>
229
+ </>
230
+ )}
231
+ </AnimatePresence>
232
+ );
233
+ }
@@ -0,0 +1,173 @@
1
+ import * as Dialog from '@radix-ui/react-dialog';
2
+ import { motion, AnimatePresence } from 'framer-motion';
3
+ import { X, Keyboard } from 'lucide-react';
4
+ import { cn } from '@/utils/cn';
5
+
6
+ interface ShortcutItem {
7
+ keys: string[];
8
+ description: string;
9
+ }
10
+
11
+ interface ShortcutGroup {
12
+ title: string;
13
+ shortcuts: ShortcutItem[];
14
+ }
15
+
16
+ const shortcutGroups: ShortcutGroup[] = [
17
+ {
18
+ title: 'General',
19
+ shortcuts: [
20
+ { keys: ['Ctrl', 'K'], description: 'Open command palette' },
21
+ { keys: ['Ctrl', 'Q'], description: 'Toggle sidebar' },
22
+ { keys: ['?'], description: 'Show keyboard shortcuts' },
23
+ { keys: ['Esc'], description: 'Close dialogs / Cancel' },
24
+ ],
25
+ },
26
+ {
27
+ title: 'Navigation',
28
+ shortcuts: [
29
+ { keys: ['Ctrl', '1'], description: 'Go to Dashboard' },
30
+ { keys: ['Ctrl', '2'], description: 'Go to Sessions' },
31
+ { keys: ['Ctrl', '3'], description: 'Go to Analytics' },
32
+ { keys: ['Ctrl', '4'], description: 'Go to Documents' },
33
+ { keys: ['Ctrl', ','], description: 'Open Settings' },
34
+ ],
35
+ },
36
+ {
37
+ title: 'Session Actions',
38
+ shortcuts: [
39
+ { keys: ['Ctrl', 'N'], description: 'New session' },
40
+ { keys: ['Ctrl', 'O'], description: 'Add documents' },
41
+ { keys: ['Ctrl', 'Enter'], description: 'Process all documents' },
42
+ { keys: ['Ctrl', 'S'], description: 'Save session' },
43
+ ],
44
+ },
45
+ {
46
+ title: 'Document Actions',
47
+ shortcuts: [
48
+ { keys: ['Delete'], description: 'Remove selected document' },
49
+ { keys: ['Ctrl', 'Click'], description: 'Multi-select documents' },
50
+ { keys: ['Shift', 'Click'], description: 'Range select documents' },
51
+ ],
52
+ },
53
+ ];
54
+
55
+ // For Mac, replace Ctrl with Cmd
56
+ const isMac = typeof navigator !== 'undefined' && navigator.platform.toUpperCase().indexOf('MAC') >= 0;
57
+
58
+ function formatKey(key: string): string {
59
+ if (isMac && key === 'Ctrl') return '\u2318';
60
+ if (key === 'Shift') return '\u21E7';
61
+ if (key === 'Alt') return isMac ? '\u2325' : 'Alt';
62
+ if (key === 'Enter') return '\u23CE';
63
+ if (key === 'Esc') return 'Esc';
64
+ if (key === 'Delete') return isMac ? '\u232B' : 'Del';
65
+ return key;
66
+ }
67
+
68
+ interface KeyboardShortcutsModalProps {
69
+ open: boolean;
70
+ onOpenChange: (open: boolean) => void;
71
+ }
72
+
73
+ export function KeyboardShortcutsModal({ open, onOpenChange }: KeyboardShortcutsModalProps) {
74
+ return (
75
+ <Dialog.Root open={open} onOpenChange={onOpenChange}>
76
+ <AnimatePresence>
77
+ {open && (
78
+ <Dialog.Portal forceMount>
79
+ <Dialog.Overlay asChild>
80
+ <motion.div
81
+ initial={{ opacity: 0 }}
82
+ animate={{ opacity: 1 }}
83
+ exit={{ opacity: 0 }}
84
+ className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50"
85
+ />
86
+ </Dialog.Overlay>
87
+ <Dialog.Content asChild>
88
+ <motion.div
89
+ initial={{ opacity: 0, scale: 0.95, y: 20 }}
90
+ animate={{ opacity: 1, scale: 1, y: 0 }}
91
+ exit={{ opacity: 0, scale: 0.95, y: 20 }}
92
+ transition={{ type: 'spring', duration: 0.3 }}
93
+ className={cn(
94
+ 'fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50',
95
+ 'w-full max-w-2xl max-h-[85vh] overflow-hidden',
96
+ 'bg-background rounded-xl border border-border shadow-2xl',
97
+ 'flex flex-col'
98
+ )}
99
+ >
100
+ {/* Header */}
101
+ <div className="flex items-center justify-between px-6 py-4 border-b border-border">
102
+ <div className="flex items-center gap-3">
103
+ <div className="p-2 rounded-lg bg-primary/10">
104
+ <Keyboard className="w-5 h-5 text-primary" />
105
+ </div>
106
+ <div>
107
+ <Dialog.Title className="text-lg font-semibold">
108
+ Keyboard Shortcuts
109
+ </Dialog.Title>
110
+ <Dialog.Description className="text-sm text-muted-foreground">
111
+ Quick reference for all available shortcuts
112
+ </Dialog.Description>
113
+ </div>
114
+ </div>
115
+ <Dialog.Close asChild>
116
+ <button
117
+ className="p-2 rounded-lg hover:bg-muted transition-colors"
118
+ aria-label="Close"
119
+ >
120
+ <X className="w-4 h-4" />
121
+ </button>
122
+ </Dialog.Close>
123
+ </div>
124
+
125
+ {/* Content */}
126
+ <div className="flex-1 overflow-y-auto p-6">
127
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
128
+ {shortcutGroups.map((group) => (
129
+ <div key={group.title}>
130
+ <h3 className="text-sm font-semibold text-muted-foreground mb-3 uppercase tracking-wider">
131
+ {group.title}
132
+ </h3>
133
+ <div className="space-y-2">
134
+ {group.shortcuts.map((shortcut, index) => (
135
+ <div
136
+ key={index}
137
+ className="flex items-center justify-between py-2 px-3 rounded-lg hover:bg-muted/50 transition-colors"
138
+ >
139
+ <span className="text-sm">{shortcut.description}</span>
140
+ <div className="flex items-center gap-1">
141
+ {shortcut.keys.map((key, keyIndex) => (
142
+ <span key={keyIndex} className="flex items-center gap-1">
143
+ <kbd className="min-w-[24px] h-6 px-1.5 flex items-center justify-center text-xs font-mono bg-muted rounded border border-border shadow-sm">
144
+ {formatKey(key)}
145
+ </kbd>
146
+ {keyIndex < shortcut.keys.length - 1 && (
147
+ <span className="text-muted-foreground text-xs">+</span>
148
+ )}
149
+ </span>
150
+ ))}
151
+ </div>
152
+ </div>
153
+ ))}
154
+ </div>
155
+ </div>
156
+ ))}
157
+ </div>
158
+ </div>
159
+
160
+ {/* Footer */}
161
+ <div className="px-6 py-3 border-t border-border bg-muted/30">
162
+ <p className="text-xs text-muted-foreground text-center">
163
+ Press <kbd className="px-1 py-0.5 text-[10px] font-mono bg-muted rounded border border-border">?</kbd> anytime to show this dialog
164
+ </p>
165
+ </div>
166
+ </motion.div>
167
+ </Dialog.Content>
168
+ </Dialog.Portal>
169
+ )}
170
+ </AnimatePresence>
171
+ </Dialog.Root>
172
+ );
173
+ }