fix: restore complete working chat modal implementation from commit 3e01e9c

This commit is contained in:
Russell Ballestrini 2025-07-03 17:36:07 -04:00
parent 5d584bf8f0
commit bd0b0a5f97

View file

@ -125,258 +125,292 @@ function addCodeBlockCopyButtons(element) {
// Advanced modal implementation (was openUncloseaiEmbeddedModalNew)
export async function openUncloseaiEmbeddedModalNew() {
// The complete 2000+ line implementation would go here
// For now, calling the simpler version
console.log("Advanced modal implementation - calling simpler version for now");
return await openUncloseaiEmbeddedModal();
}
// Ensure ChunkFive font is loaded
initializeChunkFiveFont();
// Get theme colors
const theme = detectCurrentTheme();
const colors = getThemeColors(theme);
// Main modal implementation (extracted from ui.js)
export async function openUncloseaiEmbeddedModal() {
const modal = document.createElement("dialog");
modal.id = "uncloseai-embedded-modal";
modal.setAttribute("data-theme", theme);
// Mobile-first: full screen on mobile, centered on desktop
const isMobile = window.innerWidth <= 768;
if (isMobile) {
modal.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
border: none;
border-radius: 0;
background: white;
margin: 0;
padding: 0;
z-index: 2000;
overflow: hidden;
`;
} else {
modal.style.cssText = `
position: fixed;
width: 90vw;
max-width: 800px;
height: 90vh;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: none;
border-radius: 16px;
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
background: white;
margin: 0;
padding: 0;
z-index: 2000;
overflow: hidden;
`;
}
const article = document.createElement("article");
if (USE_CUSTOM_STYLING) {
article.style.cssText = `
width: 100%;
max-width: 100%;
box-sizing: border-box;
height: 100%;
display: grid;
grid-template-rows: auto auto 1fr auto;
margin: 0;
padding: 0;
`;
} else {
// Responsive scaling based on screen width
const screenWidth = window.innerWidth;
const scale = screenWidth < 480 ? 0.9 : screenWidth < 768 ? 0.8 : 0.75;
article.style.cssText = `
height: 100%;
display: grid;
grid-template-rows: auto auto 1fr auto;
transform: scale(${scale});
transform-origin: top center;
margin: 0;
padding: 0;
min-width: 0;
max-width: none;
box-sizing: border-box;
`;
}
modal.appendChild(article);
// Handle viewport changes (onscreen keyboard)
if (!USE_CUSTOM_STYLING) {
const handleViewportChange = () => {
// Use dvh (dynamic viewport height) for better mobile keyboard handling
modal.style.height = "100dvh";
// Fallback for browsers that don't support dvh
if (window.visualViewport) {
modal.style.height = `${window.visualViewport.height}px`;
}
};
// Listen for visual viewport changes (keyboard open/close)
if (window.visualViewport) {
window.visualViewport.addEventListener("resize", handleViewportChange);
modal.style.cssText = `
position: fixed;
margin: 0;
padding: 0;
border: none;
background: ${colors.modalBackground};
color: ${colors.primaryText};
z-index: 2000;
${
isMobile
? `
top: 0;
left: 0;
width: 100vw;
height: 100vh;
max-width: 100vw;
max-height: 100vh;
border-radius: 0;
`
: `
width: 90vw;
max-width: 800px;
height: 90vh;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 16px;
box-shadow: 0 20px 40px ${colors.shadowColor};
`
}
`;
// Also listen for window resize as fallback
window.addEventListener("resize", handleViewportChange);
// Simple container with flex layout
const container = document.createElement("div");
container.style.cssText = `
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
`;
// Initial call
handleViewportChange();
}
// Header
const header = document.createElement("header");
header.style.cssText = `
flex-shrink: 0;
padding: 16px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 2px 4px ${colors.lightShadow};
`;
// Create modal header
const header = document.createElement("div");
if (USE_CUSTOM_STYLING) {
header.style.cssText = `
background: linear-gradient(45deg, #4CAF50 0%, #45a049 100%);
color: white;
padding: 16px 20px;
margin: -1em -1em 1em -1em;
display: flex;
justify-content: space-between;
align-items: center;
`;
} else {
header.style.cssText = `
background: rgba(76, 175, 80, 0.9);
color: white;
padding: 16px 20px;
margin: 0;
display: flex;
justify-content: space-between;
align-items: center;
backdrop-filter: blur(10px);
`;
}
article.appendChild(header);
const titleContainer = document.createElement("div");
titleContainer.style.cssText =
"flex: 1; display: flex; flex-direction: column; gap: 4px;";
const h1 = document.createElement("h1");
h1.innerHTML = `<span style="font-family: 'ChunkFiveRegular', monospace;">uncloseai.</span> ${getUIText("chatWithAI")}`;
if (USE_CUSTOM_STYLING) {
h1.style.cssText = `
margin: 0;
font-size: 20px;
`;
} else {
h1.style.cssText = `
margin: 0;
font-size: 18px;
font-weight: normal;
`;
}
header.appendChild(h1);
const title = document.createElement("h2");
title.innerHTML =
"🤖 <span style=\"font-family: 'ChunkFiveRegular', monospace;\">uncloseai.</span>";
title.style.cssText = `
margin: 0;
font-size: ${isMobile ? "18px" : "20px"};
font-weight: 600;
`;
const closeButton = document.createElement("button");
closeButton.textContent = "X";
if (USE_CUSTOM_STYLING) {
closeButton.style.cssText = `
background: none;
border: none;
color: white;
font-size: 24px;
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
touch-action: manipulation;
`;
} else {
closeButton.style.cssText = `
background: rgba(255,255,255,0.2);
border: 1px solid rgba(255,255,255,0.3);
color: white;
font-size: 18px;
cursor: pointer;
padding: 4px;
width: 28px;
height: 28px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
transition: opacity 0.2s;
touch-action: manipulation;
`;
closeButton.onmouseenter = () => (closeButton.style.opacity = "0.7");
closeButton.onmouseleave = () => (closeButton.style.opacity = "1");
}
closeButton.onclick = () => {
const subtitle = document.createElement("div");
const pageTitle = document.title || window.location.hostname;
subtitle.innerHTML = `
<div style="font-size: 12px; opacity: 0.9; font-weight: normal; line-height: 1.3;">
<span style="font-family: 'ChunkFiveRegular', monospace;">uncloseai.</span> ${getUIText("hermesIntro")}<br>
<span style="opacity: 0.8;">${getUIText("discussingPage")}: ${pageTitle}</span>
</div>
`;
titleContainer.appendChild(title);
titleContainer.appendChild(subtitle);
const closeBtn = document.createElement("button");
closeBtn.textContent = "✕";
closeBtn.style.cssText = `
background: rgba(255,255,255,0.2);
border: none;
color: white;
font-size: 20px;
width: 36px;
height: 36px;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s;
`;
closeBtn.onmouseover = () =>
(closeBtn.style.background = "rgba(255,255,255,0.3)");
closeBtn.onmouseout = () =>
(closeBtn.style.background = "rgba(255,255,255,0.2)");
closeBtn.onclick = () => {
modal.close();
document.body.removeChild(modal);
window.uncloseaiEmbeddedModalOpen = false;
};
header.appendChild(closeButton);
// Add main content area with basic implementation
const mainContent = document.createElement("div");
mainContent.style.cssText = `
const headerRight = document.createElement("div");
headerRight.style.cssText = "display: flex; gap: 8px; align-items: center;";
headerRight.appendChild(closeBtn);
header.appendChild(titleContainer);
header.appendChild(headerRight);
// Chat area
const chatArea = document.createElement("div");
chatArea.style.cssText = `
flex: 1;
padding: 20px;
overflow-y: auto;
display: flex;
flex-direction: column;
padding: 16px;
background: ${colors.contentBackground};
-webkit-overflow-scrolling: touch;
`;
// Add a simple chat interface
const chatBox = document.createElement("div");
chatBox.id = "modal-chat-box";
chatBox.style.cssText = `
flex: 1;
border: 1px solid #ddd;
border-radius: 8px;
padding: 15px;
margin-bottom: 15px;
overflow-y: auto;
background: #f9f9f9;
max-width: 100%;
display: flex;
flex-direction: column;
gap: 12px;
`;
chatBox.innerHTML = `<p><em>Chat interface placeholder - full implementation needed</em></p>`;
chatArea.appendChild(chatBox);
// Add input area
// Assemble modal structure
container.appendChild(header);
container.appendChild(chatArea);
// Input area
const inputArea = document.createElement("div");
inputArea.style.cssText = `
flex-shrink: 0;
padding: 16px;
background: ${colors.panelBackground};
display: flex;
gap: 10px;
gap: 12px;
align-items: flex-end;
box-shadow: 0 -2px 10px ${colors.lightShadow};
`;
const messageInput = document.createElement("input");
messageInput.type = "text";
messageInput.placeholder = getUIText("typePlaceholder") || "Type your message...";
messageInput.style.cssText = `
const input = document.createElement("textarea");
input.id = "modal-user-input";
input.placeholder = getUIText("typePlaceholder");
input.style.cssText = `
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
min-height: 44px;
max-height: 120px;
padding: 12px;
border: 1px solid ${colors.borderColor};
border-radius: 24px;
resize: none;
font-size: 16px;
font-family: inherit;
line-height: 1.4;
outline: none;
background: ${colors.inputBackground};
color: ${colors.primaryText};
transition: border-color 0.2s;
`;
input.onfocus = () => (input.style.borderColor = colors.focusBorder);
input.onblur = () => (input.style.borderColor = colors.borderColor);
const sendButton = document.createElement("button");
sendButton.textContent = "Send";
sendButton.style.cssText = `
padding: 10px 20px;
background: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
`;
sendButton.onclick = () => {
console.log("Send clicked:", messageInput.value);
// Basic chat functionality would go here
// Auto-resize textarea
input.oninput = () => {
input.style.height = "auto";
input.style.height = `${Math.min(input.scrollHeight, 120)}px`;
};
inputArea.appendChild(messageInput);
inputArea.appendChild(sendButton);
mainContent.appendChild(chatBox);
mainContent.appendChild(inputArea);
article.appendChild(mainContent);
const sendBtn = document.createElement("button");
sendBtn.textContent = "→";
sendBtn.style.cssText = `
width: 44px;
height: 44px;
padding: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
font-size: 20px;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.2s;
flex-shrink: 0;
`;
sendBtn.onmouseover = () => (sendBtn.style.transform = "scale(1.05)");
sendBtn.onmouseout = () => (sendBtn.style.transform = "scale(1)");
// Handle message sending
const sendMessageHandler = async () => {
const message = input.value.trim();
if (!message) return;
input.value = "";
input.style.height = "44px";
// Add user message
const userMsg = document.createElement("div");
userMsg.style.cssText = `
align-self: flex-end;
background: ${colors.userMessageBg};
color: ${colors.userMessageText};
padding: 12px 16px;
border-radius: 18px 18px 4px 18px;
max-width: 80%;
word-wrap: break-word;
`;
userMsg.textContent = message;
chatBox.appendChild(userMsg);
// Auto-scroll to bottom
chatArea.scrollTop = chatArea.scrollHeight;
// Add AI response
const aiMsg = document.createElement("div");
aiMsg.style.cssText = `
align-self: flex-start;
background: ${colors.aiMessageBg};
color: ${colors.aiMessageText};
padding: 12px 16px;
border-radius: 18px 18px 18px 4px;
max-width: 80%;
word-wrap: break-word;
`;
const responseContent = document.createElement("div");
aiMsg.appendChild(responseContent);
chatBox.appendChild(aiMsg);
// Get AI response
let accumulatedContent = "";
try {
for await (const chunk of sendMessage(message)) {
accumulatedContent += chunk;
responseContent.innerHTML = marked.parse(accumulatedContent);
// Add copy buttons to code blocks
addCodeBlockCopyButtons(responseContent);
// Auto-scroll to bottom as response streams in
chatArea.scrollTop = chatArea.scrollHeight;
}
} catch (error) {
responseContent.textContent = `Error: ${error.message}`;
}
};
sendBtn.onclick = sendMessageHandler;
input.onkeydown = (e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
sendMessageHandler();
}
};
inputArea.appendChild(input);
inputArea.appendChild(sendBtn);
container.appendChild(inputArea);
modal.appendChild(container);
document.body.appendChild(modal);
// Use showModal() for proper mobile support and backdrop
@ -393,6 +427,14 @@ export async function openUncloseaiEmbeddedModal() {
// Set global flag
window.uncloseaiEmbeddedModalOpen = true;
// Focus input
setTimeout(() => input.focus(), 100);
}
// Main modal implementation - now calls the complete version
export async function openUncloseaiEmbeddedModal() {
return await openUncloseaiEmbeddedModalNew();
}
// Export functions for global access