From 6c1da79b4ad00368c885dd5740c6ab60fd73ebfe Mon Sep 17 00:00:00 2001 From: iRedTea Date: Sat, 17 Oct 2026 15:07:49 +0300 Subject: [PATCH] feat: the model's short name up to 9 characters, the chat 408px wide so nine of the widest letters fit --- README.md | 2 +- internal/server/ai.go | 6 +++--- internal/server/aimodels_test.go | 4 ++-- internal/server/web/static/app.css | 4 ++-- internal/server/web/templates/settings.html | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0b16e15..355cf18 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ показать или записать файл - всё с правами пользователя. Плашка «Агент» - делает шаги сам, спрашивая перед каждой командой и записью; «Думать» - сначала рассуждает (свёрнуто над ответом). Модели задаёт администратор в «Настройки › ИИ-ассистент»: их может быть несколько, у каждой своё - название в чате (и короткое, до 7 символов, - для кнопки выбора), тип - Gradio (по умолчанию - прокси к Qwen Omni), OpenAI-совместимый API или Anthropic, + название в чате (и короткое, до 9 символов, - для кнопки выбора), тип - Gradio (по умолчанию - прокси к Qwen Omni), OpenAI-совместимый API или Anthropic, адрес, модель и ключ (хранится на сервере, в браузер не уходит). Если моделей несколько, в чате рядом с плашками появляется выбор, первая - по умолчанию; выбор запоминается за пользователем. В «Аккаунт › Вид» можно показывать выбор всегда. diff --git a/internal/server/ai.go b/internal/server/ai.go index 5d185a5..93a116d 100644 --- a/internal/server/ai.go +++ b/internal/server/ai.go @@ -44,7 +44,7 @@ const ( func aiUserKey(id int64) string { return "ai_user_" + strconv.FormatInt(id, 10) } // aiConfig is one model the assistant can ask: its id and the names the chat -// shows (Short, up to 7 characters, on the button of the model in use), the +// shows (Short, up to 9 characters, on the button of the model in use), the // kind of API, its address, model and key. type aiConfig struct { ID string `json:"id"` @@ -77,8 +77,8 @@ func (c aiConfig) tidy() aiConfig { c.Type = "gradio" } c.Name, c.Model, c.Key = strings.TrimSpace(c.Name), strings.TrimSpace(c.Model), strings.TrimSpace(c.Key) - if r := []rune(strings.TrimSpace(c.Short)); len(r) > 7 { - c.Short = strings.TrimSpace(string(r[:7])) + if r := []rune(strings.TrimSpace(c.Short)); len(r) > 9 { + c.Short = strings.TrimSpace(string(r[:9])) } else { c.Short = string(r) } diff --git a/internal/server/aimodels_test.go b/internal/server/aimodels_test.go index a76e059..12027e8 100644 --- a/internal/server/aimodels_test.go +++ b/internal/server/aimodels_test.go @@ -60,11 +60,11 @@ func TestAIModelsSaveAndList(t *testing.T) { {"", "", "", "", "", "", ""}, // an empty card is dropped }) ms := s.aiModels() - if len(ms) != 2 || ms[0].Name != "Qwen" || ms[1].Name != "Claude" || ms[1].Key != "secret-key" || ms[0].ID == "" || ms[0].ID == ms[1].ID || ms[1].Short != "Сонет 4" { + if len(ms) != 2 || ms[0].Name != "Qwen" || ms[1].Name != "Claude" || ms[1].Key != "secret-key" || ms[0].ID == "" || ms[0].ID == ms[1].ID || ms[1].Short != "Сонет 4.5" { t.Fatalf("models: %+v", ms) } body := get(t, s, c, "/api/panel/ai").Body.String() - if strings.Contains(body, "secret-key") || !strings.Contains(body, `"name":"Claude"`) || !strings.Contains(body, `"short":"Сонет 4"`) || !strings.Contains(body, ms[1].ID) { + if strings.Contains(body, "secret-key") || !strings.Contains(body, `"name":"Claude"`) || !strings.Contains(body, `"short":"Сонет 4.5"`) || !strings.Contains(body, ms[1].ID) { t.Fatalf("state: %s", body) } // Saved again with its id, a model keeps it. diff --git a/internal/server/web/static/app.css b/internal/server/web/static/app.css index 0b50d5c..683c5aa 100644 --- a/internal/server/web/static/app.css +++ b/internal/server/web/static/app.css @@ -844,7 +844,7 @@ dialog.machinedlg { max-width:1000px; width:94vw; max-height:90vh; padding:0; ov display:inline-flex; align-items:center; justify-content:center; box-shadow:0 8px 24px rgba(0,0,0,.45); } .ai-fab[hidden] { display:none; } body.touch.term-active .ai-fab { bottom:104px; } -.ai-box { position:fixed; right:28px; bottom:96px; z-index:41; width:384px; max-width:calc(100vw - 24px); height:min(500px, calc(100vh - 120px)); +.ai-box { position:fixed; right:28px; bottom:96px; z-index:41; width:408px; max-width:calc(100vw - 24px); height:min(500px, calc(100vh - 120px)); display:flex; flex-direction:column; background:var(--panel); border:1px solid var(--border); border-radius:12px; box-shadow:0 18px 44px rgba(0,0,0,.55); overflow:hidden; transform-origin:bottom right; opacity:0; visibility:hidden; pointer-events:none; transform:translateY(14px) scale(.96); @@ -963,7 +963,7 @@ dialog.uidlg { width:fit-content; min-width:min(300px, 92vw); max-width:min(560p pointed at; the list opens upwards, as a small card. */ .ai-mpick { position:relative; margin-left:auto; min-width:0; } .ai-mpick[hidden] { display:none; } -/* A short name (7 characters at most) is shown whole. */ +/* A short name (9 characters at most) is shown whole. */ .ai-mpick.short { flex-shrink:0; } .ai-mbtn { display:inline-flex; align-items:center; gap:3px; max-width:100%; height:28px; padding:0 5px 0 7px; border:0; border-radius:8px; background:transparent; color:var(--muted); font-size:12.5px; font-weight:500; transition:background-color .15s, color .15s; } diff --git a/internal/server/web/templates/settings.html b/internal/server/web/templates/settings.html index 545cb6e..0ddcfde 100644 --- a/internal/server/web/templates/settings.html +++ b/internal/server/web/templates/settings.html @@ -157,7 +157,7 @@ {{range .AIModels}}
- +