Files
remotion_service/tmp/preset-grid-redesign.html
T
Daniil e0929b4511 rev 4
2026-04-07 13:43:04 +03:00

668 lines
21 KiB
HTML

<!DOCTYPE html>
<html lang="ru" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Preset Grid Redesign — Coffee Project</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700&family=Lobster&family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<style>
/*
* Uses ONLY tokens from global.scss:
* --bg-canvas, --bg-default, --bg-surface, --bg-hover
* --text-primary, --text-secondary, --text-tertiary
* --border-default, --border-subtle
* --color-primary, --color-secondary
* --purple-50..900, --green-50..900
* --shadow-sm, --shadow-md
* --radius-sm, --radius-md, --radius-lg
* --duration-fast, --duration-normal, --ease-out
* --accent-shadow, --accent-shadow-hover
* --accent-solid-start, --accent-solid-end, --accent-foreground
*/
/* ─── Light (Catppuccin Latte) ─── */
:root, [data-theme="light"] {
--bg-canvas: #e6e9ef;
--bg-default: #eff1f5;
--bg-surface: #dce0e8;
--bg-hover: #ccd0da;
--text-primary: #4c4f69;
--text-secondary: #5c5f77;
--text-tertiary: #8c8fa1;
--border-default: #bcc0cc;
--border-subtle: #dce0e8;
--color-primary: #8839ef;
--color-secondary: #a777f2;
--purple-50: #f7efff;
--purple-100: #eedfff;
--purple-200: #dfc8ff;
--purple-300: #c8abff;
--purple-400: #a777f2;
--purple-500: #8839ef;
--purple-600: #7430ca;
--accent-solid-start: #a777f2;
--accent-solid-end: #7430ca;
--accent-foreground: #ffffff;
--accent-shadow: rgba(136,57,239,0.28);
--accent-shadow-hover: rgba(136,57,239,0.38);
--shadow-sm: 0 1px 2px rgba(76,79,105,0.06), 0 2px 8px rgba(76,79,105,0.04);
--shadow-md: 0 4px 6px -1px rgba(76,79,105,0.08), 0 24px 48px -12px rgba(76,79,105,0.1);
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--duration-fast: 150ms;
--duration-normal: 250ms;
--ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
--page-glow: radial-gradient(circle at 50% 0%, rgba(136,57,239,0.08) 0%, transparent 62%);
--preview-bg: #0c0a1a;
}
/* ─── Dark (Catppuccin Mocha) ─── */
[data-theme="dark"] {
--bg-canvas: #11111b;
--bg-default: #1e1e2e;
--bg-surface: #313244;
--bg-hover: #45475a;
--text-primary: #cdd6f4;
--text-secondary: #bac2de;
--text-tertiary: #9399b2;
--border-default: #45475a;
--border-subtle: #313244;
--color-primary: #cba6f7;
--color-secondary: #6a5a93;
--purple-50: #2b253b;
--purple-100: #362f4c;
--purple-200: #4b4168;
--purple-300: #6a5a93;
--purple-400: #cba6f7;
--purple-500: #d9bcfa;
--purple-600: #e4cffc;
--accent-solid-start: #6a5a93;
--accent-solid-end: #362f4c;
--accent-foreground: #f5e0dc;
--accent-shadow: rgba(203,166,247,0.22);
--accent-shadow-hover: rgba(203,166,247,0.3);
--shadow-sm: 0 1px 2px rgba(17,17,27,0.5);
--shadow-md: 0 4px 6px -1px rgba(17,17,27,0.58), 0 24px 48px -12px rgba(17,17,27,0.52);
--page-glow: radial-gradient(circle at 50% 0%, rgba(203,166,247,0.12) 0%, transparent 55%);
--preview-bg: #0c0a1a;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Manrope', -apple-system, sans-serif;
background: var(--bg-canvas);
color: var(--text-primary);
min-height: 100vh;
-webkit-font-smoothing: antialiased;
}
body::before {
content: "";
position: fixed;
inset: 0;
background-image: var(--page-glow);
pointer-events: none;
z-index: 0;
}
/* ─── Page shell ─── */
.page-shell {
position: relative;
z-index: 1;
max-width: 1200px;
margin: 0 auto;
padding: 40px 32px;
}
.page-title {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 24px;
}
/* ─── Grid ─── */
.preset-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 16px;
}
/* ─── Card ─── */
.preset-card {
position: relative;
display: flex;
flex-direction: column;
background: var(--bg-default);
border: 1.5px solid var(--border-subtle);
border-radius: var(--radius-md);
overflow: hidden;
cursor: pointer;
box-shadow: var(--shadow-sm);
/* Only transition border-color — cheap GPU op */
transition: border-color var(--duration-normal) var(--ease-out);
animation: cardIn 0.35s var(--ease-out) backwards;
}
.preset-card:nth-child(1) { animation-delay: 0ms; }
.preset-card:nth-child(2) { animation-delay: 50ms; }
.preset-card:nth-child(3) { animation-delay: 100ms; }
.preset-card:nth-child(4) { animation-delay: 150ms; }
.preset-card:nth-child(5) { animation-delay: 200ms; }
@keyframes cardIn {
from { opacity: 0; transform: translateY(8px); }
}
.preset-card:hover {
border-color: var(--color-secondary);
}
.preset-card.selected {
border-color: var(--color-primary);
box-shadow: var(--shadow-sm), 0 0 0 1px var(--color-primary);
}
.preset-card.selected:hover {
border-color: var(--color-primary);
}
/* ─── Preview ─── */
.preview-area {
position: relative;
aspect-ratio: var(--video-ratio, 16 / 9);
background: var(--preview-bg);
display: flex;
flex-direction: column;
overflow: hidden;
}
/* ─── Checkmark ─── */
.check-indicator {
position: absolute;
top: 8px;
right: 8px;
width: 22px;
height: 22px;
background: var(--color-primary);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
opacity: 0;
transform: scale(0.6);
transition: opacity var(--duration-fast), transform var(--duration-fast);
}
.selected .check-indicator {
opacity: 1;
transform: scale(1);
}
.check-indicator svg {
width: 12px;
height: 12px;
color: var(--accent-foreground);
}
/* ─── Subtitle content ─── */
.sub-content {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
flex: 1;
padding: 12px;
}
.sub-content.pos-bottom { justify-content: flex-end; }
.sub-content.pos-center { justify-content: center; }
.sub-content.pos-top { justify-content: flex-start; }
.sub-content.align-center { align-items: center; text-align: center; }
.sub-content.align-left { align-items: flex-start; text-align: left; }
.sub-bubble {
max-width: 92%;
padding: 6px 10px;
border-radius: 6px;
word-break: break-word;
line-height: 1.3;
}
/* ─── Footer ─── */
.card-footer {
padding: 10px 12px;
display: flex;
flex-direction: column;
gap: 3px;
}
.footer-row {
display: flex;
align-items: center;
gap: 8px;
}
.preset-name {
font-size: 13px;
font-weight: 600;
color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Badge — matches project Badge (Radix plum soft) */
.sys-badge {
flex-shrink: 0;
display: inline-flex;
align-items: center;
font-size: 11px;
font-weight: 500;
line-height: 1;
color: var(--color-primary);
background: var(--purple-100);
padding: 3px 8px;
border-radius: var(--radius-sm);
}
.style-hint {
display: flex;
align-items: center;
gap: 5px;
font-size: 11px;
color: var(--text-tertiary);
white-space: nowrap;
overflow: hidden;
}
.color-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
border: 1px solid rgba(128,128,128,0.15);
}
.sep {
color: var(--border-default);
font-size: 10px;
}
/* ─── Hover actions ─── */
.card-actions {
position: absolute;
top: 8px;
right: 8px;
display: flex;
gap: 4px;
opacity: 0;
transition: opacity var(--duration-fast);
z-index: 3;
}
.selected .card-actions { display: none; }
.preset-card:hover .card-actions { opacity: 1; }
.act-btn {
display: flex;
align-items: center;
justify-content: center;
width: 26px;
height: 26px;
border: none;
border-radius: 6px;
background: var(--bg-surface);
color: var(--text-secondary);
cursor: pointer;
transition: background var(--duration-fast), color var(--duration-fast);
}
.act-btn:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.act-btn svg { width: 13px; height: 13px; }
/* ─── Create card ─── */
.create-card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
background: transparent;
border: 1.5px dashed var(--border-default);
border-radius: var(--radius-md);
cursor: pointer;
transition: border-color var(--duration-normal) var(--ease-out);
}
.create-inner {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
aspect-ratio: var(--video-ratio, 16 / 9);
width: 100%;
}
.create-card:hover {
border-color: var(--color-secondary);
}
.create-card svg {
width: 28px;
height: 28px;
color: var(--text-tertiary);
transition: color var(--duration-normal);
}
.create-card:hover svg { color: var(--color-primary); }
.create-label {
font-size: 13px;
font-weight: 500;
color: var(--text-tertiary);
transition: color var(--duration-normal);
}
.create-card:hover .create-label { color: var(--text-secondary); }
/* ─── Footer ─── */
.page-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 32px;
padding-top: 16px;
border-top: 1px solid var(--border-subtle);
}
/* Button — matches project Button component */
.btn {
font-family: 'Manrope', sans-serif;
font-size: 14px;
font-weight: 600;
padding: 8px 20px;
border-radius: var(--radius-sm);
cursor: pointer;
letter-spacing: -0.01em;
transition: filter var(--duration-fast), box-shadow var(--duration-normal) var(--ease-out);
border: none;
}
.btn:active { transform: scale(0.96); }
.btn-outline {
background: transparent;
border: 1px solid var(--border-default);
color: var(--text-secondary);
}
.btn-outline:hover {
filter: brightness(0.95);
}
.btn-primary {
background: linear-gradient(135deg, var(--accent-solid-start), var(--accent-solid-end));
background-image: linear-gradient(180deg, hsla(0,0%,100%,0.12) 0%, transparent 100%);
background-color: var(--color-primary);
color: var(--accent-foreground);
box-shadow: inset 0 1px 1px hsla(0,0%,100%,0.2), 0 1px 2px rgba(0,0,0,0.1);
border-top: 1px solid hsla(0,0%,100%,0.1);
}
.btn-primary:hover {
filter: brightness(1.08);
box-shadow: inset 0 1px 1px hsla(0,0%,100%,0.2), 0 4px 14px var(--accent-shadow-hover);
}
/* ─── Demo controls ─── */
.demo-bar {
display: flex;
gap: 24px;
margin-bottom: 24px;
align-items: center;
flex-wrap: wrap;
}
.demo-group {
display: flex;
align-items: center;
gap: 8px;
}
.demo-label {
font-size: 11px;
color: var(--text-tertiary);
letter-spacing: 0.04em;
text-transform: uppercase;
font-weight: 600;
}
.toggle-btn {
font-family: 'Manrope', sans-serif;
font-size: 12px;
font-weight: 500;
padding: 4px 12px;
border: 1px solid var(--border-default);
border-radius: var(--radius-sm);
background: transparent;
color: var(--text-tertiary);
cursor: pointer;
transition: all var(--duration-fast);
}
.toggle-btn:hover {
border-color: var(--color-secondary);
color: var(--text-secondary);
}
.toggle-btn.active {
border-color: var(--color-primary);
background: var(--purple-100);
color: var(--color-primary);
}
/* ─── Responsive ─── */
@media (max-width: 768px) {
.preset-grid {
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 12px;
}
.page-shell { padding: 24px 16px; }
.style-hint { display: none; }
}
@media (max-width: 480px) {
.preset-grid { grid-template-columns: repeat(2, 1fr); }
}
</style>
</head>
<body>
<div class="page-shell">
<div class="demo-bar">
<div class="demo-group">
<span class="demo-label">Ratio</span>
<button class="toggle-btn active" onclick="setRatio(16/9, this)">16:9</button>
<button class="toggle-btn" onclick="setRatio(9/16, this)">9:16</button>
<button class="toggle-btn" onclick="setRatio(4/3, this)">4:3</button>
<button class="toggle-btn" onclick="setRatio(1, this)">1:1</button>
<button class="toggle-btn" onclick="setRatio(2.35, this)">2.35:1</button>
</div>
<div class="demo-group">
<span class="demo-label">Theme</span>
<button class="toggle-btn active" onclick="setTheme('light', this)">Light</button>
<button class="toggle-btn" onclick="setTheme('dark', this)">Dark</button>
</div>
</div>
<h2 class="page-title">Выбор пресета субтитров</h2>
<div class="preset-grid" id="grid">
<!-- Классические (Selected) -->
<div class="preset-card selected" onclick="selectCard(this)">
<div class="preview-area">
<div class="check-indicator">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</div>
<div class="sub-content pos-bottom align-center">
<div class="sub-bubble" style="background: rgba(0,0,0,0.6);">
<span style="font-family: 'Lobster', cursive; font-size: 18px; color: #fff;">Пример <span style="color: #FFD700;">субтитров</span></span>
</div>
</div>
</div>
<div class="card-footer">
<div class="footer-row">
<span class="preset-name">Классические</span>
<span class="sys-badge">Системный</span>
</div>
<div class="style-hint">
Lobster <span class="sep">·</span>
<span class="color-dot" style="background: #FFD700;"></span> Золотой
</div>
</div>
</div>
<!-- Неон -->
<div class="preset-card" onclick="selectCard(this)">
<div class="preview-area">
<div class="check-indicator">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</div>
<div class="card-actions">
<button class="act-btn" title="Редактировать"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg></button>
<button class="act-btn" title="Удалить"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg></button>
</div>
<div class="sub-content pos-center align-center">
<div class="sub-bubble" style="background: rgba(0,255,255,0.12); border-radius: 8px; box-shadow: 0 0 16px rgba(0,255,255,0.2);">
<span style="font-family: 'Inter', sans-serif; font-weight: 700; font-size: 16px; color: #00ffff; text-shadow: 0 0 10px rgba(0,255,255,0.6);">Пример <span style="color: #ff00ff; text-shadow: 0 0 10px rgba(255,0,255,0.6);">субтитров</span></span>
</div>
</div>
</div>
<div class="card-footer">
<div class="footer-row">
<span class="preset-name">Неон</span>
<span class="sys-badge">Системный</span>
</div>
<div class="style-hint">
Inter Bold <span class="sep">·</span>
<span class="color-dot" style="background: #00ffff;"></span> Неоновый
</div>
</div>
</div>
<!-- Минимализм -->
<div class="preset-card" onclick="selectCard(this)">
<div class="preview-area">
<div class="check-indicator">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</div>
<div class="card-actions">
<button class="act-btn" title="Редактировать"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg></button>
<button class="act-btn" title="Удалить"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg></button>
</div>
<div class="sub-content pos-bottom align-center">
<div class="sub-bubble" style="background: transparent;">
<span style="font-family: 'Inter', sans-serif; font-weight: 400; font-size: 15px; color: rgba(255,255,255,0.9);">Пример субтитров</span>
</div>
</div>
</div>
<div class="card-footer">
<div class="footer-row">
<span class="preset-name">Минимализм</span>
<span class="sys-badge">Системный</span>
</div>
<div class="style-hint">
Inter <span class="sep">·</span>
<span class="color-dot" style="background: #ffffff;"></span> Белый
</div>
</div>
</div>
<!-- Жирный -->
<div class="preset-card" onclick="selectCard(this)">
<div class="preview-area">
<div class="check-indicator">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</div>
<div class="card-actions">
<button class="act-btn" title="Редактировать"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg></button>
<button class="act-btn" title="Удалить"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg></button>
</div>
<div class="sub-content pos-bottom align-left">
<div class="sub-bubble" style="background: rgba(0,0,0,0.75); border-radius: 4px;">
<span style="font-family: 'Inter', sans-serif; font-weight: 900; font-size: 20px; color: #ffffff; -webkit-text-stroke: 1px #000;">Пример <span style="color: #ff006e;">субтитров</span></span>
</div>
</div>
</div>
<div class="card-footer">
<div class="footer-row">
<span class="preset-name">Жирный</span>
<span class="sys-badge">Системный</span>
</div>
<div class="style-hint">
Inter Black <span class="sep">·</span>
<span class="color-dot" style="background: #ff006e;"></span> Розовый
</div>
</div>
</div>
<!-- Create new -->
<div class="create-card" style="animation: cardIn 0.35s var(--ease-out) 250ms backwards;">
<div class="create-inner">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round">
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
</svg>
<span class="create-label">Создать пресет</span>
</div>
</div>
</div>
<div class="page-footer">
<button class="btn btn-outline">Назад</button>
<button class="btn btn-primary">Генерировать</button>
</div>
</div>
<script>
function selectCard(card) {
document.querySelectorAll('.preset-card').forEach(c => c.classList.remove('selected'));
card.classList.add('selected');
}
function setRatio(ratio, btn) {
document.querySelectorAll('.toggle-btn').forEach(b => {
if (b.closest('.demo-group')?.querySelector('.demo-label')?.textContent === 'Ratio')
b.classList.remove('active');
});
btn.classList.add('active');
document.querySelectorAll('.preview-area, .create-inner').forEach(el => {
el.style.aspectRatio = ratio;
});
}
function setTheme(theme, btn) {
document.querySelectorAll('.toggle-btn').forEach(b => {
if (b.closest('.demo-group')?.querySelector('.demo-label')?.textContent === 'Theme')
b.classList.remove('active');
});
btn.classList.add('active');
document.documentElement.setAttribute('data-theme', theme);
}
</script>
</body>
</html>