diff --git a/reference/index.html b/reference/index.html deleted file mode 100644 index 13d1022..0000000 --- a/reference/index.html +++ /dev/null @@ -1,288 +0,0 @@ - - - - - - Twitch Panels Creator - - - -
-
-
-

Twitch Panels

- -
-
- -
- -
-
-

Тексты панелей

- -
- - -
- -
-
- -
-

Настройки текста

- -
-
- -
- - 18 -
-
- -
- - -
- -
- -
- - #ffffff -
-
- -
- -
- - - -
-
- -
- -
- - 10 -
-
- -
- -
- - 0 -
-
-
-
-
- - -
-
-

Фоновое изображение

- -
-
- -
-
-
-
-
-
-
-
-
-
-
- -
- - - - -
- -
-
- -
- - 100 -
-
- -
- -
- - 100 -
-
-
-
-
- -
-
-

Панели 0

-
- - 0 / 0 - -
-
- -
-
-
- - - - -

Добавьте тексты для создания панелей

-
-
- -
- - -
-
-
-
-
-
- - - - diff --git a/reference/script.js b/reference/script.js deleted file mode 100644 index ba342db..0000000 --- a/reference/script.js +++ /dev/null @@ -1,625 +0,0 @@ -// State -let texts = []; -let panels = []; -let currentPanelIndex = 0; -let backgroundImage = null; -let originalImage = null; -let settings = { - fontSize: 18, - fontFamily: "Arial", - textColor: "#ffffff", - alignment: "left", - sidePadding: 10, - centerOffset: 0, - bgBrightness: 100, - bgContrast: 100, -}; - -// Crop state -let cropBox = { - x: 0, - y: 0, - width: 0, - height: 0, -}; -let isDragging = false; -let isResizing = false; -let resizeHandle = null; -let dragStart = { x: 0, y: 0 }; -let cropBoxStart = { x: 0, y: 0, width: 0, height: 0 }; - -// Initialize -document.addEventListener("DOMContentLoaded", () => { - initializeTheme(); - initializeEventListeners(); - loadDefaultBackground(); - addDefaultTexts(); -}); - -// Theme Management -function initializeTheme() { - const savedTheme = localStorage.getItem("theme") || "light"; - document.documentElement.setAttribute("data-theme", savedTheme); -} - -function toggleTheme() { - const currentTheme = document.documentElement.getAttribute("data-theme"); - const newTheme = currentTheme === "light" ? "dark" : "light"; - document.documentElement.setAttribute("data-theme", newTheme); - localStorage.setItem("theme", newTheme); -} - -// Event Listeners -function initializeEventListeners() { - // Theme toggle - document.getElementById("themeToggle").addEventListener("click", toggleTheme); - - // Add text - document.getElementById("addTextBtn").addEventListener("click", addText); - document.getElementById("panelTextInput").addEventListener("keypress", (e) => { - if (e.key === "Enter") addText(); - }); - - // Settings - document.getElementById("fontSize").addEventListener("input", (e) => { - settings.fontSize = parseInt(e.target.value); - document.getElementById("fontSizeValue").textContent = e.target.value; - updateAllPanels(); - }); - - document.getElementById("fontFamily").addEventListener("change", (e) => { - settings.fontFamily = e.target.value; - updateAllPanels(); - }); - - document.getElementById("textColor").addEventListener("input", (e) => { - settings.textColor = e.target.value; - document.getElementById("textColorValue").textContent = e.target.value; - updateAllPanels(); - }); - - document.querySelectorAll(".align-btn").forEach((btn) => { - btn.addEventListener("click", (e) => { - document.querySelectorAll(".align-btn").forEach((b) => b.classList.remove("active")); - btn.classList.add("active"); - settings.alignment = btn.dataset.align; - updateAllPanels(); - }); - }); - - document.getElementById("sidePadding").addEventListener("input", (e) => { - settings.sidePadding = parseInt(e.target.value); - document.getElementById("sidePaddingValue").textContent = e.target.value; - updateAllPanels(); - }); - - document.getElementById("centerOffset").addEventListener("input", (e) => { - settings.centerOffset = parseInt(e.target.value); - document.getElementById("centerOffsetValue").textContent = e.target.value; - updateAllPanels(); - }); - - // Background controls - document.getElementById("uploadBgBtn").addEventListener("click", () => { - document.getElementById("bgImageInput").click(); - }); - - document.getElementById("bgImageInput").addEventListener("change", handleBackgroundUpload); - - document.getElementById("bgBrightness").addEventListener("input", (e) => { - settings.bgBrightness = parseInt(e.target.value); - document.getElementById("bgBrightnessValue").textContent = e.target.value; - drawCropCanvas(); - updateAllPanels(); - }); - - document.getElementById("bgContrast").addEventListener("input", (e) => { - settings.bgContrast = parseInt(e.target.value); - document.getElementById("bgContrastValue").textContent = e.target.value; - drawCropCanvas(); - updateAllPanels(); - }); - - document.getElementById("resetCropBtn").addEventListener("click", resetCrop); - - // Crop canvas events - const canvas = document.getElementById("cropCanvas"); - canvas.addEventListener("mousedown", handleCropMouseDown); - canvas.addEventListener("mousemove", handleCropMouseMove); - canvas.addEventListener("mouseup", handleCropMouseUp); - canvas.addEventListener("mouseleave", handleCropMouseUp); - - // Panel navigation - document.getElementById("prevPanel").addEventListener("click", () => { - if (currentPanelIndex > 0) { - currentPanelIndex--; - displayCurrentPanel(); - } - }); - - document.getElementById("nextPanel").addEventListener("click", () => { - if (currentPanelIndex < panels.length - 1) { - currentPanelIndex++; - displayCurrentPanel(); - } - }); - - document.getElementById("downloadCurrentBtn").addEventListener("click", downloadCurrentPanel); - document.getElementById("downloadAllBtn").addEventListener("click", downloadAllPanels); -} - -// Load default background -function loadDefaultBackground() { - const img = new Image(); - img.crossOrigin = "anonymous"; - img.onload = () => { - originalImage = img; - backgroundImage = img; - initializeCropBox(); - drawCropCanvas(); - updateAllPanels(); - }; - img.src = "https://images.unsplash.com/photo-1579546929518-9e396f3cc809?w=800&h=250&fit=crop"; -} - -// Add default texts -function addDefaultTexts() { - const defaultTexts = ["links", "about me", "projects"]; - defaultTexts.forEach((text) => { - texts.push({ id: Date.now() + Math.random(), text }); - }); - renderTexts(); - updateAllPanels(); -} - -// Add text -function addText() { - const input = document.getElementById("panelTextInput"); - const text = input.value.trim(); - - if (!text) return; - - texts.push({ id: Date.now(), text }); - input.value = ""; - - renderTexts(); - updateAllPanels(); -} - -// Render texts list -function renderTexts() { - const container = document.getElementById("textsList"); - - if (texts.length === 0) { - container.innerHTML = ` -
- - - -

