mirror of
https://github.com/coalaura/whiskr.git
synced 2025-09-08 17:06:42 +00:00
better notifications
This commit is contained in:
@@ -127,6 +127,44 @@ body:not(.loading) #loading {
|
|||||||
pointer-events: none;
|
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 {
|
#page {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -752,6 +790,7 @@ select {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body.loading #version,
|
body.loading #version,
|
||||||
|
.notification::before,
|
||||||
#title-refresh,
|
#title-refresh,
|
||||||
#loading .inner::after,
|
#loading .inner::after,
|
||||||
.modal.loading .content::after,
|
.modal.loading .content::after,
|
||||||
|
7
static/css/icons/error.svg
Normal file
7
static/css/icons/error.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
|
||||||
|
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||||
|
|
||||||
|
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
After Width: | Height: | Size: 1.1 KiB |
@@ -25,6 +25,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="notifications"></div>
|
||||||
|
|
||||||
<div id="page">
|
<div id="page">
|
||||||
<div id="title" class="hidden">
|
<div id="title" class="hidden">
|
||||||
<button id="title-refresh"></button>
|
<button id="title-refresh"></button>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
(() => {
|
(() => {
|
||||||
const $version = document.getElementById("version"),
|
const $version = document.getElementById("version"),
|
||||||
$total = document.getElementById("total"),
|
$total = document.getElementById("total"),
|
||||||
|
$notifications = document.getElementById("notifications"),
|
||||||
$title = document.getElementById("title"),
|
$title = document.getElementById("title"),
|
||||||
$titleRefresh = document.getElementById("title-refresh"),
|
$titleRefresh = document.getElementById("title-refresh"),
|
||||||
$titleText = document.getElementById("title-text"),
|
$titleText = document.getElementById("title-text"),
|
||||||
@@ -55,6 +56,36 @@
|
|||||||
$total.textContent = formatMoney(totalCost);
|
$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() {
|
function updateTitle() {
|
||||||
$title.classList.toggle("hidden", !messages.length);
|
$title.classList.toggle("hidden", !messages.length);
|
||||||
|
|
||||||
@@ -1077,7 +1108,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
alert(err.message);
|
notify(err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
titleController = null;
|
titleController = null;
|
||||||
@@ -1121,9 +1152,9 @@
|
|||||||
const data = await json("/-/data");
|
const data = await json("/-/data");
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
alert("Failed to load data.");
|
notify("Failed to load data.", true);
|
||||||
|
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// start icon preload
|
// start icon preload
|
||||||
@@ -1177,8 +1208,6 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
dropdown($prompt);
|
dropdown($prompt);
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearMessages() {
|
function clearMessages() {
|
||||||
@@ -1190,8 +1219,8 @@
|
|||||||
function restore() {
|
function restore() {
|
||||||
$message.value = loadValue("message", "");
|
$message.value = loadValue("message", "");
|
||||||
$role.value = loadValue("role", "user");
|
$role.value = loadValue("role", "user");
|
||||||
$model.value = loadValue("model", modelList[0].id);
|
$model.value = loadValue("model", modelList.length ? modelList[0].id : "");
|
||||||
$prompt.value = loadValue("prompt", promptList[0].key);
|
$prompt.value = loadValue("prompt", promptList.length ? promptList[0].key : "");
|
||||||
$temperature.value = loadValue("temperature", 0.85);
|
$temperature.value = loadValue("temperature", 0.85);
|
||||||
$iterations.value = loadValue("iterations", 3);
|
$iterations.value = loadValue("iterations", 3);
|
||||||
$reasoningEffort.value = loadValue("reasoning-effort", "medium");
|
$reasoningEffort.value = loadValue("reasoning-effort", "medium");
|
||||||
@@ -1500,7 +1529,7 @@
|
|||||||
|
|
||||||
pushAttachment(file);
|
pushAttachment(file);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(err.message);
|
notify(err.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1661,9 +1690,5 @@
|
|||||||
restore();
|
restore();
|
||||||
|
|
||||||
document.body.classList.remove("loading");
|
document.body.classList.remove("loading");
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
document.getElementById("loading").remove();
|
|
||||||
}, 500);
|
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
@@ -66,6 +66,10 @@ function fillSelect($select, options, callback) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wait(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
function escapeHtml(text) {
|
function escapeHtml(text) {
|
||||||
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user