Compare commits

..
Author SHA1 Message Date
Gerald LonlasandClaude Opus 4.7 ff4a63aa7b fix(web): use toast for error notifications instead of window.alert
Replace blocking window.alert calls with a notifyError helper that
mirrors notifySuccess, giving consistent toast-based UX for both
success and failure paths on the Refresh Models and Test Connection
buttons.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 00:44:46 +08:00
3 changed files with 46 additions and 2 deletions
+11
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
import base64
import json
import math
import re
import secrets
@@ -242,6 +243,16 @@ def extract_responses_text(response: Any) -> str:
return ""
def dump_openai_response(response: Any) -> str:
if hasattr(response, "model_dump"):
return json.dumps(response.model_dump(), indent=2, default=str)
if hasattr(response, "to_dict"):
return json.dumps(response.to_dict(), indent=2, default=str)
if isinstance(response, dict):
return json.dumps(response, indent=2, default=str)
return str(response)
_THINK_BLOCK_RE = re.compile(r"<think>.*?</think>", flags=re.IGNORECASE | re.DOTALL)
_THINK_START_RE = re.compile(r"<think>.*$", flags=re.IGNORECASE | re.DOTALL)
+20
View File
@@ -2,6 +2,7 @@ from __future__ import annotations
import base64
from io import BytesIO
import json
import sys
import types
@@ -182,6 +183,25 @@ def test_extract_responses_text_variants() -> None:
assert client.extract_responses_text(ns(output_text="", output=[])) == ""
def test_dump_openai_response_branches() -> None:
class WithModelDump:
def model_dump(self):
return {"a": 1}
class WithToDict:
def to_dict(self):
return {"b": 2}
class Plain:
def __str__(self) -> str:
return "plain-response"
assert json.loads(client.dump_openai_response(WithModelDump())) == {"a": 1}
assert json.loads(client.dump_openai_response(WithToDict())) == {"b": 2}
assert json.loads(client.dump_openai_response({"c": 3})) == {"c": 3}
assert client.dump_openai_response(Plain()) == "plain-response"
def test_strip_think_content_case_insensitive_and_multiple() -> None:
text = "A\n<THINK>hidden-1</THINK>\nB\n<think>hidden-2</think>\nC"
assert client.strip_think_content(text) == "A\n\nB\n\nC"
+15 -2
View File
@@ -190,6 +190,19 @@ function notifySuccess(message) {
}
}
function notifyError(message) {
if (typeof app.extensionManager?.toast?.add === "function") {
app.extensionManager.toast.add({
severity: "error",
summary: "LMStudio",
detail: message,
life: 5000,
});
} else {
console.error(`[LMStudio] ${message}`);
}
}
function fitNodeToWidgets(node) {
if (typeof node.computeSize !== "function" || typeof node.setSize !== "function") {
return;
@@ -292,7 +305,7 @@ function attachButtons(node) {
} catch (error) {
const message = error?.message || String(error);
console.error("[LMStudio] Model refresh failed", error);
window.alert(`Model refresh failed: ${message}`);
notifyError(`Model refresh failed: ${message}`);
}
});
@@ -302,7 +315,7 @@ function attachButtons(node) {
} catch (error) {
const message = error?.message || String(error);
console.error("[LMStudio] Connectivity test failed", error);
window.alert(`Connectivity test failed: ${message}`);
notifyError(`Connectivity test failed: ${message}`);
}
});