Compare commits

..

6 Commits

3 changed files with 81 additions and 36 deletions

View File

@ -6,7 +6,6 @@ import uuid
import signal import signal
from datetime import datetime from datetime import datetime
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
import aiohttp_debugtoolbar
from snek import snode from snek import snode
from snek.view.threads import ThreadsView from snek.view.threads import ThreadsView
@ -497,7 +496,6 @@ class Application(BaseApplication):
raise raised_exception raise raised_exception
app = Application(db_path="sqlite:///snek.db") app = Application(db_path="sqlite:///snek.db")
#aiohttp_debugtoolbar.setup(app)
async def main(): async def main():

View File

@ -407,34 +407,48 @@ a {
width: 250px; width: 250px;
padding-left: 20px; padding-left: 20px;
padding-right: 20px; padding-right: 20px;
padding-top: 10px; padding-top: 20px;
overflow-y: auto; overflow-y: auto;
grid-area: sidebar; grid-area: sidebar;
} }
.sidebar h2 { .sidebar h2 {
color: #f05a28; color: #f05a28;
font-size: 1.2em; font-size: 0.75em;
margin-bottom: 20px; font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 10px;
margin-top: 20px;
}
.sidebar h2:first-child {
margin-top: 0;
} }
.sidebar ul { .sidebar ul {
list-style: none; list-style: none;
margin-bottom: 15px;
} }
.sidebar ul li { .sidebar ul li {
margin-bottom: 15px; margin-bottom: 4px;
} }
.sidebar ul li a { .sidebar ul li a {
color: #ccc; color: #ccc;
text-decoration: none; text-decoration: none;
font-size: 1em; font-size: 0.9em;
transition: color 0.3s; transition: color 0.3s;
display: block;
padding: 4px 8px;
border-radius: 4px;
transition: background-color 0.2s, color 0.2s;
} }
.sidebar ul li a:hover { .sidebar ul li a:hover {
color: #fff; color: #fff;
background-color: rgba(255, 255, 255, 0.05);
} }
@keyframes glow { @keyframes glow {

View File

@ -37,10 +37,9 @@
{% include "dialog_help.html" %} {% include "dialog_help.html" %}
{% include "dialog_online.html" %} {% include "dialog_online.html" %}
<script type="module"> <script type="module">
import { app } from "/app.js"; import {app} from "/app.js";
import { Schedule } from "/schedule.js";
// --- Cache selectors --- // --- Cache selectors ---
const chatInputField = document.querySelector("chat-input"); const chatInputField = document.querySelector("chat-input");
const messagesContainer = document.querySelector(".chat-messages"); const messagesContainer = document.querySelector(".chat-messages");
const chatArea = document.querySelector(".chat-area"); const chatArea = document.querySelector(".chat-area");
@ -99,21 +98,61 @@ setInterval(() => requestIdleCallback(updateTimes), 30000);
// --- Paste & drag/drop uploads --- // --- Paste & drag/drop uploads ---
const textBox = chatInputField.textarea; const textBox = chatInputField.textarea;
function uploadDataTransfer(dt) {
if (dt.items.length > 0) {
const uploadButton = chatInputField.fileUploadGrid;
for (const item of dt.items) {
if (item.kind === "file") {
const file = item.getAsFile();
if (file) {
uploadButton.uploadsStarted++
uploadButton.createTile(file)
} else {
console.error("Failed to get file from DataTransferItem");
}
}
}
}
}
textBox.addEventListener("paste", async (e) => { textBox.addEventListener("paste", async (e) => {
try { try {
const clipboardItems = await navigator.clipboard.read(); console.log("Pasted data:", e.clipboardData);
const dt = new DataTransfer(); if (e.clipboardData.types.every(v => v.startsWith("text/"))) {
for (const item of clipboardItems) { const codeType = e.clipboardData.types.find(t => !t.startsWith('text/plain') && !t.startsWith('text/html') && !t.startsWith('text/rtf'));
for (const type of item.types.filter(t => !t.startsWith('text/'))) { const probablyCode = codeType ||e.clipboardData.types.some(t => !t.startsWith('text/plain'));
const blob = await item.getType(type); for (const item of e.clipboardData.items) {
dt.items.add(new File([blob], "image.png", { type })); if (item.kind === "string" && item.type === "text/plain") {
e.preventDefault();
item.getAsString(text => {
const value = chatInputField.value;
if (probablyCode) {
let code = text;
const minIndentDepth = code.split('\n').reduce((acc, line) => {
if (!line.trim()) return acc;
const match = line.match(/^(\s*)/);
return match ? Math.min(acc, match[1].length) : acc;
}, 9000);
code = code.split('\n').map(line => line.slice(minIndentDepth)).join('\n')
text = `\`\`\`${codeType?.split('/')?.[1] ?? ''}\n${code}\n\`\`\`\n`;
}
const area = chatInputField.textarea
if(area){
const start = area.selectionStart
if ("\n" !== value[start - 1]) {
text = `\n${text}`;
}
document.execCommand('insertText', false, text);
}
});
break;
} }
} }
if (dt.items.length > 0) { } else {
const uploadButton = chatInputField.uploadButton; uploadDataTransfer(e.clipboardData);
const input = uploadButton.shadowRoot.querySelector('.file-input');
input.files = dt.files;
await uploadButton.uploadFiles();
} }
} catch (error) { } catch (error) {
console.error("Failed to read clipboard contents: ", error); console.error("Failed to read clipboard contents: ", error);
@ -121,13 +160,7 @@ textBox.addEventListener("paste", async (e) => {
}); });
chatArea.addEventListener("drop", async (e) => { chatArea.addEventListener("drop", async (e) => {
e.preventDefault(); e.preventDefault();
const dt = e.dataTransfer; uploadDataTransfer(e.dataTransfer);
if (dt.items.length > 0) {
const uploadButton = chatInputField.uploadButton;
const input = uploadButton.shadowRoot.querySelector('.file-input');
input.files = dt.files;
await uploadButton.uploadFiles();
}
}); });
chatArea.addEventListener("dragover", e => { chatArea.addEventListener("dragover", e => {
e.preventDefault(); e.preventDefault();