Skip to content
Snippets Groups Projects
Commit efbc330e authored by Konrad Völkel's avatar Konrad Völkel
Browse files

another fix

parent 28971c8a
No related branches found
No related tags found
No related merge requests found
Pipeline #151718 passed
......@@ -15,12 +15,12 @@
<body>
<div id="chatbox">
<div id="chatbox-title">HHU Search Helper</div>
<div id="chatbox-title">Ask hAInrich</div>
<div id="messages"></div>
<div id="messageInfo"></div>
<div id="input-container">
<input type="text" id="user-input" placeholder="Try searching: 'minimum admission grade' or 'I'm from outside the EU'">
<button onclick="saveMessage()">Send</button>
<input type="text" id="user-input" placeholder="Try asking: 'minimum admission grade' or 'I'm from outside the EU'">
<button onclick="sendAsk()">Ask</button>
</div>
</div>
......
import { sendMessage, copyToClipboard } from './modules/ui.js';
import { sendMessage } from './modules/ui.js';
import { tokenizeText } from './modules/semanticSearch.js';
import { extractFAQfromHTML } from './modules/faqData.js';
import { loadEmbedding, removeOutdatedEntries } from './modules/embeddings.js'
......@@ -38,10 +38,10 @@ localStorage.setItem('smallEmbedding', JSON.stringify(tokenEmbeddingStack));
const inputElement = document.getElementById('user-input');
const resultsIframe = parent.document.getElementById('resultsIframe');
let content = "";
let topResult = null;
inputElement.addEventListener('input', function (event) {
window.focus();
const query = event.target.value.trim();
const results = resultsIframe.contentDocument || resultsIframe.contentWindow.document;
......@@ -55,11 +55,13 @@ inputElement.addEventListener('input', function (event) {
}, 500);
return;
}
let resultLinks;
resultsIframe.style.display = 'block';
resultsContainer.innerHTML = '';
processQuery(query, worker, function (outputs) {
window.focus();
outputs.forEach(async output => {
......@@ -87,37 +89,60 @@ inputElement.addEventListener('input', function (event) {
resultLink.appendChild(bestTokenDiv);
window.focus();
/** open the answer field in chat view for selected panel */
console.log("inside", output.question);
resultLink.onclick = function () {
window.focus();
copyToClipboard(output.question);
linkAndClick(output.question);
};
resultsContainer.appendChild(resultLink);
});
});
});
if (outputs.length > 0) {
topResult = outputs[0];
}
});
});
/** search for query on enter press and put it in chat */
document.getElementById('user-input').addEventListener('keypress', function (event) {
if (event.key === 'Enter') {
event.preventDefault();
sendMessage(worker);
if (topResult) {
linkAndClick(topResult.question);
}
}
});
worker.onmessage = (events) => handleWorker(events);
export function linkAndClick(output) {
saveMessage();
setTimeout(() => {
const encodedQuestion = encodeURIComponent(output);
const panels = document.querySelectorAll(
`.info-panel[data-question="${encodedQuestion}"]`
);
if (panels.length > 0) {
panels[panels.length - 1].click();
}
}, 250);
}
/** show results in chat window if "send" is clicked */
worker.onmessage = (events) => handleWorker(events);
/** show results in chat window if "ask" is clicked */
function saveMessage(){
return sendMessage(worker);
}
window.saveMessage = saveMessage;
function sendAsk(){
if (topResult) {
linkAndClick(topResult.question);
}
}
window.sendAsk = sendAsk;
/** give worker work */
worker.postMessage({ act: 'initialize', textData, embeddingStack, tokenEmbeddingStack });
......@@ -2,7 +2,7 @@
/** open mail client of user with prewritten message and stats */
export function submitFeedback(query, bestToken, feedback) {
export function submitFeedback(query, bestAnswers, feedback) {
/** temporary mail adress, adjust if needed */
const feedbackAdress = 'huepfen-richtlinie.7r@icloud.com';
......@@ -10,22 +10,27 @@ export function submitFeedback(query, bestToken, feedback) {
let subject = '';
let message = '';
const bestAnswersAsString = Object.entries(bestAnswers)
.map(([question, token]) => `> Question: ${question} \n > Answer: ${token}\n`)
.join("\n\n");
if (feedback === 'up') {
subject = 'Positive Feedback: FAQ Search Feature';
message = `Hello,
\nI used your FAQ search feature and was pleased with the result. Details below:
\nQuery: ${query}\nBest Tokens: ${bestToken}\nFeedback: ${feedback}\nTimestamp: ${timestamp}
\nI also have some additional suggestions:
\nI used your FAQ search feature and was pleased with the result.
\nI also have some additional suggestions: ...
\nBest Tokens:\n\n${bestAnswersAsString}\nFeedback: ${feedback}\nTimestamp: ${timestamp}\nQuery: ${query}
\nKeep it up!
\nBest regards`;
} else if (feedback === 'down') {
subject = 'Negative Feedback: FAQ Search Feature';
message = `Hello,
\nI tried used FAQ search feature, and unfortunately, I was not satisfied with this result. Detail below:
\nQuery: ${query}\nBest Tokens: ${bestToken}\nFeedback: ${feedback}\nTimestamp: ${timestamp}
\nI also have some additional suggestions:
\nI tried used FAQ search feature, and unfortunately, I was not satisfied with this result.
\nI also have some additional suggestions: ...
Detail below:
\nBest Tokens: ${bestAnswersAsString}\nFeedback: ${feedback}\nTimestamp: ${timestamp}\nQuery: ${query}
\nI hope this feedback lets you improve the experience.
\nBest regards`;
}
......
import { submitFeedback } from "./feedback.js";
/** handle what needs to be displayed in chat container */
export function appendMessage(content, className, linkId = null, akkordeonId = null, bestToken = null, query = null) {
export function appendMessage(content, className, linkId = null, akkordeonId = null, bestToken = null, query = null, answer = null) {
const messageContainer = document.createElement('div');
if (className === 'user-message') {
const messageDiv = document.createElement('div');
messageDiv.textContent = content;
messageDiv.className = className;
messageContainer.appendChild(messageDiv);
} else {
/** drawing what chat response looks like */
messageContainer.className = 'bot-message-container';
const numberBox = document.createElement('div');
......@@ -29,17 +25,41 @@ export function appendMessage(content, className, linkId = null, akkordeonId = n
const panelDiv = document.createElement('div');
panelDiv.textContent = '';
panelDiv.className = 'info-panel';
/** now the user can copy the relevant question to clipboard and use the browser search */
panelDiv.onclick = function () {
panelDiv.setAttribute('data-question', encodeURIComponent(content));
messageDiv.onclick = function () {
copyToClipboard(content);
};
panelDiv.onclick = function (event) {
event.stopPropagation();
let nextElem = messageContainer.nextElementSibling;
if (nextElem && nextElem.classList.contains('answer-container')) {
nextElem.remove();
panelDiv.classList.remove('active');
return;
}
let highlightToken = answer.replace(bestToken, `<span class="highlight">${bestToken}</span>`);
const answerContainer = document.createElement('div');
answerContainer.className = 'answer-container';
answerContainer.innerHTML = highlightToken;
messageContainer.parentNode.insertBefore(answerContainer, messageContainer.nextSibling);
panelDiv.classList.add('active');
}
messageContainer.appendChild(numberBox);
messageContainer.appendChild(messageDiv);
messageContainer.appendChild(panelDiv);
}
}
const messagesDiv = document.getElementById('messages');
messagesDiv.appendChild(messageContainer);
......@@ -53,6 +73,19 @@ export function sendMessage(worker) {
const userInput = document.getElementById('user-input');
const message = userInput.value.trim();
const resultsIframe = parent.document.getElementById('resultsIframe');
const results = resultsIframe.contentDocument || resultsIframe.contentWindow.document;
const resultsContainer = results.getElementById('results');
/** hide results when message is sent */
resultsContainer.innerHTML = '';
setTimeout(() => {
resultsIframe.style.display = 'none';
}, 500);
resultsIframe.style.display = 'block';
resultsContainer.innerHTML = '';
if (message !== "") {
......@@ -77,21 +110,34 @@ export function copyToClipboard(text) {
}
/** show user that clipboard action was successful */
export function copySuccsess() {
let currentConfirm = null;
let showTimeoutId = null;
let hideTimeoutId = null;
let removeTimeoutId = null;
function copySuccsess() {
if (currentConfirm) {
clearTimeout(showTimeoutId);
clearTimeout(hideTimeoutId);
clearTimeout(removeTimeoutId);
currentConfirm.remove();
}
const confirm = document.createElement('div');
confirm.innerText = 'Copied to clipboard!';
confirm.classList.add('confirm');
document.body.appendChild(confirm);
currentConfirm = document.createElement('div');
currentConfirm.innerText = 'Question copied to clipboard!';
currentConfirm.classList.add('confirm');
document.body.appendChild(currentConfirm);
setTimeout(() => {
confirm.classList.add('visible');
showTimeoutId = setTimeout(() => {
currentConfirm.classList.add('visible');
}, 10);
setTimeout(() => {
confirm.classList.remove('visible');
setTimeout(() => {
confirm.remove();
hideTimeoutId = setTimeout(() => {
currentConfirm.classList.remove('visible');
removeTimeoutId = setTimeout(() => {
currentConfirm.remove();
currentConfirm = null;
}, 500);
}, 2000);
}
......@@ -132,13 +178,13 @@ export function drawUserInfo() {
const messagesDiv = document.getElementById('messages');
const infoDiv = document.createElement('div');
infoDiv.className = 'bot-message';
infoDiv.innerHTML = "These question panels might be useful to you:<br>";
infoDiv.className = 'bot-intro';
infoDiv.innerHTML = "This might be useful:<br>";
messagesDiv.appendChild(infoDiv);
}
/** new message to inform user of the limited usability */
/** new message to inform user of the limited usability; out for now*/
export function discloseLimitations() {
const messagesDiv = document.getElementById('messages');
......
......@@ -23,10 +23,17 @@ export function handleWorker(events){
case 'searchResults':
const bestTokens = results.map(result => result.bestToken);
const questions = results.map(result => result.question);
const bestAnswer = {};
drawFeedbackButtons(query, bestTokens);
for (let i = 0; i < questions.length; i++) {
bestAnswer[questions[i]] = bestTokens[i];
}
drawFeedbackButtons(query, bestAnswer);
drawUserInfo();
/** pass our three results to ui so they can be displayed and worked with in chat */
results.forEach(result => {
appendMessage(
......@@ -35,11 +42,12 @@ export function handleWorker(events){
result.buttonId,
result.akkordeonId,
result.bestToken,
query
query,
result.answer
);
});
discloseLimitations();
/**discloseLimitations();*/
break;
......@@ -75,12 +83,12 @@ function exportAsJson(data, fileName) {
const jsonString = JSON.stringify(data, null, 2);
const blob = new Blob([jsonString], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const url = URL.createbestAnswerectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = fileName;
link.click();
URL.revokeObjectURL(url);
URL.revokebestAnswerectURL(url);
}
\ No newline at end of file
......@@ -9,9 +9,9 @@
width: 100%;
max-height: 400px;
overflow-x: hidden;
overflow-y: auto;
display: flex;
flex-direction: column;
overflow-y: hidden;
}
......@@ -73,8 +73,6 @@
}
.user-message {
align-self: flex-end;
background-color: #007bff;
......@@ -87,17 +85,40 @@
text-align: center;
}
.bot-message {
.answer-container {
white-space: pre-wrap;
align-self: flex-start;
background-color: #003865;
color: white;
margin-left: 10px;
margin-left: 52px;
margin: 10px 2;
max-width: 60%;
word-wrap: break-word;
text-align: middle;
cursor: default;
opacity: 0;
animation: showUp 0.3s forwards;
}
.highlight {
background-color: rgb(59, 136, 172);
color: white; /* Adjust as needed for contrast */
}
@keyframes showUp {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.info-for-user {
position: relative;
......@@ -112,21 +133,57 @@
display: inline-flex;
align-items: center;
height: auto;
position: relative;
gap: 20px;
padding: 50px;
border-radius: 4px;
font-size: 14px;
max-width: 50%;
vertical-align: top;
margin-right: 20px;
}
.bot-message {
align-self: flex-start;
background-color: #003865;
color: white;
margin-left: 10px;
margin: 10px 2;
max-width: 60%;
word-wrap: break-word;
text-align: middle;
z-index: 100;
cursor: pointer;
}
.bot-intro {
align-self: flex-start;
background-color: #003865;
color: white;
margin-left: 10px;
margin: 10px 2;
max-width: 60%;
word-wrap: break-word;
text-align: middle;
}
.bot-message-container {
display: flex;
align-items: stretch;
}
.bot-message:hover {
background-color: #002144;
}
.bot-message:active {
transform: scale(1.03);
}
......@@ -148,7 +205,7 @@
}
.info-panel::before {
content: "Copy to Clipboard";
content: "show full answer";
position: absolute;
background-color: rgba(0, 0, 0, 0.8);
color: white;
......@@ -164,11 +221,18 @@
max-width: 300px;
}
.info-panel.active::before {
content: "hide answer";
}
.info-panel:hover::before {
opacity: 1;
visibility: visible;
}
.info-panel:active {
transform: scale(1.03);
}
.number-box {
background-color: #aeb5bc;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment