feat(frontend): add archive format family, compression control, and fixed double-extension parsing
Also adds a --color-family-archives CSS variable (light + dark) that the plan didn't call out explicitly but is needed for the new family tile/icon to render with a color instead of falling back silently. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,14 +1,12 @@
|
|||||||
import { X } from '@phosphor-icons/react';
|
import { X } from '@phosphor-icons/react';
|
||||||
import { formatBytes } from '../utils/formatBytes.js';
|
import { formatBytes } from '../utils/formatBytes.js';
|
||||||
import { fileTypeMeta } from '../utils/fileFamily.js';
|
import { fileTypeMeta } from '../utils/fileFamily.js';
|
||||||
|
import { extensionOf } from '../utils/archiveExtensions.js';
|
||||||
|
|
||||||
const QUALITY_FORMATS = ['jpg', 'jpeg', 'webp', 'avif', 'tiff'];
|
const QUALITY_FORMATS = ['jpg', 'jpeg', 'webp', 'avif', 'tiff'];
|
||||||
const ICON_SIZES = [16, 32, 48, 256, 512];
|
const ICON_SIZES = [16, 32, 48, 256, 512];
|
||||||
const DEFAULT_ICON_SIZE = 256;
|
const DEFAULT_ICON_SIZE = 256;
|
||||||
|
const ARCHIVE_COMPRESSION_FORMATS = ['zip', 'tar.gz', 'tar.bz2', '7z', 'tar.7z'];
|
||||||
function extensionOf(fileName) {
|
|
||||||
return fileName.split('.').pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
function rangeProgress(value, min, max) {
|
function rangeProgress(value, min, max) {
|
||||||
return `${((value - min) / (max - min)) * 100}%`;
|
return `${((value - min) / (max - min)) * 100}%`;
|
||||||
@@ -93,6 +91,16 @@ export function FileConfigCard({ item, index, t, onTargetFormatChange, onQuality
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{ARCHIVE_COMPRESSION_FORMATS.includes(item.targetFormat) && (
|
||||||
|
<RangeField
|
||||||
|
label={t('quality.compression')}
|
||||||
|
value={item.quality}
|
||||||
|
min={0}
|
||||||
|
max={9}
|
||||||
|
onChange={(value) => onQualityChange(index, value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{item.targetFormat === 'ico' && (
|
{item.targetFormat === 'ico' && (
|
||||||
<div className="chip-field">
|
<div className="chip-field">
|
||||||
<span>{t('quality.iconSize')}</span>
|
<span>{t('quality.iconSize')}</span>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Image, FileText, TextAa, BookOpen } from '@phosphor-icons/react';
|
import { Image, FileText, TextAa, BookOpen, Archive } from '@phosphor-icons/react';
|
||||||
import { FORMAT_FAMILIES } from '../data/formats.js';
|
import { FORMAT_FAMILIES } from '../data/formats.js';
|
||||||
|
|
||||||
const FAMILY_ICONS = {
|
const FAMILY_ICONS = {
|
||||||
@@ -7,6 +7,7 @@ const FAMILY_ICONS = {
|
|||||||
documents: FileText,
|
documents: FileText,
|
||||||
fonts: TextAa,
|
fonts: TextAa,
|
||||||
ebooks: BookOpen,
|
ebooks: BookOpen,
|
||||||
|
archives: Archive,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function FormatsGrid() {
|
export function FormatsGrid() {
|
||||||
|
|||||||
@@ -15,4 +15,8 @@ export const FORMAT_FAMILIES = [
|
|||||||
key: 'ebooks',
|
key: 'ebooks',
|
||||||
formats: ['epub', 'fb2', 'lrf', 'mobi', 'pdb', 'rb', 'snb', 'tcr', 'azw3', 'pdf'],
|
formats: ['epub', 'fb2', 'lrf', 'mobi', 'pdb', 'rb', 'snb', 'tcr', 'azw3', 'pdf'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'archives',
|
||||||
|
formats: ['zip', 'tar', 'tar.gz', 'tar.bz2', '7z', 'tar.7z', 'rar'],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
--color-family-documents: var(--color-aurora-cyan);
|
--color-family-documents: var(--color-aurora-cyan);
|
||||||
--color-family-fonts: #f5a524;
|
--color-family-fonts: #f5a524;
|
||||||
--color-family-ebooks: #10b981;
|
--color-family-ebooks: #10b981;
|
||||||
|
--color-family-archives: #64748b;
|
||||||
|
|
||||||
--font-sans: 'Plus Jakarta Sans', system-ui, 'Segoe UI', Roboto, sans-serif;
|
--font-sans: 'Plus Jakarta Sans', system-ui, 'Segoe UI', Roboto, sans-serif;
|
||||||
--font-display: 'Bricolage Grotesque', var(--font-sans);
|
--font-display: 'Bricolage Grotesque', var(--font-sans);
|
||||||
@@ -51,6 +52,7 @@
|
|||||||
--color-family-documents: var(--color-aurora-cyan);
|
--color-family-documents: var(--color-aurora-cyan);
|
||||||
--color-family-fonts: #ffb84d;
|
--color-family-fonts: #ffb84d;
|
||||||
--color-family-ebooks: #34d399;
|
--color-family-ebooks: #34d399;
|
||||||
|
--color-family-archives: #94a3b8;
|
||||||
}
|
}
|
||||||
|
|
||||||
*, *::before, *::after {
|
*, *::before, *::after {
|
||||||
|
|||||||
@@ -40,7 +40,8 @@
|
|||||||
"images": "Images",
|
"images": "Images",
|
||||||
"documents": "Documents",
|
"documents": "Documents",
|
||||||
"fonts": "Fonts",
|
"fonts": "Fonts",
|
||||||
"ebooks": "Ebooks"
|
"ebooks": "Ebooks",
|
||||||
|
"archives": "Archives"
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"rights": "Ombrora Convert — Online file conversion"
|
"rights": "Ombrora Convert — Online file conversion"
|
||||||
|
|||||||
@@ -40,7 +40,8 @@
|
|||||||
"images": "Images",
|
"images": "Images",
|
||||||
"documents": "Documents",
|
"documents": "Documents",
|
||||||
"fonts": "Polices",
|
"fonts": "Polices",
|
||||||
"ebooks": "Ebooks"
|
"ebooks": "Ebooks",
|
||||||
|
"archives": "Archives"
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"rights": "Ombrora Convert — Conversion de fichiers en ligne"
|
"rights": "Ombrora Convert — Conversion de fichiers en ligne"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { ReassuranceStrip } from '../components/ReassuranceStrip.jsx';
|
|||||||
import { FormatsGrid } from '../components/FormatsGrid.jsx';
|
import { FormatsGrid } from '../components/FormatsGrid.jsx';
|
||||||
import { FormatMarquee } from '../components/FormatMarquee.jsx';
|
import { FormatMarquee } from '../components/FormatMarquee.jsx';
|
||||||
import { SeoHead } from '../components/SeoHead.jsx';
|
import { SeoHead } from '../components/SeoHead.jsx';
|
||||||
|
import { extensionOf } from '../utils/archiveExtensions.js';
|
||||||
import '../styles/home.css';
|
import '../styles/home.css';
|
||||||
import '../styles/sections.css';
|
import '../styles/sections.css';
|
||||||
|
|
||||||
@@ -19,14 +20,15 @@ const DEFAULT_QUALITY = {
|
|||||||
avif: 50,
|
avif: 50,
|
||||||
tiff: 80,
|
tiff: 80,
|
||||||
png: 6,
|
png: 6,
|
||||||
|
zip: 6,
|
||||||
|
'tar.gz': 6,
|
||||||
|
'tar.bz2': 9,
|
||||||
|
'7z': 5,
|
||||||
|
'tar.7z': 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_ICON_SIZE = 256;
|
const DEFAULT_ICON_SIZE = 256;
|
||||||
|
|
||||||
function extensionOf(fileName) {
|
|
||||||
return fileName.split('.').pop().toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
function defaultQualityFor(targetFormat) {
|
function defaultQualityFor(targetFormat) {
|
||||||
return DEFAULT_QUALITY[targetFormat] ?? null;
|
return DEFAULT_QUALITY[targetFormat] ?? null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
const DOUBLE_EXTENSIONS = ['tar.gz', 'tar.bz2', 'tar.7z'];
|
||||||
|
|
||||||
|
export function extensionOf(filename) {
|
||||||
|
const lower = filename.toLowerCase();
|
||||||
|
const match = DOUBLE_EXTENSIONS.find((ext) => lower.endsWith(`.${ext}`));
|
||||||
|
return match ?? filename.split('.').pop().toLowerCase();
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Image, FileText, TextAa, BookOpen, File as FileIcon } from '@phosphor-icons/react';
|
import { Image, FileText, TextAa, BookOpen, Archive, File as FileIcon } from '@phosphor-icons/react';
|
||||||
import { FORMAT_FAMILIES } from '../data/formats.js';
|
import { FORMAT_FAMILIES } from '../data/formats.js';
|
||||||
|
|
||||||
const FAMILY_ICONS = {
|
const FAMILY_ICONS = {
|
||||||
@@ -6,6 +6,7 @@ const FAMILY_ICONS = {
|
|||||||
documents: FileText,
|
documents: FileText,
|
||||||
fonts: TextAa,
|
fonts: TextAa,
|
||||||
ebooks: BookOpen,
|
ebooks: BookOpen,
|
||||||
|
archives: Archive,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function familyForExtension(extension) {
|
export function familyForExtension(extension) {
|
||||||
|
|||||||
Reference in New Issue
Block a user