From 76198f28c52abda29dfc748c4278dd52d5e7b871 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Fri, 30 Jan 2026 14:58:14 +0100 Subject: [PATCH] feat: Expand supported image and audio formats, update UI color palette, and refresh application assets. --- index.html | 2 +- public/logo.png | Bin 0 -> 49630 bytes public/vite.svg | 1 - src/adapters/ffmpeg-adapter.ts | 5 ++- src/adapters/heic-adapter.ts | 7 ++- src/adapters/image-magic-adapter.ts | 7 ++- src/core/types.ts | 54 ++++++++++++++++++++--- src/index.css | 42 +++++++++--------- src/workers/audio.worker.ts | 65 ++++++++++++++++++++++++++-- src/workers/image.worker.ts | 21 +++++++-- 10 files changed, 162 insertions(+), 42 deletions(-) create mode 100644 public/logo.png delete mode 100644 public/vite.svg diff --git a/index.html b/index.html index 9c0acd0..c0d8caa 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + K-Convert - Privacy-First File Converter diff --git a/public/logo.png b/public/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c0b4e57a8417cf0f33ebce267bdfbe544193e51b GIT binary patch literal 49630 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 P6AR{h%**;JVAp>EWnfA1 literal 0 HcmV?d00001 diff --git a/public/vite.svg b/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/adapters/ffmpeg-adapter.ts b/src/adapters/ffmpeg-adapter.ts index 709d207..a8fcbef 100644 --- a/src/adapters/ffmpeg-adapter.ts +++ b/src/adapters/ffmpeg-adapter.ts @@ -27,7 +27,10 @@ function getWorker(): WorkerType { return workerInstance; } -const SUPPORTED_FORMATS: AudioFormat[] = ['mp3', 'wav', 'ogg', 'm4a', 'flac']; +const SUPPORTED_FORMATS: AudioFormat[] = [ + 'mp3', 'wav', 'ogg', 'm4a', 'flac', + 'aac', 'aiff', 'alac', 'wma', 'opus', 'm4r', 'amr' +]; export class FfmpegAdapter implements IFileConverter { readonly name = 'ffmpeg-adapter'; diff --git a/src/adapters/heic-adapter.ts b/src/adapters/heic-adapter.ts index 4d37dde..098462f 100644 --- a/src/adapters/heic-adapter.ts +++ b/src/adapters/heic-adapter.ts @@ -17,8 +17,11 @@ import type { ImageConverterWorker } from '../workers/image.worker'; // Formats heic2any can directly output const HEIC2ANY_OUTPUTS: ImageFormat[] = ['jpeg', 'png']; -// All image formats we support as final output -const ALL_IMAGE_OUTPUTS: ImageFormat[] = ['jpeg', 'png', 'webp', 'gif', 'bmp', 'tiff', 'ico']; +// All image formats we support as final output (via two-step conversion) +const ALL_IMAGE_OUTPUTS: ImageFormat[] = [ + 'jpeg', 'png', 'webp', 'gif', 'bmp', 'tiff', 'ico', + 'avif', 'jxl', 'svg', 'psd', 'tga' +]; type WorkerType = Comlink.Remote; let workerInstance: WorkerType | null = null; diff --git a/src/adapters/image-magic-adapter.ts b/src/adapters/image-magic-adapter.ts index 0ab649e..5798582 100644 --- a/src/adapters/image-magic-adapter.ts +++ b/src/adapters/image-magic-adapter.ts @@ -27,8 +27,11 @@ function getWorker(): WorkerType { return workerInstance; } -// Formats that ImageMagick can handle -const SUPPORTED_FORMATS: ImageFormat[] = ['jpeg', 'png', 'webp', 'gif', 'bmp', 'tiff', 'ico']; +// Formats that ImageMagick can handle (excludes HEIC which uses heic-adapter) +const SUPPORTED_FORMATS: ImageFormat[] = [ + 'jpeg', 'png', 'webp', 'gif', 'bmp', 'tiff', 'ico', + 'avif', 'jxl', 'svg', 'psd', 'raw', 'tga' +]; export class ImageMagickAdapter implements IFileConverter { readonly name = 'imagemagick-adapter'; diff --git a/src/core/types.ts b/src/core/types.ts index 2cf2226..f34f732 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -6,8 +6,16 @@ // Format Definitions // ----------------------------------------------------------------------------- -export type ImageFormat = 'jpeg' | 'png' | 'webp' | 'gif' | 'bmp' | 'tiff' | 'heic' | 'ico'; -export type AudioFormat = 'mp3' | 'wav' | 'ogg' | 'm4a' | 'flac'; +// Image formats +export type ImageFormat = + | 'jpeg' | 'png' | 'webp' | 'gif' | 'bmp' | 'tiff' | 'heic' | 'ico' + | 'avif' | 'jxl' | 'svg' | 'psd' | 'raw' | 'tga'; + +// Audio formats +export type AudioFormat = + | 'mp3' | 'wav' | 'ogg' | 'm4a' | 'flac' + | 'aac' | 'aiff' | 'alac' | 'wma' | 'opus' | 'm4r' | 'amr'; + export type SupportedFormat = ImageFormat | AudioFormat; export type FormatCategory = 'image' | 'audio'; @@ -20,7 +28,7 @@ export interface FormatMetadata { } export const FORMAT_METADATA: Record = { - // Images + // Images - Common jpeg: { extension: 'jpg', mimeType: 'image/jpeg', displayName: 'JPEG', category: 'image' }, png: { extension: 'png', mimeType: 'image/png', displayName: 'PNG', category: 'image' }, webp: { extension: 'webp', mimeType: 'image/webp', displayName: 'WebP', category: 'image' }, @@ -29,16 +37,32 @@ export const FORMAT_METADATA: Record = { tiff: { extension: 'tiff', mimeType: 'image/tiff', displayName: 'TIFF', category: 'image' }, heic: { extension: 'heic', mimeType: 'image/heic', displayName: 'HEIC', category: 'image' }, ico: { extension: 'ico', mimeType: 'image/x-icon', displayName: 'ICO', category: 'image' }, - // Audio + // Images - New formats + avif: { extension: 'avif', mimeType: 'image/avif', displayName: 'AVIF', category: 'image' }, + jxl: { extension: 'jxl', mimeType: 'image/jxl', displayName: 'JPEG XL', category: 'image' }, + svg: { extension: 'svg', mimeType: 'image/svg+xml', displayName: 'SVG', category: 'image' }, + psd: { extension: 'psd', mimeType: 'image/vnd.adobe.photoshop', displayName: 'PSD', category: 'image' }, + raw: { extension: 'raw', mimeType: 'image/x-raw', displayName: 'RAW', category: 'image' }, + tga: { extension: 'tga', mimeType: 'image/x-tga', displayName: 'TGA', category: 'image' }, + // Audio - Common mp3: { extension: 'mp3', mimeType: 'audio/mpeg', displayName: 'MP3', category: 'audio' }, wav: { extension: 'wav', mimeType: 'audio/wav', displayName: 'WAV', category: 'audio' }, - ogg: { extension: 'ogg', mimeType: 'audio/ogg', displayName: 'OGG', category: 'audio' }, - m4a: { extension: 'm4a', mimeType: 'audio/mp4', displayName: 'M4A (AAC)', category: 'audio' }, + ogg: { extension: 'ogg', mimeType: 'audio/ogg', displayName: 'OGG Vorbis', category: 'audio' }, + m4a: { extension: 'm4a', mimeType: 'audio/mp4', displayName: 'M4A', category: 'audio' }, flac: { extension: 'flac', mimeType: 'audio/flac', displayName: 'FLAC', category: 'audio' }, + // Audio - New formats + aac: { extension: 'aac', mimeType: 'audio/aac', displayName: 'AAC', category: 'audio' }, + aiff: { extension: 'aiff', mimeType: 'audio/aiff', displayName: 'AIFF', category: 'audio' }, + alac: { extension: 'm4a', mimeType: 'audio/x-m4a', displayName: 'ALAC', category: 'audio' }, + wma: { extension: 'wma', mimeType: 'audio/x-ms-wma', displayName: 'WMA', category: 'audio' }, + opus: { extension: 'opus', mimeType: 'audio/opus', displayName: 'Opus', category: 'audio' }, + m4r: { extension: 'm4r', mimeType: 'audio/x-m4r', displayName: 'M4R (Ringtone)', category: 'audio' }, + amr: { extension: 'amr', mimeType: 'audio/amr', displayName: 'AMR', category: 'audio' }, }; // MIME type to format reverse lookup export const MIME_TO_FORMAT: Record = { + // Images 'image/jpeg': 'jpeg', 'image/png': 'png', 'image/webp': 'webp', @@ -49,15 +73,31 @@ export const MIME_TO_FORMAT: Record = { 'image/heif': 'heic', 'image/x-icon': 'ico', 'image/vnd.microsoft.icon': 'ico', + 'image/avif': 'avif', + 'image/jxl': 'jxl', + 'image/svg+xml': 'svg', + 'image/vnd.adobe.photoshop': 'psd', + 'image/x-raw': 'raw', + 'image/x-tga': 'tga', + 'image/x-targa': 'tga', + // Audio 'audio/mpeg': 'mp3', 'audio/mp3': 'mp3', 'audio/wav': 'wav', 'audio/wave': 'wav', + 'audio/x-wav': 'wav', 'audio/ogg': 'ogg', 'audio/mp4': 'm4a', 'audio/x-m4a': 'm4a', - 'audio/aac': 'm4a', 'audio/flac': 'flac', + 'audio/x-flac': 'flac', + 'audio/aac': 'aac', + 'audio/aiff': 'aiff', + 'audio/x-aiff': 'aiff', + 'audio/x-ms-wma': 'wma', + 'audio/opus': 'opus', + 'audio/x-m4r': 'm4r', + 'audio/amr': 'amr', }; // ----------------------------------------------------------------------------- diff --git a/src/index.css b/src/index.css index 8c685a9..e2c8f99 100644 --- a/src/index.css +++ b/src/index.css @@ -52,18 +52,18 @@ --card-foreground: oklch(0.145 0 0); --popover: oklch(1 0 0); --popover-foreground: oklch(0.145 0 0); - --primary: oklch(0.55 0.2 280); - --primary-foreground: oklch(0.985 0 0); + --primary: oklch(0.6927 0.1596 55.43); + --primary-foreground: oklch(0.98 0 0); --secondary: oklch(0.97 0 0); --secondary-foreground: oklch(0.205 0 0); --muted: oklch(0.97 0 0); --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.95 0.05 280); + --accent: oklch(0.95 0.05 55); --accent-foreground: oklch(0.205 0 0); --destructive: oklch(0.577 0.245 27.325); --border: oklch(0.922 0 0); --input: oklch(0.922 0 0); - --ring: oklch(0.55 0.2 280); + --ring: oklch(0.6927 0.1596 55.43); --chart-1: oklch(0.646 0.222 41.116); --chart-2: oklch(0.6 0.118 184.704); --chart-3: oklch(0.398 0.07 227.392); @@ -71,46 +71,46 @@ --chart-5: oklch(0.769 0.188 70.08); --sidebar: oklch(0.985 0 0); --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: oklch(0.55 0.2 280); + --sidebar-primary: oklch(0.6927 0.1596 55.43); --sidebar-primary-foreground: oklch(0.985 0 0); --sidebar-accent: oklch(0.97 0 0); --sidebar-accent-foreground: oklch(0.205 0 0); --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.55 0.2 280); + --sidebar-ring: oklch(0.6927 0.1596 55.43); } .dark { - --background: oklch(0.12 0.02 280); + --background: oklch(0.12 0.02 55); --foreground: oklch(0.95 0 0); - --card: oklch(0.16 0.02 280); + --card: oklch(0.16 0.02 55); --card-foreground: oklch(0.95 0 0); - --popover: oklch(0.16 0.02 280); + --popover: oklch(0.16 0.02 55); --popover-foreground: oklch(0.95 0 0); - --primary: oklch(0.7 0.22 280); - --primary-foreground: oklch(0.12 0.02 280); - --secondary: oklch(0.22 0.03 280); + --primary: oklch(0.7 0.2 55); + --primary-foreground: oklch(0.12 0.02 55); + --secondary: oklch(0.22 0.03 55); --secondary-foreground: oklch(0.95 0 0); - --muted: oklch(0.22 0.03 280); + --muted: oklch(0.22 0.03 55); --muted-foreground: oklch(0.65 0 0); - --accent: oklch(0.28 0.06 280); + --accent: oklch(0.28 0.06 55); --accent-foreground: oklch(0.95 0 0); --destructive: oklch(0.65 0.22 25); --border: oklch(1 0 0 / 12%); --input: oklch(1 0 0 / 15%); - --ring: oklch(0.7 0.22 280); - --chart-1: oklch(0.7 0.22 280); + --ring: oklch(0.7 0.2 55); + --chart-1: oklch(0.7 0.2 55); --chart-2: oklch(0.696 0.17 162.48); --chart-3: oklch(0.769 0.188 70.08); --chart-4: oklch(0.627 0.265 303.9); --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.14 0.02 280); + --sidebar: oklch(0.14 0.02 55); --sidebar-foreground: oklch(0.95 0 0); - --sidebar-primary: oklch(0.7 0.22 280); - --sidebar-primary-foreground: oklch(0.12 0.02 280); - --sidebar-accent: oklch(0.22 0.03 280); + --sidebar-primary: oklch(0.7 0.2 55); + --sidebar-primary-foreground: oklch(0.12 0.02 55); + --sidebar-accent: oklch(0.22 0.03 55); --sidebar-accent-foreground: oklch(0.95 0 0); --sidebar-border: oklch(1 0 0 / 12%); - --sidebar-ring: oklch(0.7 0.22 280); + --sidebar-ring: oklch(0.7 0.2 55); } @layer base { diff --git a/src/workers/audio.worker.ts b/src/workers/audio.worker.ts index 694e84f..cc39454 100644 --- a/src/workers/audio.worker.ts +++ b/src/workers/audio.worker.ts @@ -7,7 +7,10 @@ import * as Comlink from 'comlink'; import { FFmpeg } from '@ffmpeg/ffmpeg'; import { toBlobURL } from '@ffmpeg/util'; -type AudioFormat = 'mp3' | 'wav' | 'ogg' | 'm4a' | 'flac'; +// All supported audio formats +type AudioFormat = + | 'mp3' | 'wav' | 'ogg' | 'm4a' | 'flac' + | 'aac' | 'aiff' | 'alac' | 'wma' | 'opus' | 'm4r' | 'amr'; let ffmpeg: FFmpeg | null = null; let ffmpegLoading: Promise | null = null; @@ -30,6 +33,8 @@ async function ensureFFmpegLoaded(): Promise { coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'), wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'), }); + + console.log('[AudioWorker] FFmpeg WASM initialized'); })(); await ffmpegLoading; @@ -42,34 +47,66 @@ function getFFmpegArgs( bitrate?: number ): string[] { const args: string[] = []; + const br = bitrate || 192; // Codec selection based on output format switch (outputFormat) { case 'mp3': args.push('-c:a', 'libmp3lame'); - args.push('-b:a', `${bitrate || 192}k`); + args.push('-b:a', `${br}k`); break; case 'wav': args.push('-c:a', 'pcm_s16le'); break; case 'ogg': args.push('-c:a', 'libvorbis'); - args.push('-q:a', '4'); // Quality level 0-10 + args.push('-q:a', '4'); break; case 'm4a': args.push('-c:a', 'aac'); - args.push('-b:a', `${bitrate || 192}k`); + args.push('-b:a', `${br}k`); break; case 'flac': args.push('-c:a', 'flac'); args.push('-compression_level', '5'); break; + case 'aac': + args.push('-c:a', 'aac'); + args.push('-b:a', `${br}k`); + break; + case 'aiff': + args.push('-c:a', 'pcm_s16be'); + break; + case 'alac': + // ALAC in M4A container + args.push('-c:a', 'alac'); + break; + case 'wma': + args.push('-c:a', 'wmav2'); + args.push('-b:a', `${br}k`); + break; + case 'opus': + args.push('-c:a', 'libopus'); + args.push('-b:a', `${br}k`); + break; + case 'm4r': + // M4R is just AAC with different extension (iPhone ringtones) + args.push('-c:a', 'aac'); + args.push('-b:a', `${br}k`); + break; + case 'amr': + args.push('-c:a', 'libopencore_amrnb'); + args.push('-ar', '8000'); // AMR requires 8kHz sample rate + args.push('-ac', '1'); // AMR is mono only + break; } return args; } function getExtension(format: AudioFormat): string { + // ALAC uses m4a container + if (format === 'alac') return 'm4a'; return format; } @@ -80,11 +117,31 @@ function getMimeType(format: AudioFormat): string { ogg: 'audio/ogg', m4a: 'audio/mp4', flac: 'audio/flac', + aac: 'audio/aac', + aiff: 'audio/aiff', + alac: 'audio/x-m4a', + wma: 'audio/x-ms-wma', + opus: 'audio/opus', + m4r: 'audio/x-m4r', + amr: 'audio/amr', }; return mimeMap[format]; } const audioConverter = { + /** + * Initialize the worker (can be called early to warm up) + */ + async init(): Promise { + try { + await ensureFFmpegLoaded(); + return true; + } catch (error) { + console.error('[AudioWorker] Init failed:', error); + return false; + } + }, + /** * Convert audio file using FFmpeg */ diff --git a/src/workers/image.worker.ts b/src/workers/image.worker.ts index ebd8e83..ef472df 100644 --- a/src/workers/image.worker.ts +++ b/src/workers/image.worker.ts @@ -13,7 +13,10 @@ import { MagickGeometry, } from '@imagemagick/magick-wasm'; -type ImageFormat = 'jpeg' | 'png' | 'webp' | 'gif' | 'bmp' | 'tiff' | 'heic' | 'ico'; +// All supported image formats +type ImageFormat = + | 'jpeg' | 'png' | 'webp' | 'gif' | 'bmp' | 'tiff' | 'heic' | 'ico' + | 'avif' | 'jxl' | 'svg' | 'psd' | 'raw' | 'tga'; let magickInitialized = false; let initPromise: Promise | null = null; @@ -46,6 +49,12 @@ function getMagickFormat(format: ImageFormat): MagickFormat { tiff: MagickFormat.Tiff, heic: MagickFormat.Heic, ico: MagickFormat.Ico, + avif: MagickFormat.Avif, + jxl: MagickFormat.Jxl, + svg: MagickFormat.Svg, + psd: MagickFormat.Psd, + raw: MagickFormat.Raw, // Note: May need specific raw format handling + tga: MagickFormat.Tga, }; return formatMap[format]; } @@ -60,6 +69,12 @@ function getMimeType(format: ImageFormat): string { tiff: 'image/tiff', heic: 'image/heic', ico: 'image/x-icon', + avif: 'image/avif', + jxl: 'image/jxl', + svg: 'image/svg+xml', + psd: 'image/vnd.adobe.photoshop', + raw: 'image/x-raw', + tga: 'image/x-tga', }; return mimeMap[format]; } @@ -103,7 +118,7 @@ const imageConverter = { } // Set quality for lossy formats - if (outputFormat === 'jpeg' || outputFormat === 'webp') { + if (['jpeg', 'webp', 'avif', 'jxl'].includes(outputFormat)) { image.quality = quality; } @@ -143,7 +158,7 @@ const imageConverter = { geometry.ignoreAspectRatio = false; image.resize(geometry); - if (outputFormat === 'jpeg' || outputFormat === 'webp') { + if (['jpeg', 'webp', 'avif', 'jxl'].includes(outputFormat)) { image.quality = quality; }