diff --git a/static/css/chat.css b/static/css/chat.css
index 261d00f..c4decc2 100644
--- a/static/css/chat.css
+++ b/static/css/chat.css
@@ -127,6 +127,44 @@ body:not(.loading) #loading {
pointer-events: none;
}
+#notifications {
+ position: absolute;
+ top: 15px;
+ right: 20px;
+}
+
+.notification {
+ position: relative;
+ background: #24273a;
+ padding: 12px 15px;
+ border-radius: 6px;
+ width: 280px;
+ margin-bottom: 10px;
+ transition: 250ms;
+ color: #ed8796;
+ border: 2px solid #ed8796;
+ left: 0px;
+ overflow: hidden;
+}
+
+.notification.off-screen {
+ height: 0px !important;
+ border-width: 0px;
+ left: calc(100% + 20px);
+ padding: 0px 15px;
+ margin-bottom: 0px;
+}
+
+.notification::before {
+ content: "";
+ background-image: url(icons/error.svg);
+ position: absolute;
+ top: 4px;
+ right: 4px;
+ width: 16px !important;
+ height: 16px !important;
+}
+
#page {
display: flex;
flex-direction: column;
@@ -752,6 +790,7 @@ select {
}
body.loading #version,
+.notification::before,
#title-refresh,
#loading .inner::after,
.modal.loading .content::after,
diff --git a/static/css/icons/error.svg b/static/css/icons/error.svg
new file mode 100644
index 0000000..4232937
--- /dev/null
+++ b/static/css/icons/error.svg
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/static/index.html b/static/index.html
index 5cca6f7..6cea0c1 100644
--- a/static/index.html
+++ b/static/index.html
@@ -25,6 +25,8 @@
+
diff --git a/static/js/chat.js b/static/js/chat.js
index 0f276cf..f6a077e 100644
--- a/static/js/chat.js
+++ b/static/js/chat.js
@@ -1,6 +1,7 @@
(() => {
const $version = document.getElementById("version"),
$total = document.getElementById("total"),
+ $notifications = document.getElementById("notifications"),
$title = document.getElementById("title"),
$titleRefresh = document.getElementById("title-refresh"),
$titleText = document.getElementById("title-text"),
@@ -55,6 +56,36 @@
$total.textContent = formatMoney(totalCost);
}
+ async function notify(msg, persistent = false) {
+ console.warn(msg);
+
+ const notification = make("div", "notification", "off-screen");
+
+ notification.textContent = msg;
+
+ $notifications.appendChild(notification);
+
+ await wait(250);
+
+ notification.classList.remove("off-screen");
+
+ if (persistent) {
+ return;
+ }
+
+ await wait(5000);
+
+ notification.style.height = `${notification.getBoundingClientRect().height}px`;
+
+ notification.classList.add("off-screen");
+
+ await wait(250);
+
+ notification.remove();
+ }
+
+ window.notify = notify;
+
function updateTitle() {
$title.classList.toggle("hidden", !messages.length);
@@ -1077,7 +1108,7 @@
return;
}
- alert(err.message);
+ notify(err.message);
}
titleController = null;
@@ -1121,9 +1152,9 @@
const data = await json("/-/data");
if (!data) {
- alert("Failed to load data.");
+ notify("Failed to load data.", true);
- return false;
+ return;
}
// start icon preload
@@ -1177,8 +1208,6 @@
});
dropdown($prompt);
-
- return data;
}
function clearMessages() {
@@ -1190,8 +1219,8 @@
function restore() {
$message.value = loadValue("message", "");
$role.value = loadValue("role", "user");
- $model.value = loadValue("model", modelList[0].id);
- $prompt.value = loadValue("prompt", promptList[0].key);
+ $model.value = loadValue("model", modelList.length ? modelList[0].id : "");
+ $prompt.value = loadValue("prompt", promptList.length ? promptList[0].key : "");
$temperature.value = loadValue("temperature", 0.85);
$iterations.value = loadValue("iterations", 3);
$reasoningEffort.value = loadValue("reasoning-effort", "medium");
@@ -1500,7 +1529,7 @@
pushAttachment(file);
} catch (err) {
- alert(err.message);
+ notify(err.message);
}
});
@@ -1661,9 +1690,5 @@
restore();
document.body.classList.remove("loading");
-
- setTimeout(() => {
- document.getElementById("loading").remove();
- }, 500);
});
})();
diff --git a/static/js/lib.js b/static/js/lib.js
index 884b032..f99bf38 100644
--- a/static/js/lib.js
+++ b/static/js/lib.js
@@ -66,6 +66,10 @@ function fillSelect($select, options, callback) {
}
}
+function wait(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
function escapeHtml(text) {
return text.replace(/&/g, "&").replace(//g, ">");
}