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.
- package/.eslintrc.json +43 -0
- package/.github/workflows/build.yml +64 -0
- package/.github/workflows/ci.yml +39 -0
- package/.vscode/extensions.json +3 -0
- package/Current.md +97 -0
- package/DocHub_Image.png +0 -0
- package/README.md +666 -0
- package/USER_GUIDE.md +1173 -0
- package/Updater.md +311 -0
- package/build/256x256.png +0 -0
- package/build/512x512.png +0 -0
- package/build/app-update.yml +4 -0
- package/build/create-icon.js +208 -0
- package/build/icon.ico +0 -0
- package/build/icon.png +0 -0
- package/build/icon_1024x1024.png +0 -0
- package/dist/assets/Analytics-BpsG9895.js +1 -0
- package/dist/assets/Card-IAZin8kp.js +1 -0
- package/dist/assets/CurrentSession-B-rFkHvf.js +12 -0
- package/dist/assets/Dashboard-C_5gMb0q.js +1 -0
- package/dist/assets/Documents-CqZ25axS.js +1 -0
- package/dist/assets/Input-l89xwXBi.js +1 -0
- package/dist/assets/Reporting-DqdHJY_a.js +1 -0
- package/dist/assets/Search-XNbu5z_3.js +1 -0
- package/dist/assets/SessionManager-lH9hZfzH.js +1 -0
- package/dist/assets/Sessions-ClZOPYNc.js +1 -0
- package/dist/assets/Settings-DUEHGURa.js +11 -0
- package/dist/assets/index-8xUe8ptc.js +24 -0
- package/dist/assets/index-RYyJqF7O.css +1 -0
- package/dist/assets/path-BkOl0AGO.js +1 -0
- package/dist/assets/promises-ID_B9S-h.js +1 -0
- package/dist/assets/urlHelpers-TvgahX0r.js +1 -0
- package/dist/assets/useToast-yRSO1dkm.js +1 -0
- package/dist/assets/vendor-charts-RkGK5ROP.js +36 -0
- package/dist/assets/vendor-db-l0sNRNKZ.js +1 -0
- package/dist/assets/vendor-react-BVZ_anCF.js +4 -0
- package/dist/assets/vendor-search-Dw8P0qyA.js +1 -0
- package/dist/assets/vendor-ui-BU7NfluV.js +53 -0
- package/dist/electron/PowerAutomateApiService-LfW09ZGr.js +147 -0
- package/dist/electron/main-CXkNtyv-.js +19789 -0
- package/dist/electron/main.js +5 -0
- package/dist/electron/preload.js +1 -0
- package/dist/icon.png +0 -0
- package/dist/index.html +27 -0
- package/docs/CODEBASE_ANALYSIS_REPORT.md +309 -0
- package/docs/DEBUG_LOGGING_GUIDE.md +244 -0
- package/docs/README.md +115 -0
- package/docs/TOC_WIRING_GUIDE.md +344 -0
- package/docs/analysis/Bullet_Symbol_Bug_Analysis.md +136 -0
- package/docs/analysis/DOCXMLATER_ANALYSIS_SUMMARY.txt +169 -0
- package/docs/analysis/Document_Processing_Issues_Analysis.md +704 -0
- package/docs/analysis/FIELD_PRESERVATION_ANALYSIS.md +1200 -0
- package/docs/analysis/INDENTATION_PRESERVE_ANALYSIS.md +181 -0
- package/docs/analysis/INDENTATION_PRESERVE_IMPLEMENTATION.md +207 -0
- package/docs/analysis/List_Implementation.md +206 -0
- package/docs/analysis/List_Implementation_Accuracy_Report.md +366 -0
- package/docs/analysis/PROCESSING_OPTIONS_UI_UPDATES.md +220 -0
- package/docs/analysis/RefactorStyles.md +852 -0
- package/docs/analysis/STYLE_PARAMETER_ENHANCEMENT.md +143 -0
- package/docs/analysis/docxmlater-comparison-todo-2025-11-13.md +636 -0
- package/docs/analysis/docxmlater-implementation-analysis-2025-11-13.md +340 -0
- package/docs/analysis/docxmlater-template_ui-integration-analysis.md +263 -0
- package/docs/analysis/github-issues-to-create.md +237 -0
- package/docs/api/API_README.md +538 -0
- package/docs/api/API_REFERENCE.md +751 -0
- package/docs/api/TYPE_DEFINITIONS.md +869 -0
- package/docs/architecture/FONT_EMBEDDING_GUIDE.md +318 -0
- package/docs/architecture/docxmlater-functions-and-structure.md +726 -0
- package/docs/docxmlater-readme.md +1341 -0
- package/docs/fixes/EXECUTION_LOG_TEST_BASE.md +573 -0
- package/docs/fixes/HYPERLINK_TEXT_SANITIZATION.md +253 -0
- package/docs/fixes/README.md +37 -0
- package/docs/github-issues/issue-1-body.md +125 -0
- package/docs/github-issues/issue-10-body.md +850 -0
- package/docs/github-issues/issue-2-body.md +200 -0
- package/docs/github-issues/issue-3-body.md +270 -0
- package/docs/github-issues/issue-4-body.md +169 -0
- package/docs/github-issues/issue-5-body.md +173 -0
- package/docs/github-issues/issue-6-body.md +158 -0
- package/docs/github-issues/issue-7-body.md +171 -0
- package/docs/github-issues/issue-8-body.md +407 -0
- package/docs/github-issues/issue-9-body.md +515 -0
- package/docs/github-issues/issue-tracker.md +274 -0
- package/docs/github-issues/predictive-analysis-2025-10-18.md +2131 -0
- package/docs/implementation/List_Framework_Refactor_Plan.md +336 -0
- package/docs/implementation/PRIMARY_TEXT_COLOR_FEATURE.md +217 -0
- package/docs/implementation/RELEASE_PLAN_v2.1.0.md +362 -0
- package/docs/implementation/RefactorStyles.md +588 -0
- package/docs/implementation/implement-plan.md +489 -0
- package/docs/implementation/missing-helpers-implementation.md +391 -0
- package/docs/implementation/refactor-plan.md +520 -0
- package/docs/implementation/session-implementation-complete.md +233 -0
- package/docs/implementation/session-management-plan.md +250 -0
- package/docs/setup-checklist.md +77 -0
- package/docs/versions/changelog.md +345 -0
- package/electron/customUpdater.ts +656 -0
- package/electron/main.ts +2441 -0
- package/electron/memoryConfig.ts +187 -0
- package/electron/preload.ts +394 -0
- package/electron/proxyConfig.ts +340 -0
- package/electron/services/BackupService.ts +452 -0
- package/electron/services/DictionaryService.ts +402 -0
- package/electron/services/LocalDictionaryLookupService.ts +147 -0
- package/electron/services/PowerAutomateApiService.ts +231 -0
- package/electron/services/SharePointSyncService.ts +474 -0
- package/electron/windowsCertStore.ts +427 -0
- package/electron/zscalerConfig.ts +381 -0
- package/eslint.config.js +92 -0
- package/jest.config.js +52 -0
- package/package.json +214 -0
- package/postcss.config.mjs +6 -0
- package/public/icon.png +0 -0
- package/publish-release.ps1 +5 -0
- package/renovate.json +30 -0
- package/src/App.tsx +216 -0
- package/src/__mocks__/p-limit.js +12 -0
- package/src/__mocks__/styleMock.js +1 -0
- package/src/components/common/BugReportButton.tsx +44 -0
- package/src/components/common/BugReportDialog.tsx +193 -0
- package/src/components/common/Button.tsx +153 -0
- package/src/components/common/Card.tsx +86 -0
- package/src/components/common/ColorPickerDialog.tsx +177 -0
- package/src/components/common/ConfirmDialog.tsx +96 -0
- package/src/components/common/DebugConsole.tsx +275 -0
- package/src/components/common/EmptyState.tsx +183 -0
- package/src/components/common/ErrorBoundary.tsx +98 -0
- package/src/components/common/ErrorDetailsDialog.tsx +153 -0
- package/src/components/common/ErrorFallback.tsx +218 -0
- package/src/components/common/Input.tsx +109 -0
- package/src/components/common/Skeleton.tsx +184 -0
- package/src/components/common/SplashScreen.tsx +81 -0
- package/src/components/common/Toast.tsx +155 -0
- package/src/components/common/Tooltip.tsx +79 -0
- package/src/components/common/UpdateNotification.tsx +320 -0
- package/src/components/comparison/ComparisonWindow.tsx +374 -0
- package/src/components/comparison/SideBySideDiff.tsx +486 -0
- package/src/components/comparison/index.ts +8 -0
- package/src/components/document/DocumentUploader.tsx +288 -0
- package/src/components/document/HyperlinkPreview.tsx +430 -0
- package/src/components/document/HyperlinkService.md +1484 -0
- package/src/components/document/Hyperlink_Technical_Documentation.md +496 -0
- package/src/components/document/InlineChangesView.tsx +707 -0
- package/src/components/document/ProcessingProgress.tsx +303 -0
- package/src/components/document/ProcessingResults.tsx +256 -0
- package/src/components/document/TrackedChangesDetail.tsx +530 -0
- package/src/components/document/TrackedChangesPanel.tsx +546 -0
- package/src/components/document/VirtualDocumentList.tsx +240 -0
- package/src/components/editor/DocumentEditor.tsx +723 -0
- package/src/components/editor/DocumentEditorModal.tsx +640 -0
- package/src/components/editor/EditorQuickActions.tsx +502 -0
- package/src/components/editor/EditorToolbar.tsx +312 -0
- package/src/components/editor/TableEditor.tsx +926 -0
- package/src/components/editor/index.ts +18 -0
- package/src/components/layout/Header.tsx +190 -0
- package/src/components/layout/Sidebar.tsx +313 -0
- package/src/components/layout/TitleBar.tsx +190 -0
- package/src/components/navigation/CommandPalette.tsx +233 -0
- package/src/components/navigation/KeyboardShortcutsModal.tsx +173 -0
- package/src/components/sessions/ChangeItem.tsx +408 -0
- package/src/components/sessions/ChangeViewer.tsx +1155 -0
- package/src/components/sessions/DocumentComparisonModal.tsx +314 -0
- package/src/components/sessions/ProcessingOptions.tsx +297 -0
- package/src/components/sessions/ReplacementsTab.tsx +438 -0
- package/src/components/sessions/RevisionHandlingOptions.tsx +87 -0
- package/src/components/sessions/SessionManager.tsx +188 -0
- package/src/components/sessions/StylesEditor.tsx +1335 -0
- package/src/components/sessions/TabContainer.tsx +151 -0
- package/src/components/sessions/VirtualSessionList.tsx +157 -0
- package/src/components/sessions/sessionToProcessorManager.tsx +420 -0
- package/src/components/settings/CertificateManager.tsx +410 -0
- package/src/components/settings/SegmentedControl.tsx +88 -0
- package/src/components/settings/SettingRow.tsx +52 -0
- package/src/contexts/GlobalStatsContext.tsx +396 -0
- package/src/contexts/SessionContext.tsx +2129 -0
- package/src/contexts/ThemeContext.tsx +428 -0
- package/src/contexts/UserSettingsContext.tsx +290 -0
- package/src/contexts/__tests__/GlobalStatsContext.test.tsx +390 -0
- package/src/global.d.ts +273 -0
- package/src/hooks/useDocumentQueue.tsx +210 -0
- package/src/hooks/useToast.tsx +55 -0
- package/src/main.tsx +10 -0
- package/src/pages/Analytics.tsx +386 -0
- package/src/pages/CurrentSession.tsx +1174 -0
- package/src/pages/Dashboard.tsx +319 -0
- package/src/pages/Documents.tsx +317 -0
- package/src/pages/Projects.tsx +250 -0
- package/src/pages/Reporting.tsx +386 -0
- package/src/pages/Search.tsx +349 -0
- package/src/pages/Sessions.tsx +285 -0
- package/src/pages/Settings.tsx +2662 -0
- package/src/services/HyperlinkService.ts +1085 -0
- package/src/services/document/DocXMLaterProcessor.ts +617 -0
- package/src/services/document/DocumentProcessingComparison.ts +856 -0
- package/src/services/document/DocumentSnapshotService.ts +575 -0
- package/src/services/document/WordDocumentProcessor.ts +10509 -0
- package/src/services/document/__tests__/DocXMLaterProcessor.hyperlinks.test.md +311 -0
- package/src/services/document/__tests__/WordDocumentProcessor.integration.test.ts +515 -0
- package/src/services/document/__tests__/WordDocumentProcessor.test.ts +812 -0
- package/src/services/document/blanklines/BlankLineManager.ts +658 -0
- package/src/services/document/blanklines/__tests__/paragraphChecks.test.ts +281 -0
- package/src/services/document/blanklines/helpers/blankLineInsertion.ts +87 -0
- package/src/services/document/blanklines/helpers/blankLineSnapshot.ts +251 -0
- package/src/services/document/blanklines/helpers/clearCustom.ts +121 -0
- package/src/services/document/blanklines/helpers/contextChecks.ts +117 -0
- package/src/services/document/blanklines/helpers/imageChecks.ts +51 -0
- package/src/services/document/blanklines/helpers/paragraphChecks.ts +236 -0
- package/src/services/document/blanklines/helpers/removeBlanksBetweenListItems.ts +91 -0
- package/src/services/document/blanklines/helpers/removeTrailingBlanks.ts +35 -0
- package/src/services/document/blanklines/helpers/tableGuards.ts +21 -0
- package/src/services/document/blanklines/index.ts +67 -0
- package/src/services/document/blanklines/rules/additionRules.ts +337 -0
- package/src/services/document/blanklines/rules/indentationRules.ts +317 -0
- package/src/services/document/blanklines/rules/removalRules.ts +362 -0
- package/src/services/document/blanklines/rules/ruleTypes.ts +92 -0
- package/src/services/document/blanklines/types.ts +29 -0
- package/src/services/document/helpers/ImageBorderCropper.ts +377 -0
- package/src/services/document/helpers/__tests__/whitespace.test.ts +272 -0
- package/src/services/document/helpers/whitespace.ts +117 -0
- package/src/services/document/list/ListNormalizer.ts +947 -0
- package/src/services/document/list/index.ts +45 -0
- package/src/services/document/list/list-detection.ts +275 -0
- package/src/services/document/list/list-types.ts +162 -0
- package/src/services/document/processors/HyperlinkProcessor.ts +370 -0
- package/src/services/document/processors/ListProcessor.ts +257 -0
- package/src/services/document/processors/StructureProcessor.ts +176 -0
- package/src/services/document/processors/StyleProcessor.ts +389 -0
- package/src/services/document/processors/TableProcessor.ts +2238 -0
- package/src/services/document/processors/__tests__/HyperlinkProcessor.test.ts +314 -0
- package/src/services/document/processors/__tests__/ListProcessor.test.ts +291 -0
- package/src/services/document/processors/__tests__/StructureProcessor.test.ts +257 -0
- package/src/services/document/processors/__tests__/TableProcessor.hlp-tips-bullets.test.ts +459 -0
- package/src/services/document/processors/__tests__/TableProcessor.test.ts +1604 -0
- package/src/services/document/processors/index.ts +28 -0
- package/src/services/document/types/docx-processing.ts +310 -0
- package/src/services/editor/EditorActionHandlers.ts +901 -0
- package/src/services/editor/index.ts +13 -0
- package/src/setupTests.ts +47 -0
- package/src/styles/global.css +782 -0
- package/src/types/backup.ts +132 -0
- package/src/types/dictionary.ts +125 -0
- package/src/types/document-processing.ts +331 -0
- package/src/types/docxmlater-augments.d.ts +142 -0
- package/src/types/editor.ts +280 -0
- package/src/types/electron.ts +340 -0
- package/src/types/globalStats.ts +155 -0
- package/src/types/hyperlink.ts +471 -0
- package/src/types/operations.ts +354 -0
- package/src/types/session.ts +427 -0
- package/src/types/settings.ts +112 -0
- package/src/utils/MemoryMonitor.ts +248 -0
- package/src/utils/cn.ts +6 -0
- package/src/utils/colorConvert.ts +306 -0
- package/src/utils/diffUtils.ts +347 -0
- package/src/utils/documentUtils.ts +202 -0
- package/src/utils/electronGuard.ts +62 -0
- package/src/utils/indexedDB.ts +915 -0
- package/src/utils/logger.ts +717 -0
- package/src/utils/pathSecurity.ts +232 -0
- package/src/utils/pathValidator.ts +236 -0
- package/src/utils/processingTimeEstimator.ts +153 -0
- package/src/utils/safeJsonParse.ts +62 -0
- package/src/utils/textSanitizer.ts +162 -0
- package/src/utils/urlHelpers.ts +304 -0
- package/src/utils/urlPatterns.ts +198 -0
- package/src/utils/urlSanitizer.ts +152 -0
- package/src/vite-env.d.ts +11 -0
- package/tsconfig.electron.json +19 -0
- package/tsconfig.json +36 -0
- package/tsconfig.node.json +12 -0
- package/typedoc.json +45 -0
- 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
|
+
}
|