From cfa48395a326e4b033ed7fab354a5cc6efd93d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20=C4=B0pek?= Date: Tue, 27 Jan 2026 22:43:42 +0300 Subject: [PATCH] feat: AI-powered SRT subtitle translator with marker-based alignment - Smart chunking with [B#] markers for precise block-to-timestamp mapping - 50 parallel requests for blazing fast translation - Custom instruction support for domain-specific terminology - Retry with exponential backoff for reliability - Dark theme UI with real-time progress visualization - Default model: google/gemini-3-flash-preview for best performance/cost - Supports 20+ languages via OpenRouter API --- README.md | 145 ++++++++++++ assets/ui.webp | Bin 0 -> 65732 bytes index.html | 142 ++++++++++++ src/app.js | 612 +++++++++++++++++++++++++++++++++++++++++++++++++ src/styles.css | 385 +++++++++++++++++++++++++++++++ 5 files changed, 1284 insertions(+) create mode 100644 README.md create mode 100644 assets/ui.webp create mode 100644 index.html create mode 100644 src/app.js create mode 100644 src/styles.css diff --git a/README.md b/README.md new file mode 100644 index 0000000..7909f95 --- /dev/null +++ b/README.md @@ -0,0 +1,145 @@ +# SRT Translator + +A powerful, browser-based tool for translating SRT subtitle files using AI. Built for speed, accuracy, and ease of use. + +![SRT Translator UI](assets/ui.webp) + +## 🎯 What Problem Does It Solve? + +Translating subtitles is tedious and expensive: +- **Manual translation** is slow and costly +- **Generic AI translation** loses subtitle timing and structure +- **Existing tools** don't handle the unique challenges of SRT format (split sentences, timing markers, etc.) + +**SRT Translator** solves this by: +1. Preserving all timestamps exactly as-is +2. Using a smart marker system to maintain line-by-line correspondence +3. Processing chunks in parallel for maximum speed +4. Allowing custom instructions for domain-specific terminology + +## ✨ Features + +- **Drag & Drop** - Just drop your SRT file and go +- **50 Parallel Requests** - Blazing fast translation +- **Smart Chunking** - Processes 75 subtitle blocks per request for optimal speed/accuracy balance +- **Marker-Based Alignment** - Each subtitle block stays aligned with its timestamp +- **Custom Instructions** - Add context-specific translation rules (e.g., "Use informal 'sen' instead of formal 'siz'") +- **Custom Model Support** - Use any model available on OpenRouter +- **Multi-Language** - Translate to 15+ popular languages +- **Real-Time Progress** - Visual feedback for each chunk's status +- **Retry with Backoff** - Automatic retry on failures + +## 🚀 Quick Start + +1. Open `index.html` in your browser +2. Enter your [OpenRouter API key](https://openrouter.ai/keys) +3. Drop an SRT file +4. Select target language +5. Click "Translate" +6. Download your translated SRT + +## ⚙️ Configuration + +| Setting | Default | Description | +|---------|---------|-------------| +| **Model** | `google/gemini-3-flash-preview` | AI model for translation | +| **Chunk Size** | 75 | Number of subtitle blocks per API request | +| **Parallel Requests** | 50 | Maximum concurrent API calls | + +### Recommended Model + +For the best **performance/cost** balance, we recommend: + +``` +google/gemini-3-flash-preview +``` + +This model offers: +- Fast response times +- Excellent instruction following +- Great translation quality +- Cost-effective pricing + +Other options: +- `google/gemini-2.5-flash` - Slightly cheaper, good quality +- `anthropic/claude-3.5-sonnet` - Premium quality, higher cost +- `google/gemini-2.5-flash-lite` - Budget option, may have lower accuracy + +## 🧠 How It Works + +### The Marker System + +Traditional AI translation loses track of which text belongs to which subtitle block. We solve this with markers: + +**Input to AI:** +``` +[B1] I don't think OpenAI will +[B2] be around in 5 years. +[B3] They're burning cash. +``` + +**AI Output:** +``` +[B1] OpenAI'ın var olacağını +[B2] 5 yıl içinde sanmıyorum. +[B3] Paralarını yakıyorlar. +``` + +Each `[B#]` marker ensures the translated text maps back to the correct timestamp. + +### Translation Priority + +1. **Line Structure** (Mandatory) - Each marker line stays separate +2. **Natural Translation** - Idiomatic, not word-for-word +3. **Word Count** (Soft) - Similar length per line when possible + +## 📁 Project Structure + +``` +srt-translator/ +├── index.html # Main UI entry point +├── README.md # Documentation +├── src/ +│ ├── app.js # Core application logic +│ └── styles.css # Dark theme styling +└── assets/ + └── ui.webp # UI screenshot +``` + +## 🔧 Custom Instructions Examples + +**Turkish informal:** +``` +Türkçe çeviride "siz" yerine "sen" formu kullan. +``` + +**Technical content:** +``` +Keep technical terms like "API", "SDK", "cache" untranslated. +``` + +**YouTube tone:** +``` +Use casual, engaging language suitable for YouTube videos. +``` + +## 📝 API Requirements + +- **Provider:** [OpenRouter](https://openrouter.ai) +- **API Key:** Get one at https://openrouter.ai/keys +- **Models:** Any chat completion model on OpenRouter + +## 🛡️ Privacy + +- Your API key is stored locally in your browser (localStorage) +- SRT files are processed client-side +- Only subtitle text is sent to the AI API +- No data is stored on any server + +## 📄 License + +MIT License - Use freely for personal or commercial projects. + +--- + +Built with ❤️ for content creators who need fast, accurate subtitle translations. diff --git a/assets/ui.webp b/assets/ui.webp new file mode 100644 index 0000000000000000000000000000000000000000..2659000a60f5190d5839a3b85d8765de5c86e5b8 GIT binary patch literal 65732 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 M#E`6YwyoE~ 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 + + + + + + SRT Translator + + + + +
+
+

SRT Translator

+

AI-Powered Subtitle Translation via OpenRouter

+
+ +
+
API Configuration
+ +
+ +
+
+ +
+
Translation Settings
+
+ + + +
+
+ +
+
+ +
+
Upload SRT File
+
+ + + +

Drop your .srt file here or click to browse

+ +
+ +
+ + + + + + +
+ + + + + \ No newline at end of file diff --git a/src/app.js b/src/app.js new file mode 100644 index 0000000..840b756 --- /dev/null +++ b/src/app.js @@ -0,0 +1,612 @@ +// ============================================ +// SRT Translator - App Logic +// ============================================ + +const DEFAULT_MODEL = 'google/gemini-3-flash-preview'; +const DEFAULT_PARALLEL = 50; +const MAX_RETRIES = 3; +const RETRY_DELAY_MS = 1000; // 1 second +const REQUEST_TIMEOUT_MS = 30000; // 30 seconds + +// State +let srtContent = ''; +let srtBlocks = []; +let translatedResult = ''; +let originalFileName = ''; + +// DOM Elements +const elements = { + dropZone: document.getElementById('dropZone'), + fileInput: document.getElementById('fileInput'), + dropText: document.getElementById('dropText'), + fileName: document.getElementById('fileName'), + translateBtn: document.getElementById('translateBtn'), + translateBtnText: document.getElementById('translateBtnText'), + progressCard: document.getElementById('progressCard'), + downloadBtn: document.getElementById('downloadBtn'), + apiKey: document.getElementById('apiKey'), + customModel: document.getElementById('customModel'), + targetLanguage: document.getElementById('targetLanguage'), + chunkSize: document.getElementById('chunkSize'), + parallelRequests: document.getElementById('parallelRequests'), + customInstructions: document.getElementById('customInstructions'), + chunksGrid: document.getElementById('chunksGrid'), + logContainer: document.getElementById('logContainer'), + progressFill: document.getElementById('progressFill'), + progressPercent: document.getElementById('progressPercent'), + totalChunks: document.getElementById('totalChunks'), + completedChunks: document.getElementById('completedChunks'), + failedChunks: document.getElementById('failedChunks') +}; + +// ============================================ +// Initialization +// ============================================ + +function init() { + loadSavedSettings(); + setupEventListeners(); +} + +function loadSavedSettings() { + const savedApiKey = localStorage.getItem('srt_openrouter_api_key'); + const savedModel = localStorage.getItem('srt_custom_model'); + const savedLanguage = localStorage.getItem('srt_target_language'); + const savedChunkSize = localStorage.getItem('srt_chunk_size'); + const savedParallel = localStorage.getItem('srt_parallel_requests'); + const savedInstructions = localStorage.getItem('srt_custom_instructions'); + + if (savedApiKey) elements.apiKey.value = savedApiKey; + if (savedModel) elements.customModel.value = savedModel; + if (savedLanguage) elements.targetLanguage.value = savedLanguage; + if (savedChunkSize) elements.chunkSize.value = savedChunkSize; + if (savedParallel) elements.parallelRequests.value = savedParallel; + else elements.parallelRequests.value = DEFAULT_PARALLEL; + if (savedInstructions) elements.customInstructions.value = savedInstructions; +} + +function saveSettings() { + localStorage.setItem('srt_openrouter_api_key', elements.apiKey.value); + localStorage.setItem('srt_custom_model', elements.customModel.value); + localStorage.setItem('srt_target_language', elements.targetLanguage.value); + localStorage.setItem('srt_chunk_size', elements.chunkSize.value); + localStorage.setItem('srt_parallel_requests', elements.parallelRequests.value); + localStorage.setItem('srt_custom_instructions', elements.customInstructions.value); +} + +// ============================================ +// Event Listeners +// ============================================ + +function setupEventListeners() { + // File upload + elements.dropZone.addEventListener('click', () => elements.fileInput.click()); + elements.dropZone.addEventListener('dragover', handleDragOver); + elements.dropZone.addEventListener('dragleave', handleDragLeave); + elements.dropZone.addEventListener('drop', handleDrop); + elements.fileInput.addEventListener('change', handleFileSelect); + + // Settings changes + elements.apiKey.addEventListener('input', () => { + saveSettings(); + updateTranslateButton(); + }); + elements.customModel.addEventListener('change', saveSettings); + elements.targetLanguage.addEventListener('change', () => { + saveSettings(); + updateTranslateButtonText(); + }); + elements.chunkSize.addEventListener('change', saveSettings); + elements.parallelRequests.addEventListener('change', saveSettings); + elements.customInstructions.addEventListener('change', saveSettings); + + // Actions + elements.translateBtn.addEventListener('click', startTranslation); + elements.downloadBtn.addEventListener('click', downloadResult); + + // Initial UI state + updateTranslateButton(); + updateTranslateButtonText(); +} + +function handleDragOver(e) { + e.preventDefault(); + elements.dropZone.classList.add('dragover'); +} + +function handleDragLeave() { + elements.dropZone.classList.remove('dragover'); +} + +function handleDrop(e) { + e.preventDefault(); + elements.dropZone.classList.remove('dragover'); + const file = e.dataTransfer.files[0]; + if (file && file.name.endsWith('.srt')) { + processFile(file); + } +} + +function handleFileSelect(e) { + const file = e.target.files[0]; + if (file) processFile(file); +} + +// ============================================ +// File Processing +// ============================================ + +function processFile(file) { + originalFileName = file.name; + const reader = new FileReader(); + + reader.onload = (e) => { + srtContent = e.target.result; + srtBlocks = parseSRT(srtContent); + + elements.dropText.textContent = `Loaded: ${srtBlocks.length} subtitle blocks`; + elements.fileName.textContent = file.name; + elements.fileName.classList.remove('hidden'); + + updateTranslateButton(); + }; + + reader.readAsText(file); +} + +function parseSRT(content) { + const blocks = []; + const lines = content.split('\n'); + let currentBlock = null; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); + + if (/^\d+$/.test(line)) { + if (currentBlock) { + blocks.push(currentBlock); + } + currentBlock = { + index: parseInt(line), + timestamp: '', + text: [] + }; + } else if (currentBlock && line.includes('-->')) { + currentBlock.timestamp = line; + } else if (currentBlock && line !== '') { + currentBlock.text.push(line); + } + } + + if (currentBlock) { + blocks.push(currentBlock); + } + + return blocks; +} + +// ============================================ +// Smart Chunking +// ============================================ + +function createSmartChunks(blocks, targetSize) { + const chunks = []; + let currentChunk = []; + + for (let i = 0; i < blocks.length; i++) { + currentChunk.push(blocks[i]); + + if (currentChunk.length >= targetSize) { + let splitIndex = -1; + + // Look for sentence ending in last 10 blocks + for (let j = currentChunk.length - 1; j >= Math.max(0, currentChunk.length - 10); j--) { + const text = currentChunk[j].text.join(' '); + if (/[.!?][\s"']*$/.test(text)) { + splitIndex = j; + break; + } + } + + if (splitIndex === -1) { + chunks.push([...currentChunk]); + currentChunk = []; + } else { + chunks.push(currentChunk.slice(0, splitIndex + 1)); + currentChunk = currentChunk.slice(splitIndex + 1); + } + } + } + + if (currentChunk.length > 0) { + chunks.push(currentChunk); + } + + return chunks; +} + +function blocksToSRT(blocks) { + return blocks.map(block => { + return `${block.index}\n${block.timestamp}\n${block.text.join('\n')}`; + }).join('\n\n'); +} + +// ============================================ +// UI Updates +// ============================================ + +function updateTranslateButton() { + elements.translateBtn.disabled = !srtContent || !elements.apiKey.value; +} + +function updateTranslateButtonText() { + const lang = elements.targetLanguage.value; + elements.translateBtnText.textContent = `Translate to ${lang}`; +} + +function log(message, type = 'info') { + const time = new Date().toLocaleTimeString(); + const entry = document.createElement('div'); + entry.className = 'log-entry'; + entry.innerHTML = `${time}${message}`; + elements.logContainer.insertBefore(entry, elements.logContainer.firstChild); +} + +function updateProgress(completed, failed, total) { + elements.completedChunks.textContent = completed; + elements.failedChunks.textContent = failed; + + const percent = Math.round((completed + failed) / total * 100); + elements.progressPercent.textContent = `${percent}%`; + elements.progressFill.style.width = `${percent}%`; +} + +function setChunkStatus(index, status) { + const indicator = document.querySelector(`[data-chunk="${index}"]`); + if (indicator) { + indicator.className = `chunk-indicator ${status}`; + } +} + +// ============================================ +// Translation API +// ============================================ + +function buildSystemPrompt(targetLanguage, customInstructions) { + let prompt = `You are an expert subtitle translator for ${targetLanguage}. + +INPUT: Lines starting with markers [B1], [B2], etc. + +PRIORITY ORDER (highest to lowest): +1. LINE STRUCTURE (MANDATORY): Each [B#] line stays separate. Never merge or swap content between lines. +2. NATURAL TRANSLATION: Translate idiomatically, not word-for-word. Omit unnecessary pronouns if the language allows it. +3. WORD COUNT (SOFT): Try to keep similar word count per line, but naturalness comes first. + +RULES: +- Keep [B#] markers at the start of each output line +- Return EXACTLY the same number of lines as input +- Do NOT add any annotations, word counts, or comments +- Just output the translated text with markers, nothing else + +EXAMPLE: +Input: +[B1] I don't think OpenAI will +[B2] be around in 5 years. + +Output: +[B1] OpenAI'ın var olacağını +[B2] 5 yıl içinde sanmıyorum. + +CRITICAL: [B1] → [B1], [B2] → [B2]. Never shift content.`; + + if (customInstructions && customInstructions.trim()) { + prompt += `\n\n⚠️ MANDATORY USER INSTRUCTIONS (MUST FOLLOW):\n${customInstructions.trim()}\n\nYou MUST follow these instructions exactly. They override any conflicting rules.`; + } + + return prompt; +} + +// Extract text with block markers - one per line +function extractTextWithMarkers(blocks) { + return blocks.map((block, i) => `[B${i + 1}] ${block.text.join(' ')}`).join('\n'); +} + +// Parse translated text with markers back into blocks +function parseMarkedTranslation(blocks, translatedText) { + const result = []; + + for (let i = 0; i < blocks.length; i++) { + const block = blocks[i]; + const markerStart = `[B${i + 1}]`; + const markerEnd = `[B${i + 2}]`; + + const startIdx = translatedText.indexOf(markerStart); + if (startIdx === -1) { + // Marker not found, use original + result.push({ + index: block.index, + timestamp: block.timestamp, + text: [block.text.join(' ')] + }); + continue; + } + + const textStart = startIdx + markerStart.length; + let textEnd; + + if (i === blocks.length - 1) { + textEnd = translatedText.length; + } else { + const endIdx = translatedText.indexOf(markerEnd); + textEnd = endIdx !== -1 ? endIdx : translatedText.length; + } + + const blockText = translatedText.substring(textStart, textEnd).trim(); + + result.push({ + index: block.index, + timestamp: block.timestamp, + text: [blockText] + }); + } + + return result; +} + +async function translateChunk(chunk, index, apiKey, model, targetLanguage, customInstructions) { + // Extract text with block markers + const markedText = extractTextWithMarkers(chunk); + const systemPrompt = buildSystemPrompt(targetLanguage, customInstructions); + + console.log(`[Chunk ${index + 1}] Sending API request...`); + + // Create abort controller for timeout + const controller = new AbortController(); + const timeoutId = setTimeout(() => { + console.log(`[Chunk ${index + 1}] TIMEOUT - aborting request`); + controller.abort(); + }, REQUEST_TIMEOUT_MS); + + try { + const response = await fetch('https://openrouter.ai/api/v1/chat/completions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${apiKey}` + }, + body: JSON.stringify({ + model: model, + messages: [ + { role: 'system', content: systemPrompt }, + { role: 'user', content: markedText } + ], + temperature: 0.3 + }), + signal: controller.signal + }); + + console.log(`[Chunk ${index + 1}] Response received, status: ${response.status}`); + clearTimeout(timeoutId); + + if (!response.ok) { + const error = await response.text(); + throw new Error(`API Error: ${response.status} - ${error}`); + } + + const data = await response.json(); + console.log(`[Chunk ${index + 1}] Parsed response, parsing markers...`); + const translatedText = data.choices[0].message.content; + + // Parse markers and map back to blocks + const translatedBlocks = parseMarkedTranslation(chunk, translatedText); + + // Convert back to SRT format + return translatedBlocks.map(block => { + return `${block.index}\n${block.timestamp}\n${block.text.join('\n')}`; + }).join('\n\n'); + + } catch (error) { + clearTimeout(timeoutId); + console.log(`[Chunk ${index + 1}] Error:`, error.name, error.message); + if (error.name === 'AbortError') { + throw new Error(`Request timed out after ${REQUEST_TIMEOUT_MS / 1000}s`); + } + throw error; + } +} + +// Retry wrapper with exponential backoff +async function translateChunkWithRetry(chunk, index, apiKey, model, targetLanguage, customInstructions, onRetry) { + let lastError; + + for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) { + try { + return await translateChunk(chunk, index, apiKey, model, targetLanguage, customInstructions); + } catch (error) { + lastError = error; + + if (attempt < MAX_RETRIES) { + const delay = RETRY_DELAY_MS * Math.pow(2, attempt - 1); // Exponential backoff + onRetry(index, attempt, delay, error.message); + await new Promise(resolve => setTimeout(resolve, delay)); + } + } + } + + throw lastError; +} + +// ============================================ +// Main Translation Flow +// ============================================ + +async function startTranslation() { + const apiKey = elements.apiKey.value; + if (!apiKey || !srtContent) return; + + // Get settings + const model = elements.customModel.value.trim() || DEFAULT_MODEL; + const targetLanguage = elements.targetLanguage.value; + const targetChunkSize = parseInt(elements.chunkSize.value) || 50; + const maxParallel = parseInt(elements.parallelRequests.value) || DEFAULT_PARALLEL; + const customInstructions = elements.customInstructions.value; + + // Create chunks + const chunks = createSmartChunks(srtBlocks, targetChunkSize); + + // Setup UI + elements.progressCard.classList.remove('hidden'); + elements.downloadBtn.classList.add('hidden'); + elements.translateBtn.disabled = true; + elements.logContainer.innerHTML = ''; + + elements.totalChunks.textContent = chunks.length; + + // Create chunk indicators + elements.chunksGrid.innerHTML = ''; + chunks.forEach((_, i) => { + const indicator = document.createElement('div'); + indicator.className = 'chunk-indicator pending'; + indicator.dataset.chunk = i; + indicator.textContent = i + 1; + elements.chunksGrid.appendChild(indicator); + }); + + log(`Starting translation to ${targetLanguage}...`, 'info'); + log(`Model: ${model}`, 'info'); + log(`Chunks: ${chunks.length} | Size: ~${targetChunkSize} | Parallel: ${maxParallel}`, 'info'); + + let completed = 0; + let failed = 0; + const results = new Array(chunks.length).fill(null); + + // Process a single chunk + async function processChunk(chunk, index) { + setChunkStatus(index, 'processing'); + const blockCount = chunk.length; + const textLength = chunk.reduce((sum, b) => sum + b.text.join(' ').length, 0); + + // Debug for problematic chunks + if (index === 107) { + log(`⚠️ CHUNK 108 DEBUG: ${blockCount} blocks, ${textLength} chars`, 'error'); + console.log('Chunk 108 content:', chunk); + } + + try { + log(`Chunk ${index + 1} starting (${blockCount} blocks, ${textLength} chars)...`, 'info'); + + const translated = await translateChunkWithRetry( + chunk, + index, + apiKey, + model, + targetLanguage, + customInstructions, + (idx, attempt, delay, errorMsg) => { + setChunkStatus(idx, 'error'); + log(`Chunk ${idx + 1} retry ${attempt}/${MAX_RETRIES}: ${errorMsg}`, 'error'); + setTimeout(() => setChunkStatus(idx, 'processing'), 100); + } + ); + + results[index] = translated; + completed++; + setChunkStatus(index, 'done'); + log(`Chunk ${index + 1} completed`, 'success'); + } catch (error) { + failed++; + setChunkStatus(index, 'error'); + log(`Chunk ${index + 1} FAILED: ${error.message}`, 'error'); + results[index] = blocksToSRT(chunk); + } + + updateProgress(completed, failed, chunks.length); + } + + // Process all chunks with controlled concurrency + const processing = new Set(); + let nextIndex = 0; + + async function processNext() { + while (nextIndex < chunks.length) { + const index = nextIndex++; + const chunk = chunks[index]; + + const promise = processChunk(chunk, index).finally(() => { + processing.delete(promise); + }); + processing.add(promise); + + // If at max concurrency, wait for one to complete + if (processing.size >= maxParallel) { + await Promise.race(processing); + } + } + // Wait for remaining + await Promise.all(processing); + } + + await processNext(); + + // Merge results + translatedResult = results.join('\n\n'); + + const status = completed === chunks.length ? 'success' : 'error'; + log(`Translation complete! ${completed} successful, ${failed} failed`, status); + + elements.translateBtn.disabled = false; + elements.downloadBtn.classList.remove('hidden'); +} + +// ============================================ +// Download +// ============================================ + +function downloadResult() { + const lang = elements.targetLanguage.value; + const langCode = getLanguageCode(lang); + + const blob = new Blob([translatedResult], { type: 'text/plain;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = originalFileName.replace('.srt', `_${langCode}.srt`); + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + + log(`Downloaded: ${a.download}`, 'success'); +} + +function getLanguageCode(language) { + const codes = { + 'Turkish': 'TR', + 'Spanish': 'ES', + 'French': 'FR', + 'German': 'DE', + 'Italian': 'IT', + 'Portuguese': 'PT', + 'Russian': 'RU', + 'Japanese': 'JA', + 'Korean': 'KO', + 'Chinese (Simplified)': 'ZH-CN', + 'Chinese (Traditional)': 'ZH-TW', + 'Arabic': 'AR', + 'Hindi': 'HI', + 'Dutch': 'NL', + 'Polish': 'PL', + 'Swedish': 'SV', + 'Vietnamese': 'VI', + 'Thai': 'TH', + 'Indonesian': 'ID', + 'Greek': 'EL' + }; + return codes[language] || 'TRANSLATED'; +} + +// ============================================ +// Initialize App +// ============================================ + +init(); diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..01aa0e0 --- /dev/null +++ b/src/styles.css @@ -0,0 +1,385 @@ +:root { + --bg-primary: #0a0a0f; + --bg-secondary: #12121a; + --bg-tertiary: #1a1a25; + --accent: #6366f1; + --accent-glow: rgba(99, 102, 241, 0.3); + --text-primary: #f1f5f9; + --text-secondary: #94a3b8; + --success: #22c55e; + --warning: #f59e0b; + --error: #ef4444; + --border: rgba(255, 255, 255, 0.08); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + background: var(--bg-primary); + color: var(--text-primary); + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + padding: 2rem; +} + +.container { + width: 100%; + max-width: 800px; +} + +header { + text-align: center; + margin-bottom: 2.5rem; +} + +h1 { + font-size: 2.5rem; + font-weight: 700; + background: linear-gradient(135deg, var(--accent) 0%, #a855f7 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + margin-bottom: 0.5rem; +} + +.subtitle { + color: var(--text-secondary); + font-size: 1rem; +} + +.card { + background: var(--bg-secondary); + border: 1px solid var(--border); + border-radius: 16px; + padding: 1.5rem; + margin-bottom: 1.5rem; +} + +.card-title { + font-size: 0.875rem; + font-weight: 600; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 1rem; +} + +.input-field, +.select-field, +.textarea-field { + width: 100%; + padding: 0.875rem 1rem; + background: var(--bg-tertiary); + border: 1px solid var(--border); + border-radius: 10px; + color: var(--text-primary); + font-size: 0.95rem; + font-family: inherit; + transition: all 0.2s ease; +} + +.input-field:focus, +.select-field:focus, +.textarea-field:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-glow); +} + +.select-field { + cursor: pointer; + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2394a3b8'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 1rem center; + background-size: 1rem; + padding-right: 2.5rem; +} + +.select-field option { + background: var(--bg-secondary); + color: var(--text-primary); +} + +.textarea-field { + resize: vertical; + min-height: 80px; +} + +.model-row { + margin-top: 1rem; +} + +.settings-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; +} + +.settings-grid label span, +.instruction-section label span { + display: block; + font-size: 0.8rem; + color: var(--text-secondary); + margin-bottom: 0.5rem; +} + +.instruction-section { + margin-top: 1rem; +} + +.drop-zone { + border: 2px dashed var(--border); + border-radius: 12px; + padding: 3rem 2rem; + text-align: center; + cursor: pointer; + transition: all 0.3s ease; + background: var(--bg-tertiary); +} + +.drop-zone:hover, +.drop-zone.dragover { + border-color: var(--accent); + background: rgba(99, 102, 241, 0.05); +} + +.drop-zone svg { + width: 48px; + height: 48px; + color: var(--text-secondary); + margin-bottom: 1rem; +} + +.drop-zone p { + color: var(--text-secondary); + margin-bottom: 0.5rem; +} + +.drop-zone .filename { + color: var(--accent); + font-weight: 600; +} + +.hidden { + display: none !important; +} + +.btn { + width: 100%; + padding: 1rem 1.5rem; + font-size: 1rem; + font-weight: 600; + border: none; + border-radius: 10px; + cursor: pointer; + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; +} + +.btn-primary { + background: linear-gradient(135deg, var(--accent) 0%, #8b5cf6 100%); + color: white; + box-shadow: 0 4px 15px var(--accent-glow); +} + +.btn-primary:hover:not(:disabled) { + transform: translateY(-2px); + box-shadow: 0 6px 20px var(--accent-glow); +} + +.btn-primary:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.btn-success { + background: linear-gradient(135deg, var(--success) 0%, #16a34a 100%); + color: white; + margin-top: 1.5rem; +} + +.btn-success:hover { + transform: translateY(-2px); +} + +.progress-section { + margin-top: 1.5rem; +} + +.progress-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.75rem; +} + +.progress-label { + font-size: 0.875rem; + color: var(--text-secondary); +} + +.progress-percentage { + font-size: 0.875rem; + font-weight: 600; + color: var(--accent); +} + +.progress-bar { + height: 8px; + background: var(--bg-tertiary); + border-radius: 4px; + overflow: hidden; +} + +.progress-fill { + height: 100%; + background: linear-gradient(90deg, var(--accent) 0%, #a855f7 100%); + border-radius: 4px; + transition: width 0.3s ease; + width: 0%; +} + +.chunks-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(40px, 1fr)); + gap: 0.5rem; + margin-top: 1rem; +} + +.chunk-indicator { + aspect-ratio: 1; + border-radius: 6px; + background: var(--bg-tertiary); + border: 1px solid var(--border); + display: flex; + align-items: center; + justify-content: center; + font-size: 0.7rem; + font-weight: 600; + color: var(--text-secondary); + transition: all 0.3s ease; +} + +.chunk-indicator.pending { + background: var(--bg-tertiary); +} + +.chunk-indicator.processing { + background: rgba(245, 158, 11, 0.2); + border-color: var(--warning); + color: var(--warning); + animation: pulse 1.5s infinite; +} + +.chunk-indicator.done { + background: rgba(34, 197, 94, 0.2); + border-color: var(--success); + color: var(--success); +} + +.chunk-indicator.error { + background: rgba(239, 68, 68, 0.2); + border-color: var(--error); + color: var(--error); +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.6; } +} + +.stats { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; + margin-top: 1rem; +} + +.stat { + background: var(--bg-tertiary); + padding: 1rem; + border-radius: 10px; + text-align: center; +} + +.stat-value { + font-size: 1.5rem; + font-weight: 700; + color: var(--accent); +} + +.stat-label { + font-size: 0.75rem; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.05em; + margin-top: 0.25rem; +} + +.log-container { + max-height: 200px; + overflow-y: auto; + background: var(--bg-tertiary); + border-radius: 10px; + padding: 1rem; + font-family: 'Monaco', 'Menlo', monospace; + font-size: 0.8rem; + margin-top: 1rem; +} + +.log-entry { + padding: 0.25rem 0; + border-bottom: 1px solid var(--border); +} + +.log-entry:last-child { + border-bottom: none; +} + +.log-time { + color: var(--text-secondary); + margin-right: 0.5rem; +} + +.log-success { color: var(--success); } +.log-error { color: var(--error); } +.log-info { color: var(--accent); } + +/* Responsive */ +@media (max-width: 640px) { + body { + padding: 1rem; + } + + h1 { + font-size: 1.75rem; + } + + .settings-grid { + grid-template-columns: 1fr; + } + + .stats { + grid-template-columns: repeat(3, 1fr); + gap: 0.5rem; + } + + .stat { + padding: 0.75rem 0.5rem; + } + + .stat-value { + font-size: 1.25rem; + } +}