BeRU Deployer
Deploy BeRU Streamlit RAG System - Add app, models logic, configs, and optimizations for HF Spaces
dec533d | <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>BeRU Chat - Multimodal</title> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> | |
| <style> | |
| /* *** EXISTING STYLES (keeping all your original styles) *** */ | |
| body { | |
| font-family: 'Roboto', sans-serif; | |
| margin: 0; | |
| padding: 0; | |
| transition: background-color 0.5s ease, color 0.5s ease; | |
| } | |
| .light-mode { | |
| background-color: #caf2fa; | |
| color: #333; | |
| } | |
| .dark-mode { | |
| background-color: #1e1e1e; | |
| color: #f5f5f5; | |
| } | |
| .dark-mode .chat-container { | |
| background-color: #1e1e1e; | |
| color: #f5f5f5; | |
| } | |
| .sidebar { | |
| width: 300px; | |
| height: 100vh; | |
| background-color: #0d131a; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| z-index: 1; | |
| overflow-x: hidden; | |
| transition: width 0.3s, background-color 0.5s ease; | |
| } | |
| .light-mode .sidebar { | |
| background-color: #01414e; | |
| } | |
| .sidebar.collapsed { | |
| width: 50px; | |
| } | |
| .tooltip { | |
| position: absolute; | |
| top: 0; | |
| right: -20px; | |
| } | |
| .tooltip .tooltiptext { | |
| visibility: hidden; | |
| width: 120px; | |
| background-color: rgb(0, 0, 0); | |
| color: #fff; | |
| text-align: center; | |
| border-radius: 6px; | |
| padding: 5px 0; | |
| position: absolute; | |
| z-index: 1; | |
| bottom: 125%; | |
| left: 50%; | |
| margin-left: -60px; | |
| opacity: 0; | |
| transition: opacity 0.3s; | |
| } | |
| #sidebar-toggle { | |
| background-color: transparent; | |
| margin: -22%; | |
| align-items: center; | |
| margin-top: 600%; | |
| } | |
| .tooltip .tooltiptext::after { | |
| content: ""; | |
| position: absolute; | |
| top: 100%; | |
| left: 50%; | |
| border-width: 5px; | |
| border-style: solid; | |
| border-color: black transparent transparent transparent; | |
| } | |
| .tooltip:hover .tooltiptext { | |
| visibility: visible; | |
| opacity: 1; | |
| } | |
| .sidebar-content { | |
| padding-top: 20px; | |
| transition: opacity 0.3s; | |
| } | |
| .sidebar.collapsed .sidebar-content { | |
| opacity: 0; | |
| pointer-events: none; | |
| } | |
| .conversation-list { | |
| padding: 0 20px; | |
| } | |
| .conversation { | |
| margin-bottom: 10px; | |
| } | |
| .conversation-text { | |
| font-weight: bold; | |
| color: #fff; | |
| transition: color 0.5s ease; | |
| } | |
| .light-mode .conversation-text { | |
| color: #ccc; | |
| } | |
| .conversation-content { | |
| color: #ddd; | |
| transition: color 0.5s ease; | |
| } | |
| .light-mode .conversation-content { | |
| color: #888; | |
| } | |
| #new-conversation-btn { | |
| background-color: #3a3b3b; | |
| color: #fff; | |
| border: none; | |
| padding: 10px 20px; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| transition: background-color 0.3s, color 0.5s ease; | |
| } | |
| #new-conversation-btn:hover { | |
| background-color: #242020; | |
| } | |
| .light-mode #new-conversation-btn { | |
| background-color: #c9c9c9; | |
| color: #171717; | |
| } | |
| .light-mode #new-conversation-btn:hover { | |
| background-color: #e0e0e0; | |
| color: #171717; | |
| } | |
| .chat-container { | |
| width: calc(100% - 300px); | |
| margin-left: 300px; | |
| height: 100vh; | |
| overflow: hidden; | |
| transition: all 0.3s ease-in-out, background-color 0.5s ease; | |
| } | |
| .sidebar.collapsed ~ .chat-container { | |
| width: calc(100% - 50px); | |
| margin-left: 50px; | |
| } | |
| .chat-content { | |
| display: flex; | |
| flex-direction: column; | |
| height: 100%; | |
| padding-bottom: 80px; | |
| } | |
| .logo-container { | |
| display: flex; | |
| align-items: center; | |
| } | |
| .logo { | |
| width: 30px; | |
| height: 30px; | |
| margin-right: 10px; | |
| } | |
| .chat-header { | |
| margin-left: 2%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| font-size: 10px; | |
| height: 60px; | |
| background-color: #171717; | |
| transition: background-color 0.5s ease, color 0.5s ease; | |
| } | |
| .light-mode h1 { | |
| color: black; | |
| } | |
| .dark-mode h1 { | |
| color: #f5f5f5; | |
| } | |
| .light-mode .chat-header { | |
| background-color: #caf2fa; | |
| color: #333; | |
| } | |
| .dark-mode .chat-header { | |
| background-color: #1e1e1e; | |
| color: #f5f5f5; | |
| } | |
| h1 { | |
| color: #cfcfcf; | |
| font-family: 'Trebuchet MS', sans-serif; | |
| transition: color 0.5s ease; | |
| } | |
| /* Toggle Switch Styles */ | |
| .toggle-switch { | |
| position: relative; | |
| width: 50px; | |
| height: 25px; | |
| margin-right: 20px; | |
| --light: #d8dbe0; | |
| --dark: #28292c; | |
| } | |
| .switch-label { | |
| position: absolute; | |
| width: 100%; | |
| height: 100%; | |
| background-color: var(--dark); | |
| border-radius: 12px; | |
| cursor: pointer; | |
| border: 1.5px solid var(--dark); | |
| transition: background-color 0.3s; | |
| } | |
| .checkbox { | |
| position: absolute; | |
| display: none; | |
| } | |
| .slider { | |
| position: absolute; | |
| width: 100%; | |
| height: 100%; | |
| border-radius: 12px; | |
| transition: 0.3s; | |
| } | |
| .checkbox:checked ~ .slider { | |
| background-color: var(--light); | |
| } | |
| .slider::before { | |
| content: ""; | |
| position: absolute; | |
| top: 5.5px; | |
| left: 5.5px; | |
| width: 14px; | |
| height: 14px; | |
| border-radius: 50%; | |
| box-shadow: inset 7px -2px 0px 0px var(--light); | |
| background-color: var(--dark); | |
| transition: 0.3s; | |
| } | |
| .checkbox:checked ~ .slider::before { | |
| transform: translateX(26px); | |
| background-color: var(--dark); | |
| box-shadow: none; | |
| } | |
| .chat-box { | |
| display: flex; | |
| flex-direction: column; | |
| flex: 1; | |
| overflow-y: auto; | |
| padding: 15px; | |
| overflow-x: hidden; | |
| } | |
| .chat-box::-webkit-scrollbar { | |
| width: 3px; | |
| } | |
| .chat-box::-webkit-scrollbar-track { | |
| background: transparent; | |
| } | |
| .chat-box::-webkit-scrollbar-track-piece { | |
| background: #b0b0b000; | |
| border-radius: 999px; | |
| } | |
| .chat-box::-webkit-scrollbar-thumb { | |
| background-color: #ffd700; | |
| border-radius: 999px; | |
| border: 2px solid transparent; | |
| background-clip: padding-box; | |
| } | |
| .chat-box { | |
| scrollbar-width: thin; | |
| scrollbar-color: #ffd700 #b0b0b0; | |
| } | |
| .chat-box p { | |
| margin: 10px 0; | |
| font-size: 16px; | |
| } | |
| .messageBox { | |
| position: fixed; | |
| bottom: 20px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| display: flex; | |
| align-items: center; | |
| background-color: #2d2d2d; | |
| padding: 0 12px; | |
| border-radius: 10px; | |
| border: 1px solid rgb(63, 63, 63); | |
| width: 60%; | |
| max-width: 800px; | |
| height: 50px; | |
| transition: all 0.3s ease-in-out, background-color 0.5s ease, border-color 0.5s ease; | |
| } | |
| .light-mode .messageBox { | |
| background-color: white; | |
| border: 1px solid #1d495f; | |
| } | |
| .messageBox:focus-within { | |
| border: 1px solid rgb(110, 110, 110); | |
| } | |
| #messageInput { | |
| flex: 1; | |
| height: 100%; | |
| background-color: transparent; | |
| outline: none; | |
| border: none; | |
| padding: 0 12px; | |
| color: white; | |
| width: auto; | |
| font-family: 'Roboto', sans-serif; | |
| font-size: 14px; | |
| transition: color 0.5s ease; | |
| } | |
| .light-mode #messageInput { | |
| color: #171717; | |
| } | |
| #sendButton { | |
| width: 50px; | |
| height: 100%; | |
| background-color: transparent; | |
| outline: none; | |
| border: none; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| padding: 0; | |
| } | |
| #sendButton svg { | |
| height: 60%; | |
| width: auto; | |
| transition: all 0.3s; | |
| } | |
| #sendButton svg path { | |
| transition: all 0.3s; | |
| } | |
| #sendButton:hover svg path, | |
| #sendButton:active svg path { | |
| fill: #3c3c3c; | |
| stroke: white; | |
| } | |
| .light-mode #sendButton:hover svg path, | |
| .light-mode #sendButton:active svg path { | |
| fill: #fbf7e7; | |
| stroke: #ffcd07 ; | |
| } | |
| .message-row { | |
| width: 100%; | |
| margin: 8px 0; | |
| display: flex; | |
| } | |
| .message-user { | |
| justify-content: flex-end; | |
| } | |
| .message-bot { | |
| justify-content: flex-start; | |
| } | |
| .message-bubble { | |
| max-width: 70%; | |
| padding: 10px 14px; | |
| border-radius: 16px; | |
| font-size: 14px; | |
| transition: background-color 0.5s ease, color 0.5s ease; | |
| } | |
| .message-user .message-bubble { | |
| background-color: #1b798e; | |
| color: white; | |
| border-bottom-right-radius: 4px; | |
| border: 1px solid #FFD700; | |
| } | |
| .message-bot .message-bubble { | |
| background-color: #2d2d2d; | |
| color: white; | |
| border-bottom-left-radius: 4px; | |
| border: 1px solid #FFD700; | |
| } | |
| .light-mode .message-bot .message-bubble { | |
| background-color: #fefdf6; | |
| color: #111; | |
| } | |
| /* ✅ NEW: Image Gallery Styles */ | |
| .image-gallery { | |
| display: flex; | |
| gap: 10px; | |
| flex-wrap: wrap; | |
| margin-top: 12px; | |
| padding-top: 12px; | |
| border-top: 1px solid rgba(255, 215, 0, 0.3); | |
| } | |
| .image-container { | |
| position: relative; | |
| border-radius: 8px; | |
| overflow: hidden; | |
| cursor: pointer; | |
| transition: transform 0.2s ease; | |
| border: 2px solid #FFD700; | |
| } | |
| .image-container:hover { | |
| transform: scale(1.05); | |
| } | |
| .image-container img { | |
| width: 150px; | |
| height: 150px; | |
| object-fit: cover; | |
| display: block; | |
| } | |
| .image-caption { | |
| position: absolute; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| background: rgba(0, 0, 0, 0.7); | |
| color: #FFD700; | |
| padding: 4px 8px; | |
| font-size: 10px; | |
| text-align: center; | |
| } | |
| /* ✅ NEW: Image Modal/Lightbox */ | |
| .image-modal { | |
| display: none; | |
| position: fixed; | |
| z-index: 9999; | |
| left: 0; | |
| top: 0; | |
| width: 100%; | |
| height: 100%; | |
| background-color: rgba(0, 0, 0, 0.9); | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .image-modal.active { | |
| display: flex; | |
| } | |
| .modal-content { | |
| max-width: 90%; | |
| max-height: 90%; | |
| border-radius: 8px; | |
| box-shadow: 0 4px 20px rgba(255, 215, 0, 0.5); | |
| } | |
| .modal-close { | |
| position: absolute; | |
| top: 20px; | |
| right: 35px; | |
| color: #FFD700; | |
| font-size: 40px; | |
| font-weight: bold; | |
| cursor: pointer; | |
| transition: color 0.3s; | |
| } | |
| .modal-close:hover { | |
| color: #fff; | |
| } | |
| .fileUploadWrapper { | |
| position: relative; | |
| display: flex; | |
| align-items: center; | |
| margin-right: 8px; | |
| } | |
| .fileUploadWrapper label { | |
| display: flex; | |
| align-items: center; | |
| cursor: pointer; | |
| padding: 0; | |
| margin: 0; | |
| } | |
| .fileUploadWrapper svg { | |
| width: 20px; | |
| height: 20px; | |
| fill: #6c6c6c; | |
| transition: all 0.3s ease; | |
| } | |
| .fileUploadWrapper label:hover svg { | |
| fill: #10a37f; | |
| } | |
| .fileUploadWrapper .tooltip { | |
| display: none; | |
| position: absolute; | |
| bottom: 125%; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| background-color: #000; | |
| color: #fff; | |
| padding: 5px 10px; | |
| border-radius: 6px; | |
| font-size: 12px; | |
| white-space: nowrap; | |
| z-index: 1; | |
| } | |
| .fileUploadWrapper label:hover .tooltip { | |
| display: block; | |
| } | |
| .fileUploadWrapper input { | |
| display: none; | |
| } | |
| .fileUploadWrapper, | |
| #sendButton { | |
| width: 40px; | |
| height: 40px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| margin: 0 8px; | |
| } | |
| .fileUploadWrapper svg, | |
| #sendButton svg { | |
| width: 20px; | |
| height: 20px; | |
| fill: #6c6c6c; | |
| transition: all 0.3s ease; | |
| } | |
| .fileUploadWrapper label:hover svg path, | |
| .fileUploadWrapper label:hover svg circle, | |
| #sendButton:hover svg path { | |
| stroke: white; | |
| transition: stroke 0.3s ease; | |
| } | |
| .light-mode .fileUploadWrapper label:hover svg path, | |
| .light-mode .fileUploadWrapper label:hover svg circle, | |
| .light-mode #sendButton:hover svg path { | |
| stroke: #ffcd07; | |
| transition: stroke 0.3s ease; | |
| } | |
| #sendButton svg, | |
| .fileUploadWrapper svg { | |
| transition: all 0.3s ease; | |
| } | |
| /* Mode Dropdown Styles */ | |
| .mode-dropdown-wrapper { | |
| position: relative; | |
| display: flex; | |
| align-items: center; | |
| margin: 0 8px; | |
| } | |
| .mode-dropdown-button { | |
| display: inline-flex; | |
| justify-content: center; | |
| align-items: center; | |
| padding: 6px 12px; | |
| background-color: transparent; | |
| border: none; | |
| color: #e5e5e5; | |
| font-size: 14px; | |
| font-family: 'Roboto', sans-serif; | |
| font-weight: 500; | |
| cursor: pointer; | |
| border-radius: 6px; | |
| white-space: nowrap; | |
| transition: background-color 0.3s, color 0.5s ease; | |
| } | |
| .light-mode .mode-dropdown-button { | |
| background-color: #f9f9f9; | |
| color: #171717; | |
| } | |
| .mode-dropdown-button:hover { | |
| background-color: rgba(110, 110, 110, 0.12); | |
| } | |
| .light-mode .mode-dropdown-button:hover { | |
| background-color: #f0f0f0; | |
| } | |
| .dropdown-arrow { | |
| width: 16px; | |
| height: 16px; | |
| margin-left: 6px; | |
| transition: transform 0.2s ease, color 0.5s ease; | |
| } | |
| .mode-dropdown-button[aria-expanded="true"] .dropdown-arrow { | |
| transform: rotate(180deg); | |
| } | |
| .mode-dropdown-menu { | |
| position: absolute; | |
| bottom: 100%; | |
| right: 0; | |
| margin-bottom: 8px; | |
| min-width: 180px; | |
| background-color: #2d2d2d; | |
| border: 1px solid rgb(63, 63, 63); | |
| border-radius: 8px; | |
| box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.4); | |
| z-index: 1000; | |
| opacity: 0; | |
| transform: translateY(-8px); | |
| transition: opacity 0.2s ease, transform 0.2s ease, background-color 0.5s ease, border-color 0.5s ease; | |
| pointer-events: none; | |
| } | |
| .mode-dropdown-menu:not(.hidden) { | |
| opacity: 1; | |
| transform: translateY(0); | |
| pointer-events: auto; | |
| } | |
| .light-mode .mode-dropdown-menu { | |
| background-color: #ffffff; | |
| border-color: #1d495f; | |
| box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.15); | |
| } | |
| .dropdown-items { | |
| padding: 4px 0; | |
| } | |
| .dropdown-item { | |
| display: flex; | |
| align-items: center; | |
| padding: 10px 16px; | |
| color: #ddd; | |
| text-decoration: none; | |
| font-size: 14px; | |
| font-family: 'Roboto', sans-serif; | |
| cursor: pointer; | |
| transition: background-color 0.15s ease, color 0.5s ease; | |
| } | |
| .dropdown-item:first-child { | |
| border-top-left-radius: 8px; | |
| border-top-right-radius: 8px; | |
| } | |
| .dropdown-item:last-child { | |
| border-bottom-left-radius: 8px; | |
| border-bottom-right-radius: 8px; | |
| } | |
| .dropdown-item:hover { | |
| background-color: rgba(110, 110, 110, 0.25); | |
| color: #fff; | |
| } | |
| .light-mode .dropdown-item { | |
| color: #1f2937; | |
| } | |
| .light-mode .dropdown-item:hover { | |
| background-color: #f3f4f6; | |
| color: #1e40af; | |
| } | |
| .dropdown-item .font-semibold { | |
| font-weight: 600; | |
| } | |
| .hidden { | |
| display: none; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="sidebar collapsed"> | |
| <div class="tooltip"> | |
| <span class="tooltiptext">Open Sidebar</span> | |
| <button id="sidebar-toggle"> | |
| <i class="fas fa-chevron-right"></i> | |
| </button> | |
| </div> | |
| <div class="sidebar-content"> | |
| <div class="conversation-list"> | |
| <div class="conversation"> | |
| <p class="conversation-text">Last Conversation:</p> | |
| <p class="conversation-content">No conversation yet</p> | |
| </div> | |
| </div> | |
| <button id="new-conversation-btn">Start New Conversation</button> | |
| </div> | |
| </div> | |
| <div class="chat-container light-mode"> | |
| <div class="chat-content"> | |
| <div class="chat-header"> | |
| <div class="logo-container"> | |
| <h1>BeRU </h1> | |
| </div> | |
| <div class="toggle-switch"> | |
| <label class="switch-label"> | |
| <input type="checkbox" id="toggle-checkbox" class="checkbox"> | |
| <span class="slider"></span> | |
| </label> | |
| </div> | |
| </div> | |
| <div id="chat-box" class="chat-box"></div> | |
| <div class="messageBox"> | |
| <div class="fileUploadWrapper"> | |
| <label for="file"> | |
| <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 337 337"> | |
| <circle stroke-width="20" stroke="#6c6c6c" fill="none" r="158.5" cy="168.5" cx="168.5"></circle> | |
| <path stroke-linecap="round" stroke-width="25" stroke="#6c6c6c" d="M167.759 79V259"></path> | |
| <path stroke-linecap="round" stroke-width="25" stroke="#6c6c6c" d="M79 167.138H259"></path> | |
| </svg> | |
| <span class="tooltip">Add an image</span> | |
| </label> | |
| <input type="file" id="file" name="file" /> | |
| </div> | |
| <input required="" placeholder="Message..." type="text" id="messageInput" /> | |
| <div class="mode-dropdown-wrapper"> | |
| <button id="modeDropdownButton" type="button" class="mode-dropdown-button" aria-expanded="false" aria-haspopup="true"> | |
| Detailed | |
| <svg class="dropdown-arrow" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> | |
| <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" /> | |
| </svg> | |
| </button> | |
| <div id="modeDropdownMenu" class="mode-dropdown-menu hidden" role="menu" aria-orientation="vertical" aria-labelledby="modeDropdownButton" tabindex="-1"> | |
| <div class="dropdown-items"> | |
| <a href="#" class="dropdown-item" role="menuitem" tabindex="-1"> | |
| <span class="font-semibold">Short and Concise</span> | |
| </a> | |
| <a href="#" class="dropdown-item" role="menuitem" tabindex="-1"> | |
| <span class="font-semibold">Detailed</span> | |
| </a> | |
| <a href="#" class="dropdown-item" role="menuitem" tabindex="-1"> | |
| <span class="font-semibold">Step-by-Step</span> | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| <button id="sendButton"> | |
| <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 664 663"> | |
| <path fill="none" d="M646.293 331.888L17.7538 17.6187L155.245 331.888M646.293 331.888L17.753 646.157L155.245 331.888M646.293 331.888L318.735 330.228L155.245 331.888"></path> | |
| <path stroke-linejoin="round" stroke-linecap="round" stroke-width="33.67" stroke="#6c6c6c" d="M646.293 331.888L17.7538 17.6187L155.245 331.888M646.293 331.888L17.753 646.157L155.245 331.888M646.293 331.888L318.735 330.228L155.245 331.888"></path> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- ✅ NEW: Image Modal --> | |
| <div id="imageModal" class="image-modal"> | |
| <span class="modal-close" id="modalClose">×</span> | |
| <img class="modal-content" id="modalImage"> | |
| </div> | |
| <script> | |
| const chatBox = document.getElementById('chat-box'); | |
| const userInput = document.getElementById('messageInput'); | |
| const sendButton = document.getElementById('sendButton'); | |
| const sidebarToggle = document.getElementById('sidebar-toggle'); | |
| const modeToggle = document.getElementById('toggle-checkbox'); | |
| const sidebar = document.querySelector('.sidebar'); | |
| const chatContainer = document.querySelector('.chat-container'); | |
| const messageBox = document.querySelector('.messageBox'); | |
| const imageModal = document.getElementById('imageModal'); | |
| const modalImage = document.getElementById('modalImage'); | |
| const modalClose = document.getElementById('modalClose'); | |
| let currentMode = 'Detailed'; | |
| let sessionId = 'session-' + Date.now(); | |
| // ✅ NEW: Image Modal Functions | |
| function openImageModal(imageSrc) { | |
| imageModal.classList.add('active'); | |
| modalImage.src = imageSrc; | |
| } | |
| function closeImageModal() { | |
| imageModal.classList.remove('active'); | |
| modalImage.src = ''; | |
| } | |
| modalClose.addEventListener('click', closeImageModal); | |
| imageModal.addEventListener('click', (e) => { | |
| if (e.target === imageModal) { | |
| closeImageModal(); | |
| } | |
| }); | |
| // Utility Functions | |
| function getCurrentTime() { | |
| const now = new Date(); | |
| return now.toLocaleTimeString(); | |
| } | |
| // ✅ MODIFIED: Chat Functions with Image Support | |
| function appendMessage(sender, message, images = []) { | |
| const wrapper = document.createElement('div'); | |
| wrapper.classList.add('message-row'); | |
| wrapper.classList.add(sender === 'user' ? 'message-user' : 'message-bot'); | |
| const bubble = document.createElement('div'); | |
| bubble.classList.add('message-bubble'); | |
| // Add text message | |
| bubble.innerHTML = message.replace(/\n/g, '<br>'); | |
| // ✅ NEW: Add images if present | |
| if (images && images.length > 0) { | |
| const gallery = document.createElement('div'); | |
| gallery.classList.add('image-gallery'); | |
| images.forEach((img, index) => { | |
| const imgContainer = document.createElement('div'); | |
| imgContainer.classList.add('image-container'); | |
| const imgElement = document.createElement('img'); | |
| imgElement.src = img.data; | |
| imgElement.alt = `Image from ${img.source}`; | |
| imgElement.loading = 'lazy'; | |
| // Click to open modal | |
| imgElement.addEventListener('click', () => { | |
| openImageModal(img.data); | |
| }); | |
| const caption = document.createElement('div'); | |
| caption.classList.add('image-caption'); | |
| caption.textContent = `📄 ${img.source} | Page ${img.page}`; | |
| imgContainer.appendChild(imgElement); | |
| imgContainer.appendChild(caption); | |
| gallery.appendChild(imgContainer); | |
| }); | |
| bubble.appendChild(gallery); | |
| } | |
| wrapper.appendChild(bubble); | |
| chatBox.appendChild(wrapper); | |
| chatBox.scrollTop = chatBox.scrollHeight; | |
| } | |
| async function sendMessage() { | |
| const message = userInput.value.trim(); | |
| if (message === '') return; | |
| // Append user message | |
| appendMessage('user', message); | |
| userInput.value = ''; | |
| // Show loading message | |
| appendMessage('ChatGPT', '⏳ Thinking...'); | |
| try { | |
| const response = await fetch('/api/chat', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| message: message, | |
| mode: currentMode, | |
| session_id: sessionId, | |
| include_images: true // ✅ NEW: Request images | |
| }) | |
| }); | |
| const data = await response.json(); | |
| // Remove loading message | |
| chatBox.removeChild(chatBox.lastChild); | |
| if (data.error) { | |
| appendMessage('ChatGPT', '❌ Error: ' + data.error); | |
| } else { | |
| // ✅ NEW: Pass images to appendMessage | |
| const images = data.images || []; | |
| appendMessage('ChatGPT', data.response, images); | |
| // Log image info | |
| if (images.length > 0) { | |
| console.log(`📷 Received ${images.length} images`); | |
| } | |
| } | |
| } catch (error) { | |
| // Remove loading message | |
| chatBox.removeChild(chatBox.lastChild); | |
| appendMessage('ChatGPT', '❌ Connection error. Please check if the server is running.'); | |
| console.error('Error:', error); | |
| } | |
| } | |
| // Event Listeners | |
| document.addEventListener('DOMContentLoaded', function() { | |
| const newConversationBtn = document.getElementById('new-conversation-btn'); | |
| const conversationContent = document.querySelector('.conversation-content'); | |
| // Sidebar Toggle | |
| sidebarToggle.addEventListener('click', function() { | |
| sidebar.classList.toggle('collapsed'); | |
| adjustMessageBoxPosition(); | |
| }); | |
| function adjustMessageBoxPosition() { | |
| const sidebarWidth = sidebar.classList.contains('collapsed') ? 50 : 300; | |
| const chatAreaWidth = window.innerWidth - sidebarWidth; | |
| messageBox.style.left = sidebarWidth + (chatAreaWidth / 2) + 'px'; | |
| messageBox.style.transform = 'translateX(-50%)'; | |
| } | |
| adjustMessageBoxPosition(); | |
| window.addEventListener('resize', adjustMessageBoxPosition); | |
| // New Conversation | |
| newConversationBtn.addEventListener('click', async function() { | |
| conversationContent.textContent = 'New Conversation Started!'; | |
| chatBox.innerHTML = ''; | |
| sessionId = 'session-' + Date.now(); | |
| try { | |
| await fetch('/api/new-conversation', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({session_id: sessionId}) | |
| }); | |
| } catch (error) { | |
| console.error('Error starting new conversation:', error); | |
| } | |
| adjustMessageBoxPosition(); | |
| }); | |
| // Theme Toggle | |
| modeToggle.addEventListener('change', function() { | |
| document.body.classList.toggle('dark-mode'); | |
| document.body.classList.toggle('light-mode'); | |
| chatContainer.classList.toggle('light-mode'); | |
| chatContainer.classList.toggle('dark-mode'); | |
| adjustMessageBoxPosition(); | |
| }); | |
| // Set initial mode | |
| document.body.classList.add('light-mode'); | |
| // Send button | |
| sendButton.addEventListener('click', sendMessage); | |
| userInput.addEventListener('keydown', (event) => { | |
| if (event.key === 'Enter') sendMessage(); | |
| }); | |
| // Mode Dropdown | |
| const modeDropdownButton = document.getElementById('modeDropdownButton'); | |
| const modeDropdownMenu = document.getElementById('modeDropdownMenu'); | |
| if (modeDropdownButton && modeDropdownMenu) { | |
| function toggleMenu() { | |
| const isHidden = modeDropdownMenu.classList.contains('hidden'); | |
| if (isHidden) { | |
| modeDropdownMenu.classList.remove('hidden'); | |
| modeDropdownButton.setAttribute('aria-expanded', 'true'); | |
| } else { | |
| modeDropdownMenu.classList.add('hidden'); | |
| modeDropdownButton.setAttribute('aria-expanded', 'false'); | |
| } | |
| } | |
| modeDropdownButton.addEventListener('click', (event) => { | |
| event.stopPropagation(); | |
| toggleMenu(); | |
| }); | |
| document.addEventListener('click', (event) => { | |
| if (!modeDropdownButton.contains(event.target) && !modeDropdownMenu.contains(event.target)) { | |
| if (!modeDropdownMenu.classList.contains('hidden')) { | |
| toggleMenu(); | |
| } | |
| } | |
| }); | |
| modeDropdownMenu.querySelectorAll('.dropdown-item').forEach(item => { | |
| item.addEventListener('click', (event) => { | |
| event.preventDefault(); | |
| const selectedMode = event.currentTarget.querySelector('.font-semibold').textContent.trim(); | |
| console.log('Mode selected:', selectedMode); | |
| currentMode = selectedMode; | |
| const buttonTextNode = Array.from(modeDropdownButton.childNodes).find(node => | |
| node.nodeType === Node.TEXT_NODE && node.textContent.trim() !== '' | |
| ); | |
| if (buttonTextNode) { | |
| buttonTextNode.textContent = selectedMode; | |
| } else { | |
| const textNode = document.createTextNode(selectedMode); | |
| modeDropdownButton.insertBefore(textNode, modeDropdownButton.querySelector('.dropdown-arrow')); | |
| } | |
| toggleMenu(); | |
| }); | |
| }); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> | |