Files
agentik-desktop/sketches/005-new-chat-picker/index.html
T

437 lines
20 KiB
HTML
Raw 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">
<title>Новый диалог — выбор ассистента</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg: #121218; --panel: #17212B; --panel2: #1C2733; --line: #232B36;
--fg: #EBEBEB; --dim: #768C9E; --accent: #6AB2F2; --badge: #5EB5F7;
--err: #EC3942; --ok: #63C88A; --user-bubble: #2B5278; --bot-bubble: #1C2733;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
-webkit-font-smoothing: antialiased;
background: #0A0A0F; color: var(--fg); line-height: 1.45;
display: flex; flex-direction: column; align-items: center; justify-content: center;
min-height: 100vh; padding: 24px; gap: 16px;
}
/* ============ переключатель состояний ============ */
.switcher { display: flex; gap: 7px; flex-wrap: wrap; justify-content: center; max-width: 1180px; }
.switcher button {
padding: 7px 13px; border-radius: 9px; border: 1px solid var(--line); background: var(--panel);
color: var(--dim); font-size: 12.5px; cursor: pointer; font-family: inherit;
}
.switcher button:hover { color: var(--fg); }
.switcher button.on { background: var(--accent); border-color: var(--accent); color: #0F1922; font-weight: 600; }
/* ============ окно клиента ============ */
.window {
width: 1180px; height: 740px; background: var(--bg); border-radius: 12px;
display: flex; overflow: hidden; border: 1px solid var(--line); position: relative;
box-shadow: 0 24px 80px rgba(0,0,0,.6);
}
.sidebar { width: 320px; background: var(--panel); border-right: 1px solid var(--line); display: flex; flex-direction: column; }
.sb-head { padding: 14px 16px 10px; }
.sb-title { font-size: 17px; font-weight: 600; display: flex; align-items: center; justify-content: space-between; }
.search {
margin: 0 16px 10px; padding: 8px 11px; border-radius: 9px; border: 1px solid transparent;
background: var(--panel2); color: var(--fg); font-size: 13px; outline: none; font-family: inherit;
}
.search::placeholder { color: var(--dim); }
.folders { display: flex; gap: 6px; padding: 0 16px 10px; overflow-x: auto; }
.folder {
padding: 5px 11px; border-radius: 14px; font-size: 12px; color: var(--dim);
background: var(--panel2); cursor: pointer; white-space: nowrap; border: 1px solid transparent;
}
.folder.active { color: var(--fg); border-color: var(--accent); background: rgba(106,178,242,.12); }
.folder .cnt { color: var(--badge); font-weight: 600; margin-left: 4px; }
.chat-list { flex: 1; overflow-y: auto; padding: 2px 8px 12px; }
.chat-row { display: flex; gap: 11px; padding: 9px 10px; border-radius: 10px; align-items: flex-start; cursor: pointer; }
.chat-row:hover { background: var(--panel2); }
.chat-row.active { background: #233040; }
.avatar {
width: 42px; height: 42px; border-radius: 50%; flex: 0 0 42px; display: grid; place-items: center;
font-size: 15px; font-weight: 600; color: #0F1922; background: var(--accent);
}
.avatar.g2 { background: #86C99A; }
.avatar.g3 { background: #C9A886; }
/* аватар-картинка: клиент показывает СВОЮ копию файла */
.avatar.pic {
background: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Crect width='40' height='40' fill='%232F4A63'/%3E%3Ccircle cx='20' cy='15' r='6.5' fill='%23DCE6F0'/%3E%3Cpath d='M7 40c0-7.5 5.8-12 13-12s13 4.5 13 12z' fill='%23DCE6F0'/%3E%3C/svg%3E") center/cover;
}
.chat-mid { flex: 1; min-width: 0; }
.chat-name { font-size: 14px; font-weight: 500; display: flex; justify-content: space-between; gap: 8px; }
.chat-time { font-size: 11px; color: var(--dim); font-weight: 400; }
.chat-prev { font-size: 12.5px; color: var(--dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 2px; }
.row-tail { flex: 0 0 auto; }
.badge { background: var(--badge); color: #0F1922; font-size: 11px; font-weight: 700; border-radius: 10px; padding: 1px 7px; }
/* кнопка «+» рядом с заголовком. Состояние .hidden — при нуле ассистентов */
.ic-btn { width: 32px; height: 32px; border-radius: 8px; border: none; background: transparent;
color: var(--dim); cursor: pointer; font-size: 16px; }
.ic-btn:hover { background: var(--panel2); color: var(--fg); }
.add-btn { font-size: 19px; color: var(--accent); }
.add-btn.hidden { display: none; }
/* пустой список */
.empty { padding: 44px 26px; text-align: center; }
.empty .big { font-size: 34px; opacity: .28; margin-bottom: 12px; }
.empty .ttl { font-size: 14px; font-weight: 600; margin-bottom: 7px; }
.empty .sub { font-size: 12.5px; color: var(--dim); line-height: 1.55; }
.empty .link { color: var(--accent); cursor: pointer; text-decoration: none; border-bottom: 1px dashed var(--accent); }
.main { flex: 1; display: flex; flex-direction: column; }
.chat-head { height: 58px; flex: 0 0 58px; border-bottom: 1px solid var(--line); display: flex; align-items: center; gap: 11px; padding: 0 16px; }
.chat-head .avatar { width: 36px; height: 36px; flex: 0 0 36px; font-size: 13px; }
.head-name { font-size: 14.5px; font-weight: 600; }
.head-sub { font-size: 11.5px; color: var(--dim); }
.head-sub .dot { color: var(--ok); }
.chat-head .ic-btn { margin-left: auto; }
.messages { flex: 1; overflow-y: auto; padding: 18px 24px; display: flex; flex-direction: column; gap: 12px; }
.msg { max-width: 74%; padding: 9px 13px; border-radius: 13px; font-size: 13.5px; }
.msg.user { align-self: flex-end; background: var(--user-bubble); border-bottom-right-radius: 4px; }
.msg.bot { align-self: flex-start; background: var(--bot-bubble); border-bottom-left-radius: 4px; }
/* ============ затемнение и окно ============ */
.scrim { position: absolute; inset: 0; background: rgba(0,0,0,.62); z-index: 100; display: none; }
.scrim.on { display: block; }
.layer { position: absolute; inset: 0; display: none; align-items: center; justify-content: center; z-index: 150; pointer-events: none; }
.layer.on { display: flex; }
.layer > * { pointer-events: auto; }
.dialog {
width: 480px; min-width: 480px; flex: 0 0 auto; max-height: 600px; background: var(--panel);
border: 1px solid var(--line); border-radius: 14px; display: flex; flex-direction: column;
overflow: hidden; box-shadow: 0 26px 80px rgba(0,0,0,.7);
}
.d-head { display: flex; align-items: center; gap: 11px; padding: 16px 18px 14px; border-bottom: 1px solid var(--line); }
.d-head h2 { font-size: 16px; font-weight: 600; flex: 1; }
.x-btn { width: 30px; height: 30px; border-radius: 8px; border: none; background: transparent;
color: var(--dim); cursor: pointer; font-size: 17px; }
.x-btn:hover { background: var(--panel2); color: var(--fg); }
.d-body { flex: 1; overflow-y: auto; padding: 14px 18px 8px; }
.d-foot { padding: 13px 18px 16px; border-top: 1px solid var(--line); display: flex; gap: 9px; }
.d-lead { font-size: 12.5px; color: var(--dim); margin-bottom: 12px; }
/* ── вариант выбора ассистента ── */
.agent-opt {
display: flex; align-items: center; gap: 12px; padding: 11px 12px; border-radius: 11px;
background: var(--panel2); margin-bottom: 9px; cursor: pointer;
border: 1.5px solid transparent;
}
.agent-opt:hover { border-color: #33414F; }
.agent-opt.on { border-color: var(--accent); background: rgba(106,178,242,.10); }
.agent-opt .avatar { width: 40px; height: 40px; flex: 0 0 40px; font-size: 13px; }
.ag-mid { flex: 1; min-width: 0; }
.ag-name { font-size: 14px; font-weight: 550; }
.ag-addr { font-size: 11.5px; color: var(--dim); font-family: ui-monospace, "SF Mono", Menlo, monospace;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 3px; }
.ag-state { font-size: 11px; display: flex; align-items: center; gap: 5px; margin-top: 4px; }
.ag-state .d { width: 7px; height: 7px; border-radius: 50%; background: var(--ok); }
.ag-state.ok { color: var(--ok); }
.ag-state.off { color: var(--dim); } .ag-state.off .d { background: var(--dim); }
.ag-state.err { color: var(--err); } .ag-state.err .d { background: var(--err); }
/* кружок-галочка выбора */
.pick { width: 20px; height: 20px; flex: 0 0 20px; border-radius: 50%; border: 2px solid #3D4A58; }
.agent-opt.on .pick { border-color: var(--accent); background: var(--accent); position: relative; }
.agent-opt.on .pick::after {
content: ""; position: absolute; left: 5px; top: 1.5px; width: 5px; height: 9px;
border: solid #0F1922; border-width: 0 2px 2px 0; transform: rotate(42deg);
}
/* предупреждение под списком */
.warn {
display: none; gap: 8px; align-items: flex-start; margin-top: 4px; padding: 10px 12px;
border-radius: 10px; background: rgba(236,57,66,.09); border: 1px solid rgba(236,57,66,.32);
font-size: 12px; color: #F0A6AA;
}
.warn.on { display: flex; }
.warn .i { flex: 0 0 auto; }
/* кнопки */
.btn {
padding: 10px 16px; border-radius: 10px; border: 1px solid var(--line); background: var(--panel2);
color: var(--fg); font-size: 13.5px; cursor: pointer; font-family: inherit;
}
.btn:hover { border-color: #3D4A58; }
.btn.prim { background: var(--accent); border-color: var(--accent); color: #0F1922; font-weight: 600; }
.btn.prim:disabled { background: #253544; border-color: #253544; color: #5C6B7A; cursor: not-allowed; }
</style>
</head>
<body>
<div class="switcher">
<button data-view="pick" class="on">Выбор: 2 ассистента</button>
<button data-view="offline">Третий не отвечает</button>
<button data-view="one">Ассистент один</button>
<button data-view="zero">Ассистентов нет</button>
<button data-view="made">Диалог создан</button>
</div>
<div class="window">
<!-- ================= СПИСОК ДИАЛОГОВ ================= -->
<aside class="sidebar">
<div class="sb-head">
<div class="sb-title">
<span>Диалоги</span>
<button class="ic-btn add-btn" id="newChatBtn" title="Новый диалог">+</button>
</div>
</div>
<input class="search" placeholder="Поиск диалога…" id="searchBox">
<div class="folders" id="folders">
<div class="folder active">Все чаты <span class="cnt" id="cnt">2</span></div>
<div class="folder">Работа</div>
<div class="folder">Дом</div>
<div class="folder">+</div>
</div>
<div class="chat-list" id="chatList"></div>
</aside>
<!-- ================= ЧАТ ================= -->
<main class="main" id="mainPane">
<header class="chat-head">
<div class="avatar" id="headAv">АГ</div>
<div>
<div class="head-name" id="headName">agentik · рабочий</div>
<div class="head-sub"><span class="dot">●</span> на связи</div>
</div>
<button class="ic-btn" title="Настройки">⚙</button>
</header>
<div class="messages" id="msgs">
<div class="msg user">Проверь, почему падает CI</div>
<div class="msg bot">Нашёл: длинное тире в именах тестов ломает сборку. Починил, CI зелёный.</div>
</div>
</main>
<div class="scrim" id="scrim"></div>
<!-- ================= ОКНО: НОВЫЙ ДИАЛОГ ================= -->
<div class="layer" id="layerNew">
<div class="dialog">
<div class="d-head">
<h2>Новый диалог</h2>
<button class="x-btn" onclick="closeDialog()">✕</button>
</div>
<div class="d-body">
<div class="d-lead" id="lead">В каком ассистенте создать диалог?</div>
<div id="agentOpts">
<div class="agent-opt on" data-id="a1" data-ok="1" data-tag="АГ">
<div class="avatar">АГ</div>
<div class="ag-mid">
<div class="ag-name">agentik · рабочий</div>
<div class="ag-addr">http://192.168.88.135:8080/agentik</div>
<div class="ag-state ok"><span class="d"></span>на связи</div>
</div>
<div class="pick"></div>
</div>
<div class="agent-opt" data-id="a2" data-ok="1" data-tag="ДМ">
<div class="avatar g2">ДМ</div>
<div class="ag-mid">
<div class="ag-name">домашний</div>
<div class="ag-addr">http://192.168.76.199:8080/agentik</div>
<div class="ag-state ok"><span class="d"></span>на связи</div>
</div>
<div class="pick"></div>
</div>
<div class="agent-opt" data-id="a3" data-ok="0" data-tag="КА" data-pic="1">
<div class="avatar pic"></div>
<div class="ag-mid">
<div class="ag-name">Кот-ассистент</div>
<div class="ag-addr">http://192.168.88.24:8080/agentik</div>
<div class="ag-state err"><span class="d"></span>не отвечает</div>
</div>
<div class="pick"></div>
</div>
</div>
<div class="warn" id="warn">
<span class="i">⚠</span>
<span id="warnText">Ассистент сейчас не отвечает. Диалог создать можно, но
сообщение не уйдёт, пока он не поднимется.</span>
</div>
</div>
<div class="d-foot">
<button class="btn" style="flex:1" onclick="closeDialog()">Отмена</button>
<button class="btn prim" style="flex:1" id="createBtn" disabled>Создать диалог</button>
</div>
</div>
</div>
</div>
<script>
const el = id => document.getElementById(id);
// ───────── данные состояний ─────────
const AG_ALL = `
<div class="agent-opt on" data-id="a1" data-ok="1">
<div class="avatar">АГ</div>
<div class="ag-mid">
<div class="ag-name">agentik · рабочий</div>
<div class="ag-addr">http://192.168.88.135:8080/agentik</div>
<div class="ag-state ok"><span class="d"></span>на связи</div>
</div>
<div class="pick"></div>
</div>
<div class="agent-opt" data-id="a2" data-ok="1">
<div class="avatar g2">ДМ</div>
<div class="ag-mid">
<div class="ag-name">домашний</div>
<div class="ag-addr">http://192.168.76.199:8080/agentik</div>
<div class="ag-state ok"><span class="d"></span>на связи</div>
</div>
<div class="pick"></div>
</div>
<div class="agent-opt" data-id="a3" data-ok="0">
<div class="avatar pic"></div>
<div class="ag-mid">
<div class="ag-name">Кот-ассистент</div>
<div class="ag-addr">http://192.168.88.24:8080/agentik</div>
<div class="ag-state err"><span class="d"></span>не отвечает</div>
</div>
<div class="pick"></div>
</div>`;
const CHATS_TWO = `
<div class="chat-row active">
<div class="avatar">АГ</div>
<div class="chat-mid">
<div class="chat-name"><span>agentik · рабочий</span><span class="chat-time">14:22</span></div>
<div class="chat-prev">Собрал отчёт, жду правок</div>
</div>
<div class="row-tail"><span class="badge">2</span></div>
</div>
<div class="chat-row">
<div class="avatar g2">ДМ</div>
<div class="chat-mid">
<div class="chat-name"><span>домашний</span><span class="chat-time">12:05</span></div>
<div class="chat-prev">Файл лежит на диске</div>
</div>
</div>`;
const CHATS_NEW = `
<div class="chat-row active">
<div class="avatar g2">ДМ</div>
<div class="chat-mid">
<div class="chat-name"><span>домашний</span><span class="chat-time">сейчас</span></div>
<div class="chat-prev">Новый диалог</div>
</div>
</div>
<div class="chat-row">
<div class="avatar">АГ</div>
<div class="chat-mid">
<div class="chat-name"><span>agentik · рабочий</span><span class="chat-time">14:22</span></div>
<div class="chat-prev">Собрал отчёт, жду правок</div>
</div>
<div class="row-tail"><span class="badge">2</span></div>
</div>`;
const CHATS_EMPTY = `
<div class="empty">
<div class="big">💬</div>
<div class="ttl">Диалогов пока нет</div>
<div class="sub">Ассистенты ещё не добавлены.<br>
Добавьте первого в <a class="link" onclick="alert('Откроется окно настроек — макет 004-settings-agents')">настройках</a>.</div>
</div>`;
// ───────── управление окном ─────────
function setAgents(html) { el('agentOpts').innerHTML = html; bindOpts(); }
function bindOpts() {
document.querySelectorAll('.agent-opt').forEach(o => o.onclick = () => {
document.querySelectorAll('.agent-opt').forEach(x => x.classList.remove('on'));
o.classList.add('on');
el('createBtn').disabled = false;
el('warn').classList.toggle('on', o.dataset.ok === '0');
});
}
function openDialog() {
el('scrim').classList.add('on');
el('layerNew').classList.add('on');
el('createBtn').disabled = false;
}
function closeDialog() {
el('scrim').classList.remove('on');
el('layerNew').classList.remove('on');
}
el('newChatBtn').onclick = openDialog;
document.querySelectorAll('.switcher button').forEach(b => b.onclick = () => {
document.querySelectorAll('.switcher button').forEach(x => x.classList.remove('on'));
b.classList.add('on');
render(b.dataset.view);
});
function render(v) {
closeDialog();
el('warn').classList.remove('on');
if (v === 'pick') {
el('chatList').innerHTML = CHATS_TWO;
el('folders').style.display = ''; el('searchBox').style.display = '';
el('cnt').textContent = '2';
el('newChatBtn').classList.remove('hidden');
setAgents(AG_ALL);
el('createBtn').disabled = false;
openDialog();
}
if (v === 'offline') {
el('chatList').innerHTML = CHATS_TWO;
el('folders').style.display = ''; el('searchBox').style.display = '';
el('cnt').textContent = '2';
el('newChatBtn').classList.remove('hidden');
setAgents(AG_ALL);
const off = document.querySelector('.agent-opt[data-ok="0"]');
document.querySelectorAll('.agent-opt').forEach(x => x.classList.remove('on'));
off.classList.add('on');
el('warn').classList.add('on');
el('createBtn').disabled = false;
openDialog();
}
if (v === 'one') {
el('chatList').innerHTML = CHATS_TWO;
el('folders').style.display = ''; el('searchBox').style.display = '';
el('cnt').textContent = '2';
el('newChatBtn').classList.remove('hidden');
}
if (v === 'zero') {
el('chatList').innerHTML = CHATS_EMPTY;
el('folders').style.display = 'none'; el('searchBox').style.display = 'none';
el('newChatBtn').classList.add('hidden');
}
if (v === 'made') {
el('chatList').innerHTML = CHATS_NEW;
el('folders').style.display = ''; el('searchBox').style.display = '';
el('cnt').textContent = '2';
el('newChatBtn').classList.remove('hidden');
el('headAv').textContent = 'ДМ'; el('headAv').className = 'avatar g2';
el('headName').textContent = 'домашний';
}
}
bindOpts();
render('pick');
</script>
</body>
</html>