Skip to content

Commit 0458d9d

Browse files
committed
Show reponse stats also when info button is toggled
This way it is also possible to show it on touch devices.
1 parent 17c4c7a commit 0458d9d

File tree

2 files changed

+55
-3
lines changed

2 files changed

+55
-3
lines changed

llamafile/server/www/chatbot.css

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ p {
3333
box-sizing: border-box;
3434
}
3535

36+
.hidden {
37+
display: none !important;
38+
}
39+
3640
.chat-container {
3741
width: 100%;
3842
max-width: 960px;
@@ -172,6 +176,28 @@ ul li:first-child {
172176
height: 16px;
173177
}
174178

179+
.message-controls button.toggled {
180+
box-shadow: inset 0 0 2px 1px rgba(0, 0, 0, 0.2);
181+
}
182+
183+
.message-wrapper #info-container {
184+
font-size: 0.75rem;
185+
background: #f5f5f5;
186+
padding: 0.25rem 0.5rem;
187+
border-radius: 4px;
188+
width: max-content;
189+
max-width: 100%;
190+
display: inline-block;
191+
vertical-align: top;
192+
}
193+
.message-wrapper:not(:last-child) #info-container {
194+
margin-bottom: var(--large-spacing);
195+
}
196+
197+
.message-wrapper span {
198+
display: inline-block;
199+
}
200+
175201
.message img {
176202
max-width: 100%;
177203
height: auto;

llamafile/server/www/chatbot.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,23 +66,35 @@ function wrapMessageElement(messageElement, role) {
6666
const wrapper = document.createElement("div");
6767
wrapper.appendChild(messageElement);
6868
if (role == "assistant") {
69-
const controlContainer = document.createElement("div");
69+
const controlContainer = wrapper.appendChild(document.createElement("div"));
7070
controlContainer.appendChild(createCopyButton(() => messageElement.textContent, () => messageElement.innerHTML));
71-
controlContainer.appendChild(infoButton());
71+
controlContainer.appendChild(infoButton(wrapper));
7272
controlContainer.classList.add("message-controls");
73-
wrapper.appendChild(controlContainer);
7473
}
7574
wrapper.classList.add("message-wrapper", role);
7675
return wrapper;
7776
}
7877

7978
function infoButton(container, stats) {
8079
let button = container?.querySelector("#stats");
80+
let statsElement = container?.querySelector("#info-container");
8181
if (!button) {
8282
button = document.createElement("button");
8383
button.id = "stats";
8484
button.innerText = "i";
8585
button.style.fontFamily = "monospace";
86+
87+
statsElement = document.createElement("div");
88+
statsElement.id = "info-container";
89+
statsElement.className = "hidden";
90+
container.append(statsElement);
91+
button.addEventListener("click", () => {
92+
const show = !button.classList.contains("toggled");
93+
statsElement.classList.toggle("hidden", !show);
94+
button.classList.toggle("toggled", show);
95+
if (show)
96+
requestAnimationFrame(() => scrollIntoViewIfNeeded(statsElement, container.parentElement));
97+
});
8698
}
8799
button.style.display = stats ? "" : "none";
88100
if (stats) {
@@ -102,10 +114,24 @@ function infoButton(container, stats) {
102114
parts.push("Incomplete");
103115
}
104116
button.title = parts.join("\n");
117+
statsElement.innerHTML = "";
118+
parts.forEach(part => statsElement.appendChild(wrapInSpan(part + " ")));
105119
}
106120
return button;
107121
}
108122

123+
function scrollIntoViewIfNeeded(elem, container) {
124+
let rectElem = elem.getBoundingClientRect(), rectContainer = container.getBoundingClientRect();
125+
if (rectElem.bottom > rectContainer.bottom) elem.scrollIntoView(false);
126+
if (rectElem.top < rectContainer.top) elem.scrollIntoView();
127+
}
128+
129+
function wrapInSpan(innerText) {
130+
const span = document.createElement("span");
131+
span.innerText = innerText;
132+
return span;
133+
}
134+
109135
function createMessageElement(content) {
110136
const messageDiv = document.createElement("div");
111137
messageDiv.classList.add("message");

0 commit comments

Comments
 (0)