From f1f79335ec01cc2baa7c163bd965f9c6c6c9c9ef Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Thu, 4 Jun 2026 21:18:50 +0300 Subject: [PATCH] =?UTF-8?q?fix(assistant):=20=D0=B4=D0=BB=D0=B8=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D0=B5=20=D1=84=D0=BE=D1=80=D0=BC=D1=83=D0=BB=D1=8B?= =?UTF-8?q?=20=D0=BD=D0=B5=20=D0=BE=D0=B1=D1=80=D0=B5=D0=B7=D0=B0=D1=8E?= =?UTF-8?q?=D1=82=D1=81=D1=8F=20+=20=D0=BB=D0=B8=D0=BC=D0=B8=D1=82=D1=8B?= =?UTF-8?q?=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D0=B5=D0=B9=20=D0=B2=20=D0=B0?= =?UTF-8?q?=D0=B4=D0=BC=D0=B8=D0=BD=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Рендер ответа: display-формулы KaTeX прокручиваются по горизонтали (overflow-x:auto), пузырь ассистента во всю ширину, панель шире (380px) — длинные выражения больше не режутся по правому краю. Админка: к моделям Kilo добавлены ctx/out (из /models); на карточке Kilo показывается «контекст N · ответ до M токенов · бесплатно». Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/src/controllers/adminController.js | 17 +++++++++-------- frontend/js/admin/sections/assistant.js | 9 +++++++-- frontend/js/assistant.js | 10 ++++++++-- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/backend/src/controllers/adminController.js b/backend/src/controllers/adminController.js index 6fec4ab..47ba6a5 100644 --- a/backend/src/controllers/adminController.js +++ b/backend/src/controllers/adminController.js @@ -887,15 +887,16 @@ const ASSISTANT_PRESETS = [ ]; // Проверенные бесплатные модели Kilo (чистый русский) — для выпадающего списка // Проверенные бесплатные модели шлюза Kilo (отдают чистый русский). Порядок — от мощных к лёгким. +// ctx — окно контекста, out — макс. токенов в ответе (данные из /api/openrouter/models). Все бесплатные ($0). const KILO_MODELS = [ - { id: 'nvidia/nemotron-3-ultra-550b-a55b:free', label: 'Nemotron 550B — флагман (1M)' }, - { id: 'nvidia/nemotron-3-super-120b-a12b:free', label: 'Nemotron 120B — баланс (1M)' }, - { id: 'qwen/qwen3.7-plus:free', label: 'Qwen3.7 Plus — умная, медленная (1M)' }, - { id: 'openrouter/owl-alpha', label: 'Owl Alpha — чистый русский (1M)' }, - { id: 'nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free', label: 'Nemotron Nano 30B — быстрая (256K)' }, - { id: 'poolside/laguna-m.1:free', label: 'Laguna M.1 — быстрая (262K)' }, - { id: 'poolside/laguna-xs.2:free', label: 'Laguna XS — лёгкая (262K)' }, - { id: 'openrouter/free', label: 'Free Router — авто-выбор (быстро)' }, + { id: 'nvidia/nemotron-3-ultra-550b-a55b:free', label: 'Nemotron 550B — флагман (1M)', ctx: 1000000, out: 65536 }, + { id: 'nvidia/nemotron-3-super-120b-a12b:free', label: 'Nemotron 120B — баланс (1M)', ctx: 1000000, out: 262144 }, + { id: 'qwen/qwen3.7-plus:free', label: 'Qwen3.7 Plus — умная, медленная (1M)', ctx: 1000000, out: 65536 }, + { id: 'openrouter/owl-alpha', label: 'Owl Alpha — чистый русский (1M)', ctx: 1048756, out: 262144 }, + { id: 'nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free', label: 'Nemotron Nano 30B — быстрая (256K)', ctx: 256000, out: 65536 }, + { id: 'poolside/laguna-m.1:free', label: 'Laguna M.1 — быстрая (262K)', ctx: 262144, out: 32768 }, + { id: 'poolside/laguna-xs.2:free', label: 'Laguna XS — лёгкая (262K)', ctx: 262144, out: 32768 }, + { id: 'openrouter/free', label: 'Free Router — авто-выбор (быстро)', ctx: 200000, out: null }, ]; function _aset(k) { const r = db.prepare('SELECT value FROM app_settings WHERE key = ?').get(k); return r && r.value != null ? r.value : null; } diff --git a/frontend/js/admin/sections/assistant.js b/frontend/js/admin/sections/assistant.js index 548b726..12c0893 100644 --- a/frontend/js/admin/sections/assistant.js +++ b/frontend/js/admin/sections/assistant.js @@ -7,6 +7,7 @@ var esc = (window.LS && LS.escapeHtml) ? LS.escapeHtml : function (s) { return String(s == null ? '' : s).replace(/[&<>"]/g, function (c) { return ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]; }); }; var IN = 'padding:8px 11px;border:1px solid var(--border,#e2e8f0);border-radius:9px;font:inherit;font-size:.85rem;width:100%;box-sizing:border-box;background:var(--surface,#fff);color:var(--text,#0f172a)'; var SPARK = ''; + function fmtTok(n) { if (!n) return '—'; if (n >= 1000000) { var m = n / 1000000; return (m >= 10 ? Math.round(m) : m.toFixed(1).replace(/\.0$/, '')) + 'M'; } if (n >= 1000) return Math.round(n / 1000) + 'K'; return String(n); } function ensureStyle() { if (document.getElementById('asst-adm-style')) return; @@ -20,6 +21,8 @@ '.asst-pcb{flex:1;min-width:0;}', '.asst-pcn{font-weight:800;font-size:.92rem;color:var(--text,#0f172a);display:flex;align-items:center;gap:7px;flex-wrap:wrap;}', '.asst-pcs{font-size:.76rem;color:#8a94a6;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}', + '.asst-pclim{font-size:.7rem;color:#8a94a6;margin-top:5px;display:flex;align-items:center;gap:5px;}', + '.asst-pclim b{color:var(--text-2,#475569);font-weight:700;}', '.asst-bdg{font-size:.6rem;font-weight:800;text-transform:uppercase;letter-spacing:.03em;padding:2px 8px;border-radius:99px;}', '.asst-bdg.act{background:#9B5DE5;color:#fff;}', '.asst-bdg.key{background:rgba(5,150,82,.12);color:#059652;}', @@ -124,18 +127,20 @@ else listEl.innerHTML = providers.map(function (p) { var isKilo = /kilocode\.ai/.test(p.url || ''); var act = p.id === activeId; - var ksel = ''; + var ksel = '', lim = ''; if (isKilo) { var opts = kiloModels.slice(); if (!opts.some(function (m) { return m.id === p.model; })) opts = [{ id: p.model, label: p.model }].concat(opts); ksel = ''; + var km = kiloModels.find(function (m) { return m.id === p.model; }); + if (km) lim = '
контекст ' + fmtTok(km.ctx) + ' · ответ до ' + fmtTok(km.out) + ' токенов · бесплатно
'; } return '
' + '
' + SPARK + '
' + '
' + esc(p.name || 'Провайдер') + (act ? 'активен' : '') + '' + (p.hasKey ? 'ключ есть' : 'нет ключа') + '
' + - '
' + esc(p.model || '') + '
' + ksel + '
' + + '
' + esc(p.model || '') + '
' + ksel + lim + '
' + '
' + (act ? '' : '') + '' + diff --git a/frontend/js/assistant.js b/frontend/js/assistant.js index e292c79..4bf3d07 100644 --- a/frontend/js/assistant.js +++ b/frontend/js/assistant.js @@ -280,7 +280,7 @@ '.asst-dot{position:absolute;top:0;right:0;width:13px;height:13px;border-radius:50%;background:#F15BB5;border:2px solid #fff;}', reduceMotion ? '' : '.asst-fab.pulse{animation:asstPulse 2.2s ease-in-out infinite;}', '@keyframes asstPulse{0%,100%{box-shadow:0 8px 24px rgba(139,92,246,.32);}50%{box-shadow:0 8px 30px rgba(241,91,181,.5);}}', - '.asst-bubble{position:absolute;left:0;bottom:66px;width:330px;max-width:88vw;background:#fff;border-radius:18px;', + '.asst-bubble{position:absolute;left:0;bottom:66px;width:380px;max-width:92vw;background:#fff;border-radius:18px;', ' box-shadow:0 20px 56px rgba(15,23,42,.24);padding:15px 17px;border:1px solid rgba(15,23,42,.07);', ' opacity:0;transform:translateY(8px) scale(.98);pointer-events:none;transition:opacity .18s,transform .18s;transform-origin:bottom left;}', '.asst-name-face{display:inline-block;width:20px;height:20px;vertical-align:-4px;margin-right:7px;}', @@ -314,12 +314,18 @@ '.asst-rich ul,.asst-rich ol{margin:4px 0 4px 18px;padding:0;}', '.asst-rich li{margin:2px 0;}', '.asst-rich code{background:rgba(15,23,42,.06);border-radius:4px;padding:1px 4px;}', + // длинные формулы не помещаются в узкий блок → горизонтальная прокрутка, ничего не обрезается + '.asst-rich{overflow-wrap:anywhere;}', + '.asst-rich .katex-display{margin:6px 0;overflow-x:auto;overflow-y:hidden;padding-bottom:4px;max-width:100%;}', + '.asst-rich .katex-display::-webkit-scrollbar{height:6px;}', + '.asst-rich .katex-display::-webkit-scrollbar-thumb{background:rgba(15,23,42,.18);border-radius:99px;}', + '.asst-rich .katex{max-width:100%;}', '.asst-md-h{font-weight:800;color:#0F172A;margin:6px 0 2px;}', '.asst-chat{max-height:46vh;overflow:auto;display:flex;flex-direction:column;gap:8px;margin-bottom:8px;}', '.asst-chat:empty{display:none;}', '.asst-msg{font-size:.84rem;line-height:1.5;border-radius:12px;padding:8px 11px;max-width:92%;word-break:break-word;}', '.asst-msg-user{align-self:flex-end;background:#9B5DE5;color:#fff;}', - '.asst-msg-assistant{align-self:flex-start;background:rgba(15,23,42,.05);}', + '.asst-msg-assistant{align-self:flex-start;background:rgba(15,23,42,.05);max-width:100%;}', '.asst-msg-assistant .asst-rich{color:#28324a;}', '.asst-msg-ph{opacity:.6;}', '.asst-msg-links{align-self:flex-start;font-size:.74rem;}',