wa-dev-tools/public/shared.js
treamz 44c85f6e44 Add video converter: convert, compress, extract audio, GIF
- New tool: /video with 4 modes (convert formats, compress, extract audio, GIF)
- Backend: ffmpeg-based processing with progress tracking
- Supports MP4, WebM, MOV, AVI, MKV input, up to 200MB
- Frontend: drag-drop upload, mode tabs, progress bar, download
- Added to sidebar, dashboard (Images category), WA_CATEGORIES
- Total tools: 14
2026-03-21 23:28:11 +03:00

163 lines
12 KiB
JavaScript
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.

/* WA Dev Tools — Shared JS (sidebar, theme, tailwind config, categories) */
/* Tailwind config */
if (typeof tailwind !== 'undefined') {
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',
}
}
}
};
}
/* Categories */
const WA_CATEGORIES = [
{ id: 'images', title: 'Изображения', description: 'Сжатие, редактирование и генерация', icon: '<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"/>', tools: ['compress', 'placeholder', 'svgeditor', 'editor', 'video'] },
{ id: 'code', title: 'Код', description: 'Форматирование, очистка и конвертация', icon: '<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"/>', tools: ['formatter', 'sanitizer', 'converter'] },
{ id: 'web', title: 'Веб', description: 'HTTP-клиент, парсер и анализ', icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418"/>', tools: ['parser', 'httpclient', 'redirects'] },
{ id: 'utils', title: 'Утилиты', description: 'Пароли, Markdown и другие', icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M11.42 15.17l-5.645 3.232a.75.75 0 01-1.09-.791l1.078-6.285L1.347 7.06a.75.75 0 01.416-1.28l6.31-.917L10.89.598a.75.75 0 011.341 0l2.816 5.266 6.31.917a.75.75 0 01.416 1.28l-4.416 4.266 1.078 6.285a.75.75 0 01-1.09.791L12 15.17z"/>', tools: ['password', 'md'] },
];
/* Sidebar tools definition */
const WA_TOOLS = [
{ id: 'home', path: '/dashboard', title: 'Главная', category: null, icon: '<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"/>' },
// Изображения
{ id: 'compress', path: '/compress', title: 'Сжатие', category: 'images', icon: '<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"/>' },
{ id: 'placeholder', path: '/placeholder', title: 'Placeholder', category: 'images', icon: '<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"/>' },
{ id: 'svgeditor', path: '/svgeditor', title: 'SVG', category: 'images', icon: '<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"/>' },
{ id: 'editor', path: '/editor', title: 'Фото', category: 'images', icon: '<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"/>' },
{ id: 'video', path: '/video', title: 'Видео', category: 'images', icon: '<path stroke-linecap="round" stroke-linejoin="round" d="m15.75 10.5 4.72-4.72a.75.75 0 0 1 1.28.53v11.38a.75.75 0 0 1-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 0 0 2.25-2.25v-9a2.25 2.25 0 0 0-2.25-2.25h-9A2.25 2.25 0 0 0 2.25 7.5v9a2.25 2.25 0 0 0 2.25 2.25Z"/>' },
// Код
{ id: 'formatter', path: '/formatter', title: 'Formatter', category: 'code', icon: '<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"/>' },
{ id: 'sanitizer', path: '/sanitizer', title: 'Sanitizer', category: 'code', icon: '<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"/>' },
{ id: 'converter', path: '/converter', title: 'Converter', category: 'code', icon: '<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"/>' },
// Веб
{ id: 'parser', path: '/parser', title: 'Parser', category: 'web', icon: '<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"/>' },
{ id: 'httpclient', path: '/httpclient', title: 'HTTP', category: 'web', icon: '<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"/>' },
{ id: 'redirects', path: '/redirects', title: 'Redirects', category: 'web', icon: '<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"/>' },
// Утилиты
{ id: 'password', path: '/password', title: 'Пароли', category: 'utils', icon: '<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"/>' },
{ id: 'md', path: '/md', title: 'Markdown', category: 'utils', icon: '<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"/>' },
// Admin
{ id: 'logs', path: '/logs', title: 'Логи', category: null, icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.625 4.5h12.75a1.875 1.875 0 010 3.75H5.625a1.875 1.875 0 010-3.75z"/>' },
];
/* Build sidebar SVG icon */
function _svgIcon(innerPath) {
return `<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">${innerPath}</svg>`;
}
/* Inject sidebar into the page */
function initSidebar(activeId) {
if (!activeId) {
const path = window.location.pathname;
const match = WA_TOOLS.find(t => t.path !== '/dashboard' && path.startsWith(t.path));
activeId = match ? match.id : 'home';
}
// Build links grouped by category
let linksHtml = '';
let lastCategory = '__none__';
for (const t of WA_TOOLS) {
// Add category divider
if (t.category !== lastCategory && t.category !== null && lastCategory !== '__none__') {
linksHtml += '<div class="sidebar-divider"></div>';
}
lastCategory = t.category;
const isActive = t.id === activeId ? ' active' : '';
linksHtml += `<a href="${t.path}" class="sidebar-link${isActive}" title="${t.title}">${_svgIcon(t.icon)}</a>\n`;
}
const nav = document.createElement('nav');
nav.className = 'sidebar';
nav.innerHTML = `
<div class="sidebar-logo">WA</div>
<div class="sidebar-nav">
${linksHtml}
</div>
<div class="sidebar-bottom">
<div class="sidebar-user" id="sidebarUser" style="display:none;">
<div class="sidebar-avatar" id="sidebarAvatar" title="Профиль"></div>
</div>
<div class="theme-toggle" id="themeToggle" title="Переключить тему">
<div class="theme-toggle-knob">
<svg class="wa-sun-icon" width="14" height="14" 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="wa-moon-icon" width="14" height="14" 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>`;
document.body.prepend(nav);
_updateThemeIcons();
_loadUser();
}
/* Load user info for sidebar avatar */
async function _loadUser() {
try {
const res = await fetch('/auth/me');
if (!res.ok) return;
const user = await res.json();
const avatar = document.getElementById('sidebarAvatar');
const container = document.getElementById('sidebarUser');
if (!avatar || !container) return;
const initials = (user.name || user.email || '?').charAt(0).toUpperCase();
avatar.textContent = initials;
avatar.title = user.name || user.email;
container.style.display = 'block';
avatar.addEventListener('click', () => {
if (confirm('Выйти из аккаунта?')) {
window.location.href = '/auth/logout';
}
});
} catch {}
}
/* Theme management */
function initTheme() {
const html = document.documentElement;
const saved = localStorage.getItem('theme');
if (saved === 'dark' || (!saved && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
html.classList.add('dark');
} else if (saved === '') {
html.classList.remove('dark');
}
document.addEventListener('click', (e) => {
const toggle = e.target.closest('#themeToggle');
if (!toggle) return;
const isDark = html.classList.toggle('dark');
localStorage.setItem('theme', isDark ? 'dark' : '');
_updateThemeIcons();
});
}
function _updateThemeIcons() {
const isDark = document.documentElement.classList.contains('dark');
document.querySelectorAll('.wa-sun-icon').forEach(el => el.style.display = isDark ? 'none' : 'block');
document.querySelectorAll('.wa-moon-icon').forEach(el => el.style.display = isDark ? 'block' : 'none');
}
/* Auto-init on DOM ready */
document.addEventListener('DOMContentLoaded', () => {
initSidebar(window.WA_TOOL_ID);
});
/* Init theme immediately (before DOM ready to prevent flash) */
initTheme();