From 987af80286052e36d19acedf953c0856c5374903 Mon Sep 17 00:00:00 2001 From: Anthony GAEREMYNCK <1@anthony.sh> Date: Fri, 31 Jul 2026 16:34:13 +0200 Subject: [PATCH] feat(frontend): redesign homepage and file config form with aurora palette Rebuild the visual identity from the actual Ombrora mark (violet/cyan aurora blobs) instead of the generic blue/amber scheme, add Bricolage Grotesque and IBM Plex Mono for display and code-like text, and give the hero an animated aurora backdrop, gradient headline, and a format-pair marquee driven by the real registered converters. Rework the file config form: target format and icon size are both chip groups now instead of a native select, and quality/compression sliders get a gradient-filled track, custom thumb, and a mono value pill to match. File type icons are color-coded by family (images/documents/fonts/ebooks) and reused across FileConfigCard, FileCard, and FormatsGrid. Co-Authored-By: Claude Sonnet 5 --- frontend/src/FileCard.jsx | 36 +- frontend/src/api.js | 5 + frontend/src/components/FileConfigCard.jsx | 147 ++++--- frontend/src/components/FormatMarquee.jsx | 31 ++ frontend/src/components/FormatsGrid.jsx | 26 +- frontend/src/components/ReassuranceStrip.jsx | 21 +- frontend/src/index.css | 60 ++- frontend/src/locales/en.json | 10 +- frontend/src/locales/fr.json | 10 +- frontend/src/pages/HomePage.jsx | 94 ++-- frontend/src/styles/home.css | 434 ++++++++++++++++--- frontend/src/styles/layout.css | 16 +- frontend/src/styles/sections.css | 64 ++- frontend/src/utils/fileFamily.js | 22 + 14 files changed, 770 insertions(+), 206 deletions(-) create mode 100644 frontend/src/components/FormatMarquee.jsx create mode 100644 frontend/src/utils/fileFamily.js diff --git a/frontend/src/FileCard.jsx b/frontend/src/FileCard.jsx index ebc5839..e7983cd 100644 --- a/frontend/src/FileCard.jsx +++ b/frontend/src/FileCard.jsx @@ -1,8 +1,13 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { File as FileIcon, CircleNotch, DownloadSimple } from '@phosphor-icons/react'; +import { CircleNotch, DownloadSimple, CheckCircle, WarningCircle } from '@phosphor-icons/react'; import { fetchJobStatus, downloadUrl } from './api.js'; import { formatBytes } from './utils/formatBytes.js'; +import { fileTypeMeta } from './utils/fileFamily.js'; + +function extensionOf(fileName) { + return fileName.split('.').pop(); +} export function FileCard({ fileName, fileSize, targetFormat, jobId, initialError }) { const { t } = useTranslation(); @@ -29,27 +34,42 @@ export function FileCard({ fileName, fileSize, targetFormat, jobId, initialError }; }, [jobId, initialError]); + const { Icon, colorVar } = fileTypeMeta(extensionOf(fileName)); + return ( -
  • +
  • - + + + {fileName} {targetFormat && {targetFormat.toUpperCase()}} {formatBytes(fileSize)}
    {(status === 'pending' || status === 'processing') && ( - - - {t('job.converting')} - +
    + + + {t('job.converting')} + +
    )} {status === 'done' && ( + {t('job.download')} ({formatBytes(outputSize)}) )} - {status === 'failed' && {errorMessage}} + {status === 'failed' && ( + + + {errorMessage} + + )}
  • ); } diff --git a/frontend/src/api.js b/frontend/src/api.js index 8ab9466..ea47096 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -4,6 +4,11 @@ export async function fetchFormats(source) { return data.targets; } +export async function fetchConfig() { + const response = await fetch('/api/config'); + return response.json(); +} + export async function uploadFiles(items) { const formData = new FormData(); const targetFormats = []; diff --git a/frontend/src/components/FileConfigCard.jsx b/frontend/src/components/FileConfigCard.jsx index cfc6c43..a67272e 100644 --- a/frontend/src/components/FileConfigCard.jsx +++ b/frontend/src/components/FileConfigCard.jsx @@ -1,96 +1,131 @@ -import { File as FileIcon } from '@phosphor-icons/react'; import { formatBytes } from '../utils/formatBytes.js'; +import { fileTypeMeta } from '../utils/fileFamily.js'; const QUALITY_FORMATS = ['jpg', 'jpeg', 'webp', 'avif', 'tiff']; const ICON_SIZES = [16, 32, 48, 256, 512]; const DEFAULT_ICON_SIZE = 256; +function extensionOf(fileName) { + return fileName.split('.').pop(); +} + +function rangeProgress(value, min, max) { + return `${((value - min) / (max - min)) * 100}%`; +} + +function RangeField({ label, value, min, max, onChange }) { + return ( +
    +
    + {label} + {value} +
    + onChange(Number(event.target.value))} + /> +
    + ); +} + export function FileConfigCard({ item, index, t, onTargetFormatChange, onQualityChange, onIconSizeChange }) { + const { Icon, colorVar } = fileTypeMeta(extensionOf(item.file.name)); + return (
  • - + + + {item.file.name} {formatBytes(item.file.size)}
    {item.targets.length > 0 ? (
    -
    - - {item.targetFormat && {item.targetFormat.toUpperCase()}} +
    {QUALITY_FORMATS.includes(item.targetFormat) && ( - + onQualityChange(index, value)} + /> )} {item.targetFormat === 'png' && ( - + onQualityChange(index, value)} + /> )} {item.targetFormat === 'ico' && ( - + + )} {item.targetFormat === 'pdf' && ( -