mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 19:33:11 +02:00
Add prompt to make Stirling your default PDF app (#4890)
# Description of Changes - Adds a reusable banner component/system to the core app - Adds banner at the top of the desktop app if Stirling isn't your default PDF editor, with a button to make it your default - Adds a permanent button in the settings to do it manually (in case you've dismissed the banner) - Simplifies the file loading logic to fix a bug where the input file could be duplicated occasionally. Now, the TS just receives files from one buffer, regardless of how they've been passed to the app in Rust. ## Caveats I've only been able to get the setting of default apps working properly on Mac. The Windows build isn't signed (yet) so we can't use the proper API for it, so currently it just sends you to the Settings UI. I've also not been able to test it on Linux at all.
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { useBanner } from '@app/contexts/BannerContext';
|
||||
|
||||
interface AppLayoutProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* App layout wrapper that handles banner rendering and viewport sizing
|
||||
* Automatically adjusts child components to fit remaining space after banner
|
||||
*/
|
||||
export function AppLayout({ children }: AppLayoutProps) {
|
||||
const { banner } = useBanner();
|
||||
|
||||
return (
|
||||
<>
|
||||
<style>{`
|
||||
.h-screen,
|
||||
.right-rail {
|
||||
height: 100% !important;
|
||||
}
|
||||
`}</style>
|
||||
<div style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
|
||||
{banner}
|
||||
<div style={{ flex: 1, minHeight: 0, height: 0 }}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import { OnboardingProvider } from "@app/contexts/OnboardingContext";
|
||||
import { TourOrchestrationProvider } from "@app/contexts/TourOrchestrationContext";
|
||||
import { AdminTourOrchestrationProvider } from "@app/contexts/AdminTourOrchestrationContext";
|
||||
import { PageEditorProvider } from "@app/contexts/PageEditorContext";
|
||||
import { BannerProvider } from "@app/contexts/BannerContext";
|
||||
import ErrorBoundary from "@app/components/shared/ErrorBoundary";
|
||||
import { useScarfTracking } from "@app/hooks/useScarfTracking";
|
||||
import { useAppInitialization } from "@app/hooks/useAppInitialization";
|
||||
@@ -50,22 +51,23 @@ export function AppProviders({ children, appConfigRetryOptions, appConfigProvide
|
||||
<PreferencesProvider>
|
||||
<RainbowThemeProvider>
|
||||
<ErrorBoundary>
|
||||
<OnboardingProvider>
|
||||
<AppConfigProvider
|
||||
retryOptions={appConfigRetryOptions}
|
||||
{...appConfigProviderProps}
|
||||
>
|
||||
<ScarfTrackingInitializer />
|
||||
<FileContextProvider enableUrlSync={true} enablePersistence={true}>
|
||||
<AppInitializer />
|
||||
<ToolRegistryProvider>
|
||||
<NavigationProvider>
|
||||
<FilesModalProvider>
|
||||
<ToolWorkflowProvider>
|
||||
<HotkeyProvider>
|
||||
<SidebarProvider>
|
||||
<ViewerProvider>
|
||||
<PageEditorProvider>
|
||||
<BannerProvider>
|
||||
<OnboardingProvider>
|
||||
<AppConfigProvider
|
||||
retryOptions={appConfigRetryOptions}
|
||||
{...appConfigProviderProps}
|
||||
>
|
||||
<ScarfTrackingInitializer />
|
||||
<FileContextProvider enableUrlSync={true} enablePersistence={true}>
|
||||
<AppInitializer />
|
||||
<ToolRegistryProvider>
|
||||
<NavigationProvider>
|
||||
<FilesModalProvider>
|
||||
<ToolWorkflowProvider>
|
||||
<HotkeyProvider>
|
||||
<SidebarProvider>
|
||||
<ViewerProvider>
|
||||
<PageEditorProvider>
|
||||
<SignatureProvider>
|
||||
<RightRailProvider>
|
||||
<TourOrchestrationProvider>
|
||||
@@ -76,16 +78,17 @@ export function AppProviders({ children, appConfigRetryOptions, appConfigProvide
|
||||
</RightRailProvider>
|
||||
</SignatureProvider>
|
||||
</PageEditorProvider>
|
||||
</ViewerProvider>
|
||||
</SidebarProvider>
|
||||
</HotkeyProvider>
|
||||
</ToolWorkflowProvider>
|
||||
</FilesModalProvider>
|
||||
</NavigationProvider>
|
||||
</ToolRegistryProvider>
|
||||
</FileContextProvider>
|
||||
</AppConfigProvider>
|
||||
</OnboardingProvider>
|
||||
</ViewerProvider>
|
||||
</SidebarProvider>
|
||||
</HotkeyProvider>
|
||||
</ToolWorkflowProvider>
|
||||
</FilesModalProvider>
|
||||
</NavigationProvider>
|
||||
</ToolRegistryProvider>
|
||||
</FileContextProvider>
|
||||
</AppConfigProvider>
|
||||
</OnboardingProvider>
|
||||
</BannerProvider>
|
||||
</ErrorBoundary>
|
||||
</RainbowThemeProvider>
|
||||
</PreferencesProvider>
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import React from 'react';
|
||||
import { Paper, Group, Text, Button, ActionIcon } from '@mantine/core';
|
||||
import LocalIcon from '@app/components/shared/LocalIcon';
|
||||
|
||||
interface InfoBannerProps {
|
||||
icon: string;
|
||||
message: string;
|
||||
buttonText: string;
|
||||
buttonIcon?: string;
|
||||
onButtonClick: () => void;
|
||||
onDismiss: () => void;
|
||||
loading?: boolean;
|
||||
show?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic info banner component for displaying dismissible messages at the top of the app
|
||||
*/
|
||||
export const InfoBanner: React.FC<InfoBannerProps> = ({
|
||||
icon,
|
||||
message,
|
||||
buttonText,
|
||||
buttonIcon = 'check-circle-rounded',
|
||||
onButtonClick,
|
||||
onDismiss,
|
||||
loading = false,
|
||||
show = true,
|
||||
}) => {
|
||||
if (!show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper
|
||||
p="sm"
|
||||
radius={0}
|
||||
style={{
|
||||
background: 'var(--mantine-color-blue-0)',
|
||||
borderBottom: '1px solid var(--mantine-color-blue-2)',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
<Group gap="sm" align="center" wrap="nowrap">
|
||||
<LocalIcon icon={icon} width="1.2rem" height="1.2rem" style={{ color: 'var(--mantine-color-blue-6)', flexShrink: 0 }} />
|
||||
<Text fw={500} size="sm" style={{ color: 'var(--mantine-color-blue-9)' }}>
|
||||
{message}
|
||||
</Text>
|
||||
<Button
|
||||
variant="light"
|
||||
color="blue"
|
||||
size="xs"
|
||||
onClick={onButtonClick}
|
||||
loading={loading}
|
||||
leftSection={<LocalIcon icon={buttonIcon} width="0.9rem" height="0.9rem" />}
|
||||
style={{ flexShrink: 0 }}
|
||||
>
|
||||
{buttonText}
|
||||
</Button>
|
||||
</Group>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
color="gray"
|
||||
size="sm"
|
||||
onClick={onDismiss}
|
||||
aria-label="Dismiss"
|
||||
style={{ position: 'absolute', top: '50%', right: '0.5rem', transform: 'translateY(-50%)' }}
|
||||
>
|
||||
<LocalIcon icon="close-rounded" width="1rem" height="1rem" />
|
||||
</ActionIcon>
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user