mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-05-01 23:16:31 +02:00
Chore/remove usage of mantine color scheme (#6108)
Remove instances of `colorScheme === "dark" ?` in the app and rely on the theme.css' light and dark variables instead.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
dist/
|
||||
# Tauri/Cargo build output (binary assets named *.js etc. confuse Prettier)
|
||||
src-tauri/target/
|
||||
node_modules/
|
||||
public/vendor/
|
||||
public/pdfjs*/
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import { Button, Group, useMantineColorScheme } from "@mantine/core";
|
||||
import { Button, Group } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
import { useFilesModalContext } from "@app/contexts/FilesModalContext";
|
||||
import LocalIcon from "@app/components/shared/LocalIcon";
|
||||
import { useLogoAssets } from "@app/hooks/useLogoAssets";
|
||||
import { Wordmark } from "@app/components/shared/Wordmark";
|
||||
import styles from "@app/components/fileEditor/FileEditor.module.css";
|
||||
import { useFileActionTerminology } from "@app/hooks/useFileActionTerminology";
|
||||
import { useFileActionIcons } from "@app/hooks/useFileActionIcons";
|
||||
@@ -24,9 +24,7 @@ const AddFileCard = ({
|
||||
const { t } = useTranslation();
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const { openFilesModal } = useFilesModalContext();
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const [isUploadHover, setIsUploadHover] = useState(false);
|
||||
const { wordmark } = useLogoAssets();
|
||||
const terminology = useFileActionTerminology();
|
||||
const icons = useFileActionIcons();
|
||||
|
||||
@@ -98,9 +96,9 @@ const AddFileCard = ({
|
||||
<div className={styles.addFileContent}>
|
||||
{/* Stirling PDF Branding */}
|
||||
<Group gap="xs" align="center">
|
||||
<img
|
||||
src={colorScheme === "dark" ? wordmark.white : wordmark.grey}
|
||||
<Wordmark
|
||||
alt="Stirling PDF"
|
||||
muted
|
||||
style={{ height: "2.2rem", width: "auto" }}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
Button,
|
||||
Group,
|
||||
Text,
|
||||
Stack,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import { Button, Group, Text, Stack } from "@mantine/core";
|
||||
import HistoryIcon from "@mui/icons-material/History";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useFileManagerContext } from "@app/contexts/FileManagerContext";
|
||||
import LocalIcon from "@app/components/shared/LocalIcon";
|
||||
import { useLogoAssets } from "@app/hooks/useLogoAssets";
|
||||
import { Wordmark } from "@app/components/shared/Wordmark";
|
||||
import { useFileActionTerminology } from "@app/hooks/useFileActionTerminology";
|
||||
import { useFileActionIcons } from "@app/hooks/useFileActionIcons";
|
||||
|
||||
const EmptyFilesState: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const { onLocalFileClick } = useFileManagerContext();
|
||||
const [isUploadHover, setIsUploadHover] = useState(false);
|
||||
const { wordmark } = useLogoAssets();
|
||||
const terminology = useFileActionTerminology();
|
||||
const icons = useFileActionIcons();
|
||||
|
||||
@@ -65,9 +57,9 @@ const EmptyFilesState: React.FC = () => {
|
||||
|
||||
{/* Stirling PDF Logo */}
|
||||
<Group gap="xs" align="center">
|
||||
<img
|
||||
src={colorScheme === "dark" ? wordmark.white : wordmark.grey}
|
||||
<Wordmark
|
||||
alt="Stirling PDF"
|
||||
muted
|
||||
style={{ height: "2.2rem", width: "auto" }}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
14
frontend/src/core/components/shared/LogoIcon.tsx
Normal file
14
frontend/src/core/components/shared/LogoIcon.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import React from "react";
|
||||
import { useMantineColorScheme } from "@mantine/core";
|
||||
import { useLogoPath } from "@app/hooks/useLogoPath";
|
||||
|
||||
interface LogoIconProps extends React.ImgHTMLAttributes<HTMLImageElement> {
|
||||
alt?: string;
|
||||
}
|
||||
|
||||
export function LogoIcon({ alt = "", ...props }: LogoIconProps) {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const logoPaths = useLogoPath();
|
||||
const src = colorScheme === "dark" ? logoPaths.dark : logoPaths.light;
|
||||
return <img src={src} alt={alt} {...props} />;
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import { useMantineColorScheme } from "@mantine/core";
|
||||
import LocalIcon from "@app/components/shared/LocalIcon";
|
||||
import styles from "@app/components/shared/textInput/TextInput.module.css";
|
||||
|
||||
@@ -61,8 +60,6 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
|
||||
const handleClear = () => {
|
||||
if (onClear) {
|
||||
onClear();
|
||||
@@ -79,7 +76,7 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
{icon && (
|
||||
<span
|
||||
className={styles.icon}
|
||||
style={{ color: colorScheme === "dark" ? "#FFFFFF" : "#6B7382" }}
|
||||
style={{ color: "var(--search-text-and-icon-color)" }}
|
||||
>
|
||||
{icon}
|
||||
</span>
|
||||
@@ -99,8 +96,8 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
aria-label={ariaLabel}
|
||||
onFocus={onFocus}
|
||||
style={{
|
||||
backgroundColor: colorScheme === "dark" ? "#4B525A" : "#FFFFFF",
|
||||
color: colorScheme === "dark" ? "#FFFFFF" : "#6B7382",
|
||||
backgroundColor: "var(--input-bg)",
|
||||
color: "var(--search-text-and-icon-color)",
|
||||
paddingRight: shouldShowClearButton ? "40px" : "12px",
|
||||
paddingLeft: icon ? "40px" : "12px",
|
||||
}}
|
||||
@@ -111,7 +108,7 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
type="button"
|
||||
className={styles.clearButton}
|
||||
onClick={handleClear}
|
||||
style={{ color: colorScheme === "dark" ? "#FFFFFF" : "#6B7382" }}
|
||||
style={{ color: "var(--search-text-and-icon-color)" }}
|
||||
aria-label="Clear input"
|
||||
>
|
||||
<LocalIcon icon="close-rounded" width="1.25rem" height="1.25rem" />
|
||||
|
||||
20
frontend/src/core/components/shared/Wordmark.tsx
Normal file
20
frontend/src/core/components/shared/Wordmark.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from "react";
|
||||
import { useMantineColorScheme } from "@mantine/core";
|
||||
import { useLogoAssets } from "@app/hooks/useLogoAssets";
|
||||
|
||||
interface WordmarkProps extends React.ImgHTMLAttributes<HTMLImageElement> {
|
||||
alt?: string;
|
||||
muted?: boolean;
|
||||
}
|
||||
|
||||
export function Wordmark({ alt = "", muted = false, ...props }: WordmarkProps) {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const isDark = colorScheme === "dark";
|
||||
const { wordmark } = useLogoAssets();
|
||||
|
||||
// light: black text (standard) or grey text (muted)
|
||||
// dark: white text for both variants
|
||||
const src = isDark ? wordmark.white : muted ? wordmark.grey : wordmark.black;
|
||||
|
||||
return <img src={src} alt={alt} {...props} />;
|
||||
}
|
||||
@@ -1,10 +1,5 @@
|
||||
import { useState, useRef } from "react";
|
||||
import {
|
||||
ActionIcon,
|
||||
ScrollArea,
|
||||
Switch,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import { ActionIcon, ScrollArea, Switch } from "@mantine/core";
|
||||
import DoubleArrowIcon from "@mui/icons-material/DoubleArrow";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ToolSearch from "@app/components/tools/toolPicker/ToolSearch";
|
||||
@@ -12,8 +7,8 @@ import FullscreenToolList from "@app/components/tools/FullscreenToolList";
|
||||
import { ToolRegistryEntry } from "@app/data/toolsTaxonomy";
|
||||
import { ToolId } from "@app/types/toolId";
|
||||
import { useFocusTrap } from "@app/hooks/useFocusTrap";
|
||||
import { useLogoPath } from "@app/hooks/useLogoPath";
|
||||
import { useLogoAssets } from "@app/hooks/useLogoAssets";
|
||||
import { LogoIcon } from "@app/components/shared/LogoIcon";
|
||||
import { Wordmark } from "@app/components/shared/Wordmark";
|
||||
import { Tooltip } from "@app/components/shared/Tooltip";
|
||||
import "@app/components/tools/ToolPanel.css";
|
||||
import { ToolPanelGeometry } from "@app/hooks/tools/useToolPanelGeometry";
|
||||
@@ -52,7 +47,6 @@ const FullscreenToolSurface = ({
|
||||
geometry,
|
||||
}: FullscreenToolSurfaceProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const [isExiting, setIsExiting] = useState(false);
|
||||
const surfaceRef = useRef<HTMLDivElement>(null);
|
||||
const isRTL =
|
||||
@@ -62,9 +56,6 @@ const FullscreenToolSurface = ({
|
||||
useFocusTrap(surfaceRef, !isExiting);
|
||||
|
||||
const brandAltText = t("home.mobile.brandAlt", "Stirling PDF logo");
|
||||
const brandIconSrc = useLogoPath();
|
||||
const { wordmark } = useLogoAssets();
|
||||
const brandTextSrc = colorScheme === "dark" ? wordmark.white : wordmark.black;
|
||||
|
||||
const handleExit = () => {
|
||||
const prefersReducedMotion = window.matchMedia(
|
||||
@@ -118,13 +109,8 @@ const FullscreenToolSurface = ({
|
||||
>
|
||||
<header className="tool-panel__fullscreen-header">
|
||||
<div className="tool-panel__fullscreen-brand">
|
||||
<img
|
||||
src={brandIconSrc}
|
||||
alt=""
|
||||
className="tool-panel__fullscreen-brand-icon"
|
||||
/>
|
||||
<img
|
||||
src={brandTextSrc}
|
||||
<LogoIcon className="tool-panel__fullscreen-brand-icon" />
|
||||
<Wordmark
|
||||
alt={brandAltText}
|
||||
className="tool-panel__fullscreen-brand-text"
|
||||
/>
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
Divider,
|
||||
UnstyledButton,
|
||||
useMantineTheme,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -64,7 +63,6 @@ const ConvertSettings = ({
|
||||
}: ConvertSettingsProps) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useMantineTheme();
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const { setSelectedFiles } = useFileSelection();
|
||||
const { state, selectors } = useFileState();
|
||||
const activeFiles = state.files.ids;
|
||||
@@ -339,14 +337,8 @@ const ConvertSettings = ({
|
||||
padding: "0.5rem 0.75rem",
|
||||
border: `0.0625rem solid ${theme.colors.gray[4]}`,
|
||||
borderRadius: theme.radius.sm,
|
||||
backgroundColor:
|
||||
colorScheme === "dark"
|
||||
? theme.colors.dark[5]
|
||||
: theme.colors.gray[1],
|
||||
color:
|
||||
colorScheme === "dark"
|
||||
? theme.colors.dark[2]
|
||||
: theme.colors.gray[6],
|
||||
backgroundColor: "var(--select-placeholder-bg)",
|
||||
color: "var(--select-placeholder-text)",
|
||||
cursor: "not-allowed",
|
||||
}}
|
||||
>
|
||||
@@ -360,10 +352,7 @@ const ConvertSettings = ({
|
||||
<KeyboardArrowDownIcon
|
||||
style={{
|
||||
fontSize: "1rem",
|
||||
color:
|
||||
colorScheme === "dark"
|
||||
? theme.colors.dark[2]
|
||||
: theme.colors.gray[6],
|
||||
color: "var(--select-placeholder-text)",
|
||||
}}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
Popover,
|
||||
UnstyledButton,
|
||||
useMantineTheme,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
||||
import CloudOutlinedIcon from "@mui/icons-material/CloudOutlined";
|
||||
@@ -47,7 +46,6 @@ const GroupedFormatDropdown = ({
|
||||
}: GroupedFormatDropdownProps) => {
|
||||
const [dropdownOpened, setDropdownOpened] = useState(false);
|
||||
const theme = useMantineTheme();
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
|
||||
const groupedOptions = useMemo(() => {
|
||||
const groups: Record<string, FormatOption[]> = {};
|
||||
@@ -100,18 +98,12 @@ const GroupedFormatDropdown = ({
|
||||
borderRadius: theme.radius.sm,
|
||||
backgroundColor: disabled
|
||||
? theme.colors.gray[1]
|
||||
: colorScheme === "dark"
|
||||
? theme.colors.dark[6]
|
||||
: theme.white,
|
||||
: "var(--dropdown-trigger-bg)",
|
||||
cursor: disabled ? "not-allowed" : "pointer",
|
||||
width: "100%",
|
||||
color: disabled
|
||||
? colorScheme === "dark"
|
||||
? theme.colors.dark[1]
|
||||
: theme.colors.dark[7]
|
||||
: colorScheme === "dark"
|
||||
? theme.colors.dark[0]
|
||||
: theme.colors.dark[9],
|
||||
? "var(--dropdown-trigger-text-disabled)"
|
||||
: "var(--dropdown-trigger-text)",
|
||||
}}
|
||||
>
|
||||
<Group justify="space-between">
|
||||
@@ -123,10 +115,7 @@ const GroupedFormatDropdown = ({
|
||||
fontSize: "1rem",
|
||||
transform: dropdownOpened ? "rotate(180deg)" : "rotate(0deg)",
|
||||
transition: "transform 0.2s ease",
|
||||
color:
|
||||
colorScheme === "dark"
|
||||
? theme.colors.dark[2]
|
||||
: theme.colors.gray[6],
|
||||
color: "var(--dropdown-trigger-icon)",
|
||||
}}
|
||||
/>
|
||||
</Group>
|
||||
@@ -138,9 +127,8 @@ const GroupedFormatDropdown = ({
|
||||
maxWidth: "90vw",
|
||||
maxHeight: "40vh",
|
||||
overflow: "auto",
|
||||
backgroundColor:
|
||||
colorScheme === "dark" ? theme.colors.dark[7] : theme.white,
|
||||
border: `0.0625rem solid ${colorScheme === "dark" ? theme.colors.dark[4] : theme.colors.gray[4]}`,
|
||||
backgroundColor: "var(--dropdown-panel-bg)",
|
||||
border: `0.0625rem solid var(--dropdown-panel-border)`,
|
||||
}}
|
||||
>
|
||||
<Stack gap="md">
|
||||
@@ -149,8 +137,8 @@ const GroupedFormatDropdown = ({
|
||||
<Text
|
||||
size="sm"
|
||||
fw={600}
|
||||
c={colorScheme === "dark" ? "dark.2" : "gray.6"}
|
||||
mb="xs"
|
||||
style={{ color: "var(--dropdown-group-label)" }}
|
||||
>
|
||||
{groupName}
|
||||
</Text>
|
||||
|
||||
@@ -15,16 +15,16 @@ export function useLogoAssets() {
|
||||
folder,
|
||||
folderPath,
|
||||
getAssetPath: (name: string) => `${folderPath}/${name}`,
|
||||
wordmark: {
|
||||
black: `${folderPath}/StirlingPDFLogoBlackText.svg`,
|
||||
grey: `${folderPath}/StirlingPDFLogoGreyText.svg`,
|
||||
white: `${folderPath}/StirlingPDFLogoWhiteText.svg`,
|
||||
},
|
||||
tooltipLogo: `${folderPath}/logo-tooltip.svg`,
|
||||
firstPage: `${folderPath}/Firstpage.png`,
|
||||
favicon: `${folderPath}/favicon.ico`,
|
||||
logo192: `${folderPath}/logo192.png`,
|
||||
logo512: `${folderPath}/logo512.png`,
|
||||
wordmark: {
|
||||
white: `${folderPath}/StirlingPDFLogoWhiteText.svg`,
|
||||
black: `${folderPath}/StirlingPDFLogoBlackText.svg`,
|
||||
grey: `${folderPath}/StirlingPDFLogoGreyText.svg`,
|
||||
},
|
||||
manifestHref:
|
||||
logoVariant === "classic"
|
||||
? `${BASE_PATH}/manifest-classic.json`
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
import { useMemo } from "react";
|
||||
import { useMantineColorScheme } from "@mantine/core";
|
||||
import { useLogoAssets } from "@app/hooks/useLogoAssets";
|
||||
|
||||
/**
|
||||
* Hook to get the correct logo path based on app config (logo style) and theme (light/dark)
|
||||
*
|
||||
* Logo styles:
|
||||
* - classic: classic S logo stored in /classic-logo
|
||||
* - modern: minimalist logo stored in /modern-logo
|
||||
*
|
||||
* @returns The path to the appropriate logo SVG file
|
||||
*/
|
||||
export function useLogoPath(): string {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
/** Theme-specific no-text logo SVG URLs under the active variant folder (`modern-logo` / `classic-logo`). */
|
||||
export function useLogoPath(): { dark: string; light: string } {
|
||||
const { folderPath } = useLogoAssets();
|
||||
|
||||
return useMemo(() => {
|
||||
const themeSuffix = colorScheme === "dark" ? "Dark" : "Light";
|
||||
return `${folderPath}/StirlingPDFLogoNoText${themeSuffix}.svg`;
|
||||
}, [colorScheme, folderPath]);
|
||||
return useMemo(
|
||||
() => ({
|
||||
dark: `${folderPath}/StirlingPDFLogoNoTextDark.svg`,
|
||||
light: `${folderPath}/StirlingPDFLogoNoTextLight.svg`,
|
||||
}),
|
||||
[folderPath],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useToolWorkflow } from "@app/contexts/ToolWorkflowContext";
|
||||
import { Group, useMantineColorScheme } from "@mantine/core";
|
||||
import { Group } from "@mantine/core";
|
||||
import { useSidebarContext } from "@app/contexts/SidebarContext";
|
||||
import { useDocumentMeta } from "@app/hooks/useDocumentMeta";
|
||||
import { useBaseUrl } from "@app/hooks/useBaseUrl";
|
||||
import { useIsMobile } from "@app/hooks/useIsMobile";
|
||||
import { useAppConfig } from "@app/contexts/AppConfigContext";
|
||||
import { useLogoPath } from "@app/hooks/useLogoPath";
|
||||
import { useLogoAssets } from "@app/hooks/useLogoAssets";
|
||||
import { LogoIcon } from "@app/components/shared/LogoIcon";
|
||||
import { Wordmark } from "@app/components/shared/Wordmark";
|
||||
import { useFileContext } from "@app/contexts/file/fileHooks";
|
||||
import {
|
||||
useNavigationState,
|
||||
@@ -50,7 +50,6 @@ export default function HomePage() {
|
||||
} = useToolWorkflow();
|
||||
|
||||
const { openFilesModal } = useFilesModalContext();
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const { config } = useAppConfig();
|
||||
const isMobile = useIsMobile();
|
||||
const sliderRef = useRef<HTMLDivElement | null>(null);
|
||||
@@ -112,9 +111,6 @@ export default function HomePage() {
|
||||
)?.hideToolPanel ?? false;
|
||||
|
||||
const brandAltText = t("home.mobile.brandAlt", "Stirling PDF logo");
|
||||
const brandIconSrc = useLogoPath();
|
||||
const { wordmark } = useLogoAssets();
|
||||
const brandTextSrc = colorScheme === "dark" ? wordmark.white : wordmark.black;
|
||||
|
||||
const handleSelectMobileView = useCallback((view: MobileView) => {
|
||||
setActiveMobileView(view);
|
||||
@@ -241,17 +237,8 @@ export default function HomePage() {
|
||||
<div className="mobile-toggle">
|
||||
<div className="mobile-header">
|
||||
<div className="mobile-brand">
|
||||
<img
|
||||
src={brandIconSrc}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
className="mobile-brand-icon"
|
||||
/>
|
||||
<img
|
||||
src={brandTextSrc}
|
||||
alt={brandAltText}
|
||||
className="mobile-brand-text"
|
||||
/>
|
||||
<LogoIcon className="mobile-brand-icon" />
|
||||
<Wordmark alt={brandAltText} className="mobile-brand-text" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -10,11 +10,10 @@ import {
|
||||
Progress,
|
||||
Switch,
|
||||
Card,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useLogoPath } from "@app/hooks/useLogoPath";
|
||||
import { useLogoAssets } from "@app/hooks/useLogoAssets";
|
||||
import { LogoIcon } from "@app/components/shared/LogoIcon";
|
||||
import { Wordmark } from "@app/components/shared/Wordmark";
|
||||
import ErrorRoundedIcon from "@mui/icons-material/ErrorRounded";
|
||||
import InfoRoundedIcon from "@mui/icons-material/InfoRounded";
|
||||
import PhotoCameraRoundedIcon from "@mui/icons-material/PhotoCameraRounded";
|
||||
@@ -41,10 +40,6 @@ export default function MobileScannerPage() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const navigate = useNavigate();
|
||||
const sessionId = searchParams.get("session");
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const brandIconSrc = useLogoPath();
|
||||
const { wordmark } = useLogoAssets();
|
||||
const brandTextSrc = colorScheme === "dark" ? wordmark.white : wordmark.black;
|
||||
|
||||
const [mode, setMode] = useState<"choice" | "camera" | "file" | null>(
|
||||
"choice",
|
||||
@@ -1024,16 +1019,11 @@ export default function MobileScannerPage() {
|
||||
}}
|
||||
>
|
||||
<Group gap="sm" align="center">
|
||||
<img
|
||||
src={brandIconSrc}
|
||||
<LogoIcon
|
||||
alt={t("home.mobile.brandAlt", "Stirling PDF logo")}
|
||||
style={{ height: "32px", width: "32px" }}
|
||||
/>
|
||||
<img
|
||||
src={brandTextSrc}
|
||||
alt="Stirling PDF"
|
||||
style={{ height: "24px" }}
|
||||
/>
|
||||
<Wordmark alt="Stirling PDF" style={{ height: "24px" }} />
|
||||
</Group>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -391,6 +391,25 @@
|
||||
/* Compare page label chip (light mode): slightly lighter than surrounding rows */
|
||||
--compare-page-label-bg: var(--bg-muted);
|
||||
--compare-page-label-fg: var(--text-secondary);
|
||||
|
||||
/* Status indicator dot ring shadow */
|
||||
--status-dot-ring: 0 0 0 2px rgba(0, 0, 0, 0.08);
|
||||
|
||||
/* Input element styling */
|
||||
--input-bg: #ffffff;
|
||||
|
||||
/* Select/dropdown placeholder state */
|
||||
--select-placeholder-bg: var(--mantine-color-gray-1);
|
||||
--select-placeholder-text: var(--mantine-color-gray-6);
|
||||
|
||||
/* Grouped format dropdown */
|
||||
--dropdown-trigger-bg: var(--mantine-color-white);
|
||||
--dropdown-trigger-text: var(--mantine-color-dark-9);
|
||||
--dropdown-trigger-text-disabled: var(--mantine-color-dark-7);
|
||||
--dropdown-trigger-icon: var(--mantine-color-gray-6);
|
||||
--dropdown-panel-bg: var(--mantine-color-white);
|
||||
--dropdown-panel-border: var(--mantine-color-gray-4);
|
||||
--dropdown-group-label: var(--mantine-color-gray-6);
|
||||
}
|
||||
|
||||
/* Onboarding (light mode) */
|
||||
@@ -681,6 +700,25 @@
|
||||
/* Compare page label chip (dark mode): slightly darker than surrounding rows */
|
||||
--compare-page-label-bg: #1f2329;
|
||||
--compare-page-label-fg: var(--text-secondary);
|
||||
|
||||
/* Status indicator dot ring shadow (dark) */
|
||||
--status-dot-ring: 0 0 0 2px rgba(255, 255, 255, 0.18);
|
||||
|
||||
/* Input element styling (dark) */
|
||||
--input-bg: #4b525a;
|
||||
|
||||
/* Select/dropdown placeholder state (dark) */
|
||||
--select-placeholder-bg: var(--mantine-color-dark-5);
|
||||
--select-placeholder-text: var(--mantine-color-dark-2);
|
||||
|
||||
/* Grouped format dropdown (dark) */
|
||||
--dropdown-trigger-bg: var(--mantine-color-dark-6);
|
||||
--dropdown-trigger-text: var(--mantine-color-dark-0);
|
||||
--dropdown-trigger-text-disabled: var(--mantine-color-dark-1);
|
||||
--dropdown-trigger-icon: var(--mantine-color-dark-2);
|
||||
--dropdown-panel-bg: var(--mantine-color-dark-7);
|
||||
--dropdown-panel-border: var(--mantine-color-dark-4);
|
||||
--dropdown-group-label: var(--mantine-color-dark-2);
|
||||
}
|
||||
|
||||
/* Dropzone drop state styling */
|
||||
@@ -969,3 +1007,87 @@
|
||||
[data-theme="dark"] {
|
||||
--shadow-color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
/* Theme-aware image display utilities */
|
||||
/* Use .theme-img-light-only for images to show in light mode only */
|
||||
/* Use .theme-img-dark-only for images to show in dark mode only */
|
||||
.theme-img-light-only {
|
||||
display: inline;
|
||||
}
|
||||
.theme-img-dark-only {
|
||||
display: none;
|
||||
}
|
||||
[data-mantine-color-scheme="dark"] .theme-img-light-only {
|
||||
display: none;
|
||||
}
|
||||
[data-mantine-color-scheme="dark"] .theme-img-dark-only {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* Modern logo icon */
|
||||
.logo-icon-modern__path1 {
|
||||
fill: #acacac;
|
||||
fill-opacity: 0.3;
|
||||
}
|
||||
.logo-icon-modern__path2 {
|
||||
fill: #fc9999;
|
||||
fill-opacity: 0.5;
|
||||
}
|
||||
[data-mantine-color-scheme="dark"] .logo-icon-modern__path1 {
|
||||
fill: #e6e6e6;
|
||||
fill-opacity: 0.4;
|
||||
}
|
||||
[data-mantine-color-scheme="dark"] .logo-icon-modern__path2 {
|
||||
fill: #e6e6e6;
|
||||
fill-opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Classic logo icon — single path structure, colors change per theme */
|
||||
.logo-icon-classic__group {
|
||||
opacity: 0.2;
|
||||
}
|
||||
.logo-icon-classic__body {
|
||||
fill: #ff4b4b;
|
||||
}
|
||||
.logo-icon-classic__flap {
|
||||
fill: #545454;
|
||||
}
|
||||
.logo-icon-classic__triangle {
|
||||
fill: #d0dbdc;
|
||||
}
|
||||
.logo-icon-classic__s {
|
||||
fill: white;
|
||||
}
|
||||
[data-mantine-color-scheme="dark"] .logo-icon-classic__group {
|
||||
opacity: 1;
|
||||
}
|
||||
[data-mantine-color-scheme="dark"] .logo-icon-classic__body {
|
||||
fill: white;
|
||||
}
|
||||
[data-mantine-color-scheme="dark"] .logo-icon-classic__flap {
|
||||
fill: #d3d3d3;
|
||||
}
|
||||
[data-mantine-color-scheme="dark"] .logo-icon-classic__triangle {
|
||||
fill: #d0dbdc;
|
||||
}
|
||||
[data-mantine-color-scheme="dark"] .logo-icon-classic__s {
|
||||
fill: var(--mantine-color-body);
|
||||
}
|
||||
|
||||
/* Wordmark muted variant (used in empty/placeholder states) */
|
||||
/* Stirling text: 30% opacity in light mode, full opacity in dark */
|
||||
.wordmark__text--muted {
|
||||
fill-opacity: 0.3;
|
||||
}
|
||||
[data-mantine-color-scheme="dark"] .wordmark__text--muted {
|
||||
fill-opacity: 1;
|
||||
}
|
||||
/* PDF text: dimmed red in light mode, standard red in dark */
|
||||
.wordmark__pdf--muted {
|
||||
fill: #d62626;
|
||||
fill-opacity: 0.7;
|
||||
}
|
||||
[data-mantine-color-scheme="dark"] .wordmark__pdf--muted {
|
||||
fill: #c56565;
|
||||
fill-opacity: 1;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import React, { useMemo, useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
Box,
|
||||
Tooltip,
|
||||
useMantineTheme,
|
||||
useComputedColorScheme,
|
||||
rem,
|
||||
} from "@mantine/core";
|
||||
import { Box, Tooltip, useMantineTheme, rem } from "@mantine/core";
|
||||
import { useBackendHealth } from "@app/hooks/useBackendHealth";
|
||||
|
||||
interface BackendHealthIndicatorProps {
|
||||
@@ -18,7 +12,6 @@ export const BackendHealthIndicator: React.FC<BackendHealthIndicatorProps> = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useMantineTheme();
|
||||
const colorScheme = useComputedColorScheme("light");
|
||||
const { status, isOnline, checkHealth } = useBackendHealth();
|
||||
|
||||
const label = useMemo(() => {
|
||||
@@ -60,14 +53,7 @@ export const BackendHealthIndicator: React.FC<BackendHealthIndicatorProps> = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
label={label}
|
||||
position="left"
|
||||
offset={12}
|
||||
withArrow
|
||||
withinPortal
|
||||
color={colorScheme === "dark" ? undefined : "dark"}
|
||||
>
|
||||
<Tooltip label={label} position="left" offset={12} withArrow withinPortal>
|
||||
<Box
|
||||
component="span"
|
||||
className={className ? `${className}` : undefined}
|
||||
@@ -82,10 +68,7 @@ export const BackendHealthIndicator: React.FC<BackendHealthIndicatorProps> = ({
|
||||
height: rem(12),
|
||||
borderRadius: "50%",
|
||||
backgroundColor: dotColor,
|
||||
boxShadow:
|
||||
colorScheme === "dark"
|
||||
? "0 0 0 2px rgba(255, 255, 255, 0.18)"
|
||||
: "0 0 0 2px rgba(0, 0, 0, 0.08)",
|
||||
boxShadow: "var(--status-dot-ring)",
|
||||
cursor: "pointer",
|
||||
display: "inline-block",
|
||||
outline: "none",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import { Box, Tooltip, rem, useComputedColorScheme } from "@mantine/core";
|
||||
import { Box, Tooltip, rem } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
connectionModeService,
|
||||
@@ -18,7 +18,6 @@ interface RightRailFooterExtensionsProps {
|
||||
|
||||
function ConnectionStatusDot() {
|
||||
const { t } = useTranslation();
|
||||
const colorScheme = useComputedColorScheme("light");
|
||||
const [connectionMode, setConnectionMode] = useState<ConnectionMode | null>(
|
||||
null,
|
||||
);
|
||||
@@ -84,14 +83,7 @@ function ConnectionStatusDot() {
|
||||
}, [connectionMode, selfHostedState, isOnline, t]);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
label={label}
|
||||
position="left"
|
||||
offset={12}
|
||||
withArrow
|
||||
withinPortal
|
||||
color={colorScheme === "dark" ? undefined : "dark"}
|
||||
>
|
||||
<Tooltip label={label} position="left" offset={12} withArrow withinPortal>
|
||||
<Box
|
||||
component="span"
|
||||
role="status"
|
||||
@@ -109,10 +101,7 @@ function ConnectionStatusDot() {
|
||||
height: rem(10),
|
||||
borderRadius: "50%",
|
||||
backgroundColor: color,
|
||||
boxShadow:
|
||||
colorScheme === "dark"
|
||||
? "0 0 0 2px rgba(255, 255, 255, 0.15)"
|
||||
: "0 0 0 2px rgba(0, 0, 0, 0.07)",
|
||||
boxShadow: "var(--status-dot-ring)",
|
||||
display: "inline-block",
|
||||
cursor: "pointer",
|
||||
outline: "none",
|
||||
|
||||
@@ -2,13 +2,12 @@ import { useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useAuth } from "@app/auth/UseSession";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useLogoPath } from "@app/hooks/useLogoPath";
|
||||
import { LogoIcon } from "@app/components/shared/LogoIcon";
|
||||
|
||||
export default function LoggedInState() {
|
||||
const navigate = useNavigate();
|
||||
const { user } = useAuth();
|
||||
const { t } = useTranslation();
|
||||
const logoPath = useLogoPath();
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
@@ -47,8 +46,7 @@ export default function LoggedInState() {
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={logoPath}
|
||||
<LogoIcon
|
||||
alt="Stirling PDF Logo"
|
||||
style={{ width: "64px", height: "64px", objectFit: "contain" }}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useLogoAssets } from "@app/hooks/useLogoAssets";
|
||||
import { Wordmark } from "@app/components/shared/Wordmark";
|
||||
|
||||
interface LoginHeaderProps {
|
||||
title: string;
|
||||
@@ -11,18 +11,12 @@ export default function LoginHeader({
|
||||
subtitle,
|
||||
centerOnly = false,
|
||||
}: LoginHeaderProps) {
|
||||
const { wordmark } = useLogoAssets();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`login-header${centerOnly ? " login-header-centered" : ""}`}
|
||||
>
|
||||
<div className="login-header-logos">
|
||||
<img
|
||||
src={wordmark.black}
|
||||
alt="Stirling PDF"
|
||||
className="login-logo-text"
|
||||
/>
|
||||
<Wordmark alt="Stirling PDF" className="login-logo-text" />
|
||||
</div>
|
||||
{title && <h1 className="login-title">{title}</h1>}
|
||||
{subtitle && <p className="login-subtitle">{subtitle}</p>}
|
||||
|
||||
Reference in New Issue
Block a user