diff --git a/index.js b/index.js index 31f29fb6fc8babd8bceb4f2dfbeda31640856600..2a8ebed592d205a65a8502da95d5d4cc5849c8b6 100644 --- a/index.js +++ b/index.js @@ -10,7 +10,6 @@ const worker = new Worker('./worker.js', { type: 'module' }); /** get the up to date FAQ from site */ const textData = await extractFAQfromHTML("FAQ.html"); - /** import precompiled embeddings and check for missing entries */ console.time("loadLargeEmbeddings"); let embeddingStack = await loadEmbedding('largeEmbedding', './embeddings/largeEmbedding.json'); @@ -33,61 +32,48 @@ localStorage.setItem('largeEmbedding', JSON.stringify(embeddingStack)); localStorage.setItem('smallEmbedding', JSON.stringify(tokenEmbeddingStack)); - -/** handeling results iframe */ const inputElement = document.getElementById('user-input'); -const resultsIframe = parent.document.getElementById('resultsIframe'); +const resultsContainer = document.getElementById('results'); let topResult = null; inputElement.addEventListener('input', function (event) { - - const query = event.target.value.trim(); - const results = resultsIframe.contentDocument || resultsIframe.contentWindow.document; - const resultsContainer = results.getElementById('results'); - /** hide results when no query is typed */ if (!query) { resultsContainer.innerHTML = ''; - setTimeout(() => { - resultsIframe.style.display = 'none'; - }, 500); + resultsContainer.style.display = 'none'; return; } - - resultsIframe.style.display = 'block'; + + resultsContainer.style.display = 'block'; resultsContainer.innerHTML = ''; - processQuery(query, worker, function (outputs) { - window.focus(); outputs.forEach(async output => { - - const resultLink = results.createElement('a'); - resultLink.href = 'javascript:void(0);'; + + /** display live results */ + const resultLink = document.createElement('div'); resultLink.className = 'result'; - resultLink.style.display = 'block'; - /** display question text in result iframe */ - const questionDiv = results.createElement('div'); + /** display question text */ + const questionDiv = document.createElement('div'); questionDiv.className = 'result-question'; questionDiv.innerHTML = `<strong>${output.question}</strong>`; resultLink.appendChild(questionDiv); - /** display our most relevant token to question in result iframe */ - const bestTokenDiv = results.createElement('div'); + /** display our most relevant token to question */ + const bestTokenDiv = document.createElement('div'); bestTokenDiv.className = 'result-token'; let tokenText = output.bestToken; - /** shorten our best token; only 100 chars displayed*/ if (tokenText.length > 100) { tokenText = tokenText.substring(0, 100); } tokenText = '> ' + tokenText + ' ...'; bestTokenDiv.textContent = tokenText; resultLink.appendChild(bestTokenDiv); - + window.focus(); /** open the answer field in chat view for selected panel */ resultLink.onclick = function () { @@ -96,13 +82,13 @@ inputElement.addEventListener('input', function (event) { 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') { @@ -113,10 +99,8 @@ document.getElementById('user-input').addEventListener('keypress', function (eve } }); - export function linkAndClick(output) { saveMessage(); - setTimeout(() => { const encodedQuestion = encodeURIComponent(output); const panels = document.querySelectorAll( diff --git a/modules/ui.js b/modules/ui.js index c823d0c1e10458ebead278c625a0c3718d98e6e3..e46a55d1691449169116e7cbe8ba2b327b3d4df3 100644 --- a/modules/ui.js +++ b/modules/ui.js @@ -11,7 +11,7 @@ export function appendMessage(content, className, linkId = null, akkordeonId = n messageContainer.appendChild(messageDiv); } else { - + messageContainer.className = 'bot-message-container'; const numberBox = document.createElement('div'); @@ -24,7 +24,6 @@ export function appendMessage(content, className, linkId = null, akkordeonId = n const panelDiv = document.createElement('div'); panelDiv.innerHTML = '<i class="fa-solid fa-down-long"></i>'; panelDiv.className = 'info-panel'; - panelDiv.setAttribute('data-question', encodeURIComponent(content)); messageDiv.onclick = function(event) { @@ -33,15 +32,12 @@ export function appendMessage(content, className, linkId = null, akkordeonId = n } panelDiv.onclick = function (event) { - event.stopPropagation(); - let nextElem = messageContainer.nextElementSibling; if (nextElem && nextElem.classList.contains('answer-container')) { nextElem.remove(); panelDiv.innerHTML = '<i class="fa-solid fa-down-long"></i>'; panelDiv.classList.remove('active'); - return; } panelDiv.innerHTML = '<i class="fa-solid fa-up-long"></i>'; @@ -50,19 +46,15 @@ export function appendMessage(content, className, linkId = null, akkordeonId = n 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); - + messageContainer.appendChild(numberBox); + messageContainer.appendChild(messageDiv); + messageContainer.appendChild(panelDiv); } const messagesDiv = document.getElementById('messages'); @@ -70,31 +62,25 @@ export function appendMessage(content, className, linkId = null, akkordeonId = n messagesDiv.scrollTop = messagesDiv.scrollHeight; } -/** gets query from input field and performs search*/ +/** gets query from input field and performs search */ 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'); + + const resultsContainer = document.getElementById('results'); /** hide results when message is sent */ resultsContainer.innerHTML = ''; setTimeout(() => { - resultsIframe.style.display = 'none'; - }, 500); - - resultsIframe.style.display = 'block'; + resultsContainer.style.display = 'none'; + }, 500); + + resultsContainer.style.display = 'block'; resultsContainer.innerHTML = ''; - if (message !== "") { - appendMessage(message, 'user-message'); - userInput.value = ''; worker.postMessage({ act: 'semanticSearch', @@ -103,29 +89,23 @@ export function sendMessage(worker) { } } - /** new feedback buttons with font-awesome style */ export function drawFeedbackButtons(query, bestTokens) { - const messagesDiv = document.getElementById('messages'); const feedbackContainer = document.createElement('div'); feedbackContainer.className = 'feedback-container'; - - const plusButton = document.createElement('button'); plusButton.innerHTML = '<i class="fa-solid fa-thumbs-up"></i>'; plusButton.className = 'feedback-button thumbs-up'; plusButton.onclick = function () { submitFeedback(query, bestTokens, 'good'); - }; const minusButton = document.createElement('button'); minusButton.innerHTML = '<i class="fa-solid fa-thumbs-down"></i>'; minusButton.className = 'feedback-button thumbs-down'; minusButton.onclick = function () { - submitFeedback(query, bestTokens, 'bad'); }; @@ -134,18 +114,16 @@ export function drawFeedbackButtons(query, bestTokens) { messagesDiv.appendChild(feedbackContainer); } -/** Intro panel for chat resonses*/ +/** Intro panel for chat responses */ export function drawUserInfo() { - const messagesDiv = document.getElementById('messages'); const infoDiv = document.createElement('div'); - infoDiv.className = 'bot-intro'; infoDiv.innerHTML = "This might be useful:<br>"; - messagesDiv.appendChild(infoDiv); } + /** new message to inform user of the limited usability; out for now*/ export function discloseLimitations() { diff --git a/modules/workerInterface.js b/modules/workerInterface.js index 147917cc378a2b6e212bc9efa6593fec990cf3ba..da49b709018ec8efd3167807d85e4f51e400874c 100644 --- a/modules/workerInterface.js +++ b/modules/workerInterface.js @@ -52,7 +52,7 @@ export function handleWorker(events){ break; case 'topResults': - /** show results live in result iframe */ + /** show results live in result */ if (processQueryCache) { const outputs = results.slice(0, 3).map(result => ({ question: result.question, diff --git a/semSearch.html b/semSearch.html index 6bd18e110803a8d5fb8f4ac647606d811adfcbc1..dae0e181d875d01b16c276bf9d04e8e1a6a9a2ed 100644 --- a/semSearch.html +++ b/semSearch.html @@ -2,47 +2,32 @@ <html lang="en"> <head> <meta charset="UTF-8"> - <title>HHU Search Helper</title> - <style> + <title>HHU Search Helper - Combined</title> + <link rel="stylesheet" href="styles.css"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"> + <style> body { overflow: hidden; } - </style> + </style> </head> <body> + + <div id="chatbox"> + <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 asking: 'minimum admission grade' or 'I'm from outside the EU'"> + <button onclick="sendAsk()">Ask</button> + + + </div> + <div id="results"></div> + + </div> - <iframe id="chatIframe" src="chatbox.html" style="position: relative; width: 100%; border: none;" title="HHU Search Helper"></iframe> - <iframe id="resultsIframe" src="results.html" style="position: sticky; left: 15px; min-height: 300px; transition: opacity 1s ease-in-out, transform 1s ease-in-out; background-color: #f0f0f0; width: 95%; overflow: hidden; padding: 10px; border-radius: 5px; border: none; display: none; overflow: hidden;"></iframe> - - <script> - /** make iframes grow with iframes inside it */ - - function adjustIframeSize() { - - const chatIframe = document.getElementById('chatIframe'); - const resultsIframe = document.getElementById('resultsIframe'); - - let totalHeight = 0; - if (chatIframe.offsetHeight) { - totalHeight += chatIframe.offsetHeight; - } - if (resultsIframe.style.display !== 'none' && resultsIframe.offsetHeight) { - totalHeight += resultsIframe.offsetHeight; - } - if (window.frameElement) { - window.frameElement.style.height = totalHeight + 'px'; - } - } - - - document.getElementById('chatIframe').addEventListener('load', adjustIframeSize); - document.getElementById('resultsIframe').addEventListener('load', adjustIframeSize); - - - const observer = new MutationObserver(adjustIframeSize); - observer.observe(document.body, { childList: true, subtree: true, attributes: true }); - - </script> + <script type="module" src="index.js" async></script> </body> </html> diff --git a/styles.css b/styles.css index 63c4c9e9590f73a7226b48e3b37dcc632accb905..3d7cde2378a3a17d1a6d626e6215e8d9d023ebfb 100644 --- a/styles.css +++ b/styles.css @@ -2,20 +2,16 @@ font-family: Arial, sans-serif; } - #chatbox { - background-color: #d3d3d3; position: sticky; - width: 100%; - max-height: 400px; - overflow-x: hidden; + max-height: 900px; + overflow: hidden; display: flex; - flex-direction: column; - overflow-y: hidden; + flex-direction: column; } - #chatbox-title { + position: relative; background-color: #003865; color: white; text-align: center; @@ -28,11 +24,12 @@ } #messages { + background-color: #d3d3d3; flex: 1; left: 10px; padding: 10px; display: flex; - max-height: 270px; + max-height: 300px; flex-direction: column; overflow-x: hidden; z-index: -3; @@ -45,9 +42,10 @@ } #input-container { + background-color: #d3d3d3; display: flex; padding: 10px; - height: 50px; + height: 50px; } #input-container input { @@ -72,7 +70,6 @@ background-color: #0056b3; } - .user-message { align-self: flex-end; background-color: #007bff; @@ -84,8 +81,6 @@ word-wrap: break-word; text-align: center; } - - .answer-container { white-space: pre-wrap; @@ -99,27 +94,13 @@ cursor: default; opacity: 0; animation: showUp 0.3s forwards; - } .highlight { background-color: rgb(59, 136, 172); - color: white; /* Adjust as needed for contrast */ + color: white; } - @keyframes showUp { - from { - opacity: 0; - transform: translateY(-10px); - } - to { - opacity: 1; - transform: translateY(0); - } - } - - - .info-for-user { position: relative; margin-right: auto; @@ -128,7 +109,6 @@ background-color: rgb(0, 0, 0, 0.8); color: white; flex-direction: row; - max-width: 150px; text-align: left; display: inline-flex; align-items: center; @@ -139,8 +119,7 @@ font-size: 14px; max-width: 50%; vertical-align: top; - margin-right: 20px; - + margin-right: 20px; } .bot-message { @@ -153,8 +132,7 @@ word-wrap: break-word; text-align: middle; z-index: 100; - cursor: pointer; - + cursor: pointer; } .bot-intro { @@ -165,16 +143,12 @@ margin: 10px 2; max-width: 60%; word-wrap: break-word; - text-align: middle; - + text-align: middle; } - - .bot-message-container { display: flex; - align-items: stretch; - + align-items: stretch; } .bot-message:hover { @@ -185,7 +159,6 @@ transform: scale(1.03); } - .info-panel { background-color: #0056b3; color: rgb(255, 255, 255); @@ -249,7 +222,6 @@ .feedback-container { position: relative; margin-left: auto; - left: -20px; color: #000; cursor: pointer; @@ -257,7 +229,6 @@ display: flex; align-items: center; justify-content: center; - height: 0px; padding: 3px; font-size: 12px; border-radius: 4px; @@ -283,8 +254,7 @@ .feedback-container:hover::after { opacity: 1; - visibility: visible; - + visibility: visible; } .feedback-button { @@ -307,59 +277,61 @@ } #results { - position: sticky; + display: none; + position: relative; background-color: #f0f0f0; + width: auto; + z-index: 1000; + margin: 20px; padding: 10px; - width: 95%; - display: block; - max-height: auto; + min-height: 200px; overflow: hidden; - + border-radius: 5px; + flex-grow: 1; + animation: showUp 0.3s forwards; } - + #results .show { + position: relative; + display: block; opacity: 1; - transform: translateY(0); + width: 100%; visibility: visible; - overflow: hidden; - min-height: 200px; + padding: 5px; } #results .result { - padding: 4px 8px; + position: relative; margin-bottom: 4px; border-bottom: 1px solid #ddd; font-size: 17px; - padding-left: 5%; - padding-right: 10%; - overflow: hidden; + padding: 5px; + margin: 10px; text-decoration: none; color: inherit; - } + } #results .result:hover { + position: relative; + cursor: pointer; background-color: #e0e0e0; overflow: hidden; + padding: 5px; font-size: 18px; - } - - .confirm { - position: fixed; - top: 60px; - right: 20px; - background-color: #50bd54; - color: #fff; - padding: 10px 15px; border-radius: 5px; - opacity: 0; - transition: opacity 0.5s ease-in-out; - pointer-events: none; - z-index: 5; } - - .confirm.visible { - opacity: 1; + + @keyframes showUp { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } } +