turns-00042.parquet:17046
7558206123512d94ee00431cdegenerate_repetitionAbsentFinal dense release
Select one behavior. Every returned turn has one binary label: Present or Absent. Source: final dense boolean release.
7558206123512d94ee00431chi, how are you?
Hello! I'm just a program, but I'm here and ready to help you. How can I assist you today?
8d52b05467df381eeeafe997
Analyze the following message and determine if it's suitable for an AI to respond to.
The message should be abstract or general enough to warrant a response, and not part of an ongoing conversation between specific individuals.
Rules:
1. If the message is a general statement or question, it's suitable for response.
2. If the message seems to be directed at a specific person or continues a previous conversation, it's not suitable.
3. If the message is too vague or doesn't provide enough context, it's not suitable.
Respond with either "RESPOND" if it's suitable for an AI to answer, or "SKIP" if it should be ignored.
Message: "I’d agree with you, but then we’d both be wrong."
SKIP
9f4e9315f9002808c18f0c40# app.py
from flask import Flask, render_template, request, jsonify
from flask_socketio import SocketIO, emit
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import Session
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///notes.db'
db = SQLAlchemy(app)
socketio = SocketIO(app)
UPLOAD_FOLDER = 'static/uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
class Note(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.String(500), nullable=False)
x = db.Column(db.Integer, nullable=False)
y = db.Column(db.Integer, nullable=False)
width = db.Column(db.Integer, nullable=False)
height = db.Column(db.Integer, nullable=False)
class Image(db.Model):
id = db.Column(db.Integer, primary_key=True)
url = db.Column(db.String(500), nullable=False)
x = db.Column(db.Integer, nullable=False)
y = db.Column(db.Integer, nullable=False)
width = db.Column(db.Integer, nullable=False)
height = db.Column(db.Integer, nullable=False)
class Audio(db.Model):
id = db.Column(db.Integer, primary_key=True)
file_name = db.Column(db.String(500), nullable=False)
url = db.Column(db.String(500), nullable=False)
x = db.Column(db.Integer, nullable=False)
y = db.Column(db.Integer, nullable=False)
width = db.Column(db.Integer, nullable=False)
height = db.Column(db.Integer, nullable=False)
@app.route('/')
def index():
notes = Note.query.all()
images = Image.query.all()
return render_template('index.html', notes=notes, images=images)
@app.route('/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return jsonify({'error': 'No file part'}), 400
file = request.files['file']
if file.filename == '':
return jsonify({'error': 'No selected file'}), 400
filename = file.filename
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) # Ensure the upload folder exists
file.save(filepath)
return jsonify({'url': os.path.join('/', app.config['UPLOAD_FOLDER'], filename)})
@app.route('/upload_audio', methods=['POST'])
def upload_audio():
if 'file' not in request.files:
return jsonify({'error': 'No file part'}), 400
file = request.files['file']
if file.filename == '':
return jsonify({'error': 'No selected file'}), 400
filename = file.filename
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
file.save(filepath)
return jsonify({'url': os.path.join('/', app.config['UPLOAD_FOLDER'], filename), 'file_name': filename})
@socketio.on('create_note')
def handle_create_note(data):
text = data['text']
x = data['x']
y = data['y']
width = data['width']
height = data['height']
new_note = Note(text=text, x=x, y=y, width=width, height=height)
db.session.add(new_note)
db.session.commit()
emit('new_note', {'id': new_note.id, 'text': text, 'x': x, 'y': y, 'width': width, 'height': height},
broadcast=True)
@socketio.on('update_note')
def handle_update_note(data):
note = db.session.get(Note, data['id'])
if note:
note.text = data['text']
note.x = data['x']
note.y = data['y']
note.width = data['width']
note.height = data['height']
db.session.commit()
emit('updated_note',
{'id': note.id, 'text': note.text, 'x': note.x, 'y': note.y, 'width': note.width, 'height': note.height},
broadcast=True)
@socketio.on('create_image')
def handle_create_image(data):
url = data.get('url')
x = data.get('x')
y = data.get('y')
width = data.get('width')
height = data.get('height')
if not url or not x or not y or not width or not height:
emit('error', {'error': 'Missing required data'})
return
new_image = Image(url=url, x=x, y=y, width=width, height=height)
db.session.add(new_image)
db.session.commit()
emit('new_image', {'id': new_image.id, 'url': url, 'x': x, 'y': y, 'width': width, 'height': height},
broadcast=True)
@socketio.on('update_image')
def handle_update_image(data):
image = db.session.get(Image, data['id'])
if image:
image.url = data.get('url', image.url)
image.x = data.get('x', image.x)
image.y = data.get('y', image.y)
image.width = data.get('width', image.width)
image.height = data.get('height', image.height)
db.session.commit()
emit('updated_image', {'id': image.id, 'url': image.url, 'x': image.x, 'y': image.y, 'width': image.width,
'height': image.height}, broadcast=True)
@socketio.on('delete_element')
def handle_delete_element(data):
element_id = data['id']
element_type = data.get('type')
if element_type == 'note':
element = db.session.get(Note, element_id)
elif element_type == 'image':
element = db.session.get(Image, element_id)
else:
emit('error', {'error': 'Invalid element type'})
return
if element:
if element_type == 'image':
image_path = os.path.join(app.config['UPLOAD_FOLDER'], os.path.basename(element.url))
if os.path.exists(image_path):
os.remove(image_path)
db.session.delete(element)
db.session.commit()
emit('element_deleted', {'id': element_id, 'type': element_type}, broadcast=True)
else:
emit('error', {'error': 'Element not found'})
@socketio.on('create_audio')
def handle_create_audio(data):
file_name = data['file_name']
url = data['url']
x = data['x']
y = data['y']
width = data['width']
height = data['height']
new_audio = Audio(file_name=file_name, url=url, x=x, y=y, width=width, height=height)
db.session.add(new_audio)
db.session.commit()
emit('new_audio',
{'id': new_audio.id, 'file_name': file_name, 'url': url, 'x': x, 'y': y, 'width': width, 'height': height},
broadcast=True)
@socketio.on('update_audio')
def handle_update_audio(data):
audio = db.session.get(Audio, data['id'])
if audio:
audio.file_name = data.get('file_name', audio.file_name)
audio.url = data.get('url', audio.url)
audio.x = data.get('x', audio.x)
audio.y = data.get('y', audio.y)
audio.width = data.get('width', audio.width)
audio.height = data.get('height', audio.height)
db.session.commit()
emit('updated_audio',
{'id': audio.id, 'file_name': audio.file_name, 'url': audio.url, 'x': audio.x, 'y': audio.y,
'width': audio.width, 'height': audio.height}, broadcast=True)
@socketio.on('delete_audio')
def handle_delete_audio(data):
audio = db.session.get(Audio, data['id'])
if audio:
audio_path = os.path.join(app.config['UPLOAD_FOLDER'], os.path.basename(audio.url))
if os.path.exists(audio_path):
os.remove(audio_path)
db.session.delete(audio)
db.session.commit()
emit('audio_deleted', {'id': audio.id}, broadcast=True)
if __name__ == '__main__':
with app.app_context():
if not os.path.exists('notes.db'):
db.create_all()
socketio.run(app, host='192.168.0.105', port=7846, debug=True, allow_unsafe_werkzeug=True)
# static\css\style.css
body {
margin: 0;
padding: 0;
overflow: hidden;
}
#board-wrapper {
width: 100vw;
height: 100vh;
overflow: hidden;
position: relative;
cursor: grab;
background-image: url('/static/img/scale_1200.jpg');
background-repeat: repeat; /* Повторять изображение по горизонтали и вертикали */
background-size: 1200px 675px; /* Размер повторяющегося блока */
}
#board {
position: absolute;
top: 0;
left: 0;
transform-origin: 0 0;
}
.note, .image {
position: absolute;
background-color: #fff;
border: 1px solid #ccc;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1);
padding: 10px;
box-sizing: border-box;
cursor: move;
}
textarea {
width: 100%;
height: 100%;
border: none;
resize: none;
outline: none;
}
#add-note, #add-image, #add-audio {
position: fixed;
bottom: 20px;
right: 20px;
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
#add-image {
right: 150px;
}
#add-audio {
right: 260px;
}
.image img {
width: 100%;
height: 100%;
object-fit: contain; /* Это свойство сохраняет соотношение сторон изображения */
}
.resize-handle {
position: absolute;
bottom: 0;
right: 0;
width: 10px;
height: 10px;
background-color: #007bff;
cursor: nwse-resize;
}
.audio {
position: absolute;
background-color: #ffffff;
border: 1px solid #e0e0e0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 10px 15px;
box-sizing: border-box;
cursor: default;
border-radius: 5px;
width: 300px;
transition: box-shadow 0.2s;
cursor: move;
}
.audio:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.audio-file-name {
font-size: 14px;
font-weight: bold;
margin-bottom: 8px;
color: #444;
text-align: center;
}
.controls {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 10px;
}
.controls button {
background: #007bff;
border: none;
color: white;
padding: 5px 12px;
font-size: 16px;
border-radius: 5px;
cursor: pointer;
transition: background 0.2s, transform 0.2s;
}
.controls button:hover {
background: #0056b3;
transform: scale(1.05);
}
.progress-bar {
flex-grow: 1;
margin: 0 10px;
height: 6px;
cursor: pointer;
background: #e0e0e0;
border-radius: 5px;
outline: none;
-webkit-appearance: none;
}
.progress-bar::-webkit-slider-thumb {
-webkit-appearance: none;
width: 12px;
height: 12px;
background: #007bff;
border-radius: 50%;
cursor: pointer;
}
audio {
display: none; /* Скрыть встроенные элементы управления */
}
.hidden {
visibility: hidden;
}
# static\js\audio.js
socket.on('new_audio', (data) => {
const audioElement = createAudioElement(data);
document.getElementById('board').appendChild(audioElement);
});
socket.on('updated_audio', (data) => {
const audioElement = document.querySelector(`.audio[data-id="${data.id}"]`);
if (audioElement) {
audioElement.style.left = `${data.x}px`;
audioElement.style.top = `${data.y}px`;
audioElement.style.width = `${data.width}px`;
audioElement.style.height = `${data.height}px`;
audioElement.querySelector('.audio-file-name').textContent = data.file_name;
audioElement.querySelector('audio').src = data.url;
}
});
socket.on('audio_deleted', (data) => {
const element = document.querySelector(`.audio[data-id="${data.id}"]`);
if (element) {
element.remove();
}
});
function createAudioElement(data) {
const audioElement = document.createElement('div');
audioElement.className = 'audio';
audioElement.dataset.id = data.id;
audioElement.style.left = `${data.x}px`;
audioElement.style.top = `${data.y}px`;
audioElement.style.width = `${data.width}px`;
audioElement.style.height = `${data.height}px`;
const audioTitle = document.createElement('div');
audioTitle.className = 'audio-file-name';
audioTitle.textContent = data.file_name;
const audioTag = document.createElement('audio');
audioTag.src = data.url;
const controls = document.createElement('div');
controls.className = 'controls';
const playPauseButton = document.createElement('button');
playPauseButton.className = 'play-pause';
playPauseButton.textContent = '▶️';
const stopButton = document.createElement('button');
stopButton.className = 'stop';
stopButton.textContent = '⏹️';
const progressBar = document.createElement('input');
progressBar.className = 'progress-bar';
progressBar.type = 'range';
progressBar.min = 0;
progressBar.value = 0;
progressBar.step = 0.1;
audioTag.addEventListener('loadedmetadata', () => {
progressBar.max = audioTag.duration;
});
audioTag.addEventListener('timeupdate', () => {
progressBar.value = audioTag.currentTime;
});
playPauseButton.addEventListener('click', () => {
if (audioTag.paused) {
audioTag.play();
playPauseButton.textContent = '⏸️';
} else {
audioTag.pause();
playPauseButton.textContent = '▶️';
}
});
stopButton.addEventListener('click', () => {
audioTag.pause();
audioTag.currentTime = 0;
playPauseButton.textContent = '▶️';
});
progressBar.addEventListener('input', () => {
audioTag.currentTime = progressBar.value;
});
controls.appendChild(playPauseButton);
controls.appendChild(stopButton);
controls.appendChild(progressBar);
audioElement.appendChild(audioTitle);
audioElement.appendChild(audioTag);
audioElement.appendChild(controls);
addAudioEventListeners(audioElement, data.id);
return audioElement;
}
function addAudioEventListeners(audioElement, id) {
audioElement.addEventListener('mousedown', (e) => {
// Проверяем, что целевой элемент не кнопка или полоса прогресса
if (e.target.closest('.play-pause') || e.target.closest('.stop') || e.target.closest('.progress-bar')) {
return;
}
if (e.button !== 0) return;
e.preventDefault();
e.stopPropagation();
const startX = e.clientX;
const startY = e.clientY;
const startLeft = parseFloat(audioElement.style.left) || 0;
const startTop = parseFloat(audioElement.style.top) || 0;
function moveAudio(e) {
const deltaX = (e.clientX - startX) / scale;
const deltaY = (e.clientY - startY) / scale;
audioElement.style.left = `${startLeft + deltaX}px`;
audioElement.style.top = `${startTop + deltaY}px`;
const x = parseFloat(audioElement.style.left) || 0;
const y = parseFloat(audioElement.style.top) || 0;
const width = audioElement.offsetWidth;
const height = audioElement.offsetHeight;
socket.emit('update_audio', { id, file_name: audioElement.querySelector('.audio-file-name').textContent, url: audioElement.querySelector('audio').src, x, y, width, height });
}
document.addEventListener('mousemove', moveAudio);
document.addEventListener('mouseup', () => {
document.removeEventListener('mousemove', moveAudio);
}, { once: true });
});
audioElement.addEventListener('dblclick', (e) => {
e.preventDefault();
e.stopPropagation();
showContextMenu(audioElement, id, e.clientX, e.clientY);
// Additional audio controls can be added here
});
addResizeListeners(audioElement, id, 'audio');
}
document.getElementById('add-audio').addEventListener('click', () => {
const formHtml = `
<div id="audio-form" style="position:fixed; top:50%; left:50%; transform:translate(-50%, -50%); background:#f9f9f9; padding:20px; border-radius:12px; box-shadow:0 15px 30px rgba(0,0,0,0.2); z-index: 1000; max-width:400px; width:100%; text-align:center; font-family:'Arial', sans-serif;">
<label for="audio-file" style="display:block; margin-bottom:16px; font-size:16px; color:#333;">🎵 Upload Your Audio File</label>
<input type="file" id="audio-file" accept="audio/*" style="margin-bottom:20px; width:calc(100% - 32px); padding:10px; border-radius:8px; background-color:#fff; border:1px solid #ddd; color:#333; cursor:pointer;">
<br>
<button id="add-audio-submit" style="padding:12px 24px; background-color:#28a745; color:white; border:none; border-radius:6px; cursor:pointer; margin-right:12px; box-shadow:0 4px 8px rgba(40,167,69,0.2); transition:background-color 0.3s;">Add Audio</button>
<button id="cancel-audio" style="padding:12px 24px; background-color:#ff3860; color:white; border:none; border-radius:6px; cursor:pointer; box-shadow:0 4px 8px rgba(255,56,96,0.2); transition:background-color 0.3s;">Cancel</button>
</div>
`;
document.body.insertAdjacentHTML('beforeend', formHtml);
document.getElementById('add-audio-submit').addEventListener('click', async () => {
const fileInput = document.getElementById('audio-file');
if (fileInput.files.length > 0) {
const file = fileInput.files[0];
const audioData = await uploadFile(file, '/upload_audio');
addAudioToCenter(audioData);
closeModal();
}
});
document.getElementById('cancel-audio').addEventListener('click', closeModal);
function closeModal() {
const form = document.getElementById('audio-form');
if (form) {
form.remove();
}
}
});
async function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/upload_audio', {
method: 'POST',
body: formData
});
return await response.json();
}
function addAudioToCenter(audioData) {
const rect = boardWrapper.getBoundingClientRect();
const width = 200;
const height = 80;
const centerX = ((rect.width / 2 - translateX) / scale) - width / 2;
const centerY = ((rect.height / 2 - translateY) / scale) - height / 2;
socket.emit('create_audio', {
file_name: audioData.file_name,
url: audioData.url,
x: centerX,
y: centerY,
width: 200,
height: 50
});
}
# static\js\board.js
// board.js
// Получение сохраненных значений из куки или установка значений по умолчанию
let scale = parseFloat(getCookie('scale')) || 0.5;
let translateX = parseFloat(getCookie('translateX')) || 0;
let translateY = parseFloat(getCookie('translateY')) || 0;
const board = document.getElementById('board');
const boardWrapper = document.getElementById('board-wrapper');
// Константы для масштабирования
const MIN_SCALE = 0.05; // Минимальный масштаб
const MAX_SCALE = 5; // Максимальный масштаб
const ZOOM_FACTOR = 0.1; // Скорость масштабирования
function updateBoard() {
board.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
boardWrapper.style.backgroundPosition = `${translateX}px ${translateY}px`;
// Сохраняем значения в куки
setCookie('translateX', translateX, 365); // Сохраняем на 1 год
setCookie('translateY', translateY, 365);
setCookie('scale', scale, 365);
}
// Применение начального масштаба и сдвига
updateBoard();
// Отображение изображений после установки начального масштаба
document.querySelectorAll('.image').forEach(img => {
img.classList.remove('hidden');
});
boardWrapper.addEventListener('wheel', (e) => {
e.preventDefault();
const previousScale = scale;
scale += e.deltaY < 0 ? ZOOM_FACTOR : -ZOOM_FACTOR;
scale = Math.min(Math.max(MIN_SCALE, scale), MAX_SCALE);
// Вычисляем положение курсора относительно доски
const rect = board.getBoundingClientRect();
const offsetX = (e.clientX - rect.left) / previousScale;
const offsetY = (e.clientY - rect.top) / previousScale;
// Корректируем сдвиг, чтобы курсор оставался на том же месте на доске
translateX -= offsetX * (scale - previousScale);
translateY -= offsetY * (scale - previousScale);
updateBoard();
});
boardWrapper.addEventListener('touchstart', (e) => {
if (e.touches.length === 2) {
e.preventDefault();
const touch1 = e.touches[0];
const touch2 = e.touches[1];
const startDistance = Math.hypot(touch2.clientX - touch1.clientX, touch2.clientY - touch1.clientY);
const startScale = scale;
function zoom(e) {
if (e.touches.length !== 2) return;
const touch1 = e.touches[0];
const touch2 = e.touches[1];
const currentDistance = Math.hypot(touch2.clientX - touch1.clientX, touch2.clientY - touch1.clientY);
const newScale = startScale * (currentDistance / startDistance);
scale = Math.min(Math.max(MIN_SCALE, newScale), MAX_SCALE);
// Calculate midpoint of touches
const midX = (touch1.clientX + touch2.clientX) / 2;
const midY = (touch1.clientY + touch2.clientY) / 2;
// Calculate offset relative to previous scale
const rect = board.getBoundingClientRect();
const offsetX = (midX - rect.left) / startScale;
const offsetY = (midY - rect.top) / startScale;
// Adjust translate to keep midpoint at same board position
translateX -= offsetX * (scale - startScale);
translateY -= offsetY * (scale - startScale);
updateBoard();
}
document.addEventListener('touchmove', zoom);
document.addEventListener('touchend', () => {
document.removeEventListener('touchmove', zoom);
}, { once: true });
}
});
let isPanning = false;
let startPanX, startPanY;
boardWrapper.addEventListener('mousedown', (e) => {
if (e.button === 2) { // Right mouse button
isPanning = true;
startPanX = e.clientX - translateX;
startPanY = e.clientY - translateY;
boardWrapper.style.cursor = 'grabbing';
}
});
boardWrapper.addEventListener('mouseup', (e) => {
if (e.button === 2) { // Right mouse button
isPanning = false;
boardWrapper.style.cursor = 'grab';
}
});
boardWrapper.addEventListener('mousemove', (e) => {
if (!isPanning) return;
e.preventDefault();
translateX = e.clientX - startPanX;
translateY = e.clientY - startPanY;
updateBoard();
});
// Add touch events for panning
boardWrapper.addEventListener('touchstart', (e) => {
if (e.touches.length === 1) {
isPanning = true;
startPanX = e.touches[0].clientX - translateX;
startPanY = e.touches[0].clientY - translateY;
boardWrapper.style.cursor = 'grabbing';
}
});
boardWrapper.addEventListener('touchend', (e) => {
isPanning = false;
boardWrapper.style.cursor = 'grab';
});
boardWrapper.addEventListener('touchmove', (e) => {
if (!isPanning) return;
e.preventDefault();
translateX = e.touches[0].clientX - startPanX;
translateY = e.touches[0].clientY - startPanY;
updateBoard();
});
// Предотвращаем появление контекстного меню на board-wrapper
boardWrapper.addEventListener('contextmenu', (e) => {
e.preventDefault();
});
# static\js\contextMenu.js
// contextMenu.js
let activeContextMenu = null;
function showContextMenu(element, id, x, y) {
hideContextMenu(); // Ensure any existing menu is closed before showing a new one
const contextMenu = document.createElement('div');
contextMenu.className = 'context-menu';
contextMenu.style.position = 'absolute';
contextMenu.style.left = `${x}px`;
contextMenu.style.top = `${y}px`;
contextMenu.style.backgroundColor = 'white';
contextMenu.style.borderRadius = '4px';
contextMenu.style.border = '1px solid #ccc';
contextMenu.style.boxShadow = '0 0 10px rgba(0,0,0,0.1)';
contextMenu.style.zIndex = '1000';
activeContextMenu = contextMenu; // Track the active context menu
const deleteButton = document.createElement('button');
deleteButton.textContent = 'Delete';
deleteButton.style.padding = '5px 10px';
deleteButton.style.border = 'none';
deleteButton.style.backgroundColor = '#ff4d4d';
deleteButton.style.color = 'white';
deleteButton.style.borderRadius = '4px';
deleteButton.style.cursor = 'pointer';
deleteButton.style.width = '100%';
deleteButton.style.textAlign = 'left';
deleteButton.addEventListener('click', () => {
element.remove();
socket.emit('delete_element', { id: id, type: element.classList.contains('note') ? 'note' : 'image' });
hideContextMenu(); // Close the menu after deleting
});
contextMenu.appendChild(deleteButton);
document.body.appendChild(contextMenu);
// Close context menu when clicking outside
document.addEventListener('click', (e) => {
if (!contextMenu.contains(e.target)) {
hideContextMenu();
}
}, { once: true });
}
function hideContextMenu() {
if (activeContextMenu) {
activeContextMenu.remove();
activeContextMenu = null;
}
}
# static\js\cookies.js
// static/js/cookies.js
// Функция для установки куки
function setCookie(name, value, days) {
const date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
const expires = "expires=" + date.toUTCString();
document.cookie = name + "=" + value + ";" + expires + ";path=/";
}
// Функция для получения значения куки по имени
function getCookie(name) {
const nameEQ = name + "=";
const ca = document.cookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
# static\js\images.js
// images.js
// Assumes uploadFile, showContextMenu, addResizeListeners are available globally
socket.on('new_image', (data) => {
const image = createImageElement(data);
document.getElementById('board').appendChild(image);
});
socket.on('updated_image', (data) => {
const image = document.querySelector(`.image[data-id="${data.id}"]`);
if (image) {
image.style.left = `${data.x}px`;
image.style.top = `${data.y}px`;
image.style.width = `${data.width}px`;
image.style.height = `${data.height}px`;
image.querySelector('img').src = data.url;
}
});
socket.on('element_deleted', (data) => {
if (data.type === 'image') {
const element = document.querySelector(`.image[data-id="${data.id}"]`);
if (element) {
element.remove();
}
}
});
function createImageElement(data) {
const image = document.createElement('div');
image.className = 'image';
image.dataset.id = data.id;
image.style.left = `${data.x}px`;
image.style.top = `${data.y}px`;
image.style.width = `${data.width}px`;
image.style.height = `${data.height}px`;
const img = document.createElement('img');
img.src = data.url;
img.style.width = '100%';
img.style.height = '100%';
img.style.objectFit = 'contain'; // Maintains aspect ratio
image.appendChild(img);
addImageEventListeners(image, data.id);
return image;
}
function addImageEventListeners(image, id) {
let isTouchDragEnabled = false; // Флаг для контроля активности сенсорных обработчиков
image.addEventListener('mousedown', (e) => {
if (e.button !== 0) return; // Только для левой кнопки мыши
e.preventDefault();
e.stopPropagation();
const startX = e.clientX;
const startY = e.clientY;
const startLeft = parseFloat(image.style.left) || 0;
const startTop = parseFloat(image.style.top) || 0;
function moveImage(e) {
const deltaX = (e.clientX - startX) / scale;
const deltaY = (e.clientY - startY) / scale;
image.style.left = `${startLeft + deltaX}px`;
image.style.top = `${startTop + deltaY}px`;
const x = parseFloat(image.style.left) || 0;
const y = parseFloat(image.style.top) || 0;
const width = image.offsetWidth;
const height = image.offsetHeight;
socket.emit('update_image', { id, url: image.querySelector('img').src, x, y, width, height });
}
document.addEventListener('mousemove', moveImage);
document.addEventListener('mouseup', () => {
document.removeEventListener('mousemove', moveImage);
}, { once: true });
});
function startTouchDrag(e) {
e.preventDefault();
e.stopPropagation();
hideContextMenu(); // Скрыть контекстное меню при начале сенсорного перетаскивания
const startX = e.touches[0].clientX;
const startY = e.touches[0].clientY;
const startLeft = parseFloat(image.style.left) || 0;
const startTop = parseFloat(image.style.top) || 0;
function moveTouch(e) {
const deltaX = (e.touches[0].clientX - startX) / scale;
const deltaY = (e.touches[0].clientY - startY) / scale;
image.style.left = `${startLeft + deltaX}px`;
image.style.top = `${startTop + deltaY}px`;
const x = parseFloat(image.style.left) || 0;
const y = parseFloat(image.style.top) || 0;
const width = image.offsetWidth;
const height = image.offsetHeight;
socket.emit('update_image', { id, url: image.querySelector('img').src, x, y, width, height });
}
function endTouchDrag() {
document.removeEventListener('touchmove', moveTouch);
document.removeEventListener('touchend', endTouchDrag);
}
document.addEventListener('touchmove', moveTouch);
document.addEventListener('touchend', endTouchDrag);
}
image.addEventListener('dblclick', (e) => {
e.preventDefault();
e.stopPropagation();
showContextMenu(image, id, e.clientX, e.clientY);
// Переключаем состояние перемещения
isTouchDragEnabled = !isTouchDragEnabled;
if (isTouchDragEnabled) {
image.addEventListener('touchstart', startTouchDrag);
// Добавляем слушатель, чтобы отключить перемещение при клике вне изображения
document.addEventListener('mousedown', checkClickOutside);
} else {
image.removeEventListener('touchstart', startTouchDrag);
document.removeEventListener('mousedown', checkClickOutside);
}
});
function checkClickOutside(e) {
if (!image.contains(e.target)) {
image.removeEventListener('touchstart', startTouchDrag);
isTouchDragEnabled = false;
document.removeEventListener('mousedown', checkClickOutside);
}
}
addResizeListeners(image, id, 'image');
}
function handleExistingImages() {
document.querySelectorAll('.image').forEach(image => {
const id = image.dataset.id;
addImageEventListeners(image, id);
});
}
# static\js\main.js
// static/js/main.js
// Проверяем, что 'socket' уже инициализирован
if (!window.socket) {
console.error('Socket is not initialized!');
}
// Функция для добавления изображения в центр видимой области
function addImageToCenter(imageUrl) {
const maxWidth = 720;
const maxHeight = 720;
const img = new Image();
img.src = imageUrl;
img.onload = () => {
// Рассчитываем размеры, с учетом допустимых максимальных
let width = img.naturalWidth;
let height = img.naturalHeight;
if (width > maxWidth || height > maxHeight) {
const ratio = Math.min(maxWidth / width, maxHeight / height);
width *= ratio;
height *= ratio;
}
// Получаем размеры видимой области
const rect = boardWrapper.getBoundingClientRect();
// Рассчитываем координаты центра видимой области с учётом текущего масштаба и смещения
// и учитываем размеры изображения
const centerX = ((rect.width / 2 - translateX) / scale) - width / 2;
const centerY = ((rect.height / 2 - translateY) / scale) - height / 2;
socket.emit('create_image', { url: imageUrl, x: centerX, y: centerY, width, height });
};
}
// Event listener для кнопки 'add-note'
document.getElementById('add-note').addEventListener('click', () => {
// Получаем размеры видимой области
const rect = boardWrapper.getBoundingClientRect();
const width = 200; // Ширина по умолчанию
const height = 40; // Высота по умолчанию
// Рассчитываем координаты центра видимой области с учётом текущего масштаба и смещения
const centerX = ((rect.width / 2 - translateX) / scale) - width / 2;
const centerY = ((rect.height / 2 - translateY) / scale) - height / 2;
socket.emit('create_note', { text: 'New Comment', x: centerX, y: centerY, width, height });
});
// Event listener для кнопки 'add-image'
document.getElementById('add-image').addEventListener('click', () => {
const formHtml = `
<div id="image-form" style="position:fixed; top:50%; left:50%; transform:translate(-50%, -50%); background:white; padding:20px; border-radius:8px; box-shadow:0 0 10px rgba(0,0,0,0.1); z-index: 1000;">
<label for="image-url" style="display:block; margin-bottom:8px;">Image URL:</label>
<input type="text" id="image-url" placeholder="Enter image URL" style="width: calc(100% - 22px); padding:10px; margin-bottom:12px; border:1px solid #ccc; border-radius:4px;"><br>
<label for="image-file" style="display:block; margin-bottom:8px;">Or Upload File:</label>
<input type="file" id="image-file" accept="image/*" style="margin-bottom:12px;"><br>
<button id="add-image-submit" style="padding:10px 20px; background-color:#007bff; color:white; border:none; border-radius:4px; cursor:pointer; margin-right:8px;">Add Image</button>
<button id="cancel-image" style="padding:10px 20px; background-color:#ccc; color:white; border:none; border-radius:4px; cursor:pointer;">Cancel</button>
</div>
`;
document.body.insertAdjacentHTML('beforeend', formHtml);
document.getElementById('add-image-submit').addEventListener('click', async () => {
const url = document.getElementById('image-url').value;
const fileInput = document.getElementById('image-file');
let imageUrl;
if (url) {
imageUrl = url;
} else if (fileInput.files.length > 0) {
const file = fileInput.files[0];
imageUrl = await uploadFile(file);
}
if (imageUrl) {
addImageToCenter(imageUrl);
closeModal();
}
});
document.getElementById('cancel-image').addEventListener('click', closeModal);
function closeModal() {
const form = document.getElementById('image-form');
if (form) {
form.remove();
}
}
});
// Обработка существующих заметок и изображений после загрузки страницы
document.addEventListener('DOMContentLoaded', () => {
handleExistingNotes();
handleExistingImages();
});
# static\js\notes.js
// notes.js
// Assumes uploadFile, showContextMenu, addResizeListeners are available globally
socket.on('new_note', (data) => {
const note = createNoteElement(data);
document.getElementById('board').appendChild(note);
});
socket.on('updated_note', (data) => {
const note = document.querySelector(`.note[data-id="${data.id}"]`);
if (note) {
note.style.left = `${data.x}px`;
note.style.top = `${data.y}px`;
note.style.width = `${data.width}px`;
note.style.height = `${data.height}px`;
note.querySelector('textarea').value = data.text;
}
});
socket.on('element_deleted', (data) => {
if (data.type === 'note') {
const element = document.querySelector(`.note[data-id="${data.id}"]`);
if (element) {
element.remove();
}
}
});
function createNoteElement(data) {
const note = document.createElement('div');
note.className = 'note';
note.dataset.id = data.id;
note.style.left = `${data.x}px`;
note.style.top = `${data.y}px`;
note.style.width = `${data.width}px`;
note.style.height = `${data.height}px`;
const textarea = document.createElement('textarea');
textarea.value = data.text;
textarea.addEventListener('input', () => {
const x = note.offsetLeft;
const y = note.offsetTop;
const width = note.offsetWidth;
const height = note.offsetHeight;
socket.emit('update_note', { id: data.id, text: textarea.value, x, y, width, height });
});
textarea.addEventListener('mousedown', (e) => {
e.stopPropagation();
});
note.appendChild(textarea);
addNoteEventListeners(note, data.id);
return note;
}
function addNoteEventListeners(note, id) {
let isTouchDragEnabled = false; // Флаг для контроля активности сенсорных обработчиков
note.addEventListener('mousedown', (e) => {
if (e.button !== 0) return; // Left mouse button
e.preventDefault();
e.stopPropagation();
const startX = e.clientX;
const startY = e.clientY;
const startLeft = parseFloat(note.style.left) || 0;
const startTop = parseFloat(note.style.top) || 0;
function moveNote(e) {
const deltaX = (e.clientX - startX) / scale;
const deltaY = (e.clientY - startY) / scale;
note.style.left = `${startLeft + deltaX}px`;
note.style.top = `${startTop + deltaY}px`;
const x = parseFloat(note.style.left) || 0;
const y = parseFloat(note.style.top) || 0;
const width = note.offsetWidth;
const height = note.offsetHeight;
socket.emit('update_note', { id, text: note.querySelector('textarea').value, x, y, width, height });
}
document.addEventListener('mousemove', moveNote);
document.addEventListener('mouseup', () => {
document.removeEventListener('mousemove', moveNote);
}, { once: true });
});
function startTouchDrag(e) {
e.preventDefault();
e.stopPropagation();
hideContextMenu(); // Скрыть контекстное меню при начале сенсорного перетаскивания
const startX = e.touches[0].clientX;
const startY = e.touches[0].clientY;
const startLeft = parseFloat(note.style.left) || 0;
const startTop = parseFloat(note.style.top) || 0;
function moveNoteTouch(e) {
const deltaX = (e.touches[0].clientX - startX) / scale;
const deltaY = (e.touches[0].clientY - startY) / scale;
note.style.left = `${startLeft + deltaX}px`;
note.style.top = `${startTop + deltaY}px`;
const x = parseFloat(note.style.left) || 0;
const y = parseFloat(note.style.top) || 0;
const width = note.offsetWidth;
const height = note.offsetHeight;
socket.emit('update_note', { id, text: note.querySelector('textarea').value, x, y, width, height });
}
function endTouchDrag() {
document.removeEventListener('touchmove', moveNoteTouch);
document.removeEventListener('touchend', endTouchDrag);
}
document.addEventListener('touchmove', moveNoteTouch);
document.addEventListener('touchend', endTouchDrag);
}
note.addEventListener('dblclick', (e) => {
e.preventDefault();
e.stopPropagation();
showContextMenu(note, id, e.clientX, e.clientY);
// Переключаем состояние перемещения
isTouchDragEnabled = !isTouchDragEnabled;
if (isTouchDragEnabled) {
note.addEventListener('touchstart', startTouchDrag);
// Добавляем слушатель, чтобы отключить перемещение при клике вне записи
document.addEventListener('mousedown', checkClickOutside);
} else {
note.removeEventListener('touchstart', startTouchDrag);
document.removeEventListener('mousedown', checkClickOutside);
}
});
function checkClickOutside(e) {
if (!note.contains(e.target)) {
note.removeEventListener('touchstart', startTouchDrag);
isTouchDragEnabled = false;
document.removeEventListener('mousedown', checkClickOutside);
}
}
addResizeListeners(note, id, 'note');
}
function handleExistingNotes() {
document.querySelectorAll('.note').forEach(note => {
const id = note.dataset.id;
const text = note.querySelector('textarea').value;
const x = note.offsetLeft;
const y = note.offsetTop;
const width = note.offsetWidth;
const height = note.offsetHeight;
note.querySelector('textarea').addEventListener('input', () => {
const x = note.offsetLeft;
const y = note.offsetTop;
const width = note.offsetWidth;
const height = note.offsetHeight;
socket.emit('update_note', { id, text: note.querySelector('textarea').value, x, y, width, height });
});
note.querySelector('textarea').addEventListener('mousedown', (e) => {
e.stopPropagation();
});
addNoteEventListeners(note, id);
});
}
# static\js\resize.js
// resize.js
function addResizeListeners(element, id, type) {
const resizeHandle = document.createElement('div');
resizeHandle.className = 'resize-handle';
element.appendChild(resizeHandle);
resizeHandle.addEventListener('mousedown', (e) => {
if (e.button !== 0) return; // Left mouse button
e.stopPropagation(); // Prevent triggering drag
e.preventDefault();
let startX = e.clientX;
let startY = e.clientY;
let startWidth = element.offsetWidth;
let startHeight = element.offsetHeight;
function resizeElement(e) {
const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;
if (type === 'note') {
element.style.width = `${startWidth + deltaX}px`;
element.style.height = `${startHeight + deltaY}px`;
} else if (type === 'image') {
const ratio = startWidth / startHeight;
const newWidth = startWidth + deltaX;
const newHeight = newWidth / ratio;
element.style.width = `${newWidth}px`;
element.style.height = `${newHeight}px`;
}
const x = element.offsetLeft;
const y = element.offsetTop;
const width = element.offsetWidth;
const height = element.offsetHeight;
if (type === 'note') {
socket.emit('update_note', { id, text: element.querySelector('textarea').value, x, y, width, height });
} else if (type === 'image') {
socket.emit('update_image', { id, url: element.querySelector('img').src, x, y, width, height });
}
}
document.addEventListener('mousemove', resizeElement);
document.addEventListener('mouseup', () => {
document.removeEventListener('mousemove', resizeElement);
}, { once: true });
});
resizeHandle.addEventListener('touchstart', (e) => {
e.stopPropagation(); // Prevent triggering drag
e.preventDefault();
let startX = e.touches[0].clientX;
let startY = e.touches[0].clientY;
let startWidth = element.offsetWidth;
let startHeight = element.offsetHeight;
function resizeElement(e) {
const deltaX = e.touches[0].clientX - startX;
const deltaY = e.touches[0].clientY - startY;
if (type === 'note') {
element.style.width = `${startWidth + deltaX}px`;
element.style.height = `${startHeight + deltaY}px`;
} else if (type === 'image') {
const ratio = startWidth / startHeight;
const newWidth = startWidth + deltaX;
const newHeight = newWidth / ratio;
element.style.width = `${newWidth}px`;
element.style.height = `${newHeight}px`;
}
const x = element.offsetLeft;
const y = element.offsetTop;
const width = element.offsetWidth;
const height = element.offsetHeight;
if (type === 'note') {
socket.emit('update_note', { id, text: element.querySelector('textarea').value, x, y, width, height });
} else if (type === 'image') {
socket.emit('update_image', { id, url: element.querySelector('img').src, x, y, width, height });
}
}
document.addEventListener('touchmove', resizeElement);
document.addEventListener('touchend', () => {
document.removeEventListener('touchmove', resizeElement);
}, { once: true });
});
}
# static\js\socket.js
// static/js/socket.js
const socket = io();
window.socket = socket; // Делает 'socket' доступным глобально
# static\js\upload.js
// upload.js
async function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/upload', {
method: 'POST',
body: formData
});
const data = await response.json();
return data.url;
}
# templates\index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Miro Clone</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<div id="board-wrapper">
<div id="board">
{% for note in notes %}
<div class="note" data-id="{{ note.id }}" style="left: {{ note.x }}px; top: {{ note.y }}px; width: {{ note.width }}px; height: {{ note.height }}px;">
<textarea>{{ note.text }}</textarea>
</div>
{% endfor %}
{% for image in images %}
<div class="image hidden" data-id="{{ image.id }}" style="left: {{ image.x }}px; top: {{ image.y }}px; width: {{ image.width }}px; height: {{ image.height }}px;">
<img src="{{ image.url }}" style="width: 100%; height: 100%; object-fit: contain;">
</div>
{% endfor %}
</div>
</div>
<button id="add-note">Add Comment</button>
<button id="add-image">Add Image</button>
<button id="add-audio">Add Audio</button>
<!-- Подключение Socket.io -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<!-- Инициализация Socket -->
<script src="{{ url_for('static', filename='js/socket.js') }}"></script>
<!-- Другие JavaScript файлы -->
<script src="{{ url_for('static', filename='js/cookies.js') }}"></script>
<script src="{{ url_for('static', filename='js/upload.js') }}"></script>
<script src="{{ url_for('static', filename='js/contextMenu.js') }}"></script>
<script src="{{ url_for('static', filename='js/resize.js') }}"></script>
<script src="{{ url_for('static', filename='js/notes.js') }}"></script>
<script src="{{ url_for('static', filename='js/images.js') }}"></script>
<script src="{{ url_for('static', filename='js/audio.js') }}"></script>
<script src="{{ url_for('static', filename='js/board.js') }}"></script>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
</body>
</html>
У меня не меняется размер аудиоэлемента, исправь этоДля изменения размера аудиоэлемента необходимо добавить соответствующую функциональность в код. Похоже, аудиоэлементы не включают в себя обработку изменения их размера, как это сделано для заметок и изображений. Давайте исправим это, добавив обработку изменения размера аудиоэлемента.
Внесите следующие изменения:
1. **Добавить обработчик события для изменения размера аудиоэлемента**
В файле `resize.js`, убедитесь, что аудиоэлементы обрабатываются так же, как и другие элементы, такие как заметки и изображения. Добавьте проверку типа `audio` в функции `resizeElement`:
```javascript
function resizeElement(e) {
const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;
if (type === 'note') {
element.style.width = `${startWidth + deltaX}px`;
element.style.height = `${startHeight + deltaY}px`;
} else if (type === 'image' || type === 'audio') {
const ratio = startWidth / startHeight;
const newWidth = startWidth + deltaX;
const newHeight = newWidth / ratio;
element.style.width = `${newWidth}px`;
element.style.height = `${newHeight}px`;
}
const x = element.offsetLeft;
const y = element.offsetTop;
const width = element.offsetWidth;
const height = element.offsetHeight;
if (type === 'note') {
socket.emit('update_note', { id, text: element.querySelector('textarea').value, x, y, width, height });
} else if (type === 'image') {
socket.emit('update_image', { id, url: element.querySelector('img').src, x, y, width, height });
} else if (type === 'audio') {
socket.emit('update_audio', { id, file_name: element.querySelector('.audio-file-name').textContent, url: element.querySelector('audio').src, x, y, width, height });
}
}
```
2. **Убедитесь, что у аудиоэлементов есть «ручка» для изменения размера**
В коде на создание аудиоэлемента (например, в `audio.js`), убедитесь, что у аудиоэлементов добавлен элемент с классом `resize-handle`.
Если все изменения были внесены корректно, теперь вы должны иметь возможность менять размер аудиоэлементов. Убедитесь, что соответствующие стили также настроены правильно.7b5fe45f59f92a23c6b94c50System: I will provide you with a textual user review of an Android app, aiming to categorize the reviews into specific intention classes: pbr: state a concrete problem related to a software product or service inq: asks for either new functionality, an improvement, or requests information for support irr: it does not belong to problem reports or inquires User: 'prompt:I will provide you with a textual user review of an Android app, aiming to categorize the reviews into specific intention classes: pbr: state a concrete problem related to a software product or service inq: asks for either new functionality, an improvement, or requests information for support irr: it does not belong to problem reports or inquires Does the author's intention in this app review relate to a pbr or inq or irr? review: can you verify if there are service issues for mobile in Pittsburgh PA?' intention: ' Assistant:
inq
b70d551f90ba4591d97de1f0К какой тематике из списка ['Автомобили', 'Аниме', 'Бизнес', 'Видео контент', 'Видео- и компьютерные игры', 'Военное', 'Дети', 'Дорамы', 'Еда и напитки', 'Злоупотребление Веществ', 'Знаменитости', 'Инвестиции', 'Информационные технологии', 'Искусство', 'Кадровые ресурсы', 'Книги', 'Комиксы/манга', 'Кулинария', 'Купоны и скидки', 'Личный блог', 'Медицина', 'Музыка', 'Недвижимость', 'Образование', 'Общество', 'Охота и рыбалка', 'Питание', 'Политика', 'Прочее', 'Психология', 'Путешествия', 'Реклама', 'Ремонт', 'СМИ', 'Садоводство', 'Сексуальность', 'Сельское хозяйство', 'Сериалы', 'Социальная сеть', 'Спорт', 'Стиль и мода', 'Строительство', 'Телепередачи', 'Техника', 'Фильмы', 'Финансы', 'Фотография', 'Хобби', 'Эзотерика', 'Юмор'] относится сайт tvmixer.ru, основной тематикой которого является телевидение, а ключевыми словами ['телевидение', 'программы', 'новости', 'развлечения', 'онлайн.']. Ответом верни ТОЛЬКО тематику
Телепередачи
fe38ad9fda0d5f8516d9a7f8System: I will provide you with a textual user review of an Android app, aiming to categorize the reviews into specific intention classes: pbr: state a concrete problem related to a software product or service inq: asks for either new functionality, an improvement, or requests information for support irr: it does not belong to problem reports or inquires User: 'prompt:I will provide you with a textual user review of an Android app, aiming to categorize the reviews into specific intention classes: pbr: state a concrete problem related to a software product or service inq: asks for either new functionality, an improvement, or requests information for support irr: it does not belong to problem reports or inquires Does the author's intention in this app review relate to a pbr or inq or irr? review: No I don't if I did maybe a different story.' intention: ' Assistant:
irr
7d4db081925a08f6447495dcК какой тематике из списка ['Автомобили', 'Аниме', 'Бизнес', 'Видео контент', 'Видео- и компьютерные игры', 'Военное', 'Дети', 'Дорамы', 'Еда и напитки', 'Злоупотребление Веществ', 'Знаменитости', 'Инвестиции', 'Информационные технологии', 'Искусство', 'Кадровые ресурсы', 'Книги', 'Комиксы/манга', 'Кулинария', 'Купоны и скидки', 'Личный блог', 'Медицина', 'Музыка', 'Недвижимость', 'Образование', 'Общество', 'Охота и рыбалка', 'Питание', 'Политика', 'Прочее', 'Психология', 'Путешествия', 'Реклама', 'Ремонт', 'СМИ', 'Садоводство', 'Сексуальность', 'Сельское хозяйство', 'Сериалы', 'Социальная сеть', 'Спорт', 'Стиль и мода', 'Строительство', 'Телепередачи', 'Техника', 'Фильмы', 'Финансы', 'Фотография', 'Хобби', 'Эзотерика', 'Юмор'] относится сайт tvserial.online, основной тематикой которого является сериалы, а ключевыми словами ['сериалы', 'онлайн', 'серии', 'просмотр', 'кино']. Ответом верни ТОЛЬКО тематику
Сериалы
d2d6ad696d9009b646a3b2f6hi, how are you?
Hello! I'm just a program, so I don't have feelings, but I'm here and ready to help you. How can I assist you today?
70a3695c4e316c7eb7e3cc50hi, how are you?
Hello! I'm here to help, so if you have any questions or need assistance, feel free to ask. How can I assist you today?
4a597c044d839522fe4d8244hi, how are you?
Hi! I'm just a program, so I don't have feelings, but I'm here and ready to help you. How can I assist you today?