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,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SplashScreen Component
|
|
3
|
+
* Displays an animated loading screen while the application initializes
|
|
4
|
+
* Provides visual feedback during context provider initialization
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { motion } from 'framer-motion';
|
|
8
|
+
import { Loader2 } from 'lucide-react';
|
|
9
|
+
|
|
10
|
+
interface SplashScreenProps {
|
|
11
|
+
message?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function SplashScreen({ message = 'Initializing Documentation Hub...' }: SplashScreenProps) {
|
|
15
|
+
return (
|
|
16
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-background">
|
|
17
|
+
<motion.div
|
|
18
|
+
initial={{ opacity: 0, scale: 0.9 }}
|
|
19
|
+
animate={{ opacity: 1, scale: 1 }}
|
|
20
|
+
transition={{ duration: 0.3 }}
|
|
21
|
+
className="flex flex-col items-center space-y-6"
|
|
22
|
+
>
|
|
23
|
+
{/* Logo/Brand */}
|
|
24
|
+
<motion.div
|
|
25
|
+
animate={{
|
|
26
|
+
scale: [1, 1.05, 1],
|
|
27
|
+
}}
|
|
28
|
+
transition={{
|
|
29
|
+
duration: 2,
|
|
30
|
+
repeat: Infinity,
|
|
31
|
+
ease: 'easeInOut',
|
|
32
|
+
}}
|
|
33
|
+
className="text-4xl font-bold text-primary"
|
|
34
|
+
>
|
|
35
|
+
Documentation Hub
|
|
36
|
+
</motion.div>
|
|
37
|
+
|
|
38
|
+
{/* Loading Spinner */}
|
|
39
|
+
<motion.div
|
|
40
|
+
animate={{ rotate: 360 }}
|
|
41
|
+
transition={{
|
|
42
|
+
duration: 1,
|
|
43
|
+
repeat: Infinity,
|
|
44
|
+
ease: 'linear',
|
|
45
|
+
}}
|
|
46
|
+
>
|
|
47
|
+
<Loader2 className="h-12 w-12 text-primary" />
|
|
48
|
+
</motion.div>
|
|
49
|
+
|
|
50
|
+
{/* Loading Message */}
|
|
51
|
+
<motion.p
|
|
52
|
+
initial={{ opacity: 0 }}
|
|
53
|
+
animate={{ opacity: 1 }}
|
|
54
|
+
transition={{ delay: 0.3 }}
|
|
55
|
+
className="text-sm text-muted-foreground"
|
|
56
|
+
>
|
|
57
|
+
{message}
|
|
58
|
+
</motion.p>
|
|
59
|
+
|
|
60
|
+
{/* Progress Dots */}
|
|
61
|
+
<div className="flex space-x-2">
|
|
62
|
+
{[0, 1, 2].map((i) => (
|
|
63
|
+
<motion.div
|
|
64
|
+
key={i}
|
|
65
|
+
animate={{
|
|
66
|
+
scale: [1, 1.3, 1],
|
|
67
|
+
opacity: [0.3, 1, 0.3],
|
|
68
|
+
}}
|
|
69
|
+
transition={{
|
|
70
|
+
duration: 1.5,
|
|
71
|
+
repeat: Infinity,
|
|
72
|
+
delay: i * 0.2,
|
|
73
|
+
}}
|
|
74
|
+
className="h-2 w-2 rounded-full bg-primary"
|
|
75
|
+
/>
|
|
76
|
+
))}
|
|
77
|
+
</div>
|
|
78
|
+
</motion.div>
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as ToastPrimitives from '@radix-ui/react-toast';
|
|
3
|
+
import { X, CheckCircle2, AlertCircle, Info } from 'lucide-react';
|
|
4
|
+
import { cn } from '@/utils/cn';
|
|
5
|
+
import { Toast as ToastType } from '@/hooks/useToast';
|
|
6
|
+
|
|
7
|
+
const ToastProvider = ToastPrimitives.Provider;
|
|
8
|
+
|
|
9
|
+
const ToastViewport = React.forwardRef<
|
|
10
|
+
React.ElementRef<typeof ToastPrimitives.Viewport>,
|
|
11
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
|
|
12
|
+
>(({ className, ...props }, ref) => (
|
|
13
|
+
<ToastPrimitives.Viewport
|
|
14
|
+
ref={ref}
|
|
15
|
+
className={cn(
|
|
16
|
+
'fixed bottom-0 right-0 z-[100] flex max-h-screen w-full flex-col gap-1 p-2 sm:max-w-72',
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
));
|
|
22
|
+
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
|
|
23
|
+
|
|
24
|
+
const Toast = React.forwardRef<
|
|
25
|
+
React.ElementRef<typeof ToastPrimitives.Root>,
|
|
26
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & {
|
|
27
|
+
variant?: 'default' | 'destructive' | 'success';
|
|
28
|
+
}
|
|
29
|
+
>(({ className, variant = 'default', ...props }, ref) => {
|
|
30
|
+
return (
|
|
31
|
+
<ToastPrimitives.Root
|
|
32
|
+
ref={ref}
|
|
33
|
+
className={cn(
|
|
34
|
+
'group pointer-events-auto relative flex w-full items-center gap-2 overflow-hidden rounded-md border py-2 px-3 shadow-sm transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-bottom-full',
|
|
35
|
+
variant === 'default' && 'border-border/50 bg-background/95 backdrop-blur-sm text-foreground',
|
|
36
|
+
variant === 'destructive' && 'border-red-200 bg-red-50/95 backdrop-blur-sm text-red-800 dark:border-red-800/50 dark:bg-red-950/95 dark:text-red-200',
|
|
37
|
+
variant === 'success' && 'border-green-200 bg-green-50/95 backdrop-blur-sm text-green-800 dark:border-green-800/50 dark:bg-green-950/95 dark:text-green-200',
|
|
38
|
+
className
|
|
39
|
+
)}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
Toast.displayName = ToastPrimitives.Root.displayName;
|
|
45
|
+
|
|
46
|
+
// Status icon component for variants
|
|
47
|
+
const ToastIcon = ({ variant }: { variant?: 'default' | 'destructive' | 'success' }) => {
|
|
48
|
+
if (variant === 'success') {
|
|
49
|
+
return <CheckCircle2 className="h-3.5 w-3.5 shrink-0 text-green-600 dark:text-green-400" />;
|
|
50
|
+
}
|
|
51
|
+
if (variant === 'destructive') {
|
|
52
|
+
return <AlertCircle className="h-3.5 w-3.5 shrink-0 text-red-600 dark:text-red-400" />;
|
|
53
|
+
}
|
|
54
|
+
return <Info className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const ToastAction = React.forwardRef<
|
|
58
|
+
React.ElementRef<typeof ToastPrimitives.Action>,
|
|
59
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
|
|
60
|
+
>(({ className, ...props }, ref) => (
|
|
61
|
+
<ToastPrimitives.Action
|
|
62
|
+
ref={ref}
|
|
63
|
+
className={cn(
|
|
64
|
+
'inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive',
|
|
65
|
+
className
|
|
66
|
+
)}
|
|
67
|
+
{...props}
|
|
68
|
+
/>
|
|
69
|
+
));
|
|
70
|
+
ToastAction.displayName = ToastPrimitives.Action.displayName;
|
|
71
|
+
|
|
72
|
+
const ToastClose = React.forwardRef<
|
|
73
|
+
React.ElementRef<typeof ToastPrimitives.Close>,
|
|
74
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
|
|
75
|
+
>(({ className, ...props }, ref) => (
|
|
76
|
+
<ToastPrimitives.Close
|
|
77
|
+
ref={ref}
|
|
78
|
+
className={cn(
|
|
79
|
+
'ml-auto shrink-0 rounded p-0.5 opacity-50 transition-opacity hover:opacity-100 focus:outline-none focus:ring-1 focus:ring-ring',
|
|
80
|
+
className
|
|
81
|
+
)}
|
|
82
|
+
toast-close=""
|
|
83
|
+
{...props}
|
|
84
|
+
>
|
|
85
|
+
<X className="h-3 w-3" />
|
|
86
|
+
</ToastPrimitives.Close>
|
|
87
|
+
));
|
|
88
|
+
ToastClose.displayName = ToastPrimitives.Close.displayName;
|
|
89
|
+
|
|
90
|
+
const ToastTitle = React.forwardRef<
|
|
91
|
+
React.ElementRef<typeof ToastPrimitives.Title>,
|
|
92
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
|
|
93
|
+
>(({ className, ...props }, ref) => (
|
|
94
|
+
<ToastPrimitives.Title ref={ref} className={cn('text-xs font-medium leading-tight', className)} {...props} />
|
|
95
|
+
));
|
|
96
|
+
ToastTitle.displayName = ToastPrimitives.Title.displayName;
|
|
97
|
+
|
|
98
|
+
const ToastDescription = React.forwardRef<
|
|
99
|
+
React.ElementRef<typeof ToastPrimitives.Description>,
|
|
100
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
|
|
101
|
+
>(({ className, ...props }, ref) => (
|
|
102
|
+
<ToastPrimitives.Description
|
|
103
|
+
ref={ref}
|
|
104
|
+
className={cn('text-[11px] opacity-75 leading-tight', className)}
|
|
105
|
+
{...props}
|
|
106
|
+
/>
|
|
107
|
+
));
|
|
108
|
+
ToastDescription.displayName = ToastPrimitives.Description.displayName;
|
|
109
|
+
|
|
110
|
+
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
|
|
111
|
+
|
|
112
|
+
type ToastActionElement = React.ReactElement<typeof ToastAction>;
|
|
113
|
+
|
|
114
|
+
export {
|
|
115
|
+
type ToastProps,
|
|
116
|
+
type ToastActionElement,
|
|
117
|
+
ToastProvider,
|
|
118
|
+
ToastViewport,
|
|
119
|
+
Toast,
|
|
120
|
+
ToastTitle,
|
|
121
|
+
ToastDescription,
|
|
122
|
+
ToastClose,
|
|
123
|
+
ToastAction,
|
|
124
|
+
ToastIcon,
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// Toaster component that renders all toasts
|
|
128
|
+
// Minimal design: small, unobtrusive notifications in bottom-right corner
|
|
129
|
+
interface ToasterProps {
|
|
130
|
+
toasts: ToastType[];
|
|
131
|
+
onDismiss: (id: string) => void;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function Toaster({ toasts, onDismiss }: ToasterProps) {
|
|
135
|
+
// Only show the most recent 3 toasts to avoid clutter
|
|
136
|
+
const visibleToasts = toasts.slice(-3);
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<ToastProvider swipeDirection="right">
|
|
140
|
+
{visibleToasts.map((toast) => (
|
|
141
|
+
<Toast key={toast.id} variant={toast.variant} duration={toast.duration}>
|
|
142
|
+
<ToastIcon variant={toast.variant} />
|
|
143
|
+
<div className="flex-1 min-w-0">
|
|
144
|
+
<ToastTitle className="truncate">{toast.title}</ToastTitle>
|
|
145
|
+
{toast.description && (
|
|
146
|
+
<ToastDescription className="line-clamp-2">{toast.description}</ToastDescription>
|
|
147
|
+
)}
|
|
148
|
+
</div>
|
|
149
|
+
<ToastClose onClick={() => onDismiss(toast.id)} />
|
|
150
|
+
</Toast>
|
|
151
|
+
))}
|
|
152
|
+
<ToastViewport />
|
|
153
|
+
</ToastProvider>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
2
|
+
import { cn } from '@/utils/cn';
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
|
|
5
|
+
const TooltipProvider = TooltipPrimitive.Provider;
|
|
6
|
+
|
|
7
|
+
const Tooltip = TooltipPrimitive.Root;
|
|
8
|
+
|
|
9
|
+
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
10
|
+
|
|
11
|
+
const TooltipContent = forwardRef<
|
|
12
|
+
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
|
14
|
+
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
15
|
+
<TooltipPrimitive.Portal>
|
|
16
|
+
<TooltipPrimitive.Content
|
|
17
|
+
ref={ref}
|
|
18
|
+
sideOffset={sideOffset}
|
|
19
|
+
className={cn(
|
|
20
|
+
'z-50 overflow-hidden rounded-md bg-popover px-3 py-1.5 text-xs text-popover-foreground shadow-md',
|
|
21
|
+
'animate-in fade-in-0 zoom-in-95',
|
|
22
|
+
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
|
|
23
|
+
'data-[side=bottom]:slide-in-from-top-2',
|
|
24
|
+
'data-[side=left]:slide-in-from-right-2',
|
|
25
|
+
'data-[side=right]:slide-in-from-left-2',
|
|
26
|
+
'data-[side=top]:slide-in-from-bottom-2',
|
|
27
|
+
'border border-border',
|
|
28
|
+
className
|
|
29
|
+
)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
</TooltipPrimitive.Portal>
|
|
33
|
+
));
|
|
34
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
35
|
+
|
|
36
|
+
// Convenience wrapper component for simple tooltips
|
|
37
|
+
interface SimpleTooltipProps {
|
|
38
|
+
content: React.ReactNode;
|
|
39
|
+
children: React.ReactNode;
|
|
40
|
+
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
41
|
+
align?: 'start' | 'center' | 'end';
|
|
42
|
+
delayDuration?: number;
|
|
43
|
+
className?: string;
|
|
44
|
+
shortcut?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function SimpleTooltip({
|
|
48
|
+
content,
|
|
49
|
+
children,
|
|
50
|
+
side = 'top',
|
|
51
|
+
align = 'center',
|
|
52
|
+
delayDuration = 300,
|
|
53
|
+
className,
|
|
54
|
+
shortcut,
|
|
55
|
+
}: SimpleTooltipProps) {
|
|
56
|
+
return (
|
|
57
|
+
<Tooltip delayDuration={delayDuration}>
|
|
58
|
+
<TooltipTrigger asChild>{children}</TooltipTrigger>
|
|
59
|
+
<TooltipContent side={side} align={align} className={className}>
|
|
60
|
+
<div className="flex items-center gap-2">
|
|
61
|
+
<span>{content}</span>
|
|
62
|
+
{shortcut && (
|
|
63
|
+
<kbd className="px-1.5 py-0.5 text-[10px] font-mono bg-muted rounded border border-border">
|
|
64
|
+
{shortcut}
|
|
65
|
+
</kbd>
|
|
66
|
+
)}
|
|
67
|
+
</div>
|
|
68
|
+
</TooltipContent>
|
|
69
|
+
</Tooltip>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export {
|
|
74
|
+
Tooltip,
|
|
75
|
+
TooltipTrigger,
|
|
76
|
+
TooltipContent,
|
|
77
|
+
TooltipProvider,
|
|
78
|
+
SimpleTooltip,
|
|
79
|
+
};
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { motion, AnimatePresence } from 'framer-motion';
|
|
3
|
+
import {
|
|
4
|
+
Download,
|
|
5
|
+
X,
|
|
6
|
+
FileArchive,
|
|
7
|
+
Info,
|
|
8
|
+
ExternalLink,
|
|
9
|
+
AlertTriangle,
|
|
10
|
+
AlertCircle,
|
|
11
|
+
} from 'lucide-react';
|
|
12
|
+
import { Button } from './Button';
|
|
13
|
+
|
|
14
|
+
export function UpdateNotification() {
|
|
15
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
16
|
+
const [updateInfo, setUpdateInfo] = useState<{ version: string; fallbackUsed?: boolean } | null>(
|
|
17
|
+
null
|
|
18
|
+
);
|
|
19
|
+
const [downloadProgress, setDownloadProgress] = useState(0);
|
|
20
|
+
const [isDownloading, setIsDownloading] = useState(false);
|
|
21
|
+
const [isDownloaded, setIsDownloaded] = useState(false);
|
|
22
|
+
const [isFallbackMode, setIsFallbackMode] = useState(false);
|
|
23
|
+
const [isExtracting, setIsExtracting] = useState(false);
|
|
24
|
+
const [statusMessage, setStatusMessage] = useState<string>('');
|
|
25
|
+
const [downloadError, setDownloadError] = useState(false);
|
|
26
|
+
const [errorCount, setErrorCount] = useState(0);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
// Safely check if electronAPI is available (may not be in browser-only mode)
|
|
30
|
+
if (typeof window.electronAPI === 'undefined') {
|
|
31
|
+
console.warn('UpdateNotification: electronAPI not available (running in browser mode?)');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Listen for update available
|
|
36
|
+
const unsubAvailable = window.electronAPI.onUpdateAvailable(
|
|
37
|
+
(info: { version: string; releaseDate: string; releaseNotes: string }) => {
|
|
38
|
+
setUpdateInfo(info);
|
|
39
|
+
setIsVisible(true);
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// Listen for download progress
|
|
44
|
+
const unsubProgress = window.electronAPI.onUpdateDownloadProgress(
|
|
45
|
+
(progress: {
|
|
46
|
+
bytesPerSecond: number;
|
|
47
|
+
percent: number;
|
|
48
|
+
transferred: number;
|
|
49
|
+
total: number;
|
|
50
|
+
}) => {
|
|
51
|
+
setDownloadProgress(progress.percent);
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Listen for update downloaded
|
|
56
|
+
const unsubDownloaded = window.electronAPI.onUpdateDownloaded(
|
|
57
|
+
(info: { version: string; releaseNotes: string; fallbackUsed?: boolean }) => {
|
|
58
|
+
setIsDownloading(false);
|
|
59
|
+
setIsExtracting(false);
|
|
60
|
+
setIsDownloaded(true);
|
|
61
|
+
setUpdateInfo({ ...info, fallbackUsed: info.fallbackUsed });
|
|
62
|
+
if (info.fallbackUsed) {
|
|
63
|
+
setStatusMessage('Update ready to install (downloaded as compressed archive)');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// Listen for errors
|
|
69
|
+
const unsubError = window.electronAPI.onUpdateError((error: { message: string }) => {
|
|
70
|
+
setIsDownloading(false);
|
|
71
|
+
setIsExtracting(false);
|
|
72
|
+
setErrorCount((prev) => prev + 1);
|
|
73
|
+
|
|
74
|
+
// Check for specific error types
|
|
75
|
+
const isCertError =
|
|
76
|
+
error.message?.toLowerCase().includes('certificate') ||
|
|
77
|
+
error.message?.toLowerCase().includes('issuer') ||
|
|
78
|
+
error.message?.toLowerCase().includes('unable to verify');
|
|
79
|
+
|
|
80
|
+
const isMutualTLS =
|
|
81
|
+
error.message?.toLowerCase().includes('econnreset') ||
|
|
82
|
+
error.message?.toLowerCase().includes('connection reset') ||
|
|
83
|
+
error.message?.toLowerCase().includes('mutual');
|
|
84
|
+
|
|
85
|
+
const isProxyError =
|
|
86
|
+
error.message?.toLowerCase().includes('proxy') ||
|
|
87
|
+
error.message?.toLowerCase().includes('firewall') ||
|
|
88
|
+
error.message?.toLowerCase().includes('localhost:8005');
|
|
89
|
+
|
|
90
|
+
// Show error state after multiple failures or if fallback also failed
|
|
91
|
+
if (error.message?.includes('Fallback download failed') || errorCount >= 2) {
|
|
92
|
+
setDownloadError(true);
|
|
93
|
+
|
|
94
|
+
if (isMutualTLS) {
|
|
95
|
+
setStatusMessage(
|
|
96
|
+
'Enterprise network detected (Mutual TLS required). Your network requires special certificates. Please use manual download or contact IT.'
|
|
97
|
+
);
|
|
98
|
+
} else if (isCertError) {
|
|
99
|
+
setStatusMessage(
|
|
100
|
+
'Certificate validation failed. Your organization may use custom certificates. Please download manually.'
|
|
101
|
+
);
|
|
102
|
+
} else if (isProxyError) {
|
|
103
|
+
setStatusMessage(
|
|
104
|
+
'Corporate proxy/firewall blocking download. Please use manual download or check with IT.'
|
|
105
|
+
);
|
|
106
|
+
} else {
|
|
107
|
+
setStatusMessage('Unable to download automatically. Please try manual download.');
|
|
108
|
+
}
|
|
109
|
+
} else if (isMutualTLS) {
|
|
110
|
+
setStatusMessage('Enterprise network detected. Trying PowerShell download method...');
|
|
111
|
+
} else if (isCertError) {
|
|
112
|
+
setStatusMessage('Certificate issue detected. Attempting alternative download method...');
|
|
113
|
+
} else {
|
|
114
|
+
setStatusMessage(`Connection issue: Trying alternative method...`);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Listen for fallback mode activation
|
|
119
|
+
const unsubFallback = window.electronAPI.onUpdateFallbackMode?.((data: { message: string }) => {
|
|
120
|
+
setIsFallbackMode(true);
|
|
121
|
+
setStatusMessage(data.message || 'Using alternative download method...');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Listen for extraction status
|
|
125
|
+
const unsubExtracting = window.electronAPI.onUpdateExtracting?.((data: { message: string }) => {
|
|
126
|
+
setIsExtracting(true);
|
|
127
|
+
setStatusMessage(data.message || 'Extracting update...');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Listen for general status updates
|
|
131
|
+
const unsubStatus = window.electronAPI.onUpdateStatus?.((data: { message: string }) => {
|
|
132
|
+
setStatusMessage(data.message || '');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return () => {
|
|
136
|
+
unsubAvailable();
|
|
137
|
+
unsubProgress();
|
|
138
|
+
unsubDownloaded();
|
|
139
|
+
unsubError();
|
|
140
|
+
unsubFallback?.();
|
|
141
|
+
unsubExtracting?.();
|
|
142
|
+
unsubStatus?.();
|
|
143
|
+
};
|
|
144
|
+
}, [errorCount]); // Add errorCount to dependencies to track error state
|
|
145
|
+
|
|
146
|
+
const handleDownload = async () => {
|
|
147
|
+
setIsDownloading(true);
|
|
148
|
+
setDownloadError(false); // Reset error state when retrying
|
|
149
|
+
setErrorCount(0); // Reset error count
|
|
150
|
+
try {
|
|
151
|
+
await window.electronAPI?.downloadUpdate();
|
|
152
|
+
} catch (error) {
|
|
153
|
+
setIsDownloading(false);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const handleInstall = () => {
|
|
158
|
+
window.electronAPI?.installUpdate();
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const handleDismiss = () => {
|
|
162
|
+
setIsVisible(false);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const handleManualDownload = async () => {
|
|
166
|
+
try {
|
|
167
|
+
// Use the new IPC handler to open download in system browser
|
|
168
|
+
await window.electronAPI?.openUpdateInBrowser();
|
|
169
|
+
} catch (error) {
|
|
170
|
+
// Fallback to direct window.open if IPC fails
|
|
171
|
+
const releaseUrl = updateInfo?.version
|
|
172
|
+
? `https://github.com/ItMeDiaTech/Documentation_Hub/releases/tag/v${updateInfo.version}`
|
|
173
|
+
: 'https://github.com/ItMeDiaTech/Documentation_Hub/releases/latest';
|
|
174
|
+
window.open(releaseUrl, '_blank');
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<AnimatePresence>
|
|
180
|
+
{isVisible && (
|
|
181
|
+
<motion.div
|
|
182
|
+
initial={{ opacity: 0, y: 50 }}
|
|
183
|
+
animate={{ opacity: 1, y: 0 }}
|
|
184
|
+
exit={{ opacity: 0, y: 50 }}
|
|
185
|
+
className="fixed bottom-4 right-4 z-50"
|
|
186
|
+
>
|
|
187
|
+
<div className="bg-background border border-border rounded-lg shadow-lg p-4 w-96">
|
|
188
|
+
<div className="flex items-start justify-between mb-3">
|
|
189
|
+
<div className="flex items-center gap-2">
|
|
190
|
+
{isFallbackMode ? (
|
|
191
|
+
<FileArchive className="w-5 h-5 text-primary" />
|
|
192
|
+
) : (
|
|
193
|
+
<AlertCircle className="w-5 h-5 text-primary" />
|
|
194
|
+
)}
|
|
195
|
+
<h3 className="font-semibold">Update Available</h3>
|
|
196
|
+
</div>
|
|
197
|
+
<button
|
|
198
|
+
onClick={handleDismiss}
|
|
199
|
+
className="text-muted-foreground hover:text-foreground transition-colors"
|
|
200
|
+
>
|
|
201
|
+
<X className="w-4 h-4" />
|
|
202
|
+
</button>
|
|
203
|
+
</div>
|
|
204
|
+
|
|
205
|
+
{updateInfo && (
|
|
206
|
+
<p className="text-sm text-muted-foreground mb-4">
|
|
207
|
+
Version {updateInfo.version} is now available
|
|
208
|
+
</p>
|
|
209
|
+
)}
|
|
210
|
+
|
|
211
|
+
{statusMessage && (
|
|
212
|
+
<div className="flex items-center gap-2 mb-3">
|
|
213
|
+
<Info className="w-4 h-4 text-blue-500" />
|
|
214
|
+
<p className="text-xs text-muted-foreground">{statusMessage}</p>
|
|
215
|
+
</div>
|
|
216
|
+
)}
|
|
217
|
+
|
|
218
|
+
{!isDownloading && !isDownloaded && (
|
|
219
|
+
<div className="flex gap-2">
|
|
220
|
+
<Button
|
|
221
|
+
onClick={handleDownload}
|
|
222
|
+
variant="default"
|
|
223
|
+
size="sm"
|
|
224
|
+
className="flex-1"
|
|
225
|
+
icon={<Download className="w-4 h-4" />}
|
|
226
|
+
>
|
|
227
|
+
Download
|
|
228
|
+
</Button>
|
|
229
|
+
<Button onClick={handleDismiss} variant="outline" size="sm">
|
|
230
|
+
Later
|
|
231
|
+
</Button>
|
|
232
|
+
</div>
|
|
233
|
+
)}
|
|
234
|
+
|
|
235
|
+
{isDownloading && (
|
|
236
|
+
<div className="space-y-2">
|
|
237
|
+
<div className="flex items-center justify-between text-sm">
|
|
238
|
+
<span>
|
|
239
|
+
{isExtracting
|
|
240
|
+
? 'Extracting...'
|
|
241
|
+
: isFallbackMode
|
|
242
|
+
? 'Downloading compressed update...'
|
|
243
|
+
: 'Downloading...'}
|
|
244
|
+
</span>
|
|
245
|
+
{!isExtracting && (
|
|
246
|
+
<span className="text-muted-foreground">{Math.round(downloadProgress)}%</span>
|
|
247
|
+
)}
|
|
248
|
+
</div>
|
|
249
|
+
<div className="w-full bg-muted rounded-full h-2 overflow-hidden">
|
|
250
|
+
<div
|
|
251
|
+
className={`h-full ${isExtracting ? 'bg-blue-500' : 'bg-primary'} transition-all duration-300`}
|
|
252
|
+
style={{ width: isExtracting ? '100%' : `${downloadProgress}%` }}
|
|
253
|
+
/>
|
|
254
|
+
</div>
|
|
255
|
+
{isFallbackMode && !isExtracting && (
|
|
256
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
257
|
+
Using compressed download to bypass network restrictions
|
|
258
|
+
</p>
|
|
259
|
+
)}
|
|
260
|
+
</div>
|
|
261
|
+
)}
|
|
262
|
+
|
|
263
|
+
{isDownloaded && (
|
|
264
|
+
<div className="flex gap-2">
|
|
265
|
+
<Button
|
|
266
|
+
onClick={handleInstall}
|
|
267
|
+
variant="default"
|
|
268
|
+
size="sm"
|
|
269
|
+
className="flex-1 bg-green-600 hover:bg-green-700"
|
|
270
|
+
>
|
|
271
|
+
Install & Restart
|
|
272
|
+
</Button>
|
|
273
|
+
<Button onClick={handleDismiss} variant="outline" size="sm">
|
|
274
|
+
Later
|
|
275
|
+
</Button>
|
|
276
|
+
</div>
|
|
277
|
+
)}
|
|
278
|
+
|
|
279
|
+
{downloadError && !isDownloading && !isDownloaded && (
|
|
280
|
+
<div className="space-y-3">
|
|
281
|
+
<div className="flex items-center gap-2 text-yellow-600 dark:text-yellow-400">
|
|
282
|
+
<AlertTriangle className="w-4 h-4" />
|
|
283
|
+
<p className="text-xs font-medium">Network Security Issue</p>
|
|
284
|
+
</div>
|
|
285
|
+
{statusMessage && <p className="text-xs text-muted-foreground">{statusMessage}</p>}
|
|
286
|
+
<div className="flex gap-2">
|
|
287
|
+
<Button
|
|
288
|
+
onClick={handleManualDownload}
|
|
289
|
+
variant="default"
|
|
290
|
+
size="sm"
|
|
291
|
+
className="flex-1"
|
|
292
|
+
icon={<ExternalLink className="w-4 h-4" />}
|
|
293
|
+
>
|
|
294
|
+
Download Manually
|
|
295
|
+
</Button>
|
|
296
|
+
<Button
|
|
297
|
+
onClick={handleDownload}
|
|
298
|
+
variant="outline"
|
|
299
|
+
size="sm"
|
|
300
|
+
icon={<Download className="w-4 h-4" />}
|
|
301
|
+
>
|
|
302
|
+
Retry
|
|
303
|
+
</Button>
|
|
304
|
+
</div>
|
|
305
|
+
<div className="bg-muted/50 rounded p-2 text-xs space-y-1">
|
|
306
|
+
<p className="font-medium">Troubleshooting Tips:</p>
|
|
307
|
+
<ul className="list-disc list-inside text-muted-foreground space-y-0.5">
|
|
308
|
+
<li>Disconnect from VPN if connected</li>
|
|
309
|
+
<li>Check with IT about proxy settings</li>
|
|
310
|
+
<li>Use manual download for immediate access</li>
|
|
311
|
+
</ul>
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
314
|
+
)}
|
|
315
|
+
</div>
|
|
316
|
+
</motion.div>
|
|
317
|
+
)}
|
|
318
|
+
</AnimatePresence>
|
|
319
|
+
);
|
|
320
|
+
}
|