В настоящее время, если пользователь захватывает изображение и вставляет его, изображение в контейнере img-preview перекрывает userInput. Такое поведение можно явно наблюдать, когда пользователь захватывает и вставляет в чат-бот изображение, высота которого превышает его ширину.
Создайте изображение, отображаемое внутри контейнера img-preview-container, обрезав содержимое изображения на границе элемента контейнера, независимо от размера изображения, которое пользователь вставил с помощью клавиш «ctrl» и «v».
В настоящее время, если пользователь использует клавишу PrtSc, чтобы захватить определенную часть экрана и вставить ее в поле ввода текста с помощью Ctrl + V, вставленное изображение перекрывает поле ввода текста.
const promptInput = document.getElementById("userInput");
const chatContainer = document.getElementById("chatContainer");
const typingIndicator = document.getElementById("typingIndicator");
const sidebar = document.getElementById("sidebar");
const sidebarContent = document.getElementById("sidebarContent");
const imageContainer = document.getElementById("imageContainer");
async function sendMessage() {
const prompt = promptInput.value.trim();
if (!prompt && imageContainer.children.length === 0) {
alert("Please enter a message or add an image."); // Browser pop up message
return;
}
// Collect image data
const images = Array.from(imageContainer.querySelectorAll('.img-preview'))
.map(img => img.src.split(',')[1]); // Extract base64 data
addMessage(prompt, 'user', images);
promptInput.value = "";
showTypingIndicator();
const generatedText = await generateText(prompt, images);
addMessage(generatedText, 'bot');
hideTypingIndicator();
//clearImagePreviews(); Add this code if you want the image in the imageContainer disappear if the user sends the image.
}
async function generateText(prompt, images) {
try {
const response = await fetch("http://127.0.0.1:5000/generate_text_stream", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ prompt, images }),
});
if (!response.ok) {
console.error("Error:", response.statusText);
return "Error occurred while generating response.";
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let isFinished = false;
let generatedTextContent = "";
while (!isFinished) {
const { done, value } = await reader.read();
if (done) {
isFinished = true;
break;
}
generatedTextContent += decoder.decode(value, { stream: true });
}
return generatedTextContent;
} catch (error) {
console.error("Error:", error);
return "An error occurred.";
}
}
function addMessage(text, type, images = []) {
const messageDiv = document.createElement("div");
messageDiv.className = `message ${type}`;
const messageContent = document.createElement("div");
messageContent.className = "message-bubble fadeIn";
messageContent.innerHTML = `<p>${text}</p>`;
images.forEach(src => {
const img = document.createElement("img");
img.src = `data:image/png;base64,${src}`;
img.classList.add("message-image");
messageContent.appendChild(img);
});
messageDiv.appendChild(messageContent);
chatContainer.appendChild(messageDiv);
chatContainer.scrollTop = chatContainer.scrollHeight;
}
function clearImagePreviews() {
while (imageContainer.firstChild) {
imageContainer.removeChild(imageContainer.firstChild);
}
checkImageContainerVisibility();
}
let typingTimeout;
function showTypingIndicator() {
clearTimeout(typingTimeout);
typingIndicator.style.display = "inline-block";
}
function hideTypingIndicator() {
typingTimeout = setTimeout(() => {
typingIndicator.style.display = "none";
}, 1000);
}
function handleKeyPress(event) {
if (event.key === "Enter") {
sendMessage();
}
}
function toggleSidebar() {
if (sidebar.style.width === "500px") {
sidebar.style.width = "0";
sidebarContent.style.display = "none";
} else {
sidebar.style.width = "500px";
sidebarContent.style.display = "block";
}
}
window.onload = () => addMessage("Hello! How can I assist you today?", 'bot');
document.addEventListener('DOMContentLoaded', () => {
const textInput = document.getElementById('userInput');
textInput.addEventListener('paste', (event) => {
const items = (event.clipboardData || window.clipboardData).items;
for (const item of items) {
if (item.type.indexOf('image') !== -1) {
const file = item.getAsFile();
const reader = new FileReader();
reader.onload = (event) => {
displayImage(event.target.result);
};
reader.readAsDataURL(file);
event.preventDefault();
}
}
});
function displayImage(src) {
const imgContainer = document.createElement('div');
imgContainer.classList.add('img-preview-container');
const img = document.createElement('img');
img.src = src;
img.classList.add('img-preview');
const removeButton = document.createElement('button');
removeButton.classList.add('remove-button');
removeButton.textContent = '✖';
removeButton.addEventListener('click', () => {
imgContainer.remove();
checkImageContainerVisibility();
});
imgContainer.appendChild(img);
imgContainer.appendChild(removeButton);
imageContainer.appendChild(imgContainer);
checkImageContainerVisibility();
const all_images = imageContainer.querySelectorAll('.img-preview-container');
all_images.forEach(img => img.style.width = `${100 / all_images.length - 10}%`);
}
function checkImageContainerVisibility() {
if (imageContainer.children.length > 0) {
imageContainer.classList.remove('hidden');
} else {
imageContainer.classList.add('hidden');
}
}
// Initial check to hide image container if empty
checkImageContainerVisibility();
});
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f0f2f5;
color: #333;
margin: 0;
padding: 0;
text-align: center;
}
h1 {
color: #333;
text-align: center;
margin: 20px 0;
}
/* Generate report button styling */
button, input[type = "submit"] {
padding: 12px 24px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s, transform 0.3s;
}
button:hover, input[type = "submit"]:hover {
background-color: #0056b3;
transform: translateY(-2px);
}
/* Form styling */
form {
background: #fff;
border-radius: 8px;
padding: 20px;
max-width: 600px;
margin: 20px auto;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
font-size: 16px;
}
select, input[type = "submit"] {
width: calc(100% - 22px);
padding: 12px;
margin-bottom: 20px;
border-radius: 6px;
border: 1px solid #ddd;
font-size: 16px;
}
select {
background-color: #f9f9f9;
}
/* Report page styling */
.report-container {
background: #fff;
border-radius: 8px;
padding: 20px;
max-width: 1000px;
margin: 20px auto;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
overflow: auto;
}
a {
color: #007bff;
text-decoration: none;
font-weight: 500;
}
a:hover {
text-decoration: underline;
}
/* Section that will show report content */
.report-content {
margin-top: 20px;
}
.report-content iframe {
width: 100%;
border: none;
height: 600px;
}
/* Chat Bot */
.container {
margin-top: 0;
width: 90%;
max-width: 450px;
margin: 10px auto 0;
background-color: #fff;
border-radius: 12px;
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.1);
padding: 20px;
transition: all 0.3s;
}
.chat {
overflow-y: auto;
height: 400px;
margin-bottom: 20px;
border-bottom: 2px solid #e2e2e2;
}
.message {
display: flex;
margin-bottom: 12px;
}
.message.user {
justify-content: flex-end;
}
.message-bubble {
padding: 12px 18px;
max-width: 70%;
border-radius: 20px;
line-height: 1.6;
font-size: 0.95rem;
}
.message.user .message-bubble {
background-color: #3182ce;
color: white;
}
.message.bot .message-bubble {
background-color: #e2e2e2;
color: #333;
}
.message-image {
max-width: 100px; /* Set the maximum width for the image */
max-height: 100px; /* Set the maximum height for the image */
margin: 5px;
display: inline-block;
object-fit: cover; /* Ensures the image retains its aspect ratio */
}
input[type = "text"] {
flex: 1;
padding: 12px 18px;
border: 2px solid #e2e2e2;
border-radius: 8px 0 0 8px;
font-size: 1rem;
outline: none;
color: black;
}
.send-button {
width: 110px;
background-color: #3182ce;
color: white;
padding: 12px 18px;
border: none;
border-radius: 0 8px 8px 0;
cursor: pointer;
transition: background-color 0.3s;
}
.send-button:hover {
background-color: #2c5282;
}
.footer {
text-align: center;
padding: 15px 0;
font-size: 0.9rem;
color: #666;
position: static;
border-top: 1px solid #e2e2e2;
background-color: #fff;
position: fixed;
bottom: 0;
width: 100%;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.fadeIn {
animation: fadeIn 1s;
}
@media (max-width: 600px) {
.container {
width: 95%;
margin: 10px auto 0;
}
.chat {
height: 300px;
}
.input-container {
max-width: 95%;
}
input[type = "text"],
.send-button {
padding: 10px 14px;
font-size: 0.9rem;
}
.footer {
font-size: 0.8rem;
margin-top: 30px;
}
}
.typing-indicator {
display: none;
align-items: center;
justify-content: flex-end;
margin-top: 8px;
width: 10px;
height: 10px;
background-color: #333;
border-radius: 50%;
margin-left: 4px;
animation: typing 1s infinite;
}
@keyframes typing {
0%,
100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.2);
opacity: 0.7;
}
}
/* Side bar */
.sidebar {
position: fixed;
top: 10px;
right: 10px;
height: 100%;
width: 100%;
max-width: 500px;
background-color: none;
overflow-x: hidden;
transition: 0.5s;
padding-top: 60px;
color: white;
}
.sidebar-content {
display: none;
}
.sidebar-content h2 {
text-align: center;
}
.sidebar-content p {
padding: 10px;
}
.toggle-button {
position: fixed;
top: 10px;
right: 10px;
padding: 15px;
background-color: #3182ce;
color: white;
border: none;
cursor: pointer;
}
.toggle-button:hover {
background-color: #2c5282;
}
img {
max-width: 90%;
margin: 20px 0;
border: 1px solid #ddd;
border-radius: 10px;
}
.image-container {
width: 100%;
/*padding: 12px 18px;*/
display: flex;
flex-wrap: wrap;
gap: 10px;
background-color: #fff;
/*border: 1px solid #444;*/
min-height: 50px;
max-height: 70px;
margin-bottom: 10px;
border: 2px solid #e2e2e2;
border-radius: 8px 8px 8px 8px;
}
.hidden {
display: none;
}
.img-preview-container {
position: relative;
display: inline-block;
max-width: 15%;
}
.img-preview {
max-width: 100%;
border-radius: 5px;
}
.remove-button:hover {
background-color: #45a049;
}
.remove-button {
position: absolute;
top: 5px;
right: 5px;
background-color: #ff4d4d;
border: none;
border-radius: 50%;
color: white;
cursor: pointer;
width: 20px;
height: 20px;
font-size: 12px;
line-height: 20px;
text-align: center;
padding: 0;
}
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF-8" />
<meta name = "viewport" content = "width=device-width, initial-scale=1.0" />
<title>Portfolio Backtesting</title>
<link
rel = "stylesheet"
href = "{{ url_for('static', filename='css/style.css') }}"
/>
</head>
<body>
<h1>Portfolio Backtesting</h1>
<form method = "post" action = "/">
<label for = "cs_model">Choose Cross-Sectional Model:</label>
<select id = "cs_model" name = "cs_model">
<option value = "EW">Equal Weight (EW)</option>
<option value = "MSR">Maximum Sharpe Ratio (MSR)</option>
<option value = "GMV">Global Minimum Variance (GMV)</option>
<option value = "MDP">Minimum Drawdown Risk (MDP)</option>
<option value = "EMV">Equal Risk Contribution (EMV)</option>
<option value = "RP">Risk Parity (RP)</option>
</select>
<label for = "ts_model">Choose Time-Series Model:</label>
<select id = "ts_model" name = "ts_model">
<option value = "VT">Volatility Targeting (VT)</option>
<option value = "CVT">Conditional Value at Risk Targeting (CVT)</option>
<option value = "KL">Kelly Criterion (KL)</option>
<option value = "CPPI">
Constant Proportion Portfolio Insurance (CPPI)
</option>
</select>
<input type = "submit" value = "Generate Report" />
</form>
<div class = "sidebar" id = "sidebar">
<button class = "toggle-button" onclick = "toggleSidebar()">☰</button>
<div class = "sidebar-content" id = "sidebarContent">
<div class = "container bg-white rounded-lg shadow-md">
<h1 class = "text-3xl font-bold mb-4 text-center">ChatBot</h1>
<div class = "chat" id = "chatContainer"></div>
<div class = "image-container hidden" id = "imageContainer"></div>
<div class = "flex">
<input
type = "text"
id = "userInput"
placeholder = "Type your message here..."
class = "outline-none"
onkeyup = "handleKeyPress(event)"
/>
<button class = "send-button" onclick = "sendMessage()">Send</button>
</div>
<div class = "typing-indicator" id = "typingIndicator"></div>
</div>
</div>
</div>
<script src = "{{ url_for('static', filename='js/script.js') }}"></script>
</body>
</html>
Я думал об обрезке содержимого изображения на границе элемента контейнера. Я отредактировал сообщение для более ясной цели.
Попробуйте добавить CSS object-fit: cover
или fill
к <img>
.
Я пробовал добавить оба атрибута CSS, соответствующие объекту, но ни один из них не решил проблему.
Более простой способ сделать это — добавить правило CSS:
.image-container {
overflow:hidden
}
Таким образом, все лишнее будет скрыто.
Разве это не приводит к исчезновению изображения, если оно перекрывается? Я намеревался показать часть изображения, обрезая содержимое изображения, если оно перекрывается.
@loupdaniel Что именно вы имеете в виду под перекрытием? Я понимаю, что у вас есть контейнер и вы хотите сохранить изображение в исходных границах контейнера. Собственно, та часть изображения, которая не помещается внутрь, «лишний контент» — это то, что «переливается» из контейнера. Именно для этого и предназначено свойство overflow
CSS. Когда вы говорите, что .image-container
должно overflow:hidden
, изображение отображается вплоть до границы контейнера, но не дальше. Я проверил это в приведенном вами фрагменте, и он работал так, как задумано.
Я неправильно понял ваше решение: изображение буквально исчезнет, если оно переполнится. Протестировал код, работает отлично!
"Сделать изображение, показанное внутри img-preview-container"... Непонятно, как вы хотите, чтобы это произошло: обрезать содержимое изображения на границе элемента-контейнера или сжать всю картинку до тех пор, пока она не поместится в контейнер? Все дело в пропорциональности. Если вы хотите уменьшить его, вы можете закончить тонким вертикальным предметом, если его ширина значительно меньше высоты. Это то, чего ты хочешь?