Files
agentik-desktop/sketches/004-settings-agents/index.html
T

626 lines
30 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; align-items: center; justify-content: center; min-height: 100vh; padding: 24px;
}
.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); }
/* ── папки: как в выбранном варианте 1 ── */
.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; }
.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; }
.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); }
.ic-btn { width: 32px; height: 32px; border-radius: 8px; border: none; background: transparent;
color: var(--dim); cursor: pointer; font-size: 16px; margin-left: auto; }
.ic-btn:hover { background: var(--panel2); color: var(--fg); }
.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; }
.scrim.top { background: rgba(0,0,0,.42); z-index: 200; }
/* ============ ОКНО НАСТРОЕК ============ */
.layer { position: absolute; inset: 0; display: none; align-items: center; justify-content: center; pointer-events: none; }
.layer.on { display: flex; }
.layer > * { pointer-events: auto; }
/* порядок слоёв: настройки 150, проверка поверх 250 */
#layerSettings { z-index: 150; }
#layerCheck { z-index: 250; }
.dialog {
width: 620px; min-width: 620px; flex: 0 0 auto; max-height: 620px; background: #17212B;
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);
}
.dialog.small { width: 480px; min-width: 480px; max-height: 420px; }
.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 6px; }
.d-foot { padding: 13px 18px 16px; border-top: 1px solid var(--line); display: flex; gap: 9px; }
/* список агентов */
.agent-card {
display: flex; align-items: center; gap: 12px; padding: 11px 12px; border-radius: 11px;
background: var(--panel2); margin-bottom: 9px; border: 1px solid transparent;
}
.agent-card:hover { border-color: var(--line); }
.agent-card .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; }
.state { display: flex; align-items: center; gap: 5px; font-size: 11px; color: var(--ok); flex: 0 0 auto; }
.state .d { width: 7px; height: 7px; border-radius: 50%; background: var(--ok); }
.state.off { color: var(--dim); } .state.off .d { background: var(--dim); }
.state.err { color: var(--err); } .state.err .d { background: var(--err); }
.card-acts { display: flex; gap: 4px; flex: 0 0 auto; }
.mini-ic { width: 30px; height: 30px; border-radius: 8px; border: none; background: transparent;
color: var(--dim); cursor: pointer; font-size: 14px; }
.mini-ic:hover { background: var(--panel); color: var(--fg); }
.mini-ic.del:hover { color: var(--err); }
/* форма */
.field { margin-bottom: 15px; }
.field label { display: block; font-size: 12px; color: var(--dim); margin-bottom: 6px; }
.field input {
width: 100%; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--line);
background: var(--bg); color: var(--fg); font-size: 13.5px; outline: none; font-family: inherit;
}
.field input:focus { border-color: var(--accent); }
.field input.mono { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12.5px; }
.field .note { font-size: 11px; color: var(--dim); margin-top: 6px; }
.note code { background: var(--panel2); padding: 1px 4px; border-radius: 4px; font-size: 10.5px; }
/* адрес + кнопка проверки в одной строке */
.addr-row { display: flex; gap: 8px; }
.addr-row input { flex: 1; }
.colors { display: flex; gap: 9px; }
.swatch { width: 30px; height: 30px; border-radius: 50%; cursor: pointer; border: 2px solid transparent; }
.swatch.on { border-color: var(--fg); }
/* цвет ИЛИ картинка */
.appearance-tabs { display: flex; gap: 6px; margin-bottom: 11px; }
.appearance-tabs .tab {
padding: 6px 13px; border-radius: 9px; font-size: 12.5px; color: var(--dim);
background: var(--panel2); cursor: pointer; border: 1px solid transparent;
}
.appearance-tabs .tab.on { color: var(--fg); border-color: var(--accent); background: rgba(106,178,242,.12); }
.pane { display: none; }
.pane.on { display: block; }
.pic-row { display: flex; align-items: center; gap: 13px; }
.pic-preview {
width: 54px; height: 54px; border-radius: 50%; flex: 0 0 54px; background: var(--bg);
border: 1px dashed var(--line); display: grid; place-items: center; color: var(--dim);
font-size: 20px; overflow: hidden;
}
.pic-preview.filled { border-style: solid; border-color: var(--line); }
.pic-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pic-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.avatar.pic { background: transparent; padding: 0; overflow: hidden; }
.avatar.pic img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* кнопки */
.btn { border-radius: 10px; padding: 10px 17px; font-size: 13px; cursor: pointer; border: 1px solid var(--line);
background: transparent; color: var(--fg); font-family: inherit; white-space: nowrap; }
.btn:hover { border-color: var(--dim); }
.btn.prim { background: var(--accent); border-color: var(--accent); color: #0F1922; font-weight: 650; }
.btn.prim:disabled { background: var(--panel2); border-color: var(--line); color: var(--dim); cursor: default; }
.btn.dang { color: var(--err); border-color: rgba(236,57,66,.4); }
.btn.dang:hover { border-color: var(--err); background: rgba(236,57,66,.09); }
.btn.sm { padding: 8px 13px; font-size: 12.5px; }
.btn.wide { width: 100%; display: flex; align-items: center; justify-content: center; gap: 8px;
border-style: dashed; color: var(--dim); }
.btn.wide:hover { color: var(--fg); border-color: var(--dim); }
/* ============ ОКНО ПРОВЕРКИ СВЯЗИ ============ */
.spin { width: 17px; height: 17px; border-radius: 50%; border: 2px solid var(--line);
border-top-color: var(--accent); animation: sp .7s linear infinite; flex: 0 0 17px; }
@keyframes sp { to { transform: rotate(360deg); } }
.probe-head { display: flex; align-items: center; gap: 13px; padding: 4px 0 14px; }
.probe-head .big { width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center;
font-size: 19px; flex: 0 0 40px; }
.probe-head.ok .big { background: rgba(99,200,138,.15); color: var(--ok); }
.probe-head.err .big { background: rgba(236,57,66,.15); color: var(--err); }
.probe-head.busy .big { background: transparent; padding: 0; }
.probe-head .txt { flex: 1; }
.probe-head .ttl { font-size: 14px; font-weight: 600; }
.probe-head .sub { font-size: 12px; color: var(--dim); margin-top: 2px; }
.probe-list { font-size: 12px; }
.probe-list div { display: flex; justify-content: space-between; gap: 14px; padding: 7px 0;
border-bottom: 1px solid var(--line); }
.probe-list div:last-child { border-bottom: none; }
.probe-list span { color: var(--dim); }
.probe-list b { font-weight: 550; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 11.5px; }
.hint-line { font-size: 11.5px; color: var(--dim); margin-top: 12px; padding: 9px 11px;
background: var(--bg); border-radius: 9px; border: 1px solid var(--line); }
/* переключатель состояний — только для показа */
.switcher {
position: fixed; top: 14px; left: 50%; transform: translateX(-50%); display: flex; gap: 7px;
background: rgba(23,33,43,.96); border: 1px solid var(--line); border-radius: 11px; padding: 7px 9px; z-index: 300;
}
.switcher button { background: transparent; border: none; color: var(--dim); font-size: 12px;
padding: 5px 11px; border-radius: 7px; cursor: pointer; font-family: inherit; }
.switcher button.on { background: var(--accent); color: #0F1922; font-weight: 600; }
/* ============ список диалогов с несколькими агентами ============ */
.group-head { display: flex; align-items: center; gap: 8px; padding: 11px 10px 6px; }
.group-head .dot { width: 9px; height: 9px; border-radius: 50%; }
.group-head .nm { font-size: 10.5px; letter-spacing: 1.1px; text-transform: uppercase; color: var(--dim); font-weight: 700; }
.group-head .cnt { font-size: 10.5px; color: var(--dim); margin-left: auto; }
.accent-bar { width: 3px; border-radius: 2px; flex: 0 0 3px; align-self: stretch; }
@media (max-height: 820px) { body { padding: 8px; } .window { transform: scale(.8); } }
@media (max-height: 700px) { .window { transform: scale(.68); } }
</style>
</head>
<body>
<div class="switcher">
<button data-view="list" class="on">Список агентов</button>
<button data-view="add">Добавить агента</button>
<button data-view="check" data-mode="ok">Проверка: отвечает</button>
<button data-view="check" data-mode="err">Проверка: не отвечает</button>
<button data-view="multi">Список диалогов</button>
</div>
<div class="window">
<!-- ===== фон: клиент с папками ===== -->
<aside class="sidebar">
<div class="sb-head">
<div class="sb-title">
<span>Диалоги</span>
<button class="ic-btn" style="margin:0" title="Новый диалог">+</button>
</div>
</div>
<input class="search" placeholder="Поиск диалога…">
<div class="folders">
<div class="folder active">Все чаты <span class="cnt">3</span></div>
<div class="folder">Работа</div>
<div class="folder">Дом</div>
<div class="folder">+</div>
</div>
<div class="chat-list" id="chatArea"></div>
</aside>
<main class="main">
<header class="chat-head">
<div class="avatar">АГ</div>
<div>
<div class="head-name">agentik · рабочий</div>
<div class="head-sub"><span class="dot">●</span> на связи</div>
</div>
<button class="ic-btn" title="Настройки">⚙</button>
</header>
<div class="messages">
<div class="msg user">Проверь, почему падает CI</div>
<div class="msg bot">Нашёл: длинное тире в именах тестов ломает сборку. Починил, CI зелёный.</div>
</div>
</main>
<!-- ===== затемнение под окном настроек ===== -->
<div class="scrim on" id="scrim1"></div>
<!-- ===== ОКНО 1: настройки ===== -->
<div class="layer on" id="layerSettings">
<div class="dialog">
<div class="d-head">
<h2 id="dTitle">Агенты</h2>
<button class="x-btn" onclick="closeAll()">✕</button>
</div>
<!-- ── вид 1: список агентов ── -->
<div class="d-body" id="viewList">
<div class="agent-card">
<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>
<div class="state"><span class="d"></span>на связи</div>
<div class="card-acts">
<button class="mini-ic" title="Изменить">✎</button>
<button class="mini-ic del" title="Удалить">🗑</button>
</div>
</div>
<div class="agent-card">
<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>
<div class="state err"><span class="d"></span>не отвечает</div>
<div class="card-acts">
<button class="mini-ic" title="Проверить связь">⇄</button>
<button class="mini-ic" title="Изменить">✎</button>
<button class="mini-ic del" title="Удалить">🗑</button>
</div>
</div>
<button class="btn wide" onclick="show('add')">+ Добавить агента</button>
</div>
<!-- ── вид 2: форма ── -->
<div class="d-body" id="viewAdd" style="display:none">
<div class="field">
<label>Название</label>
<input type="text" value="рабочий" placeholder="как вы его будете звать">
<div class="note">Как называть этого агента в списке. Придумываете сами — сервер своего имени не сообщает.</div>
</div>
<div class="field">
<label>Адрес сервера агента</label>
<div class="addr-row">
<input type="text" class="mono" value="http://192.168.88.135:8080/agentik"
placeholder="http://хост:порт/agentik">
<button class="btn sm" onclick="openCheck('form')">Проверить связь</button>
</div>
<div class="note">Адрес целиком, вместе с путём <code>/agentik</code>. Если агент
на этой же машине — <code>http://localhost:8080/agentik</code>.</div>
</div>
<div class="field">
<label>Как отличать в списке</label>
<div class="appearance-tabs">
<div class="tab on" data-tab="color">Цвет</div>
<div class="tab" data-tab="pic">Картинка</div>
</div>
<!-- цвет -->
<div class="pane on" id="paneColor">
<div class="colors">
<div class="swatch on" style="background:#6AB2F2"></div>
<div class="swatch" style="background:#86C99A"></div>
<div class="swatch" style="background:#C9A886"></div>
<div class="swatch" style="background:#B79AE0"></div>
<div class="swatch" style="background:#E28C8C"></div>
</div>
<div class="note">Цвет остаётся простым выбором: им помечаются диалоги агента
в списке — полоска у строки и точка у группы.</div>
</div>
<!-- картинка -->
<div class="pane" id="panePic">
<div class="pic-row">
<div class="pic-preview" id="picPreview">🖼</div>
<div class="pic-actions">
<button class="btn sm" onclick="pickPic()">Выбрать файл…</button>
<button class="btn sm" id="picClear" onclick="clearPic()" style="display:none">Убрать</button>
</div>
</div>
<div class="note">Картинка копируется <b>внутрь клиента</b> — по исходному
адресу не подтягивается. Иначе значок пропадёт, если файл удалят,
флешку вынут или папку переименуют.</div>
</div>
</div>
</div>
<div class="d-foot" id="footList">
<button class="btn prim" style="flex:1" onclick="closeAll()">Готово</button>
</div>
<div class="d-foot" id="footAdd" style="display:none">
<button class="btn" style="flex:1" onclick="show('list')">Отмена</button>
<button class="btn prim" style="flex:1">Добавить агента</button>
</div>
</div>
</div>
<!-- ===== затемнение под окном проверки ===== -->
<div class="scrim top" id="scrim2"></div>
<!-- ===== ОКНО 2: проверка связи (поверх настроек) ===== -->
<div class="layer" id="layerCheck">
<div class="dialog small">
<div class="d-head">
<h2>Проверка связи</h2>
<button class="x-btn" onclick="closeCheck()">✕</button>
</div>
<div class="d-body">
<div class="probe-head" id="probeHead">
<div class="big" id="probeIcon"></div>
<div class="txt">
<div class="ttl" id="probeTitle">Спрашиваю агента…</div>
<div class="sub" id="probeSub">http://192.168.88.135:8080/agentik</div>
</div>
</div>
<div class="probe-list" id="probeList" style="display:none">
<div><span>Ответ на запрос</span><b id="p1">—</b></div>
<div><span>Отвечает по адресу</span><b id="p2">—</b></div>
<div><span>Диалогов у агента</span><b id="p3">—</b></div>
<div><span>Отклик</span><b id="p4">—</b></div>
</div>
<div class="hint-line" id="probeHint" style="display:none"></div>
</div>
<div class="d-foot">
<button class="btn" style="flex:1" id="probeAgain" onclick="runCheck()">Проверить снова</button>
<button class="btn prim" style="flex:1" id="probeOk" onclick="closeCheck()" disabled>Добавить агента</button>
</div>
</div>
</div>
</div>
<script>
// ---------------- данные списка диалогов ----------------
const CHATS_SINGLE = `
<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>
<div class="chat-row">
<div class="avatar g3">РЗ</div>
<div class="chat-mid">
<div class="chat-name"><span>Разбор логов nginx</span><span class="chat-time">вчера</span></div>
<div class="chat-prev">Нашёл 3 ошибки 502</div>
</div>
</div>`;
const CHATS_MULTI = `
<div class="group-head"><span class="dot" style="background:#6AB2F2"></span>
<span class="nm">рабочий</span><span class="cnt">2 диалога</span></div>
<div class="chat-row active">
<div class="accent-bar" style="background:#6AB2F2"></div>
<div class="avatar">АГ</div>
<div class="chat-mid">
<div class="chat-name"><span>CI и релизы</span><span class="chat-time">14:22</span></div>
<div class="chat-prev">Собрал отчёт, жду правок</div>
</div>
</div>
<div class="chat-row">
<div class="accent-bar" style="background:#6AB2F2"></div>
<div class="avatar">ОЧ</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>
<div class="group-head"><span class="dot" style="background:#86C99A"></span>
<span class="nm">домашний</span><span class="cnt">1 диалог</span></div>
<div class="chat-row">
<div class="accent-bar" style="background:#86C99A"></div>
<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>`;
const chatArea = document.getElementById('chatArea');
chatArea.innerHTML = CHATS_SINGLE;
// ---------------- окна ----------------
function el(id){ return document.getElementById(id); }
function show(v) {
const isList = (v === 'list');
el('viewList').style.display = isList ? 'block' : 'none';
el('viewAdd').style.display = isList ? 'none' : 'block';
el('footList').style.display = isList ? 'flex' : 'none';
el('footAdd').style.display = isList ? 'none' : 'flex';
el('dTitle').textContent = isList ? 'Агенты' : 'Новый агент';
openSettings();
}
function openSettings(){
el('scrim1').classList.add('on');
el('layerSettings').classList.add('on');
}
// закрыть окно настроек (вместе с окном проверки, если оно открыто)
function closeAll(){
el('layerCheck').classList.remove('on');
el('scrim2').classList.remove('on');
el('layerSettings').classList.remove('on');
el('scrim1').classList.remove('on');
}
function closeCheck(){
el('layerCheck').classList.remove('on');
el('scrim2').classList.remove('on');
}
// ---------------- ОКНО ПРОВЕРКИ СВЯЗИ ----------------
// отдельное окно поверх настроек: из формы нового агента или из списка
function openCheck(from, addr) {
el('scrim2').classList.add('on');
el('layerCheck').classList.add('on');
el('probeOk').textContent = (from === 'form') ? 'Добавить агента' : 'Готово';
window.__checkAddr = addr || 'http://192.168.88.135:8080/agentik';
runCheck();
}
function runCheck() {
const head = el('probeHead');
const list = el('probeList');
const hint = el('probeHint');
const icon = el('probeIcon');
const addr = window.__checkAddr || 'http://192.168.88.135:8080/agentik';
head.className = 'probe-head busy';
icon.outerHTML = '<span class="spin" id="probeIcon"></span>';
el('probeTitle').textContent = 'Спрашиваю агента…';
el('probeSub').textContent = addr;
list.style.display = 'none';
hint.style.display = 'none';
el('probeOk').disabled = true;
setTimeout(() => {
const sp = document.getElementById('probeIcon');
sp.outerHTML = '<div class="big" id="probeIcon"></div>';
const fail = (window.__checkMode === 'err');
if (fail) {
el('probeHead').className = 'probe-head err';
el('probeIcon').textContent = '✕';
el('probeTitle').textContent = 'Агент не отвечает';
el('probeSub').textContent = addr;
el('p1').textContent = 'нет ответа'; el('p2').textContent = '—';
el('p3').textContent = '—'; el('p4').textContent = '5000 мс (истекло)';
list.style.display = 'block';
hint.style.display = 'block';
hint.textContent = 'Проверьте адрес и что агент запущен. Убедитесь, что порт открыт и путь /agentik указан целиком.';
el('probeOk').disabled = true;
} else {
el('probeHead').className = 'probe-head ok';
el('probeIcon').textContent = '✓';
el('probeTitle').textContent = 'Агент отвечает';
el('probeSub').textContent = addr;
el('p1').textContent = 'ok'; el('p2').textContent = addr;
el('p3').textContent = '4'; el('p4').textContent = '38 мс';
list.style.display = 'block';
hint.style.display = 'block';
hint.textContent = 'Адрес рабочий. Можно добавлять этого агента.';
el('probeOk').disabled = false;
}
}, 1400);
}
// ---------------- переключатель для показа ----------------
document.querySelectorAll('.switcher button').forEach(b => b.onclick = () => {
document.querySelectorAll('.switcher button').forEach(x => x.classList.remove('on'));
b.classList.add('on');
const v = b.dataset.view;
if (v === 'multi') {
chatArea.innerHTML = CHATS_MULTI;
closeAll();
return;
}
chatArea.innerHTML = CHATS_SINGLE;
if (v === 'list') { show('list'); closeCheck(); }
if (v === 'add') { show('add'); closeCheck(); }
if (v === 'check') {
window.__checkMode = b.dataset.mode || 'ok';
const okAddr = 'http://192.168.88.135:8080/agentik';
const badAddr = 'http://192.168.76.199:8080/agentik';
show(window.__checkMode === 'ok' ? 'add' : 'list');
openCheck('form', window.__checkMode === 'ok' ? okAddr : badAddr);
}
});
document.querySelectorAll('.swatch').forEach(s => s.onclick = () => {
document.querySelectorAll('.swatch').forEach(x => x.classList.remove('on'));
s.classList.add('on');
});
// ---------------- как отличать: цвет ИЛИ картинка ----------------
document.querySelectorAll('.appearance-tabs .tab').forEach(tab => tab.onclick = () => {
document.querySelectorAll('.appearance-tabs .tab').forEach(x => x.classList.remove('on'));
tab.classList.add('on');
const pic = (tab.dataset.tab === 'pic');
el('paneColor').classList.toggle('on', !pic);
el('panePic').classList.toggle('on', pic);
});
// выбор картинки: в настоящем клиенте — системный диалог выбора файла,
// а выбранный файл КОПИРУЕТСЯ в папку клиента (не ссылка на исходный путь)
function pickPic() {
// для макета показываем готовый пример вместо файлового диалога
setPic('data:image/svg+xml;utf8,' + encodeURIComponent(
`<svg xmlns="http://www.w3.org/2000/svg" width="54" height="54">
<rect width="54" height="54" fill="#86C99A"/>
<circle cx="27" cy="21" r="8" fill="#121218"/>
<path d="M10 50 Q27 30 44 50 Z" fill="#121218"/>
</svg>`));
}
function setPic(src) {
const p = el('picPreview');
p.innerHTML = `<img src="${src}" alt="">`;
p.classList.add('filled');
el('picClear').style.display = '';
}
function clearPic() {
const p = el('picPreview');
p.innerHTML = '🖼';
p.classList.remove('filled');
el('picClear').style.display = 'none';
}
</script>
</body>
</html>