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"], [id$="Import"]', ); 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); } async function goTo(next) { if (next > step && validateStep) { const ok = await Promise.resolve(validateStep(step)); if (!ok) 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 fillGreetingSelect(selectEl, firstMes, alternates = []) { if (!selectEl) return; selectEl.innerHTML = ''; const main = document.createElement('option'); main.value = '0'; const mainPreview = (firstMes || '').replace(/\s+/g, ' ').trim(); main.textContent = mainPreview ? `Основное: ${mainPreview.slice(0, 50)}${mainPreview.length > 50 ? '…' : ''}` : 'Основное (first_mes)'; selectEl.appendChild(main); alternates.forEach((text, i) => { const opt = document.createElement('option'); opt.value = String(i + 1); const preview = String(text).replace(/\s+/g, ' ').trim(); opt.textContent = `Альт. ${i + 1}: ${preview.slice(0, 50)}${preview.length > 50 ? '…' : ''}`; selectEl.appendChild(opt); }); } export function getSelectedGreeting(selectEl, firstMes, alternates = []) { const v = selectEl?.value ?? '0'; if (v === '0') return firstMes || ''; const idx = parseInt(v, 10) - 1; return alternates[idx] ?? firstMes ?? ''; } 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' }); }