Files
ChatAIBot/static/js/utils.js
T
2026-06-01 07:44:38 +03:00

139 lines
4.8 KiB
JavaScript

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' });
}