diff --git a/src/lib/components/DitherComparison.svelte b/src/lib/components/DitherComparison.svelte new file mode 100644 index 0000000..2614fa2 --- /dev/null +++ b/src/lib/components/DitherComparison.svelte @@ -0,0 +1,234 @@ + + +
+
+ {#if isProcessing} +
+

Processing...

+
+ {/if} + + + + + + + + + {#if !isProcessing} + +
+
+
+ +
+
+ + + + With Dithering + + + No Dithering + + {/if} +
+

Drag to compare · 16 grey levels

+
diff --git a/src/lib/components/EreaderMockup.svelte b/src/lib/components/EreaderMockup.svelte new file mode 100644 index 0000000..e5f5e06 --- /dev/null +++ b/src/lib/components/EreaderMockup.svelte @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {#each Array(50) as _, i} + {#each Array(65) as _, j} + {#if (i + j) % 2 === 0} + + {/if} + {/each} + {/each} + + + + + + + + + diff --git a/src/lib/index.ts b/src/lib/index.ts index edcdb22..59b9177 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,3 +1,18 @@ +// Components +export { default as DropZone } from './components/DropZone.svelte'; +export { default as DeviceSelector } from './components/DeviceSelector.svelte'; +export { default as CustomDeviceModal } from './components/CustomDeviceModal.svelte'; +export { default as ImageCard } from './components/ImageCard.svelte'; +export { default as ImageGrid } from './components/ImageGrid.svelte'; +export { default as EditImageModal } from './components/EditImageModal.svelte'; +export { default as EreaderMockup } from './components/EreaderMockup.svelte'; +export { default as DitherComparison } from './components/DitherComparison.svelte'; + +// Stores +export { deviceStore } from './stores/device.svelte'; +export { imagesStore } from './stores/images.svelte'; +export { pipelineStore } from './stores/pipeline.svelte'; + // Data export { PRESET_DEVICES, DEFAULT_DEVICE, groupDevicesByBrand } from './data/devices'; diff --git a/src/lib/types.ts b/src/lib/types.ts index 12ca8a0..7a44ab3 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -40,13 +40,15 @@ export interface CustomDeviceFormData { outputFormat: OutputFormat; } +export const SUPPORTED_FORMATS = ['image/jpeg', 'image/png', 'image/webp'] as const; + export const CONSTRAINTS = { MAX_FILE_SIZE_MB: 25, MAX_FILE_SIZE_BYTES: 25 * 1024 * 1024, MIN_DIMENSION: 100, MAX_DIMENSION: 5000, MAX_DEVICE_NAME_LENGTH: 50, - SUPPORTED_FORMATS: ['image/jpeg', 'image/png', 'image/webp'], + SUPPORTED_FORMATS, GREY_LEVEL_OPTIONS: [2, 4, 8, 16, 256], OUTPUT_FORMAT_OPTIONS: ['png', 'jpeg'] } as const; diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts new file mode 100644 index 0000000..ceccaaf --- /dev/null +++ b/src/routes/+layout.ts @@ -0,0 +1,2 @@ +export const prerender = true; +export const ssr = false; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index cc88df0..d26503b 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,2 +1,584 @@ -

Welcome to SvelteKit

-

Visit svelte.dev/docs/kit to read the documentation

+ + + + 2eInk - E-Reader Screensaver Converter + + + +
+ + + + +
+ +
+ +
+
+
+
+ +
+

+ Perfect Screensavers for Your E-Reader +

+

+ Convert any image into beautifully optimized covers for Kindle, Kobo, and other e-ink devices. + Automatic greyscale conversion, dithering, and perfect sizing. +

+ +
+ + +
+ +
+
+
+
+ + +
+
+
+

How It Works

+

Everything you need to create perfect e-reader screensavers

+
+ +
+ +
+
+ +
+

E-ink Optimized

+

+ Greyscale conversion, Floyd-Steinberg dithering, and quantization tuned specifically for e-ink displays. +

+
+ + +
+
+ +
+

Device Presets

+

+ Pre-configured settings for Kindle, Kobo, reMarkable, and more. Create custom presets for any device. +

+
+ + +
+
+ +
+

Fine-Tuned Control

+

+ Adjust brightness, contrast, gamma, sharpening, and crop settings per image for perfect results. +

+
+ + +
+
+ +
+

100% Private

+

+ Runs entirely in your browser using WebAssembly. Your images never leave your device. +

+
+ + +
+
+ +
+

Batch Processing

+

+ Drop multiple images at once and download them all as a convenient ZIP archive. +

+
+ + +
+
+ +
+

Real-time Preview

+

+ Compare original and processed images side-by-side with an interactive slider. +

+
+
+
+
+ + +
+
+
+

Optimized for E-ink

+

+ E-ink displays work differently than regular screens. Here's how we optimize your images for the best results. +

+
+ +
+ +
+
+

Dithering Simulates More Shades

+

+ E-ink displays typically only show 16 grey levels. Without dithering, gradients appear as harsh bands. + Dithering uses patterns of dots to simulate smooth transitions, making images look natural on limited displays. +

+

+ We support Floyd-Steinberg (smooth, natural) and Atkinson (higher contrast, retro) algorithms. +

+
+
+ +
+
+ + +
+
+ +
+ {#each [2, 4, 16] as levels} +
+ + {#each Array(levels) as _, i} + + {/each} + +

{levels} levels

+
+ {/each} +
+
+
+

Grey Levels Match Your Device

+

+ Different e-readers support different numbers of grey levels. Basic Kindles show 16 levels, + while premium displays may show 256. We quantize your images to match your device's capabilities. +

+

+ More levels = smoother gradients, but dithering can make even 16 levels look great. +

+
+
+ + +
+
+

Perfect Sizing for Sharp Results

+

+ E-ink displays render at their native resolution. Images that don't match get scaled by the device, + often poorly. We resize your images to exactly match your e-reader's screen dimensions. +

+

+ Choose from cover (fill screen, crop edges) or contain (fit entirely, may letterbox) modes. +

+
+
+
+ +
+
+
+
+

Wrong size

+
+ +
+ +
+ +
+
+

Perfect fit

+
+
+
+
+
+
+
+ +
+ +
+

Convert Your Images

+

Drop your images below and download optimized screensavers

+
+ + +
+ +
+
+ + +
+ +
+ +
+ + +
+
+
+ + +
+ +
+ + + {#if imagesStore.hasImages} +
+ +
+
+

+ Images ({imagesStore.images.length}) +

+ + +
+ + +
+
+ View: +
+ + + +
+
+ + {#if imagesStore.completeCount > 0} + + {/if} +
+
+ + +
+ {:else} + +
+ +

+ No images yet. Drop some images above to get started. +

+
+ {/if} +
+
+ + + +
+ + + + + + diff --git a/src/routes/layout.css b/src/routes/layout.css index 9a76bbe..ec5bb35 100644 --- a/src/routes/layout.css +++ b/src/routes/layout.css @@ -3,7 +3,7 @@ @plugin '@tailwindcss/typography'; @theme { - /* Colors - e-ink inspired palette */ + /* Colors - Minimal palette, e-ink inspired */ --color-ink: #1a1a1a; --color-paper: #fafafa; --color-grey-50: #f5f5f5; @@ -22,7 +22,7 @@ --font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif; --font-mono: 'JetBrains Mono', ui-monospace, monospace; - /* Shadows */ + /* Shadows - Subtle, paper-like */ --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.06); --shadow-card-hover: 0 4px 6px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.05); --shadow-modal: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); @@ -33,6 +33,7 @@ --radius-lg: 0.75rem; } +/* Base styles */ html { scroll-behavior: smooth; } @@ -41,10 +42,9 @@ body { @apply bg-paper text-ink font-sans antialiased; } -/* Hero e-reader floating animation */ +/* Floating animation for hero mockup */ @keyframes float { - 0%, - 100% { + 0%, 100% { transform: translateY(0) rotate(3deg); } 50% { diff --git a/static/cover.jpg b/static/cover.jpg new file mode 100644 index 0000000..e080e33 Binary files /dev/null and b/static/cover.jpg differ