fix(assistant): длинные формулы не обрезаются + лимиты моделей в админке

Рендер ответа: display-формулы KaTeX прокручиваются по горизонтали
(overflow-x:auto), пузырь ассистента во всю ширину, панель шире (380px) —
длинные выражения больше не режутся по правому краю.

Админка: к моделям Kilo добавлены ctx/out (из /models); на карточке Kilo
показывается «контекст N · ответ до M токенов · бесплатно».

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-04 21:18:50 +03:00
parent 78a9eca9c0
commit f1f79335ec
3 changed files with 24 additions and 12 deletions
+7 -2
View File
@@ -7,6 +7,7 @@
var esc = (window.LS && LS.escapeHtml) ? LS.escapeHtml : function (s) { return String(s == null ? '' : s).replace(/[&<>"]/g, function (c) { return ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' })[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 = '<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v4M12 17v4M3 12h4M17 12h4M5.6 5.6l2.8 2.8M15.6 15.6l2.8 2.8M18.4 5.6l-2.8 2.8M8.4 15.6l-2.8 2.8"/></svg>';
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 = '<select class="asst-ksel" data-ksel="' + p.id + '">' + opts.map(function (m) { return '<option value="' + esc(m.id) + '"' + (m.id === p.model ? ' selected' : '') + '>' + esc(m.label) + '</option>'; }).join('') + '</select>';
var km = kiloModels.find(function (m) { return m.id === p.model; });
if (km) lim = '<div class="asst-pclim">контекст <b>' + fmtTok(km.ctx) + '</b> · ответ до <b>' + fmtTok(km.out) + '</b> токенов · <b>бесплатно</b></div>';
}
return '<div class="asst-pcard' + (act ? ' active' : '') + '">' +
'<div class="asst-pcic">' + SPARK + '</div>' +
'<div class="asst-pcb"><div class="asst-pcn">' + esc(p.name || 'Провайдер') +
(act ? '<span class="asst-bdg act">активен</span>' : '') +
'<span class="asst-bdg ' + (p.hasKey ? 'key' : 'nokey') + '">' + (p.hasKey ? 'ключ есть' : 'нет ключа') + '</span></div>' +
'<div class="asst-pcs">' + esc(p.model || '') + '</div>' + ksel + '</div>' +
'<div class="asst-pcs">' + esc(p.model || '') + '</div>' + ksel + lim + '</div>' +
'<div class="asst-pca">' +
(act ? '' : '<button class="asst-ib primary" data-act="activate" data-id="' + p.id + '">Сделать активным</button>') +
'<button class="asst-ib" data-act="test" data-id="' + p.id + '">Тест</button>' +