356 lines
20 KiB
HTML
356 lines
20 KiB
HTML
<!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"></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 id="questList"></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="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="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="csSettingChoices"> 🔘 Кнопки выбора</label>
|
|
</div>
|
|
<div class="chat-settings-meta" id="chatSettingsMeta"></div>
|
|
</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>
|
|
|
|
<script type="module" src="/static/js/app.js?v=9"></script>
|
|
</body>
|
|
</html>
|