Download ui-temp.html from adaptsum/demo: direct link, hf CLI and curl.
- Browser
- Download file 27.5 kB
-
https://huggingface.co/spaces/adaptsum/demo/resolve/main/ui-temp.html
- Command line
-
hf download hf://spaces/adaptsum/demo/ui-temp.html
-
curl -L -o ui-temp.html https://huggingface.co/spaces/adaptsum/demo/resolve/main/ui-temp.html
27.5 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <title>Chat UI with Per‑Session Summary, Settings & History</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500&display=swap" rel="stylesheet"> | |
| <!-- Include marked.js for Markdown rendering --> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <style> | |
| /* Global Styles */ | |
| * { box-sizing: border-box; } | |
| body { | |
| margin: 0; | |
| padding: 0; | |
| font-family: 'Poppins', sans-serif; | |
| background: #e0e0e0; | |
| overflow: hidden; | |
| } | |
| /* Overall App Container: Left Nav and Chat Area */ | |
| .app-container { | |
| display: flex; | |
| width: 100vw; | |
| height: 100vh; | |
| transition: all 0.3s ease; | |
| } | |
| /* Left Navigation Bar */ | |
| .nav-bar { | |
| width: 300px; | |
| background: #ffffff; | |
| padding: 20px; | |
| box-shadow: 2px 0 12px rgba(0,0,0,0.1); | |
| overflow-y: auto; | |
| transition: width 0.3s ease, padding 0.3s ease; | |
| } | |
| .nav-bar.hidden { | |
| display: none; | |
| } | |
| .nav-bar h3 { | |
| margin-top: 0; | |
| font-size: 1.3em; | |
| } | |
| .nav-bar ul { | |
| list-style: none; | |
| padding: 0; | |
| margin: 0; | |
| } | |
| .nav-bar li { | |
| padding: 10px; | |
| margin-bottom: 10px; | |
| background: #fff; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| transition: background 0.3s; | |
| font-size: 1.1em; | |
| } | |
| .nav-bar li.active, | |
| .nav-bar li:hover { | |
| background: #667eea; | |
| color: #fff; | |
| } | |
| .session-actions { | |
| display: inline-flex; | |
| gap: 5px; | |
| margin-left: 10px; | |
| } | |
| .session-summary-btn, | |
| .session-settings-btn { | |
| background: transparent; | |
| border: none; | |
| cursor: pointer; | |
| font-size: 1.2em; | |
| } | |
| .session-summary-btn:hover, | |
| .session-settings-btn:hover { | |
| color: #667eea; | |
| } | |
| .nav-bar li button.remove-session { | |
| background: transparent; | |
| border: none; | |
| color: inherit; | |
| font-size: 1em; | |
| cursor: pointer; | |
| margin-left: auto; | |
| } | |
| .new-session-btn { | |
| width: 100%; | |
| padding: 10px; | |
| background: #667eea; | |
| color: #fff; | |
| border: none; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| font-size: 1.1em; | |
| margin-top: 10px; | |
| } | |
| /* Chat Wrapper */ | |
| .chat-wrapper { | |
| flex: 1; | |
| background: #fff; | |
| margin: 20px; | |
| border-radius: 20px; | |
| box-shadow: 0 12px 40px rgba(0,0,0,0.15); | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden; | |
| position: relative; | |
| transition: width 0.3s ease; | |
| } | |
| /* Hamburger Button inside Chat Wrapper */ | |
| #hamburgerBtn { | |
| position: absolute; | |
| top: 20px; | |
| left: 20px; | |
| background: #667eea; | |
| color: #fff; | |
| border: none; | |
| padding: 10px; | |
| font-size: 1.4em; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| z-index: 20; | |
| } | |
| /* Turn Label */ | |
| #turnLabel { | |
| position: absolute; | |
| top: 20px; | |
| right: 20px; | |
| background: #ff9800; | |
| color: #fff; | |
| padding: 5px 10px; | |
| border-radius: 10px; | |
| font-size: 1em; | |
| z-index: 15; | |
| } | |
| /* Carousel Area */ | |
| .carousel-wrapper { | |
| position: relative; | |
| flex: 1; | |
| background: #F9FBFD; | |
| overflow: hidden; | |
| margin-top: 80px; | |
| } | |
| .carousel { | |
| display: flex; | |
| height: 100%; | |
| transition: transform 0.5s ease; | |
| } | |
| .card { | |
| min-width: 100%; | |
| height: 100%; | |
| padding: 90px 30px 30px 30px; | |
| display: flex; | |
| flex-direction: column; | |
| overflow-y: auto; | |
| } | |
| .conversation { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 15px; | |
| } | |
| /* Message Bubbles */ | |
| .message { | |
| padding: 10px 16px; | |
| border-radius: 16px; | |
| font-size: 1em; | |
| line-height: 1.5; | |
| max-width: 95%; | |
| position: relative; | |
| box-shadow: 0 2px 8px rgba(0,0,0,0.07); | |
| transition: background 0.3s, transform 0.3s; | |
| } | |
| .user { background: #F0F4FF; border: 1px solid #D9E2FF; align-self: flex-start; } | |
| .ai { background: #FFF4E6; border: 1px solid #FFE0B2; align-self: flex-end; } | |
| .message-text { | |
| display: block; | |
| max-height: 80px; | |
| overflow: hidden; | |
| transition: max-height 0.3s ease; | |
| } | |
| .message.expanded .message-text { max-height: none; } | |
| .toggle-btn { | |
| background: none; | |
| border: none; | |
| color: #667eea; | |
| cursor: pointer; | |
| font-size: 0.85em; | |
| margin-top: 4px; | |
| padding: 0; | |
| } | |
| .vertical-file-list { | |
| margin-bottom: 10px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 5px; | |
| } | |
| .file-item-vertical { | |
| background: #eaeaea; | |
| padding: 5px 8px; | |
| border-radius: 4px; | |
| font-size: 0.9em; | |
| } | |
| /* Navigation Buttons */ | |
| .nav { | |
| position: absolute; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| width: 50px; | |
| height: 50px; | |
| background: rgba(255,255,255,0.5); | |
| backdrop-filter: blur(4px); | |
| border: none; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| z-index: 10; | |
| transition: transform 0.3s, background 0.3s; | |
| } | |
| .nav:hover { transform: translateY(-50%) scale(1.1); background: rgba(255,255,255,0.8); } | |
| .nav:disabled { opacity: 0.5; cursor: default; } | |
| #prevBtn { left: 15px; } | |
| #nextBtn { right: 15px; } | |
| /* Input Section */ | |
| .input-container { | |
| padding: 20px; | |
| background: #FAFAFA; | |
| border-top: 1px solid #EEE; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .file-attachments { | |
| display: flex; | |
| gap: 10px; | |
| overflow-x: auto; | |
| padding-bottom: 10px; | |
| scrollbar-width: thin; | |
| } | |
| .file-item { | |
| flex: 0 0 auto; | |
| background: #eee; | |
| padding: 5px 10px; | |
| border-radius: 5px; | |
| display: flex; | |
| align-items: center; | |
| gap: 5px; | |
| font-size: 0.9em; | |
| } | |
| .file-item button { | |
| background: none; | |
| border: none; | |
| color: #667eea; | |
| cursor: pointer; | |
| font-size: 1em; | |
| } | |
| .input-row { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .attach-button { | |
| background: #fff; | |
| border: 1px solid #DDD; | |
| border-radius: 4px; | |
| padding: 8px 12px; | |
| cursor: pointer; | |
| transition: background 0.3s; | |
| } | |
| .attach-button:hover { background: #f0f0f0; } | |
| .input-container textarea { | |
| flex: 1; | |
| padding: 12px 15px; | |
| font-size: 1em; | |
| border: 1px solid #DDD; | |
| border-radius: 8px; | |
| outline: none; | |
| resize: none; | |
| overflow-y: auto; | |
| min-height: 36px; | |
| max-height: 150px; | |
| line-height: 1.4em; | |
| white-space: pre-wrap; | |
| transition: border-color 0.3s; | |
| } | |
| .input-container textarea:focus { border-color: #667eea; } | |
| .input-container button { | |
| padding: 12px 20px; | |
| font-size: 1em; | |
| background: #667eea; | |
| color: #fff; | |
| border: none; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| transition: background 0.3s; | |
| } | |
| .input-container button:hover { background: #556cd6; } | |
| #fileInput { display: none; } | |
| /* Summary Overlay Panel (Per‑Session, rendered markdown) */ | |
| #summaryOverlay { | |
| position: fixed; | |
| top: 0; | |
| right: 0; | |
| bottom: 0; | |
| width: 60%; | |
| background: #fff; | |
| box-shadow: -4px 0 12px rgba(0,0,0,0.15); | |
| transform: translateX(100%); | |
| transition: transform 0.3s ease; | |
| z-index: 20; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| #summaryOverlay.active { transform: translateX(0); } | |
| .summary-header { | |
| padding: 16px; | |
| background: #667eea; | |
| color: #fff; | |
| font-size: 1.2em; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .summary-header-buttons { | |
| display: flex; | |
| gap: 10px; | |
| } | |
| .download-summary { | |
| background: #fff; | |
| color: #667eea; | |
| border: 1px solid #667eea; | |
| border-radius: 4px; | |
| padding: 4px 8px; | |
| cursor: pointer; | |
| } | |
| .download-summary:hover { background: #667eea; color: #fff; } | |
| .close-summary { | |
| background: none; | |
| border: none; | |
| color: #fff; | |
| font-size: 1.2em; | |
| cursor: pointer; | |
| } | |
| .summary-content { | |
| padding: 16px; | |
| overflow-y: auto; | |
| flex: 1; | |
| } | |
| /* Settings Overlay Panel (Per‑Session) */ | |
| #settingsOverlay { | |
| position: fixed; | |
| top: 0; | |
| right: 0; | |
| bottom: 0; | |
| width: 40%; | |
| background: #fff; | |
| box-shadow: -4px 0 12px rgba(0,0,0,0.15); | |
| transform: translateX(100%); | |
| transition: transform 0.3s ease; | |
| z-index: 20; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| #settingsOverlay.active { transform: translateX(0); } | |
| .settings-header { | |
| padding: 16px; | |
| background: #667eea; | |
| color: #fff; | |
| font-size: 1.4em; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .close-settings { | |
| background: none; | |
| border: none; | |
| color: #fff; | |
| font-size: 1.4em; | |
| cursor: pointer; | |
| } | |
| .settings-content { | |
| padding: 16px; | |
| overflow-y: auto; | |
| flex: 1; | |
| font-size: 1.1em; | |
| line-height: 1.4; | |
| } | |
| .settings-group { | |
| margin-bottom: 16px; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .settings-group label { | |
| min-width: 100px; | |
| } | |
| .save-settings { | |
| background: #667eea; | |
| color: #fff; | |
| border: none; | |
| border-radius: 8px; | |
| padding: 10px 20px; | |
| cursor: pointer; | |
| } | |
| .save-settings:hover { background: #556cd6; } | |
| /* Responsive */ | |
| @media (max-width: 600px) { | |
| .nav-bar { display: none; } | |
| .chat-wrapper { margin: 0; } | |
| .message { font-size: 0.95em; max-width: 80%; } | |
| .nav { width: 40px; height: 40px; font-size: 1.5em; } | |
| .card { padding: 90px 20px 20px 20px; } | |
| .input-container { padding: 15px; } | |
| .input-row { flex-direction: column; gap: 10px; } | |
| .input-container button { width: 100%; } | |
| #summaryOverlay, #settingsOverlay { width: 80%; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="app-container"> | |
| <!-- Left Nav Bar for Chat History & Session Management --> | |
| <div class="nav-bar" id="navBar"> | |
| <h3>Chat History</h3> | |
| <ul id="sessionList"></ul> | |
| <button class="new-session-btn" id="newSessionBtn">New Chat</button> | |
| </div> | |
| <!-- Main Chat Wrapper --> | |
| <div class="chat-wrapper"> | |
| <!-- Hamburger Button (placed inside chat area) --> | |
| <button id="hamburgerBtn">☰</button> | |
| <!-- Turn Label --> | |
| <div id="turnLabel">Turn: 0/0</div> | |
| <div class="carousel-wrapper"> | |
| <button id="prevBtn" class="nav"> | |
| <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <polyline points="15 18 9 12 15 6"></polyline> | |
| </svg> | |
| </button> | |
| <div class="carousel" id="carousel"> | |
| <!-- Conversation cards will be dynamically rendered --> | |
| </div> | |
| <button id="nextBtn" class="nav"> | |
| <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <polyline points="9 18 15 12 9 6"></polyline> | |
| </svg> | |
| </button> | |
| </div> | |
| <!-- Input Section --> | |
| <div class="input-container"> | |
| <div id="fileAttachments" class="file-attachments"></div> | |
| <div class="input-row"> | |
| <button id="attachBtn" class="attach-button">Attach File</button> | |
| <input type="file" id="fileInput" multiple accept="image/*,.pdf"> | |
| <textarea id="chatInput" placeholder="Type your message..."></textarea> | |
| <button id="sendBtn">Send</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Summary Overlay Panel (Per‑Session, rendered markdown) --> | |
| <div id="summaryOverlay"> | |
| <div class="summary-header"> | |
| <span>Chat Summary</span> | |
| <div class="summary-header-buttons"> | |
| <button id="downloadSummary" class="download-summary">Download</button> | |
| <button class="close-summary" id="closeSummaryBtn">×</button> | |
| </div> | |
| </div> | |
| <div class="summary-content" id="summaryContent"> | |
| <!-- Rendered markdown will appear here --> | |
| </div> | |
| </div> | |
| <!-- Settings Overlay Panel (Per‑Session) --> | |
| <div id="settingsOverlay"> | |
| <div class="settings-header"> | |
| <span>Settings</span> | |
| <button class="close-settings" id="closeSettingsBtn">×</button> | |
| </div> | |
| <div class="settings-content"> | |
| <form id="settingsForm"> | |
| <div class="settings-group"> | |
| <label for="temperature">Temperature:</label> | |
| <input type="range" id="temperature" name="temperature" min="0" max="1" step="0.01"> | |
| <span id="temperatureValue"></span> | |
| </div> | |
| <div class="settings-group"> | |
| <label for="maxTokens">Max Tokens:</label> | |
| <input type="number" id="maxTokens" name="maxTokens" min="10" max="2048"> | |
| </div> | |
| <div class="settings-group"> | |
| <label for="persona">Persona:</label> | |
| <select id="persona" name="persona"> | |
| <option value="professional">Professional</option> | |
| <option value="friendly">Friendly</option> | |
| </select> | |
| </div> | |
| <button type="button" id="saveSettings" class="save-settings">Save Settings</button> | |
| </form> | |
| </div> | |
| </div> | |
| <script> | |
| // ----------------- Session Management ----------------- | |
| let sessions = []; | |
| let currentSessionIndex = 0; | |
| let currentCardIndex = 0; | |
| // Initialize with a default session | |
| function initSessions() { | |
| sessions.push({ | |
| id: Date.now(), | |
| name: "Chat Session 1", | |
| messages: [], | |
| summary: "# Chat Summary\n\nThis is the default summary for Chat Session 1.", | |
| settings: { temperature: 0.7, maxTokens: 256, persona: "professional" } | |
| }); | |
| currentSessionIndex = 0; | |
| currentCardIndex = 0; | |
| renderSessionList(); | |
| renderCurrentSession(); | |
| } | |
| // Render the left nav bar session list with icons | |
| function renderSessionList() { | |
| const sessionList = document.getElementById('sessionList'); | |
| sessionList.innerHTML = ""; | |
| sessions.forEach((session, index) => { | |
| const li = document.createElement('li'); | |
| // Session name | |
| const nameSpan = document.createElement('span'); | |
| nameSpan.textContent = session.name; | |
| li.appendChild(nameSpan); | |
| // Action icons container | |
| const actionsDiv = document.createElement('div'); | |
| actionsDiv.className = "session-actions"; | |
| const summaryBtn = document.createElement('button'); | |
| summaryBtn.className = "session-summary-btn"; | |
| summaryBtn.innerHTML = "📄"; | |
| summaryBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| currentSessionIndex = index; | |
| renderSessionList(); | |
| renderCurrentSession(); | |
| // Open summary overlay for this session | |
| document.getElementById('summaryContent').innerHTML = marked.parse(sessions[currentSessionIndex].summary); | |
| summaryOverlay.classList.add('active'); | |
| settingsOverlay.classList.remove('active'); | |
| }); | |
| actionsDiv.appendChild(summaryBtn); | |
| const settingsBtn = document.createElement('button'); | |
| settingsBtn.className = "session-settings-btn"; | |
| settingsBtn.innerHTML = "⚙"; | |
| settingsBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| currentSessionIndex = index; | |
| renderSessionList(); | |
| renderCurrentSession(); | |
| // Open settings overlay for this session | |
| const settings = sessions[currentSessionIndex].settings; | |
| temperatureInput.value = settings.temperature; | |
| temperatureValue.textContent = settings.temperature; | |
| maxTokensInput.value = settings.maxTokens; | |
| personaSelect.value = settings.persona; | |
| settingsOverlay.classList.add('active'); | |
| summaryOverlay.classList.remove('active'); | |
| }); | |
| actionsDiv.appendChild(settingsBtn); | |
| li.appendChild(actionsDiv); | |
| // Remove session button | |
| const removeBtn = document.createElement('button'); | |
| removeBtn.textContent = "×"; | |
| removeBtn.className = "remove-session"; | |
| removeBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| removeSession(index); | |
| }); | |
| li.appendChild(removeBtn); | |
| li.addEventListener('click', () => { | |
| currentSessionIndex = index; | |
| currentCardIndex = 0; | |
| renderSessionList(); | |
| renderCurrentSession(); | |
| }); | |
| if (index === currentSessionIndex) li.classList.add('active'); | |
| sessionList.appendChild(li); | |
| }); | |
| } | |
| // Create a new session | |
| document.getElementById('newSessionBtn').addEventListener('click', () => { | |
| const newSession = { | |
| id: Date.now(), | |
| name: "Chat Session " + (sessions.length + 1), | |
| messages: [], | |
| summary: "# Chat Summary\n\nThis is the default summary for Chat Session " + (sessions.length + 1) + ".", | |
| settings: { temperature: 0.7, maxTokens: 256, persona: "professional" } | |
| }; | |
| sessions.push(newSession); | |
| currentSessionIndex = sessions.length - 1; | |
| currentCardIndex = 0; | |
| renderSessionList(); | |
| renderCurrentSession(); | |
| }); | |
| // Remove a session | |
| function removeSession(index) { | |
| sessions.splice(index, 1); | |
| if (sessions.length === 0) { | |
| initSessions(); | |
| } else { | |
| if (currentSessionIndex >= sessions.length) { | |
| currentSessionIndex = sessions.length - 1; | |
| } | |
| currentCardIndex = 0; | |
| } | |
| renderSessionList(); | |
| renderCurrentSession(); | |
| } | |
| // ----------------- Carousel Rendering ----------------- | |
| const carousel = document.getElementById('carousel'); | |
| function renderCurrentSession() { | |
| const session = sessions[currentSessionIndex]; | |
| carousel.innerHTML = ""; | |
| session.messages.forEach(message => { | |
| const card = document.createElement('div'); | |
| card.className = 'card'; | |
| let attachmentHTML = ""; | |
| if (message.attachments && message.attachments.length > 0) { | |
| attachmentHTML = `<div class="vertical-file-list">` + | |
| message.attachments.map(name => `<div class="file-item-vertical">${name}</div>`).join("") + | |
| `</div>`; | |
| } | |
| card.innerHTML = ` | |
| <div class="conversation"> | |
| <div class="message user"> | |
| ${attachmentHTML} | |
| <span class="message-text">User: ${message.userText}</span> | |
| </div> | |
| <div class="message ai"> | |
| <span class="message-text">AI: ${message.aiResponse}</span> | |
| </div> | |
| </div> | |
| `; | |
| carousel.appendChild(card); | |
| processMessagesInContainer(card); | |
| }); | |
| currentCardIndex = session.messages.length > 0 ? session.messages.length - 1 : 0; | |
| updateCarousel(); | |
| } | |
| function updateCarousel() { | |
| const cards = document.querySelectorAll('.card'); | |
| carousel.style.transform = `translateX(-${currentCardIndex * 100}%)`; | |
| prevBtn.disabled = currentCardIndex === 0; | |
| nextBtn.disabled = currentCardIndex === cards.length - 1 || cards.length === 0; | |
| updateTurnLabel(cards.length); | |
| } | |
| function updateTurnLabel(totalCards) { | |
| const turnLabel = document.getElementById('turnLabel'); | |
| turnLabel.textContent = `Turn: ${totalCards ? currentCardIndex + 1 + "/" + totalCards : "0/0"}`; | |
| } | |
| // ----------------- Message Processing ----------------- | |
| function processMessage(messageEl) { | |
| if (messageEl.dataset.processed) return; | |
| const textEl = messageEl.querySelector('.message-text'); | |
| if (textEl.scrollHeight > 80) { | |
| const toggleBtn = document.createElement('button'); | |
| toggleBtn.className = 'toggle-btn'; | |
| toggleBtn.textContent = 'Read more'; | |
| toggleBtn.addEventListener('click', function() { | |
| if (messageEl.classList.contains('expanded')) { | |
| messageEl.classList.remove('expanded'); | |
| toggleBtn.textContent = 'Read more'; | |
| } else { | |
| messageEl.classList.add('expanded'); | |
| toggleBtn.textContent = 'Read less'; | |
| } | |
| }); | |
| messageEl.appendChild(toggleBtn); | |
| } | |
| messageEl.dataset.processed = 'true'; | |
| } | |
| function processMessagesInContainer(container) { | |
| container.querySelectorAll('.message').forEach(processMessage); | |
| } | |
| // ----------------- Adding Conversation ----------------- | |
| const attachedFiles = []; // For demo, store file objects (only names) | |
| function addConversation(userText, aiResponse) { | |
| const attachmentNames = attachedFiles.map(file => file.name); | |
| const message = { | |
| userText, | |
| aiResponse, | |
| attachments: attachmentNames | |
| }; | |
| sessions[currentSessionIndex].messages.push(message); | |
| clearFileAttachments(); | |
| renderCurrentSession(); | |
| } | |
| function clearFileAttachments() { | |
| attachedFiles.length = 0; | |
| updateFileAttachments(); | |
| } | |
| // ----------------- Event Listeners ----------------- | |
| const sendBtn = document.getElementById('sendBtn'); | |
| const chatInput = document.getElementById('chatInput'); | |
| sendBtn.addEventListener('click', () => { | |
| const text = chatInput.value; | |
| if (text.trim() !== '') { | |
| const aiResponse = "I'm here to help!"; | |
| addConversation(text, aiResponse); | |
| chatInput.value = ''; | |
| chatInput.style.height = "36px"; | |
| } | |
| }); | |
| chatInput.addEventListener('keydown', function(e) { | |
| if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { | |
| e.preventDefault(); | |
| sendBtn.click(); | |
| } | |
| }); | |
| const prevBtn = document.getElementById('prevBtn'); | |
| const nextBtn = document.getElementById('nextBtn'); | |
| prevBtn.addEventListener('click', () => { | |
| if (currentCardIndex > 0) { currentCardIndex--; updateCarousel(); } | |
| }); | |
| nextBtn.addEventListener('click', () => { | |
| const cards = document.querySelectorAll('.card'); | |
| if (currentCardIndex < cards.length - 1) { currentCardIndex++; updateCarousel(); } | |
| }); | |
| // ----------------- File Attachment Handling ----------------- | |
| const attachBtn = document.getElementById('attachBtn'); | |
| const fileInput = document.getElementById('fileInput'); | |
| const fileAttachments = document.getElementById('fileAttachments'); | |
| attachBtn.addEventListener('click', () => { fileInput.click(); }); | |
| fileInput.addEventListener('change', () => { | |
| for (const file of fileInput.files) { attachedFiles.push(file); } | |
| fileInput.value = ""; | |
| updateFileAttachments(); | |
| }); | |
| function updateFileAttachments() { | |
| fileAttachments.innerHTML = ""; | |
| attachedFiles.forEach((file, index) => { | |
| const fileDiv = document.createElement("div"); | |
| fileDiv.className = "file-item"; | |
| fileDiv.innerHTML = `<span>${file.name}</span> <button data-index="${index}">×</button>`; | |
| fileAttachments.appendChild(fileDiv); | |
| }); | |
| document.querySelectorAll(".file-item button").forEach(btn => { | |
| btn.addEventListener("click", (e) => { | |
| const idx = e.target.getAttribute("data-index"); | |
| attachedFiles.splice(idx, 1); | |
| updateFileAttachments(); | |
| }); | |
| }); | |
| } | |
| // ----------------- Summary Overlay (Per‑Session) ----------------- | |
| const summaryOverlay = document.getElementById('summaryOverlay'); | |
| const closeSummaryBtn = document.getElementById('closeSummaryBtn'); | |
| const summaryContent = document.getElementById('summaryContent'); | |
| const downloadSummaryBtn = document.getElementById('downloadSummary'); | |
| closeSummaryBtn.addEventListener('click', () => { summaryOverlay.classList.remove('active'); }); | |
| downloadSummaryBtn.addEventListener('click', () => { | |
| const blob = new Blob([sessions[currentSessionIndex].summary], { type: "text/markdown" }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement("a"); | |
| a.href = url; | |
| a.download = "summary.md"; | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| }); | |
| // ----------------- Settings Overlay (Per‑Session) ----------------- | |
| const settingsOverlay = document.getElementById('settingsOverlay'); | |
| const closeSettingsBtn = document.getElementById('closeSettingsBtn'); | |
| const temperatureInput = document.getElementById('temperature'); | |
| const temperatureValue = document.getElementById('temperatureValue'); | |
| const maxTokensInput = document.getElementById('maxTokens'); | |
| const personaSelect = document.getElementById('persona'); | |
| const saveSettingsBtn = document.getElementById('saveSettings'); | |
| closeSettingsBtn.addEventListener('click', () => { settingsOverlay.classList.remove('active'); }); | |
| temperatureInput.addEventListener('input', () => { | |
| temperatureValue.textContent = temperatureInput.value; | |
| }); | |
| saveSettingsBtn.addEventListener('click', () => { | |
| sessions[currentSessionIndex].settings = { | |
| temperature: parseFloat(temperatureInput.value), | |
| maxTokens: parseInt(maxTokensInput.value), | |
| persona: personaSelect.value | |
| }; | |
| console.log('Session settings saved:', sessions[currentSessionIndex].settings); | |
| settingsOverlay.classList.remove('active'); | |
| }); | |
| // ----------------- Auto-Dismiss Overlays on Outside Click ----------------- | |
| document.addEventListener('click', (e) => { | |
| if (summaryOverlay.classList.contains('active') && | |
| !summaryOverlay.contains(e.target)) { | |
| summaryOverlay.classList.remove('active'); | |
| } | |
| if (settingsOverlay.classList.contains('active') && | |
| !settingsOverlay.contains(e.target)) { | |
| settingsOverlay.classList.remove('active'); | |
| } | |
| }); | |
| // ----------------- Global Keyboard Navigation ----------------- | |
| document.addEventListener('keydown', (e) => { | |
| if (document.activeElement !== chatInput) { | |
| if (e.key === 'ArrowLeft' && currentCardIndex > 0) { | |
| currentCardIndex--; | |
| updateCarousel(); | |
| } else if (e.key === 'ArrowRight') { | |
| const cards = document.querySelectorAll('.card'); | |
| if (currentCardIndex < cards.length - 1) { | |
| currentCardIndex++; | |
| updateCarousel(); | |
| } | |
| } | |
| } | |
| }); | |
| // ----------------- Hamburger Button (Toggle Nav Bar) ----------------- | |
| const hamburgerBtn = document.getElementById('hamburgerBtn'); | |
| const navBar = document.getElementById('navBar'); | |
| hamburgerBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| if (navBar.classList.contains('hidden')) { | |
| navBar.classList.remove('hidden'); | |
| } else { | |
| navBar.classList.add('hidden'); | |
| } | |
| }); | |
| // ----------------- Initialization ----------------- | |
| initSessions(); | |
| </script> | |
| </body> | |
| </html> | |