mirror of
https://github.com/coalaura/whiskr.git
synced 2025-09-08 17:06:42 +00:00
resizable chat box
This commit is contained in:
@@ -30,7 +30,6 @@ whiskr is a private, self-hosted web chat interface for interacting with AI mode
|
|||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- resizable chat box
|
|
||||||
- cost tracker
|
- cost tracker
|
||||||
- settings
|
- settings
|
||||||
- auto-retry on edit
|
- auto-retry on edit
|
||||||
|
@@ -68,6 +68,11 @@ body {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.resizing * {
|
||||||
|
user-select: none !important;
|
||||||
|
cursor: grabbing !important;
|
||||||
|
}
|
||||||
|
|
||||||
#version {
|
#version {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@@ -565,6 +570,7 @@ body:not(.loading) #loading {
|
|||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
height: 320px;
|
height: 320px;
|
||||||
padding-bottom: 36px;
|
padding-bottom: 36px;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat::after {
|
#chat::after {
|
||||||
@@ -581,6 +587,15 @@ body:not(.loading) #loading {
|
|||||||
padding-top: 50px;
|
padding-top: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#resize-bar {
|
||||||
|
position: absolute;
|
||||||
|
top: -4px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 8px;
|
||||||
|
cursor: n-resize;
|
||||||
|
}
|
||||||
|
|
||||||
#attachments {
|
#attachments {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
|
@@ -29,6 +29,8 @@
|
|||||||
<div id="chat">
|
<div id="chat">
|
||||||
<button id="bottom" class="hidden" title="Scroll to bottom"></button>
|
<button id="bottom" class="hidden" title="Scroll to bottom"></button>
|
||||||
|
|
||||||
|
<div id="resize-bar"></div>
|
||||||
|
|
||||||
<div id="attachments" class="files"></div>
|
<div id="attachments" class="files"></div>
|
||||||
|
|
||||||
<textarea id="message" placeholder="Type something..." autocomplete="off"></textarea>
|
<textarea id="message" placeholder="Type something..." autocomplete="off"></textarea>
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
$chat = document.getElementById("chat"),
|
$chat = document.getElementById("chat"),
|
||||||
$message = document.getElementById("message"),
|
$message = document.getElementById("message"),
|
||||||
$bottom = document.getElementById("bottom"),
|
$bottom = document.getElementById("bottom"),
|
||||||
|
$resizeBar = document.getElementById("resize-bar"),
|
||||||
$attachments = document.getElementById("attachments"),
|
$attachments = document.getElementById("attachments"),
|
||||||
$role = document.getElementById("role"),
|
$role = document.getElementById("role"),
|
||||||
$model = document.getElementById("model"),
|
$model = document.getElementById("model"),
|
||||||
@@ -37,7 +38,9 @@
|
|||||||
searchTool = false;
|
searchTool = false;
|
||||||
|
|
||||||
let searchAvailable = false,
|
let searchAvailable = false,
|
||||||
activeMessage;
|
activeMessage = null,
|
||||||
|
isResizing = false,
|
||||||
|
scrollResize = false;
|
||||||
|
|
||||||
function updateScrollButton() {
|
function updateScrollButton() {
|
||||||
const bottom = $messages.scrollHeight - ($messages.scrollTop + $messages.offsetHeight);
|
const bottom = $messages.scrollHeight - ($messages.scrollTop + $messages.offsetHeight);
|
||||||
@@ -49,7 +52,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function scroll(force = false) {
|
function scroll(force = false, instant = false) {
|
||||||
if (!autoScrolling && !force) {
|
if (!autoScrolling && !force) {
|
||||||
updateScrollButton();
|
updateScrollButton();
|
||||||
|
|
||||||
@@ -59,7 +62,7 @@
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
$messages.scroll({
|
$messages.scroll({
|
||||||
top: $messages.scrollHeight,
|
top: $messages.scrollHeight,
|
||||||
behavior: "smooth",
|
behavior: instant ? "instant" : "smooth",
|
||||||
});
|
});
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
@@ -1230,6 +1233,27 @@
|
|||||||
scroll(true);
|
scroll(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$resizeBar.addEventListener("mousedown", event => {
|
||||||
|
const isAtBottom = $messages.scrollHeight - ($messages.scrollTop + $messages.offsetHeight) <= 10;
|
||||||
|
|
||||||
|
if (event.buttons === 4) {
|
||||||
|
$chat.style.height = "";
|
||||||
|
|
||||||
|
storeValue("resized", false);
|
||||||
|
|
||||||
|
scroll(isAtBottom, true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else if (event.buttons !== 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isResizing = true;
|
||||||
|
scrollResize = isAtBottom;
|
||||||
|
|
||||||
|
document.body.classList.add("resizing");
|
||||||
|
});
|
||||||
|
|
||||||
$role.addEventListener("change", () => {
|
$role.addEventListener("change", () => {
|
||||||
storeValue("role", $role.value);
|
storeValue("role", $role.value);
|
||||||
});
|
});
|
||||||
@@ -1468,9 +1492,36 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addEventListener("mousemove", event => {
|
||||||
|
if (!isResizing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = window.innerHeight,
|
||||||
|
height = clamp(window.innerHeight - event.clientY, 100, total - 240);
|
||||||
|
|
||||||
|
$chat.style.height = `${height}px`;
|
||||||
|
|
||||||
|
storeValue("resized", height);
|
||||||
|
|
||||||
|
scroll(scrollResize, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener("mouseup", () => {
|
||||||
|
isResizing = false;
|
||||||
|
|
||||||
|
document.body.classList.remove("resizing");
|
||||||
|
});
|
||||||
|
|
||||||
dropdown($role);
|
dropdown($role);
|
||||||
dropdown($reasoningEffort);
|
dropdown($reasoningEffort);
|
||||||
|
|
||||||
|
const resizedHeight = loadValue("resized");
|
||||||
|
|
||||||
|
if (resizedHeight) {
|
||||||
|
$chat.style.height = `${resizedHeight}px`;
|
||||||
|
}
|
||||||
|
|
||||||
loadData().then(() => {
|
loadData().then(() => {
|
||||||
restore();
|
restore();
|
||||||
|
|
||||||
|
@@ -84,6 +84,10 @@ function fixed(num, decimals = 0) {
|
|||||||
return num.toFixed(decimals).replace(/\.?0+$/m, "");
|
return num.toFixed(decimals).replace(/\.?0+$/m, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clamp(num, min, max) {
|
||||||
|
return Math.min(Math.max(num, min), max);
|
||||||
|
}
|
||||||
|
|
||||||
function download(name, type, data) {
|
function download(name, type, data) {
|
||||||
let blob;
|
let blob;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user