Files
2026-06-05 14:57:15 +03:00

434 lines
25 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>AI Chat</title>
<link rel="stylesheet" href="/static/css/app.css">
</head>
<body>
<header>
<button id="sidebarToggle" type="button"></button>
<h1>🤖 AI Chat</h1>
<span class="header-title" id="headerTitle">Новый чат</span>
<span id="rpgBadge" class="rpg-badge hidden" title="RPG режим">RPG</span>
<a href="/debug" class="header-icon-btn" title="Debug" style="text-decoration:none">🛠</a>
<button id="chatSettingsBtn" type="button" class="header-icon-btn" title="Настройки чата">⚙️</button>
<span id="affinityDisplay" class="affinity-display hidden" title="Симпатия к игроку"></span>
<span id="statsDisplay" class="stats-display hidden" title="Шкалы lust / stamina / tension"></span>
</header>
<div class="app-body">
<aside class="sidebar" id="sidebar">
<div class="sidebar-header">
<span>Чаты</span>
<button id="newChatBtn" type="button">+ Новый</button>
</div>
<div class="session-list" id="sessionList"></div>
<div class="quest-panel hidden" id="questPanel">
<div class="quest-panel-header">Квесты</div>
<div class="quest-panel-arc-header hidden" id="questPanelHeader"></div>
<p class="quest-panel-hint" id="questPanelHint">Клик по 🔸 — выбор, затем кнопка ниже</p>
<div id="questList"></div>
<div class="quest-panel-actions" id="questPanelActions">
<button type="button" id="questBtnDone" class="quest-btn-done" disabled>✓ Готово</button>
<button type="button" id="questBtnFail" class="quest-btn-fail" disabled>✗ Провал</button>
</div>
</div>
</aside>
<div class="main">
<div class="persona-bar" id="personaBar"></div>
<div class="system-blob" id="systemBlob">
<div class="system-blob-header">
<span>System</span>
<button type="button" id="systemBlobRefresh" title="Обновить"></button>
<button type="button" id="contextEditorOpen" title="Редактировать контекст"></button>
<button type="button" id="systemBlobToggle">Скрыть</button>
</div>
<pre class="system-blob-content" id="systemBlobContent"></pre>
</div>
<div class="messages" id="messages">
<div class="empty-state" id="emptyState">
<span class="big">💬</span>
<span>Начни новый чат</span>
</div>
</div>
<div class="input-area">
<button id="clearBtn" type="button" title="Очистить историю">🗑</button>
<textarea id="input" rows="1"
placeholder="Напиши сообщение... (Enter — отправить, Shift+Enter — новая строка)"></textarea>
<button id="sendBtn" type="button"></button>
</div>
</div>
</div>
<div class="modal-overlay" id="modalOverlay">
<div class="modal modal-wizard">
<div class="modal-wizard-header">
<h2>✨ Новый персонаж</h2>
<div class="wizard-steps">
<span class="wizard-step-dot active" data-step="1">1</span>
<span class="wizard-step-line"></span>
<span class="wizard-step-dot" data-step="2">2</span>
<span class="wizard-step-line"></span>
<span class="wizard-step-dot" data-step="3">3</span>
</div>
</div>
<div class="modal-wizard-body">
<div class="wizard-page active" data-step="1">
<p class="wizard-page-title">Основное</p>
<label>ID (латиницей)
<input type="text" id="pId" placeholder="my_hero">
</label>
<label>Имя
<input type="text" id="pName" placeholder="Мой герой">
</label>
<label>Эмодзи
<input type="text" id="pEmoji" placeholder="🦸" maxlength="4">
</label>
<label>Описание
<input type="text" id="pDesc" placeholder="Краткое описание">
</label>
</div>
<div class="wizard-page" data-step="2">
<p class="wizard-page-title">Характер и сценарий</p>
<label>Личность
<textarea id="pPersonality" rows="3" placeholder="calm, confident, sarcastic..."></textarea>
</label>
<label>Сценарий / мир
<textarea id="pScenario" rows="3" placeholder="где вы находитесь, что происходит, правила мира"></textarea>
</label>
<label>Первое сообщение (first_mes)
<textarea id="pFirstMes" rows="3" placeholder="приветствие персонажа"></textarea>
</label>
<label>Пример диалога (mes_example)
<textarea id="pMesExample" rows="3" placeholder="пример стиля речи персонажа"></textarea>
</label>
</div>
<div class="wizard-page" data-step="3">
<p class="wizard-page-title">Дополнительно</p>
<label>Lorebook JSON (опционально)
<textarea id="pLorebook" rows="3" placeholder='[]'></textarea>
</label>
<label>Системный промт (опционально, если пусто — соберём автоматически)
<textarea id="pPrompt" rows="3" placeholder=""></textarea>
</label>
<label><input type="checkbox" id="pSdEnabled"> Генерировать SD-промпт</label>
<label>LoRA
<input type="text" id="pLora" placeholder="CharacterLoRA">
</label>
<label>Теги внешности (SD)
<input type="text" id="pAppearance" placeholder="blue hair, elf ears">
</label>
</div>
</div>
<div class="modal-buttons modal-wizard-footer">
<button id="modalCancel" type="button">Отмена</button>
<div class="wizard-nav">
<button id="modalPrev" type="button" class="wizard-nav-btn hidden">← Назад</button>
<button id="modalNext" type="button" class="wizard-nav-btn">Далее →</button>
<button id="modalSave" type="button" class="hidden">Создать</button>
</div>
</div>
</div>
</div>
<div class="modal-overlay" id="cardModalOverlay">
<div class="modal modal-wizard" style="max-width:520px">
<div class="modal-wizard-header">
<h2>📥 Импорт карточки</h2>
<div class="wizard-steps">
<span class="wizard-step-dot active" data-step="1">1</span>
<span class="wizard-step-line"></span>
<span class="wizard-step-dot" data-step="2">2</span>
</div>
</div>
<div class="modal-wizard-body">
<div class="wizard-page active" data-step="1">
<p class="wizard-page-title">Файл</p>
<label>JSON или PNG (chub.io / V2)
<input type="file" id="cardFile" accept=".json,.png">
</label>
<p class="wizard-hint" id="cardPreviewHint"></p>
<label>LoRA
<input type="text" id="cardLora" placeholder="CharacterLoRA">
</label>
<label>Вес LoRA
<input type="number" id="cardLoraWeight" value="0.8" min="0" max="2" step="0.1">
</label>
</div>
<div class="wizard-page" data-step="2">
<p class="wizard-page-title">Проверь и отредактируй</p>
<label>Имя <input type="text" id="impCardName"></label>
<label>Описание <textarea id="impCardDescription" rows="3"></textarea></label>
<label>Личность <textarea id="impCardPersonality" rows="2"></textarea></label>
<label>Сценарий <textarea id="impCardScenario" rows="2"></textarea></label>
<label>Первое сообщение
<select id="impCardGreetingSelect"></select>
</label>
<label>Текст первого сообщения
<textarea id="impCardFirstMes" rows="4"></textarea>
</label>
<p class="wizard-hint hidden" id="impCardAltHint"></p>
<label>Пример диалога <textarea id="impCardMesExample" rows="2"></textarea></label>
<label>Теги внешности (SD) <input type="text" id="impCardAppearance"></label>
</div>
</div>
<div class="modal-buttons modal-wizard-footer">
<button id="cardModalCancel" type="button">Отмена</button>
<div class="wizard-nav">
<button id="cardModalPrev" type="button" class="wizard-nav-btn hidden">← Назад</button>
<button id="cardModalNext" type="button" class="wizard-nav-btn">Далее →</button>
<button id="cardModalImport" type="button" class="hidden" style="background:#e94560;color:white">Импорт</button>
</div>
</div>
</div>
</div>
<div class="modal-overlay" id="cardEditOverlay">
<div class="modal" style="max-width:560px;max-height:90vh;overflow-y:auto">
<h2>✏️ Редактор карточки</h2>
<input type="hidden" id="editCardId">
<label>Аватар (PNG)
<input type="file" id="editCardAvatar" accept=".png">
</label>
<label>Имя <input type="text" id="editName"></label>
<label>Описание <textarea id="editDescription" rows="4"></textarea></label>
<label>Личность <textarea id="editPersonality" rows="3"></textarea></label>
<label>Сценарий <textarea id="editScenario" rows="3"></textarea></label>
<label>Первое сообщение <textarea id="editFirstMes" rows="3"></textarea></label>
<label class="hidden" id="editCardAltBlock">Альтернативные приветствия (из карточки)
<select id="editCardGreetingSelect" size="4"></select>
</label>
<label>Пример диалога <textarea id="editMesExample" rows="3"></textarea></label>
<label>Теги внешности (SD) <input type="text" id="editAppearance" placeholder="silver hair, yellow eyes, wolf ears, black cloak"></label>
<label>LoRA <input type="text" id="editLora" placeholder="CharacterLoRA"></label>
<label>Вес LoRA <input type="number" id="editLoraWeight" value="0.8" min="0" max="2" step="0.1"></label>
<div class="modal-buttons">
<button id="cardEditCancel" type="button">Отмена</button>
<button id="cardEditSave" type="button" style="background:#e94560;color:white">Сохранить</button>
</div>
</div>
</div>
<div class="modal-overlay" id="personaEditOverlay">
<div class="modal" style="max-width:560px;max-height:90vh;overflow-y:auto">
<h2>✏️ Редактор персонажа</h2>
<input type="hidden" id="editPersonaId">
<label>Аватар (PNG)
<input type="file" id="editPAvatar" accept=".png">
</label>
<label>Имя <input type="text" id="editPName"></label>
<label>Эмодзи <input type="text" id="editPEmoji" maxlength="4"></label>
<label>Описание <textarea id="editPDesc" rows="3"></textarea></label>
<label>Личность <textarea id="editPPersonality" rows="3"></textarea></label>
<label>Сценарий <textarea id="editPScenario" rows="3"></textarea></label>
<label>Первое сообщение <textarea id="editPFirstMes" rows="3"></textarea></label>
<label>Пример диалога <textarea id="editPMesExample" rows="3"></textarea></label>
<label>Lorebook JSON <textarea id="editPLorebook" rows="3"></textarea></label>
<label>Системный промпт (опционально) <textarea id="editPPrompt" rows="3"></textarea></label>
<label><input type="checkbox" id="editPSdEnabled"> Генерировать SD-промпт</label>
<label>LoRA <input type="text" id="editPLora"></label>
<label>Вес LoRA <input type="number" id="editPLoraWeight" value="0.8" min="0" max="2" step="0.1"></label>
<label>Теги внешности (SD) <input type="text" id="editPAppearance"></label>
<div class="modal-buttons">
<button id="personaEditCancel" type="button">Отмена</button>
<button id="personaEditSave" type="button" style="background:#e94560;color:white">Сохранить</button>
</div>
</div>
</div>
<div class="modal-overlay" id="newChatModal">
<div class="modal modal-wizard">
<div class="modal-wizard-header">
<h2>💬 Новый чат</h2>
<div class="wizard-steps">
<span class="wizard-step-dot active" data-step="1">1</span>
<span class="wizard-step-line"></span>
<span class="wizard-step-dot" data-step="2">2</span>
<span class="wizard-step-line"></span>
<span class="wizard-step-dot" data-step="3">3</span>
</div>
</div>
<div class="modal-wizard-body">
<div class="wizard-page active" data-step="1">
<p class="wizard-page-title">Персонаж</p>
<div class="persona-pick-grid" id="newChatPersonaGrid"></div>
</div>
<div class="wizard-page" data-step="2">
<p class="wizard-page-title">Режим</p>
<label class="rpg-mode-option">
<input type="radio" name="newChatRpg" value="0" checked> Обычный чат
</label>
<label class="rpg-mode-option">
<input type="radio" name="newChatRpg" value="1"> RPG режим
</label>
</div>
<div class="wizard-page" data-step="3">
<div id="newChatPlainStep">
<p class="wizard-page-title">Название (опционально)</p>
<label>Название чата
<input type="text" id="newChatTitle" placeholder="Оставь пустым — сгенерируем автоматически">
</label>
<div id="newChatGreetingBlock" class="hidden" style="margin-top:12px">
<label>Первое сообщение
<select id="newChatGreetingSelect"></select>
</label>
<label>Текст (можно отредактировать)
<textarea id="newChatGreetingText" rows="3"></textarea>
</label>
</div>
</div>
<div id="newChatRpgStep" class="hidden">
<p class="wizard-page-title">Жанры и настройки RPG</p>
<p class="wizard-hint">Можно выбрать несколько жанров</p>
<div class="genre-grid" id="newChatGenreGrid">
<button type="button" class="genre-btn" data-genre="adventure">⚔️ Приключение</button>
<button type="button" class="genre-btn" data-genre="horror">👻 Хоррор</button>
<button type="button" class="genre-btn" data-genre="romance">💕 Романтика</button>
<button type="button" class="genre-btn" data-genre="slice_of_life">☕ Повседневность</button>
<button type="button" class="genre-btn" data-genre="fantasy">🧙 Фэнтези</button>
<button type="button" class="genre-btn" data-genre="sci_fi">🚀 Sci-Fi</button>
</div>
<p class="selected-genres-label hidden" id="newChatGenresLabel"></p>
<div class="rpg-settings-grid" style="margin-top:12px">
<label><input type="checkbox" id="ncSettingDice" checked> 🎲 Проверки d20</label>
<label><input type="checkbox" id="ncSettingNarrator" checked> 📖 Нарратор</label>
<label><input type="checkbox" id="ncSettingQuests" checked> 📜 Квесты</label>
<label><input type="checkbox" id="ncSettingAffinity" checked> 💖 Симпатия</label>
<label><input type="checkbox" id="ncSettingStats"> 📊 Шкалы (lust/stamina/tension)</label>
<label><input type="checkbox" id="ncSettingChoices" checked> 🔘 Кнопки выбора</label>
</div>
</div>
</div>
</div>
<div class="modal-buttons modal-wizard-footer">
<button id="newChatCancel" type="button">Отмена</button>
<div class="wizard-nav">
<button id="newChatPrev" type="button" class="wizard-nav-btn hidden">← Назад</button>
<button id="newChatNext" type="button" class="wizard-nav-btn">Далее →</button>
<button id="newChatCreate" type="button" class="hidden" style="background:#e94560;color:white">Создать</button>
</div>
</div>
</div>
</div>
<div class="modal-overlay" id="chatSettingsModal">
<div class="modal modal-wizard" style="max-width:520px">
<div class="modal-wizard-header">
<h2>⚙️ Настройки чата</h2>
</div>
<div class="modal-wizard-body">
<label>Название чата
<input type="text" id="chatSettingsTitle">
</label>
<p class="wizard-page-title">Персонаж чата</p>
<p class="hint-text">Смена персонажа перепривязывает этот чат. Историю можно сохранить или очистить.</p>
<div class="persona-pick-grid" id="chatSettingsPersonaGrid"></div>
<label class="rpg-mode-option">
<input type="checkbox" id="chatSettingsRpg"> RPG режим
</label>
<div id="chatSettingsRpgBlock" class="hidden">
<p class="wizard-page-title">Жанры</p>
<div class="genre-grid" id="chatSettingsGenreGrid">
<button type="button" class="genre-btn" data-genre="adventure">⚔️ Приключение</button>
<button type="button" class="genre-btn" data-genre="horror">👻 Хоррор</button>
<button type="button" class="genre-btn" data-genre="romance">💕 Романтика</button>
<button type="button" class="genre-btn" data-genre="slice_of_life">☕ Повседневность</button>
<button type="button" class="genre-btn" data-genre="fantasy">🧙 Фэнтези</button>
<button type="button" class="genre-btn" data-genre="sci_fi">🚀 Sci-Fi</button>
</div>
<p class="selected-genres-label hidden" id="chatSettingsGenresLabel"></p>
<p class="wizard-page-title" style="margin-top:12px">Настройки RPG</p>
<div class="rpg-settings-grid">
<label><input type="checkbox" id="csSettingDice"> 🎲 Проверки d20</label>
<label><input type="checkbox" id="csSettingNarrator"> 📖 Нарратор</label>
<label><input type="checkbox" id="csSettingQuests"> 📜 Квесты</label>
<label><input type="checkbox" id="csSettingAffinity"> 💖 Симпатия</label>
<label><input type="checkbox" id="csSettingStats"> 📊 Шкалы (lust/stamina/tension)</label>
<label><input type="checkbox" id="csSettingChoices"> 🔘 Кнопки выбора</label>
</div>
<div class="chat-settings-meta" id="chatSettingsMeta"></div>
<details class="rpg-debug-panel" id="chatSettingsRpgDebug">
<summary>🧪 Отладка: симпатия и шкалы</summary>
<p class="hint-text">Жёстко задаёт состояние для следующих реплик. Цель — текущий игрок (позже: пары персонаж×игрок).</p>
<div class="rpg-debug-grid">
<label>💖 Симпатия (−30…30)
<input type="number" id="debugAffinity" min="-30" max="30" step="1">
</label>
<label>🔥 Lust (010)
<input type="number" id="debugLust" min="0" max="10" step="1">
</label>
<label>⚡ Stamina (010)
<input type="number" id="debugStamina" min="0" max="10" step="1">
</label>
<label>😰 Tension (010)
<input type="number" id="debugTension" min="0" max="10" step="1">
</label>
</div>
<button type="button" id="debugRpgStateApply" class="rpg-debug-apply">Применить к сессии</button>
<span class="rpg-debug-status" id="debugRpgStateStatus"></span>
</details>
</div>
</div>
<div class="modal-buttons modal-wizard-footer">
<button id="chatSettingsCancel" type="button">Отмена</button>
<button id="chatSettingsSave" type="button" style="background:#e94560;color:white">Сохранить</button>
</div>
</div>
</div>
<div class="modal-overlay" id="contextEditorModal">
<div class="modal modal-wizard context-editor-modal">
<div class="modal-wizard-header">
<h2>✎ Контекст сессии</h2>
</div>
<div class="modal-wizard-body context-editor-body">
<p class="hint-text">Правки применяются сразу к следующим сообщениям и генерации картинок. JSON-поля — массив или объект.</p>
<label>Status quo
<textarea id="ctxStatusQuo" rows="3" spellcheck="false"></textarea>
</label>
<label>Global plot
<textarea id="ctxGlobalPlot" rows="2" spellcheck="false"></textarea>
</label>
<label>Outfit (JSON array, danbooru-теги с цветом)
<textarea id="ctxOutfit" rows="4" spellcheck="false" placeholder='["black_sports_shorts", "white_torn_tank_top"]'></textarea>
</label>
<label>Scene (JSON)
<textarea id="ctxScene" rows="4" spellcheck="false"></textarea>
</label>
<label>Facts (JSON array)
<textarea id="ctxFacts" rows="3" spellcheck="false" placeholder='[{"text":"...", "rp_day":"день 1"}]'></textarea>
</label>
<label>Plot arc (JSON)
<textarea id="ctxPlotArc" rows="6" spellcheck="false"></textarea>
</label>
<div class="context-editor-stats">
<label>💖 Affinity
<input type="number" id="ctxAffinity" min="-30" max="30" step="1">
</label>
<label>🔥 Lust
<input type="number" id="ctxLust" min="0" max="10" step="1">
</label>
<label>⚡ Stamina
<input type="number" id="ctxStamina" min="0" max="10" step="1">
</label>
<label>😰 Tension
<input type="number" id="ctxTension" min="0" max="10" step="1">
</label>
</div>
<span class="rpg-debug-status" id="contextEditorStatus"></span>
</div>
<div class="modal-buttons modal-wizard-footer">
<button id="contextEditorCancel" type="button">Отмена</button>
<button id="contextEditorSave" type="button" style="background:#9b7fd4;color:white">Сохранить</button>
</div>
</div>
</div>
<script type="module" src="/static/js/app.js?v=17"></script>
</body>
</html>