From 3b2f1acf86c1199ab9dafd93b0da905efea9821d Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Wed, 2 Jul 2025 07:01:17 -0400 Subject: [PATCH] feat: complete mobile-first hermes modal redesign - Full screen on mobile, centered modal on desktop - Simple flex layout instead of complex grid - Modern chat bubble design - Auto-resizing input with proper mobile keyboard handling - Minimal UI - only essential controls - Better touch targets and mobile interactions --- src/ui.js | 319 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 318 insertions(+), 1 deletion(-) diff --git a/src/ui.js b/src/ui.js index 984aa60..d58247d 100644 --- a/src/ui.js +++ b/src/ui.js @@ -467,6 +467,323 @@ function setupFloatingButtonHandler(floatingButton) { }; } +// New mobile-first Hermes modal +async function openHermesModalNew() { + const modal = document.createElement("dialog"); + modal.id = "hermes-modal"; + + // Mobile-first: full screen on mobile, centered on desktop + const isMobile = window.innerWidth <= 768; + + modal.style.cssText = \` + position: fixed; + margin: 0; + padding: 0; + border: none; + background: white; + 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 rgba(0,0,0,0.3); + \`} + \`; + + // Simple container with flex layout + const container = document.createElement("div"); + container.style.cssText = \` + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + overflow: hidden; + \`; + + // 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 rgba(0,0,0,0.1); + \`; + + const title = document.createElement("h2"); + title.textContent = "🤖 Hermes AI"; + title.style.cssText = \` + margin: 0; + font-size: \${isMobile ? '18px' : '20px'}; + font-weight: 600; + \`; + + 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); + hermesModalOpen = false; + }; + + header.appendChild(title); + header.appendChild(closeBtn); + + // Chat area + const chatArea = document.createElement("div"); + chatArea.style.cssText = \` + flex: 1; + overflow-y: auto; + padding: 16px; + background: #f8f9fa; + -webkit-overflow-scrolling: touch; + \`; + + const chatBox = document.createElement("div"); + chatBox.id = "modal-chat-box"; + chatBox.style.cssText = \` + max-width: 100%; + display: flex; + flex-direction: column; + gap: 12px; + \`; + chatArea.appendChild(chatBox); + + // Quick action buttons (minimal on mobile) + if (!isMobile) { + const controls = document.createElement("div"); + controls.style.cssText = \` + flex-shrink: 0; + padding: 8px 16px; + background: white; + border-top: 1px solid #e9ecef; + border-bottom: 1px solid #e9ecef; + display: flex; + gap: 8px; + overflow-x: auto; + \`; + + const actions = [ + { text: "📖 Read", action: () => window.readPageWithHermes() }, + { text: "🔊 TTS", action: () => window.openTTSModal() }, + { text: "🌐 Translate", action: () => window.openTranslateModal() } + ]; + + actions.forEach(({ text, action }) => { + const btn = document.createElement("button"); + btn.textContent = text; + btn.style.cssText = \` + padding: 8px 16px; + background: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 20px; + cursor: pointer; + white-space: nowrap; + font-size: 14px; + transition: all 0.2s; + \`; + btn.onmouseover = () => { + btn.style.background = '#e9ecef'; + btn.style.borderColor = '#adb5bd'; + }; + btn.onmouseout = () => { + btn.style.background = '#f8f9fa'; + btn.style.borderColor = '#dee2e6'; + }; + btn.onclick = action; + controls.appendChild(btn); + }); + + container.appendChild(header); + container.appendChild(chatArea); + container.appendChild(controls); + } else { + container.appendChild(header); + container.appendChild(chatArea); + } + + // Input area + const inputArea = document.createElement("div"); + inputArea.style.cssText = \` + flex-shrink: 0; + padding: 16px; + background: white; + display: flex; + gap: 12px; + align-items: flex-end; + box-shadow: 0 -2px 10px rgba(0,0,0,0.05); + \`; + + const input = document.createElement("textarea"); + input.id = "modal-user-input"; + input.placeholder = "Ask about this page..."; + input.style.cssText = \` + flex: 1; + min-height: 44px; + max-height: 120px; + padding: 12px; + border: 1px solid #dee2e6; + border-radius: 24px; + resize: none; + font-size: 16px; + font-family: inherit; + line-height: 1.4; + outline: none; + transition: border-color 0.2s; + \`; + input.onfocus = () => input.style.borderColor = '#667eea'; + input.onblur = () => input.style.borderColor = '#dee2e6'; + + // Auto-resize textarea + input.oninput = () => { + input.style.height = 'auto'; + input.style.height = Math.min(input.scrollHeight, 120) + 'px'; + }; + + 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 sendMessage = 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: #667eea; + color: white; + padding: 12px 16px; + border-radius: 18px 18px 4px 18px; + max-width: 80%; + word-wrap: break-word; + \`; + userMsg.textContent = message; + chatBox.appendChild(userMsg); + + // Add AI response placeholder + const aiMsg = document.createElement("div"); + aiMsg.style.cssText = \` + align-self: flex-start; + background: white; + padding: 12px 16px; + border-radius: 18px 18px 18px 4px; + max-width: 80%; + box-shadow: 0 1px 2px rgba(0,0,0,0.1); + \`; + aiMsg.innerHTML = 'Thinking...'; + chatBox.appendChild(aiMsg); + + chatArea.scrollTop = chatArea.scrollHeight; + + try { + let response = ''; + for await (const chunk of sendMessage(message)) { + response += chunk; + aiMsg.innerHTML = marked.parse(response); + } + } catch (error) { + aiMsg.innerHTML = \`Error: \${error.message}\`; + } + + chatArea.scrollTop = chatArea.scrollHeight; + }; + + sendBtn.onclick = sendMessage; + input.onkeydown = (e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + sendMessage(); + } + }; + + inputArea.appendChild(input); + inputArea.appendChild(sendBtn); + container.appendChild(inputArea); + modal.appendChild(container); + + document.body.appendChild(modal); + modal.showModal(); + + modal.addEventListener('click', (e) => { + if (e.target === modal) { + modal.close(); + document.body.removeChild(modal); + hermesModalOpen = false; + } + }); + + input.focus(); + + // Add intro message + const introMsg = document.createElement("div"); + introMsg.style.cssText = \` + align-self: flex-start; + background: white; + padding: 12px 16px; + border-radius: 18px 18px 18px 4px; + max-width: 80%; + box-shadow: 0 1px 2px rgba(0,0,0,0.1); + color: #495057; + \`; + introMsg.innerHTML = \`👋 Hi! I'm Hermes AI. I can help you understand this page, answer questions, or assist with various tasks. How can I help you today?\`; + chatBox.appendChild(introMsg); +} + // Function to toggle Hermes modal export async function toggleHermesModal() { const existingModal = document.getElementById("hermes-modal"); @@ -474,7 +791,7 @@ export async function toggleHermesModal() { document.body.removeChild(existingModal); hermesModalOpen = false; } else { - await openHermesModal(); + await openHermesModalNew(); hermesModalOpen = true; } }