-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[feature] Embed assistants #1489
base: main
Are you sure you want to change the base?
Changes from all commits
fb52674
1e8f976
c544c28
fa2f44d
68e9950
fa6edd6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,153 @@ | ||||||||||||||||||||||||||
export async function GET({ params }) { | ||||||||||||||||||||||||||
const { id } = params; | ||||||||||||||||||||||||||
Comment on lines
+1
to
+2
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. very nit: should the route be called embed-snippet.js ? |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const script = `(function() { | ||||||||||||||||||||||||||
function resizeIframeToContentSize(iframe) { | ||||||||||||||||||||||||||
Comment on lines
+4
to
+5
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice, i'm curious if there's a way to do this that's more maintainable ? it works great but i can imagine it would be very difficult to modify in the future i know there's a chat-ui/src/routes/assistant/[assistantId]/thumbnail.png/+server.ts Lines 44 to 55 in c544c28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or simpler, make the JS snippet completely static (a static file) that gets the assistant's id from a property in a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. <script src="https://link/to/static.js"></script>
<a huggingchat-assistant="66017fca" href="https://hf.co/chat/assistant/66017fca">Chat with {Assistant}</a> |
||||||||||||||||||||||||||
if (iframe.contentWindow) { | ||||||||||||||||||||||||||
const maxHeight = window.innerHeight * 0.8; // 80% of window height | ||||||||||||||||||||||||||
const chatContainerEl = iframe.contentWindow.document.getElementById('chat-container'); | ||||||||||||||||||||||||||
if(chatContainerEl){ | ||||||||||||||||||||||||||
const contentHeight = chatContainerEl.scrollHeight; | ||||||||||||||||||||||||||
iframe.style.height = Math.max(400, Math.min(contentHeight, maxHeight)) + "px"; | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
document.addEventListener('DOMContentLoaded', function() { | ||||||||||||||||||||||||||
const button = document.createElement('button'); | ||||||||||||||||||||||||||
button.className = 'fixed z-[1002] bottom-5 right-5 z-50 px-4 gap-1 py-1 bg-black rounded-full text-white rounded cursor-pointer hover:bg-gray-800 border border-gray-200/30 transition-colors flex items-center focus:outline-none'; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const img = document.createElement('img'); | ||||||||||||||||||||||||||
img.src = 'https://huggingface.co/chat/huggingchat/logo.svg'; | ||||||||||||||||||||||||||
img.alt = 'HuggingChat Logo'; | ||||||||||||||||||||||||||
img.className = 'size-5 mr-0.5 flex-none'; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const text = document.createTextNode('Chat'); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
button.appendChild(img); | ||||||||||||||||||||||||||
button.appendChild(text); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const modal = document.createElement('div'); | ||||||||||||||||||||||||||
modal.className = 'hidden fixed inset-0 z-[1001] overflow-auto bg-black bg-opacity-50'; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const modalContent = document.createElement('div'); | ||||||||||||||||||||||||||
modalContent.className = 'bg-white max-w-2xl rounded-xl overflow-hidden bottom-16 right-5 absolute max-sm:left-5 sm:w-[460px] shadow-2xl'; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const iframe = document.createElement('iframe'); | ||||||||||||||||||||||||||
iframe.className = 'w-full'; | ||||||||||||||||||||||||||
iframe.style.height = '400px'; // Set an initial height | ||||||||||||||||||||||||||
iframe.src = \`http://localhost:5173/chat/?embeddedAssistantId=${id}\`; | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this will have to be configurable using the base path I guess in order to work on self-hosted versions |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
iframe.onload = function() { | ||||||||||||||||||||||||||
const iframeWindow = this.contentWindow; | ||||||||||||||||||||||||||
const iframeDocument = iframeWindow.document; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
let lastHeight = 0; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
function checkSize() { | ||||||||||||||||||||||||||
const chatContainer = iframeDocument.getElementById('chat-container'); | ||||||||||||||||||||||||||
if (chatContainer) { | ||||||||||||||||||||||||||
const newHeight = chatContainer.scrollHeight; | ||||||||||||||||||||||||||
if (newHeight !== lastHeight) { | ||||||||||||||||||||||||||
resizeIframeToContentSize(iframe); | ||||||||||||||||||||||||||
lastHeight = newHeight; | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
requestAnimationFrame(checkSize); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
// Start continuous size checking | ||||||||||||||||||||||||||
checkSize(); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
// Set up MutationObserver as a backup | ||||||||||||||||||||||||||
const observer = new MutationObserver(() => { | ||||||||||||||||||||||||||
resizeIframeToContentSize(iframe); | ||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
function initMutationObserver() { | ||||||||||||||||||||||||||
const chatContainer = iframeDocument.getElementById('chat-container'); | ||||||||||||||||||||||||||
if (chatContainer) { | ||||||||||||||||||||||||||
console.error('Chat container found, setting up MutationObserver'); | ||||||||||||||||||||||||||
observer.observe(chatContainer, { childList: true, subtree: true, attributes: true, characterData: true }); | ||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||
console.error('Chat container not found, retrying...'); | ||||||||||||||||||||||||||
setTimeout(initMutationObserver, 500); // Retry after 500ms | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
// Start trying to initialize the MutationObserver | ||||||||||||||||||||||||||
initMutationObserver(); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
// Resize on load | ||||||||||||||||||||||||||
resizeIframeToContentSize(iframe); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
// Add event listener for Escape key in iframe | ||||||||||||||||||||||||||
iframeDocument.addEventListener('keydown', function(event) { | ||||||||||||||||||||||||||
if (event.key === 'Escape') { | ||||||||||||||||||||||||||
closeModal(); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
modalContent.appendChild(iframe); | ||||||||||||||||||||||||||
modal.appendChild(modalContent); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
// Store the original overflow style | ||||||||||||||||||||||||||
let originalOverflow; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
function toggleModal() { | ||||||||||||||||||||||||||
if (modal.classList.contains('hidden')) { | ||||||||||||||||||||||||||
modal.classList.remove('hidden'); | ||||||||||||||||||||||||||
resizeIframeToContentSize(iframe); | ||||||||||||||||||||||||||
// Store the original overflow and prevent scrolling | ||||||||||||||||||||||||||
originalOverflow = document.body.style.overflow; | ||||||||||||||||||||||||||
document.body.style.overflow = 'hidden'; | ||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||
modal.classList.add('hidden'); | ||||||||||||||||||||||||||
// Restore the original overflow | ||||||||||||||||||||||||||
document.body.style.overflow = originalOverflow; | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
button.onclick = toggleModal; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
window.onclick = function(event) { | ||||||||||||||||||||||||||
if (event.target == modal) { | ||||||||||||||||||||||||||
toggleModal(); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
document.addEventListener('keydown', function(event) { | ||||||||||||||||||||||||||
if (event.key === 'Escape') { | ||||||||||||||||||||||||||
closeModal(); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
// Prevent default scrolling when modal is open | ||||||||||||||||||||||||||
document.addEventListener('scroll', function(event) { | ||||||||||||||||||||||||||
if (!modal.classList.contains('hidden')) { | ||||||||||||||||||||||||||
event.preventDefault(); | ||||||||||||||||||||||||||
return false; | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
}, { passive: false }); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
// Add resize event listener to adjust iframe height when window is resized | ||||||||||||||||||||||||||
window.addEventListener('resize', function() { | ||||||||||||||||||||||||||
if (!modal.classList.contains('hidden')) { | ||||||||||||||||||||||||||
resizeIframeToContentSize(iframe); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
document.body.appendChild(button); | ||||||||||||||||||||||||||
document.body.appendChild(modal); | ||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||
})(); | ||||||||||||||||||||||||||
`; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
return new Response(script, { | ||||||||||||||||||||||||||
headers: { | ||||||||||||||||||||||||||
"Content-Type": "application/javascript", | ||||||||||||||||||||||||||
"Access-Control-Allow-Origin": "*", | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to make testing the embedded assistants easier, I'm embedding hf assistant inside the chat-ui. These lines would be removed before merging to
main
.you can change
{$page.data.assistants.at(-1)._id}
this part that grabs the last assistant id to any specific local assistant id if you want