Добавьте тексты

-
- `; - return; - } - - container.innerHTML = texts - .map( - (item) => ` -
- - -
- `, - ) - .join(""); -} - -// Update text -window.updateText = function (id, newText) { - const item = texts.find((t) => t.id === id); - if (item) { - item.text = newText; - updateAllPanels(); - } -}; - -// Delete text -window.deleteText = function (id) { - texts = texts.filter((t) => t.id !== id); - renderTexts(); - updateAllPanels(); -}; -// Handle background upload -function handleBackgroundUpload(e) { - const file = e.target.files[0]; - if (!file) return; - - const reader = new FileReader(); - reader.onload = (event) => { - const img = new Image(); - img.onload = () => { - originalImage = img; - backgroundImage = img; - initializeCropBox(); - drawCropCanvas(); - updateAllPanels(); - }; - img.src = event.target.result; - }; - reader.readAsDataURL(file); -} - -// Initialize crop box -function initializeCropBox() { - const canvas = document.getElementById("cropCanvas"); - const container = document.getElementById("cropCanvasContainer"); - const rect = container.getBoundingClientRect(); - - canvas.width = rect.width; - canvas.height = rect.height; - - // Set crop box to full canvas initially - cropBox = { - x: 0, - y: 0, - width: canvas.width, - height: canvas.height, - }; - - updateCropBoxElement(); - document.getElementById("cropBox").classList.add("active"); -} - -// Draw crop canvas -function drawCropCanvas() { - const canvas = document.getElementById("cropCanvas"); - const ctx = canvas.getContext("2d"); - - if (!originalImage) return; - - ctx.clearRect(0, 0, canvas.width, canvas.height); - - // Apply filters - ctx.filter = `brightness(${settings.bgBrightness}%) contrast(${settings.bgContrast}%)`; - - // Draw image to fit canvas - const scale = Math.max(canvas.width / originalImage.width, canvas.height / originalImage.height); - const scaledWidth = originalImage.width * scale; - const scaledHeight = originalImage.height * scale; - const x = (canvas.width - scaledWidth) / 2; - const y = (canvas.height - scaledHeight) / 2; - - ctx.drawImage(originalImage, x, y, scaledWidth, scaledHeight); - ctx.filter = "none"; -} - -// Update crop box element -function updateCropBoxElement() { - const element = document.getElementById("cropBox"); - element.style.left = cropBox.x + "px"; - element.style.top = cropBox.y + "px"; - element.style.width = cropBox.width + "px"; - element.style.height = cropBox.height + "px"; -} - -// Crop mouse handlers -function handleCropMouseDown(e) { - const rect = e.target.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; - - // Check if clicking on a handle - const handles = document.querySelectorAll(".crop-handle"); - let clickedHandle = null; - - handles.forEach((handle) => { - const handleRect = handle.getBoundingClientRect(); - const handleX = handleRect.left - rect.left + handleRect.width / 2; - const handleY = handleRect.top - rect.top + handleRect.height / 2; - - if (Math.abs(x - handleX) < 10 && Math.abs(y - handleY) < 10) { - clickedHandle = handle.classList[1]; // Get handle class (nw, ne, etc.) - } - }); - - if (clickedHandle) { - isResizing = true; - resizeHandle = clickedHandle; - } else if ( - x >= cropBox.x && - x <= cropBox.x + cropBox.width && - y >= cropBox.y && - y <= cropBox.y + cropBox.height - ) { - isDragging = true; - } - - dragStart = { x, y }; - cropBoxStart = { ...cropBox }; -} - -function handleCropMouseMove(e) { - if (!isDragging && !isResizing) return; - - const rect = e.target.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; - const dx = x - dragStart.x; - const dy = y - dragStart.y; - - if (isDragging) { - cropBox.x = Math.max(0, Math.min(cropBoxStart.x + dx, rect.width - cropBox.width)); - cropBox.y = Math.max(0, Math.min(cropBoxStart.y + dy, rect.height - cropBox.height)); - } else if (isResizing) { - const minSize = 50; - - switch (resizeHandle) { - case "nw": - cropBox.x = Math.max( - 0, - Math.min(cropBoxStart.x + dx, cropBoxStart.x + cropBoxStart.width - minSize), - ); - cropBox.y = Math.max( - 0, - Math.min(cropBoxStart.y + dy, cropBoxStart.y + cropBoxStart.height - minSize), - ); - cropBox.width = cropBoxStart.width - (cropBox.x - cropBoxStart.x); - cropBox.height = cropBoxStart.height - (cropBox.y - cropBoxStart.y); - break; - case "ne": - cropBox.y = Math.max( - 0, - Math.min(cropBoxStart.y + dy, cropBoxStart.y + cropBoxStart.height - minSize), - ); - cropBox.width = Math.max( - minSize, - Math.min(cropBoxStart.width + dx, rect.width - cropBoxStart.x), - ); - cropBox.height = cropBoxStart.height - (cropBox.y - cropBoxStart.y); - break; - case "sw": - cropBox.x = Math.max( - 0, - Math.min(cropBoxStart.x + dx, cropBoxStart.x + cropBoxStart.width - minSize), - ); - cropBox.width = cropBoxStart.width - (cropBox.x - cropBoxStart.x); - cropBox.height = Math.max( - minSize, - Math.min(cropBoxStart.height + dy, rect.height - cropBoxStart.y), - ); - break; - case "se": - cropBox.width = Math.max( - minSize, - Math.min(cropBoxStart.width + dx, rect.width - cropBoxStart.x), - ); - cropBox.height = Math.max( - minSize, - Math.min(cropBoxStart.height + dy, rect.height - cropBoxStart.y), - ); - break; - case "n": - cropBox.y = Math.max( - 0, - Math.min(cropBoxStart.y + dy, cropBoxStart.y + cropBoxStart.height - minSize), - ); - cropBox.height = cropBoxStart.height - (cropBox.y - cropBoxStart.y); - break; - case "s": - cropBox.height = Math.max( - minSize, - Math.min(cropBoxStart.height + dy, rect.height - cropBoxStart.y), - ); - break; - case "w": - cropBox.x = Math.max( - 0, - Math.min(cropBoxStart.x + dx, cropBoxStart.x + cropBoxStart.width - minSize), - ); - cropBox.width = cropBoxStart.width - (cropBox.x - cropBoxStart.x); - break; - case "e": - cropBox.width = Math.max( - minSize, - Math.min(cropBoxStart.width + dx, rect.width - cropBoxStart.x), - ); - break; - } - } - - updateCropBoxElement(); -} - -function handleCropMouseUp() { - if (isDragging || isResizing) { - applyCrop(); - } - isDragging = false; - isResizing = false; - resizeHandle = null; -} - -// Apply crop -function applyCrop() { - if (!originalImage) return; - - const canvas = document.getElementById("cropCanvas"); - const tempCanvas = document.createElement("canvas"); - const tempCtx = tempCanvas.getContext("2d"); - - // Calculate scale factor - const scale = Math.max(canvas.width / originalImage.width, canvas.height / originalImage.height); - const scaledWidth = originalImage.width * scale; - const scaledHeight = originalImage.height * scale; - const offsetX = (canvas.width - scaledWidth) / 2; - const offsetY = (canvas.height - scaledHeight) / 2; - - // Calculate crop area in original image coordinates - const cropX = (cropBox.x - offsetX) / scale; - const cropY = (cropBox.y - offsetY) / scale; - const cropWidth = cropBox.width / scale; - const cropHeight = cropBox.height / scale; - - tempCanvas.width = cropWidth; - tempCanvas.height = cropHeight; - - tempCtx.drawImage( - originalImage, - cropX, - cropY, - cropWidth, - cropHeight, - 0, - 0, - cropWidth, - cropHeight, - ); - - const img = new Image(); - img.onload = () => { - backgroundImage = img; - updateAllPanels(); - }; - img.src = tempCanvas.toDataURL(); -} - -// Reset crop -function resetCrop() { - loadDefaultBackground(); -} -// Update all panels -function updateAllPanels() { - panels = texts.map((item) => ({ - id: item.id, - text: item.text, - })); - - currentPanelIndex = Math.min(currentPanelIndex, Math.max(0, panels.length - 1)); - displayCurrentPanel(); - updatePanelNavigation(); -} - -// Display current panel -function displayCurrentPanel() { - const container = document.getElementById("panelDisplay"); - - if (panels.length === 0) { - container.innerHTML = ` -
- - - - -

