Add digital badge tab for round-display photo prep

Adds a new "Цифровой бейдж" tab for preparing 240×240 PNG circles for
round digital displays. Photos come from device upload or the existing
Flickr selection. Each photo can be drag-cropped and zoomed (mouse,
wheel, or pinch on touch), have a yellow price tag (arc band or
rectangular plate, with auto thousands separator and ₽), and a curved
nickname signature — which auto-moves to the top of the circle when a
price tag occupies the bottom. Saves are routed through the Web Share
API on phones so the badge lands in the iOS/Android Photos app
directly; history of saved badges is kept under data/badges/. Default
nickname is stored in Settings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
zuevav
2026-05-14 21:16:37 +03:00
parent a0dd7b1ed4
commit 68f4560bd5
4 changed files with 1445 additions and 0 deletions
+150
View File
@@ -81,6 +81,7 @@ if (isset($_GET['logout'])) {
<nav class="main-nav">
<button class="nav-btn" data-tab="gallery">Галерея</button>
<button class="nav-btn active" data-tab="posting">Публикация</button>
<button class="nav-btn" data-tab="badge">Цифровой бейдж</button>
<button class="nav-btn" data-tab="converter">Конвертер</button>
<button class="nav-btn" data-tab="widget">Виджет</button>
<button class="nav-btn" data-tab="settings">Настройки</button>
@@ -561,6 +562,143 @@ https://live.staticflickr.com/65535/12345678901_abcdef1234_b.jpg"></textarea>
</div>
</section>
<!-- Tab: Digital Badge (round display) -->
<section id="tab-badge" class="tab-content">
<div class="panel">
<h2>Цифровой бейдж</h2>
<p class="help-text">Подготовьте фотографии под круглые дисплеи 240×240 пикселей. Кадрируйте, добавьте ценник или подпись никнеймом дугой снизу.</p>
<!-- Photo sources -->
<div class="form-group">
<label>Источник фотографий:</label>
<div class="photo-source-buttons">
<input type="file" id="badge-file-upload" multiple accept="image/*" style="display: none;">
<button type="button" class="btn btn-secondary" id="btn-badge-upload">
<span class="btn-icon-text">📤</span> Загрузить с устройства
</button>
<button type="button" class="btn btn-secondary" id="btn-badge-from-flickr">
<span class="btn-icon-text">🖼</span> Выбранные в галерее Flickr
</button>
</div>
<p class="hint">Размер итогового кружка — 240×240 PNG с прозрачным фоном.</p>
</div>
<!-- Items grid (selected photos for badge processing) -->
<div class="form-group">
<label>Фотографии для бейджа: <span id="badge-items-count" class="photo-counter">0</span></label>
<div id="badge-items-grid" class="badge-items-grid">
<p class="placeholder">Выберите фото из галереи или загрузите с устройства</p>
</div>
</div>
<!-- Editor (visible when a photo is selected) -->
<div id="badge-editor" class="badge-editor hidden">
<div class="badge-editor-layout">
<!-- Canvas preview -->
<div class="badge-canvas-wrapper">
<canvas id="badge-canvas" width="480" height="480"></canvas>
<div class="badge-canvas-hint">Один палец — сдвиг, два пальца — масштаб. На компьютере — мышь и колесо.</div>
</div>
<!-- Controls -->
<div class="badge-controls">
<div class="form-group">
<label>Масштаб: <span id="badge-zoom-value">100%</span></label>
<input type="range" id="badge-zoom" min="100" max="400" step="1" value="100">
</div>
<div class="form-group">
<button type="button" id="btn-badge-reset" class="btn btn-secondary btn-small">Сбросить кадрирование</button>
</div>
<hr style="border:none; border-top:1px solid var(--border-color, #ddd); margin:12px 0;">
<!-- Price tag -->
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="badge-price-enabled">
<span>Добавить ценник</span>
</label>
</div>
<div id="badge-price-options" class="badge-sub-options hidden">
<div class="form-group">
<label for="badge-price-value">Цена (₽):</label>
<input type="number" inputmode="numeric" pattern="[0-9]*" id="badge-price-value" min="0" step="1" placeholder="2500">
</div>
<div class="form-group">
<label>Стиль плашки:</label>
<div class="badge-radio-row">
<label class="radio-label">
<input type="radio" name="badge-price-style" value="arc" checked>
<span>Дуга по низу</span>
</label>
<label class="radio-label">
<input type="radio" name="badge-price-style" value="rect">
<span>Прямоугольник</span>
</label>
</div>
</div>
</div>
<hr style="border:none; border-top:1px solid var(--border-color, #ddd); margin:12px 0;">
<!-- Nickname -->
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="badge-nickname-enabled">
<span>Добавить никнейм (дугой снизу)</span>
</label>
</div>
<div id="badge-nickname-options" class="badge-sub-options hidden">
<div class="form-group">
<label for="badge-nickname-value">Текст:</label>
<input type="text" id="badge-nickname-value" maxlength="40" placeholder="Никнейм">
<span class="hint">По умолчанию — из настроек.</span>
</div>
<div class="form-group">
<label>Цвет текста:</label>
<div class="badge-radio-row">
<label class="radio-label">
<input type="radio" name="badge-nick-color" value="white" checked>
<span>Белый (с тенью)</span>
</label>
<label class="radio-label">
<input type="radio" name="badge-nick-color" value="black">
<span>Чёрный</span>
</label>
</div>
</div>
</div>
<hr style="border:none; border-top:1px solid var(--border-color, #ddd); margin:12px 0;">
<div class="badge-actions">
<button type="button" id="btn-badge-download" class="btn btn-primary">
<span class="btn-icon-text">↓</span> <span id="badge-download-label">Сохранить PNG</span>
</button>
<button type="button" id="btn-badge-remove" class="btn btn-secondary btn-small">Убрать из списка</button>
</div>
</div>
</div>
</div>
<!-- Batch actions -->
<div id="badge-batch-actions" class="form-group hidden" style="margin-top: 16px;">
<button type="button" id="btn-badge-download-all" class="btn btn-secondary">
<span class="btn-icon-text">⬇</span> Скачать все бейджи
</button>
</div>
<!-- History -->
<div class="settings-section" style="margin-top: 24px;">
<h3>История сохранённых бейджей</h3>
<div id="badge-history-grid" class="badge-history-grid">
<p class="placeholder">История пуста — сгенерированные бейджи будут появляться здесь.</p>
</div>
</div>
</div>
</section>
<!-- Tab: Settings -->
<section id="tab-settings" class="tab-content">
<div class="panel">
@@ -683,6 +821,18 @@ foreach ($channels as $ch) {
<span id="cross-promo-save-status" class="save-status"></span>
</div>
<!-- Digital Badge Settings -->
<div class="settings-section">
<h3>Цифровой бейдж</h3>
<p class="help-text">Никнейм по умолчанию для подписи дугой снизу.</p>
<div class="form-group">
<label for="badge-nickname-default">Никнейм для бейджа:</label>
<input type="text" id="badge-nickname-default" maxlength="40" placeholder="Ваш никнейм">
<button id="btn-save-badge-nickname" class="btn btn-primary btn-small" style="margin-left: 10px;">Сохранить</button>
<span id="badge-nickname-save-status" class="save-status"></span>
</div>
</div>
<!-- Theme -->
<div class="settings-section">
<h3>Оформление</h3>