wa-dev-tools/public/placeholder.html
2026-03-21 22:06:15 +03:00

413 lines
26 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru" class="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Placeholder — генератор заглушек</title>
<script src="/vendor/tailwind.js"></script>
<link href="/vendor/fonts.css" rel="stylesheet">
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
fontFamily: {
mono: ['JetBrains Mono', 'monospace'],
sans: ['Manrope', 'sans-serif'],
},
colors: {
surface: { 900: '#060c18', 800: '#091020', 700: '#0d1828', 600: '#162040' },
accent: { DEFAULT: '#0054e6', dim: '#0043b8', bright: '#6b9fff' },
warn: '#ffd600',
danger: '#ff5252',
}
}
}
}
</script>
<style>
/* Sidebar */
.sidebar{position:fixed;top:0;left:0;width:56px;height:100vh;display:flex;flex-direction:column;align-items:center;padding:16px 0 12px;z-index:50;border-right:1px solid var(--surface-600);background:var(--surface-800);backdrop-filter:blur(12px);transition:background .3s,border-color .3s}
.sidebar-logo{margin-bottom:24px;color:var(--text-muted);font-size:10px;font-weight:700;letter-spacing:.08em;font-family:'JetBrains Mono',monospace}
.sidebar-nav{display:flex;flex-direction:column;gap:6px;flex:1}
.sidebar-link{width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;color:var(--text-muted);transition:all .2s;text-decoration:none;position:relative}
.sidebar-link:hover{color:var(--text-secondary);background:rgba(255,255,255,.04)}
.sidebar-link.active{color:#0054e6;background:rgba(0,84,230,.08)}
.sidebar-link.active::before{content:'';position:absolute;left:-8px;top:50%;transform:translateY(-50%);width:3px;height:20px;border-radius:0 3px 3px 0;background:#0054e6}
.sidebar-bottom{margin-top:auto;display:flex;flex-direction:column;align-items:center;gap:8px}
.main-content{margin-left:56px}
@media(max-width:640px){
.sidebar{position:fixed;top:auto;bottom:0;left:0;width:100%;height:52px;flex-direction:row;justify-content:center;padding:0 16px;border-right:none;border-top:1px solid var(--surface-600);gap:0}
.sidebar-logo{display:none}
.sidebar-nav{flex-direction:row;gap:4px;flex:none}
.sidebar-bottom{margin-top:0;margin-left:auto;flex-direction:row}
.sidebar-link.active::before{left:50%;top:auto;bottom:-6px;transform:translateX(-50%);width:20px;height:3px;border-radius:3px 3px 0 0}
.main-content{margin-left:0;padding-bottom:60px}
}
:root {
--bg: #f5f5f7;
--bg-grad1: rgba(0, 67, 184, 0.04);
--bg-grad2: rgba(0, 50, 140, 0.03);
--surface-800: rgba(255,255,255,0.85);
--surface-700: #f0f0f3;
--surface-600: #dde0e6;
--text-primary: #1a1a2e;
--text-secondary: #555;
--text-muted: #888;
--select-bg: #f0f0f3;
--select-color: #1a1a2e;
}
.dark {
--bg: #060c18;
--bg-grad1: rgba(0, 84, 230, 0.03);
--bg-grad2: rgba(0, 67, 184, 0.02);
--surface-800: rgba(9,16,32,0.8);
--surface-700: #0d1828;
--surface-600: #162040;
--text-primary: #fff;
--text-secondary: #c0c0c0;
--text-muted: #666;
--select-bg: #091020;
--select-color: #e0e0e0;
}
body {
background: var(--bg);
background-image:
radial-gradient(ellipse at 20% 50%, var(--bg-grad1) 0%, transparent 50%),
radial-gradient(ellipse at 80% 20%, var(--bg-grad2) 0%, transparent 50%);
min-height: 100vh;
transition: background 0.3s;
}
.dark .noise::before {
content: '';
position: fixed;
inset: 0;
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.03'/%3E%3C/svg%3E");
pointer-events: none;
z-index: 0;
}
.theme-toggle { position: relative; width: 44px; height: 24px; border-radius: 12px; cursor: pointer; transition: background 0.3s; }
.dark .theme-toggle { background: #162040; }
.theme-toggle { background: #dde0e6; }
.theme-toggle-knob { position: absolute; top: 2px; width: 20px; height: 20px; border-radius: 50%; transition: left 0.3s, background 0.3s; display: flex; align-items: center; justify-content: center; }
.dark .theme-toggle-knob { left: 22px; background: #0d1828; }
.theme-toggle-knob { left: 2px; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,0.15); }
select, select option { background: var(--select-bg); color: var(--select-color); }
@keyframes fadeUp { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
.fade-up { animation: fadeUp 0.4s ease-out forwards; }
.fade-up-delay { animation: fadeUp 0.4s ease-out 0.1s forwards; opacity: 0; }
.preview-frame {
background: repeating-conic-gradient(rgba(128,128,128,.08) 0% 25%, transparent 0% 50%) 50%/16px 16px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
min-height: 160px;
overflow: hidden;
}
.preview-frame img { max-width: 100%; max-height: 400px; display: block; border-radius: 8px; }
.color-swatch {
width: 36px; height: 36px; border-radius: 8px; border: 2px solid var(--surface-600);
cursor: pointer; transition: transform .15s;
}
.color-swatch:hover { transform: scale(1.1); }
input[type="color"] { position: absolute; opacity: 0; width: 0; height: 0; }
</style>
</head>
<body class="noise dark:text-gray-200 text-gray-700 font-sans antialiased">
<!-- Sidebar -->
<nav class="sidebar">
<div class="sidebar-logo">WA</div>
<div class="sidebar-nav">
<a href="/" class="sidebar-link" title="Главная">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"/></svg>
</a>
<a href="/compress" class="sidebar-link" title="Картинки">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5A2.25 2.25 0 0023.25 18.75V5.25A2.25 2.25 0 0021 3H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21z"/></svg>
</a>
<a href="/md" class="sidebar-link" title="Markdown">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"/></svg>
</a>
<a href="/placeholder" class="sidebar-link active" title="Placeholder">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.75 3.104v5.714a2.25 2.25 0 01-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 014.5 0m0 0v5.714a2.25 2.25 0 00.659 1.591L19 14.5M14.25 3.104c.251.023.501.05.75.082M19 14.5l-2.47 2.47a2.25 2.25 0 01-1.59.659H9.06a2.25 2.25 0 01-1.591-.659L5 14.5m14 0V17a2.25 2.25 0 01-2.25 2.25H7.25A2.25 2.25 0 015 17v-2.5"/></svg>
</a>
<a href="/parser" class="sidebar-link" title="Parser">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m9.86-2.06a4.5 4.5 0 00-6.364-6.364L6.257 6.514a4.5 4.5 0 001.242 7.244"/></svg>
</a>
<a href="/password" class="sidebar-link" title="Password">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z"/></svg>
</a>
<a href="/sanitizer" class="sidebar-link" title="Sanitizer">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z"/></svg>
</a>
<a href="/converter" class="sidebar-link" title="Converter">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M7.5 21L3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5"/></svg>
</a>
<a href="/formatter" class="sidebar-link" title="Formatter">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5"/></svg>
</a>
<a href="/editor" class="sidebar-link" title="Editor">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10"/></svg>
</a>
<a href="/httpclient" class="sidebar-link" title="HTTP Client">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5"/></svg>
</a>
<a href="/redirects" class="sidebar-link" title="Redirect Analyzer">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"/></svg>
</a>
<a href="/svgeditor" class="sidebar-link" title="SVG Редактор">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.53 16.122a3 3 0 00-5.78 1.128 2.25 2.25 0 01-2.4 2.245 4.5 4.5 0 008.4-2.245c0-.399-.078-.78-.22-1.128zm0 0a15.998 15.998 0 003.388-1.62m-5.043-.025a15.994 15.994 0 011.622-3.395m3.42 3.42a15.995 15.995 0 004.764-4.648l3.876-5.814a1.151 1.151 0 00-1.597-1.597L14.146 6.32a15.996 15.996 0 00-4.649 4.763m3.42 3.42a6.776 6.776 0 00-3.42-3.42"/></svg>
</a>
</div>
<div class="sidebar-bottom">
<div class="theme-toggle" id="themeToggle" title="Переключить тему">
<div class="theme-toggle-knob">
<svg class="w-3 h-3 dark:hidden text-amber-500" fill="currentColor" viewBox="0 0 20 20"><path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"/></svg>
<svg class="w-3 h-3 hidden dark:block text-gray-400" fill="currentColor" viewBox="0 0 20 20"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"/></svg>
</div>
</div>
</div>
</nav>
<div class="main-content">
<div class="relative z-10 max-w-2xl mx-auto px-4 py-8 sm:py-12">
<!-- Header -->
<div class="text-center mb-8 fade-up">
<div class="inline-flex items-center gap-2 mb-3 px-3 py-1 rounded-full border dark:border-surface-600 border-gray-300 dark:bg-surface-800/60 bg-white/60 text-xs font-mono dark:text-gray-400 text-gray-500 tracking-wide">
<span class="w-1.5 h-1.5 rounded-full bg-accent animate-pulse"></span>
PNG &middot; JPG &middot; WebP
</div>
<h1 class="text-3xl sm:text-4xl font-extrabold tracking-tight dark:text-white text-gray-900">
<span class="text-accent">Placeholder</span> генератор
</h1>
<p class="mt-2 text-sm dark:text-gray-500 text-gray-400 font-mono">изображения-заглушки любого размера</p>
</div>
<!-- Controls Card -->
<div class="dark:bg-surface-800/80 bg-white/85 border dark:border-surface-600 border-gray-200 rounded-2xl p-5 sm:p-7 backdrop-blur-sm fade-up-delay shadow-sm dark:shadow-none">
<!-- Size -->
<div class="grid grid-cols-2 gap-3 mb-4">
<div>
<label class="block text-xs font-medium dark:text-gray-500 text-gray-400 mb-1.5 font-mono uppercase tracking-wider">Ширина</label>
<input type="number" id="width" value="600" min="1" max="4000" class="w-full px-3 py-2.5 dark:bg-surface-700 bg-gray-50 border dark:border-surface-600 border-gray-200 rounded-lg text-sm font-mono focus:outline-none focus:border-accent/50 transition-colors dark:text-white text-gray-800">
</div>
<div>
<label class="block text-xs font-medium dark:text-gray-500 text-gray-400 mb-1.5 font-mono uppercase tracking-wider">Высота</label>
<input type="number" id="height" value="400" min="1" max="4000" class="w-full px-3 py-2.5 dark:bg-surface-700 bg-gray-50 border dark:border-surface-600 border-gray-200 rounded-lg text-sm font-mono focus:outline-none focus:border-accent/50 transition-colors dark:text-white text-gray-800">
</div>
</div>
<!-- Colors -->
<div class="grid grid-cols-2 gap-3 mb-4">
<div>
<label class="block text-xs font-medium dark:text-gray-500 text-gray-400 mb-1.5 font-mono uppercase tracking-wider">Фон</label>
<div class="flex items-center gap-2">
<div class="relative">
<div id="bgSwatch" class="color-swatch" style="background:#3B82F6"></div>
<input type="color" id="bgPicker" value="#3B82F6">
</div>
<input type="text" id="bgColor" value="3B82F6" maxlength="20" class="flex-1 px-3 py-2.5 dark:bg-surface-700 bg-gray-50 border dark:border-surface-600 border-gray-200 rounded-lg text-sm font-mono focus:outline-none focus:border-accent/50 transition-colors dark:text-white text-gray-800">
</div>
</div>
<div>
<label class="block text-xs font-medium dark:text-gray-500 text-gray-400 mb-1.5 font-mono uppercase tracking-wider">Текст</label>
<div class="flex items-center gap-2">
<div class="relative">
<div id="fgSwatch" class="color-swatch" style="background:#FFFFFF"></div>
<input type="color" id="fgPicker" value="#FFFFFF">
</div>
<input type="text" id="fgColor" value="FFFFFF" maxlength="20" class="flex-1 px-3 py-2.5 dark:bg-surface-700 bg-gray-50 border dark:border-surface-600 border-gray-200 rounded-lg text-sm font-mono focus:outline-none focus:border-accent/50 transition-colors dark:text-white text-gray-800">
</div>
</div>
</div>
<!-- Text & Font size -->
<div class="grid grid-cols-2 gap-3 mb-4">
<div>
<label class="block text-xs font-medium dark:text-gray-500 text-gray-400 mb-1.5 font-mono uppercase tracking-wider">Надпись</label>
<input type="text" id="text" placeholder="600x400" class="w-full px-3 py-2.5 dark:bg-surface-700 bg-gray-50 border dark:border-surface-600 border-gray-200 rounded-lg text-sm font-mono focus:outline-none focus:border-accent/50 transition-colors dark:text-white text-gray-800">
</div>
<div>
<label class="block text-xs font-medium dark:text-gray-500 text-gray-400 mb-1.5 font-mono uppercase tracking-wider">Формат</label>
<select id="format" class="w-full px-3 py-2.5 dark:bg-surface-700 bg-gray-50 border dark:border-surface-600 border-gray-200 rounded-lg text-sm focus:outline-none focus:border-accent/50 transition-colors">
<option value="png">PNG</option>
<option value="jpg">JPG</option>
<option value="webp">WebP</option>
</select>
</div>
</div>
<!-- Generate URL -->
<div class="mb-4">
<label class="block text-xs font-medium dark:text-gray-500 text-gray-400 mb-1.5 font-mono uppercase tracking-wider">URL</label>
<div class="flex gap-2">
<input type="text" id="urlField" readonly class="flex-1 px-3 py-2.5 dark:bg-surface-700 bg-gray-50 border dark:border-surface-600 border-gray-200 rounded-lg text-xs font-mono focus:outline-none dark:text-gray-300 text-gray-600 truncate">
<button id="copyUrlBtn" class="px-4 py-2.5 rounded-lg text-xs font-medium dark:bg-surface-700 bg-gray-100 dark:text-gray-300 text-gray-600 hover:text-accent transition-colors border dark:border-surface-600 border-gray-200 whitespace-nowrap">
Копировать
</button>
</div>
</div>
<!-- Generate button -->
<button id="generateBtn"
class="w-full py-3 rounded-xl font-semibold text-sm tracking-wide transition-all duration-300
bg-accent text-white border border-accent cursor-pointer
hover:bg-accent-bright hover:shadow-lg hover:shadow-accent/20
active:scale-[0.98]">
Сгенерировать
</button>
</div>
<!-- Preview -->
<div id="previewCard" class="mt-5 fade-up">
<div class="dark:bg-surface-800/80 bg-white/85 border dark:border-surface-600 border-gray-200 rounded-2xl p-5 sm:p-7 backdrop-blur-sm shadow-sm dark:shadow-none">
<div class="flex items-center justify-between mb-4">
<span class="text-xs font-mono dark:text-gray-500 text-gray-400 uppercase tracking-wider">Превью</span>
<a id="downloadLink" href="#" download class="px-3 py-1.5 rounded-lg text-xs font-medium dark:bg-surface-700 bg-gray-100 dark:text-gray-300 text-gray-600 hover:text-accent transition-colors border dark:border-surface-600 border-gray-200 no-underline">
Скачать
</a>
</div>
<div class="preview-frame p-3">
<img id="previewImg" src="/placeholder-img/600x400/3B82F6/FFFFFF" alt="placeholder">
</div>
</div>
</div>
<!-- API Reference -->
<div class="mt-5 fade-up">
<div class="dark:bg-surface-800/80 bg-white/85 border dark:border-surface-600 border-gray-200 rounded-2xl p-5 sm:p-7 backdrop-blur-sm shadow-sm dark:shadow-none">
<h3 class="text-sm font-semibold dark:text-white text-gray-800 mb-3">API</h3>
<div class="space-y-2 text-xs font-mono dark:text-gray-400 text-gray-500 leading-relaxed">
<p><code class="dark:bg-surface-700 bg-gray-100 px-2 py-0.5 rounded">/placeholder-img/:WxH/:bg/:fg</code> — картинка без текста</p>
<p><code class="dark:bg-surface-700 bg-gray-100 px-2 py-0.5 rounded">/placeholder-img/:WxH/:bg/:fg?text=Hello</code>с текстом</p>
<p><code class="dark:bg-surface-700 bg-gray-100 px-2 py-0.5 rounded">?format=png|jpg|webp</code> — формат выходного файла</p>
<p><code class="dark:bg-surface-700 bg-gray-100 px-2 py-0.5 rounded">?fontsize=30</code> — размер шрифта (авто по умолчанию)</p>
<p class="dark:text-gray-500 text-gray-400 pt-1">Цвета: HEX (<code class="dark:bg-surface-700 bg-gray-100 px-1.5 py-0.5 rounded">3B82F6</code>) или имя (<code class="dark:bg-surface-700 bg-gray-100 px-1.5 py-0.5 rounded">red</code>, <code class="dark:bg-surface-700 bg-gray-100 px-1.5 py-0.5 rounded">yellow</code>)</p>
</div>
</div>
</div>
</div>
</div><!-- /main-content -->
<script>
// Theme
const html = document.documentElement;
const savedTheme = localStorage.getItem('theme');
if (savedTheme) { html.className = savedTheme; }
else if (!window.matchMedia('(prefers-color-scheme: dark)').matches) { html.className = ''; }
document.getElementById('themeToggle').addEventListener('click', () => {
const isDark = html.classList.toggle('dark');
localStorage.setItem('theme', isDark ? 'dark' : '');
});
// Elements
const widthEl = document.getElementById('width');
const heightEl = document.getElementById('height');
const bgColorEl = document.getElementById('bgColor');
const fgColorEl = document.getElementById('fgColor');
const bgPickerEl = document.getElementById('bgPicker');
const fgPickerEl = document.getElementById('fgPicker');
const bgSwatchEl = document.getElementById('bgSwatch');
const fgSwatchEl = document.getElementById('fgSwatch');
const textEl = document.getElementById('text');
const formatEl = document.getElementById('format');
const urlField = document.getElementById('urlField');
const previewImg = document.getElementById('previewImg');
const downloadLink = document.getElementById('downloadLink');
// Color picker sync
bgSwatchEl.addEventListener('click', () => bgPickerEl.click());
fgSwatchEl.addEventListener('click', () => fgPickerEl.click());
bgPickerEl.addEventListener('input', e => {
const hex = e.target.value.replace('#', '').toUpperCase();
bgColorEl.value = hex;
bgSwatchEl.style.background = e.target.value;
updateUrl();
});
fgPickerEl.addEventListener('input', e => {
const hex = e.target.value.replace('#', '').toUpperCase();
fgColorEl.value = hex;
fgSwatchEl.style.background = e.target.value;
updateUrl();
});
bgColorEl.addEventListener('input', () => {
const v = bgColorEl.value.trim();
if (/^[0-9A-Fa-f]{6}$/.test(v)) {
bgPickerEl.value = '#' + v;
bgSwatchEl.style.background = '#' + v;
} else {
bgSwatchEl.style.background = v;
}
updateUrl();
});
fgColorEl.addEventListener('input', () => {
const v = fgColorEl.value.trim();
if (/^[0-9A-Fa-f]{6}$/.test(v)) {
fgPickerEl.value = '#' + v;
fgSwatchEl.style.background = '#' + v;
} else {
fgSwatchEl.style.background = v;
}
updateUrl();
});
[widthEl, heightEl, textEl, formatEl].forEach(el => el.addEventListener('input', updateUrl));
function buildPath() {
const w = widthEl.value || 600;
const h = heightEl.value || 400;
const bg = bgColorEl.value.trim() || '3B82F6';
const fg = fgColorEl.value.trim() || 'FFFFFF';
let url = `/placeholder-img/${w}x${h}/${bg}/${fg}`;
const params = [];
const txt = textEl.value.trim();
if (txt) params.push('text=' + encodeURIComponent(txt));
const fmt = formatEl.value;
if (fmt !== 'png') params.push('format=' + fmt);
if (params.length) url += '?' + params.join('&');
return url;
}
function updateUrl() {
const p = buildPath();
urlField.value = location.origin + p;
}
function generate() {
const p = buildPath();
previewImg.src = p;
downloadLink.href = p;
updateUrl();
}
document.getElementById('generateBtn').addEventListener('click', generate);
// Copy URL
document.getElementById('copyUrlBtn').addEventListener('click', () => {
navigator.clipboard.writeText(urlField.value).then(() => {
const btn = document.getElementById('copyUrlBtn');
const orig = btn.textContent;
btn.textContent = 'Скопировано!';
btn.classList.add('text-accent');
setTimeout(() => { btn.textContent = orig; btn.classList.remove('text-accent'); }, 1500);
});
});
// Init
updateUrl();
</script>
</body>
</html>