export function parseImagePromptFromContent(content) { if (!content || !content.includes('[IMAGE_PROMPT:')) return { text: content, prompt: null }; const match = content.match(/\[IMAGE_PROMPT:(.*?)\]/s); const prompt = match ? match[1].trim() : null; const text = content.replace(/\[IMAGE_PROMPT:.*?\]/gs, '').trim(); return { text, prompt }; } export async function copyToClipboard(text) { try { await navigator.clipboard.writeText(text); return true; } catch { return false; } } export const GENRE_LABELS = { adventure: 'Приключение', horror: 'Хоррор', romance: 'Романтика', slice_of_life: 'Повседневность', fantasy: 'Фэнтези', sci_fi: 'Sci-Fi', }; export function initWizard(modalEl, { totalSteps, onStepChange, validateStep }) { let step = 1; const pages = modalEl.querySelectorAll('.wizard-page'); const dots = modalEl.querySelectorAll('.wizard-step-dot'); const prevBtn = modalEl.querySelector('[id$="Prev"]'); const nextBtn = modalEl.querySelector('[id$="Next"]'); const saveBtn = modalEl.querySelector('[id$="Save"], [id$="Confirm"], [id$="Create"]'); function render() { pages.forEach(p => p.classList.toggle('active', Number(p.dataset.step) === step)); dots.forEach(d => { const n = Number(d.dataset.step); d.classList.toggle('active', n === step); d.classList.toggle('done', n < step); }); prevBtn?.classList.toggle('hidden', step <= 1); nextBtn?.classList.toggle('hidden', step >= totalSteps); saveBtn?.classList.toggle('hidden', step < totalSteps); onStepChange?.(step); } function goTo(next) { if (next > step && validateStep && !validateStep(step)) return; step = Math.max(1, Math.min(totalSteps, next)); render(); } prevBtn?.addEventListener('click', () => goTo(step - 1)); nextBtn?.addEventListener('click', () => goTo(step + 1)); render(); return { reset() { step = 1; render(); }, getStep: () => step, goTo, render, }; } export function bindGenreGrid(gridEl, selectedSet, onChange) { gridEl.addEventListener('click', (e) => { const btn = e.target.closest('.genre-btn'); if (!btn) return; const genre = btn.dataset.genre; if (selectedSet.has(genre)) { selectedSet.delete(genre); btn.classList.remove('selected'); } else { selectedSet.add(genre); btn.classList.add('selected'); } onChange?.(); }); } export function resetGenreGrid(gridEl, selectedSet) { selectedSet.clear(); gridEl.querySelectorAll('.genre-btn').forEach(b => b.classList.remove('selected')); } export function getRpgSettingsFromDom(prefix = '') { const id = (name) => document.getElementById(prefix + name); return { dice: id('settingDice')?.checked ?? true, narrator: id('settingNarrator')?.checked ?? true, quests: id('settingQuests')?.checked ?? true, affinity: id('settingAffinity')?.checked ?? true, choices: id('settingChoices')?.checked ?? true, }; } export function formatSessionDate(iso) { if (!iso) return ''; const d = new Date(iso.includes('T') ? iso : iso.replace(' ', 'T') + 'Z'); if (Number.isNaN(d.getTime())) return ''; const now = new Date(); const sameDay = d.toDateString() === now.toDateString(); if (sameDay) return d.toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' }); return d.toLocaleDateString('ru-RU', { day: 'numeric', month: 'short' }); }