Добавьте тексты для создания панелей

-
- `; - return; - } - - const panel = panels[currentPanelIndex]; - container.innerHTML = ``; - - setTimeout(() => { - drawPanel(panel, "currentPanelCanvas"); - }, 0); -} - -// Update panel navigation -function updatePanelNavigation() { - document.getElementById("panelCount").textContent = panels.length; - document.getElementById("panelIndicator").textContent = - panels.length > 0 ? `${currentPanelIndex + 1} / ${panels.length}` : "0 / 0"; - - document.getElementById("prevPanel").disabled = currentPanelIndex === 0 || panels.length === 0; - document.getElementById("nextPanel").disabled = - currentPanelIndex >= panels.length - 1 || panels.length === 0; - document.getElementById("downloadCurrentBtn").disabled = panels.length === 0; - document.getElementById("downloadAllBtn").disabled = panels.length === 0; -} - -// Draw panel -function drawPanel(panel, canvasId) { - const canvas = document.getElementById(canvasId); - if (!canvas) return; - - const ctx = canvas.getContext("2d"); - canvas.width = 320; - canvas.height = 100; - - // Draw background - if (backgroundImage) { - ctx.filter = `brightness(${settings.bgBrightness}%) contrast(${settings.bgContrast}%)`; - - const scale = Math.max( - canvas.width / backgroundImage.width, - canvas.height / backgroundImage.height, - ); - const scaledWidth = backgroundImage.width * scale; - const scaledHeight = backgroundImage.height * scale; - const x = (canvas.width - scaledWidth) / 2; - const y = (canvas.height - scaledHeight) / 2; - - ctx.drawImage(backgroundImage, x, y, scaledWidth, scaledHeight); - ctx.filter = "none"; - } else { - ctx.fillStyle = "#667eea"; - ctx.fillRect(0, 0, canvas.width, canvas.height); - } - - // Draw text - ctx.font = `${settings.fontSize}px ${settings.fontFamily}`; - ctx.fillStyle = settings.textColor; - ctx.textBaseline = "middle"; - - const text = panel.text; - - let x; - if (settings.alignment === "left") { - x = settings.sidePadding; - ctx.textAlign = "left"; - } else if (settings.alignment === "center") { - x = canvas.width / 2 + settings.centerOffset; - ctx.textAlign = "center"; - } else { - x = canvas.width - settings.sidePadding; - ctx.textAlign = "right"; - } - - const y = canvas.height / 2; - ctx.fillText(text, x, y); -} - -// Download current panel -function downloadCurrentPanel() { - if (panels.length === 0) return; - - const canvas = document.getElementById("currentPanelCanvas"); - const panel = panels[currentPanelIndex]; - - if (canvas && panel) { - const link = document.createElement("a"); - link.download = `${panel.text}.png`; - link.href = canvas.toDataURL(); - link.click(); - } -} - -// Download all panels -function downloadAllPanels() { - panels.forEach((panel, index) => { - setTimeout(() => { - const tempCanvas = document.createElement("canvas"); - tempCanvas.id = `temp-canvas-${panel.id}`; - document.body.appendChild(tempCanvas); - - drawPanel(panel, tempCanvas.id); - - const link = document.createElement("a"); - link.download = `${panel.text}.png`; - link.href = tempCanvas.toDataURL(); - link.click(); - - document.body.removeChild(tempCanvas); - }, 100 * index); - }); -} diff --git a/reference/styles.css b/reference/styles.css deleted file mode 100644 index 415a5b5..0000000 --- a/reference/styles.css +++ /dev/null @@ -1,763 +0,0 @@ -:root { - --bg-primary: #ffffff; - --bg-secondary: #fafafa; - --bg-card: #ffffff; - --bg-hover: #f5f5f5; - - --text-primary: #1a1a1a; - --text-secondary: #666666; - --text-tertiary: #999999; - - --border-color: #e5e5e5; - --border-hover: #d4d4d4; - - --accent-primary: #6366f1; - --accent-hover: #4f46e5; - --accent-light: #eef2ff; - - --danger: #ef4444; - - --shadow: 0 1px 3px rgba(0, 0, 0, 0.08); - --shadow-md: 0 2px 8px rgba(0, 0, 0, 0.1); - - --radius: 8px; - --transition: all 0.15s ease; -} - -[data-theme="dark"] { - --bg-primary: #0a0a0a; - --bg-secondary: #1a1a1a; - --bg-card: #1a1a1a; - --bg-hover: #2a2a2a; - - --text-primary: #f5f5f5; - --text-secondary: #a3a3a3; - --text-tertiary: #737373; - - --border-color: #2a2a2a; - --border-hover: #3a3a3a; - - --accent-primary: #818cf8; - --accent-hover: #6366f1; - --accent-light: #312e81; - - --shadow: 0 1px 3px rgba(0, 0, 0, 0.3); - --shadow-md: 0 2px 8px rgba(0, 0, 0, 0.4); -} - -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", sans-serif; - background: var(--bg-primary); - color: var(--text-primary); - min-height: 100vh; - padding: 16px; - transition: var(--transition); - line-height: 1.5; -} - -.container { - max-width: 1400px; - margin: 0 auto; -} - -/* Header */ -.header { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - padding: 16px 20px; - border-radius: var(--radius); - margin-bottom: 16px; -} - -.header-content { - display: flex; - justify-content: space-between; - align-items: center; -} - -.header h1 { - font-size: 20px; - font-weight: 600; -} - -.theme-toggle { - width: 36px; - height: 36px; - border-radius: 6px; - border: none; - background: rgba(255, 255, 255, 0.2); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: var(--transition); - position: relative; -} - -.theme-toggle:hover { - background: rgba(255, 255, 255, 0.3); -} - -.theme-toggle svg { - width: 18px; - height: 18px; - position: absolute; - transition: var(--transition); -} - -.sun-icon { - opacity: 1; - transform: rotate(0deg); -} - -.moon-icon { - opacity: 0; - transform: rotate(90deg); -} - -[data-theme="dark"] .sun-icon { - opacity: 0; - transform: rotate(90deg); -} - -[data-theme="dark"] .moon-icon { - opacity: 1; - transform: rotate(0deg); -} - -/* Main Grid */ -.main-grid { - display: grid; - grid-template-columns: 1fr; - gap: 16px; -} - -@media (min-width: 1024px) { - .main-grid { - grid-template-columns: 1fr 1fr; - } -} - -/* Card */ -.card { - background: var(--bg-card); - border: 1px solid var(--border-color); - border-radius: var(--radius); - padding: 16px; - margin-bottom: 16px; - transition: var(--transition); -} - -.card-title { - font-size: 15px; - font-weight: 600; - color: var(--text-primary); - margin-bottom: 12px; - display: flex; - align-items: center; - gap: 8px; -} - -.card-header-row { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 12px; -} - -/* Badge */ -.badge { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 20px; - height: 20px; - padding: 0 6px; - background: var(--accent-light); - color: var(--accent-primary); - border-radius: 10px; - font-size: 11px; - font-weight: 600; -} - -/* Input Group */ -.input-group { - display: flex; - gap: 8px; - margin-bottom: 12px; -} - -.text-input { - flex: 1; - padding: 10px 12px; - border: 1px solid var(--border-color); - border-radius: var(--radius); - font-size: 14px; - background: var(--bg-primary); - color: var(--text-primary); - transition: var(--transition); - font-family: inherit; -} - -.text-input:focus { - outline: none; - border-color: var(--accent-primary); -} - -.text-input::placeholder { - color: var(--text-tertiary); -} - -/* Buttons */ -.btn { - padding: 10px 16px; - border: none; - border-radius: var(--radius); - font-size: 13px; - font-weight: 500; - cursor: pointer; - transition: var(--transition); - display: inline-flex; - align-items: center; - gap: 6px; - font-family: inherit; - white-space: nowrap; -} - -.btn svg { - width: 16px; - height: 16px; -} - -.btn-primary { - background: var(--accent-primary); - color: white; -} - -.btn-primary:hover:not(:disabled) { - background: var(--accent-hover); -} - -.btn-primary:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.btn-secondary { - background: var(--bg-secondary); - color: var(--text-primary); - border: 1px solid var(--border-color); -} - -.btn-secondary:hover { - background: var(--bg-hover); -} - -.btn-outline { - background: transparent; - color: var(--text-secondary); - border: 1px solid var(--border-color); -} - -.btn-outline:hover:not(:disabled) { - background: var(--bg-hover); - border-color: var(--accent-primary); - color: var(--accent-primary); -} - -.btn-outline:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.btn-delete { - background: var(--danger); - color: white; - padding: 6px 10px; - border: none; - border-radius: 4px; - cursor: pointer; - font-size: 14px; - font-weight: 600; - transition: var(--transition); - min-width: 32px; - display: flex; - align-items: center; - justify-content: center; -} - -.btn-delete:hover { - background: #dc2626; -} - -/* Texts List */ -.texts-list { - display: flex; - flex-direction: column; - gap: 8px; -} - -.text-item { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 10px; - background: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: var(--radius); - transition: var(--transition); -} - -.text-item:hover { - border-color: var(--border-hover); -} - -.text-item input { - flex: 1; - padding: 6px 10px; - border: 1px solid var(--border-color); - border-radius: 4px; - font-size: 14px; - background: var(--bg-primary); - color: var(--text-primary); - transition: var(--transition); - font-family: inherit; -} - -.text-item input:focus { - outline: none; - border-color: var(--accent-primary); -} - -/* Settings */ -.settings-grid { - display: flex; - flex-direction: column; - gap: 12px; -} - -.setting-row { - display: grid; - grid-template-columns: 100px 1fr; - align-items: center; - gap: 12px; -} - -.setting-label { - color: var(--text-secondary); - font-size: 13px; - font-weight: 500; -} - -.setting-control { - display: flex; - align-items: center; - gap: 10px; -} - -.slider { - flex: 1; - height: 4px; - border-radius: 2px; - background: var(--bg-secondary); - outline: none; - -webkit-appearance: none; - appearance: none; -} - -.slider::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 16px; - height: 16px; - border-radius: 50%; - background: var(--accent-primary); - cursor: pointer; - transition: var(--transition); -} - -.slider::-webkit-slider-thumb:hover { - transform: scale(1.1); -} - -.slider::-moz-range-thumb { - width: 16px; - height: 16px; - border-radius: 50%; - background: var(--accent-primary); - cursor: pointer; - border: none; - transition: var(--transition); -} - -.slider::-moz-range-thumb:hover { - transform: scale(1.1); -} - -.value-display { - color: var(--text-primary); - font-weight: 500; - min-width: 40px; - text-align: right; - font-size: 13px; - padding: 4px 8px; - background: var(--bg-secondary); - border-radius: 4px; -} - -.select-input { - padding: 8px 10px; - border: 1px solid var(--border-color); - border-radius: var(--radius); - background: var(--bg-primary); - color: var(--text-primary); - font-size: 13px; - cursor: pointer; - transition: var(--transition); - font-family: inherit; - width: 100%; -} - -.select-input:focus { - outline: none; - border-color: var(--accent-primary); -} - -.color-input { - width: 40px; - height: 32px; - border: 1px solid var(--border-color); - border-radius: var(--radius); - cursor: pointer; - transition: var(--transition); - background: var(--bg-primary); -} - -.color-input:hover { - border-color: var(--accent-primary); -} - -.color-value { - font-family: "Courier New", monospace; - font-size: 12px; - font-weight: 500; - color: var(--text-secondary); - padding: 4px 8px; - background: var(--bg-secondary); - border-radius: 4px; -} - -.alignment-buttons { - display: flex; - gap: 6px; -} - -.align-btn { - flex: 1; - padding: 8px; - border: 1px solid var(--border-color); - background: var(--bg-primary); - border-radius: var(--radius); - cursor: pointer; - transition: var(--transition); - display: flex; - align-items: center; - justify-content: center; -} - -.align-btn svg { - width: 16px; - height: 16px; - stroke: var(--text-secondary); - transition: var(--transition); -} - -.align-btn:hover { - background: var(--bg-hover); - border-color: var(--accent-primary); -} - -.align-btn:hover svg { - stroke: var(--accent-primary); -} - -.align-btn.active { - background: var(--accent-primary); - border-color: var(--accent-primary); -} - -.align-btn.active svg { - stroke: white; -} - -/* Crop Editor */ -.crop-editor { - display: flex; - flex-direction: column; - gap: 12px; -} - -.crop-canvas-container { - position: relative; - width: 100%; - aspect-ratio: 16 / 5; - background: var(--bg-secondary); - border-radius: var(--radius); - overflow: hidden; - cursor: crosshair; -} - -.crop-canvas { - width: 100%; - height: 100%; - display: block; -} - -.crop-box { - position: absolute; - border: 2px solid var(--accent-primary); - box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5); - cursor: move; - display: none; -} - -.crop-box.active { - display: block; -} - -.crop-handle { - position: absolute; - width: 10px; - height: 10px; - background: white; - border: 2px solid var(--accent-primary); - border-radius: 50%; -} - -.crop-handle.nw { - top: -5px; - left: -5px; - cursor: nw-resize; -} - -.crop-handle.ne { - top: -5px; - right: -5px; - cursor: ne-resize; -} - -.crop-handle.sw { - bottom: -5px; - left: -5px; - cursor: sw-resize; -} - -.crop-handle.se { - bottom: -5px; - right: -5px; - cursor: se-resize; -} - -.crop-handle.n { - top: -5px; - left: 50%; - transform: translateX(-50%); - cursor: n-resize; -} - -.crop-handle.s { - bottom: -5px; - left: 50%; - transform: translateX(-50%); - cursor: s-resize; -} - -.crop-handle.w { - left: -5px; - top: 50%; - transform: translateY(-50%); - cursor: w-resize; -} - -.crop-handle.e { - right: -5px; - top: 50%; - transform: translateY(-50%); - cursor: e-resize; -} - -.crop-controls { - display: flex; - gap: 8px; - flex-wrap: wrap; -} - -/* Panel Viewer */ -.panel-nav { - display: flex; - align-items: center; - gap: 8px; -} - -.nav-btn { - width: 32px; - height: 32px; - padding: 0; - border: 1px solid var(--border-color); - background: var(--bg-primary); - border-radius: var(--radius); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: var(--transition); -} - -.nav-btn:hover:not(:disabled) { - background: var(--bg-hover); - border-color: var(--accent-primary); -} - -.nav-btn:hover:not(:disabled) svg { - stroke: var(--accent-primary); -} - -.nav-btn:disabled { - opacity: 0.3; - cursor: not-allowed; -} - -.nav-btn svg { - width: 16px; - height: 16px; - stroke: var(--text-secondary); -} - -.panel-indicator { - font-size: 13px; - color: var(--text-secondary); - font-weight: 500; - min-width: 50px; - text-align: center; -} - -.panel-viewer { - display: flex; - flex-direction: column; - gap: 12px; -} - -.panel-display { - background: var(--bg-secondary); - border-radius: var(--radius); - padding: 20px; - display: flex; - justify-content: center; - align-items: center; - min-height: 150px; -} - -.panel-canvas { - border-radius: var(--radius); - display: block; - max-width: 100%; - box-shadow: var(--shadow-md); -} - -.panel-actions { - display: flex; - gap: 8px; -} - -.panel-actions .btn { - flex: 1; - justify-content: center; -} - -/* Empty State */ -.empty-state { - text-align: center; - padding: 32px 16px; - color: var(--text-tertiary); -} - -.empty-state svg { - width: 48px; - height: 48px; - margin: 0 auto 12px; - opacity: 0.5; -} - -.empty-state p { - font-size: 14px; -} - -/* Responsive */ -@media (max-width: 768px) { - body { - padding: 12px; - } - - .header { - padding: 12px 16px; - } - - .header h1 { - font-size: 18px; - } - - .setting-row { - grid-template-columns: 80px 1fr; - gap: 10px; - } - - .crop-controls { - flex-direction: column; - } - - .crop-controls .btn { - width: 100%; - justify-content: center; - } -} - -/* Animations */ -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(4px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.text-item { - animation: fadeIn 0.2s ease-out; -} - -/* Scrollbar */ -::-webkit-scrollbar { - width: 8px; - height: 8px; -} - -::-webkit-scrollbar-track { - background: var(--bg-secondary); -} - -::-webkit-scrollbar-thumb { - background: var(--border-color); - border-radius: 4px; -} - -::-webkit-scrollbar-thumb:hover { - background: var(--border-hover); -}