Compare commits
6 Commits
bugfix/vid
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 2deb8a2069 | |||
| 87e19e3d02 | |||
|
|
31a754d264 | ||
| 767048d79b | |||
| 13181fa275 | |||
|
|
b99339ea93 |
@ -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():
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{% extends "app.html" %}
|
{% extends "app.html" %}
|
||||||
|
|
||||||
{% block header_text %}<h2 style="color:#fff">{{ name }}</h2>{% endblock %}
|
{% block header_text %}<h2 style="color:#fff">{{ name }}</h2>{% endblock %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% include "channel.html" %}
|
{% include "channel.html" %}
|
||||||
@ -8,11 +8,11 @@
|
|||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm/css/xterm.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm/css/xterm.css">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/xterm/lib/xterm.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/xterm/lib/xterm.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit/lib/xterm-addon-fit.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit/lib/xterm-addon-fit.js"></script>
|
||||||
|
|
||||||
<div id="terminal" class="hidden"></div>
|
<div id="terminal" class="hidden"></div>
|
||||||
<message-list class="chat-messages">
|
<message-list class="chat-messages">
|
||||||
{% if not messages %}
|
{% if not messages %}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h1>Welcome to your new channel!</h1>
|
<h1>Welcome to your new channel!</h1>
|
||||||
<p>This is the start of something great. Use the commands below to get started:</p>
|
<p>This is the start of something great. Use the commands below to get started:</p>
|
||||||
@ -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");
|
||||||
@ -52,7 +51,7 @@ chatInputField.autoCompletions = {
|
|||||||
"/clear": () => { messagesContainer.innerHTML = ''; },
|
"/clear": () => { messagesContainer.innerHTML = ''; },
|
||||||
"/live": () => { chatInputField.liveType = !chatInputField.liveType; },
|
"/live": () => { chatInputField.liveType = !chatInputField.liveType; },
|
||||||
"/help": showHelp,
|
"/help": showHelp,
|
||||||
"/container": async() =>{
|
"/container": async() =>{
|
||||||
containerDialog.openWithStatus()
|
containerDialog.openWithStatus()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -99,21 +98,61 @@ setInterval(() => requestIdleCallback(updateTimes), 30000);
|
|||||||
|
|
||||||
// --- Paste & drag/drop uploads ---
|
// --- Paste & drag/drop uploads ---
|
||||||
const textBox = chatInputField.textarea;
|
const textBox = chatInputField.textarea;
|
||||||
textBox.addEventListener("paste", async (e) => {
|
|
||||||
try {
|
function uploadDataTransfer(dt) {
|
||||||
const clipboardItems = await navigator.clipboard.read();
|
if (dt.items.length > 0) {
|
||||||
const dt = new DataTransfer();
|
const uploadButton = chatInputField.fileUploadGrid;
|
||||||
for (const item of clipboardItems) {
|
|
||||||
for (const type of item.types.filter(t => !t.startsWith('text/'))) {
|
for (const item of dt.items) {
|
||||||
const blob = await item.getType(type);
|
if (item.kind === "file") {
|
||||||
dt.items.add(new File([blob], "image.png", { type }));
|
const file = item.getAsFile();
|
||||||
|
if (file) {
|
||||||
|
uploadButton.uploadsStarted++
|
||||||
|
uploadButton.createTile(file)
|
||||||
|
} else {
|
||||||
|
console.error("Failed to get file from DataTransferItem");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dt.items.length > 0) {
|
}
|
||||||
const uploadButton = chatInputField.uploadButton;
|
}
|
||||||
const input = uploadButton.shadowRoot.querySelector('.file-input');
|
textBox.addEventListener("paste", async (e) => {
|
||||||
input.files = dt.files;
|
try {
|
||||||
await uploadButton.uploadFiles();
|
console.log("Pasted data:", e.clipboardData);
|
||||||
|
if (e.clipboardData.types.every(v => v.startsWith("text/"))) {
|
||||||
|
const codeType = e.clipboardData.types.find(t => !t.startsWith('text/plain') && !t.startsWith('text/html') && !t.startsWith('text/rtf'));
|
||||||
|
const probablyCode = codeType ||e.clipboardData.types.some(t => !t.startsWith('text/plain'));
|
||||||
|
for (const item of e.clipboardData.items) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uploadDataTransfer(e.clipboardData);
|
||||||
}
|
}
|
||||||
} 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();
|
||||||
@ -180,7 +213,7 @@ app.ws.addEventListener("starfield.render_word", (data) => {
|
|||||||
|
|
||||||
// --- Channel message event ---
|
// --- Channel message event ---
|
||||||
app.addEventListener("channel-message", (data) => {
|
app.addEventListener("channel-message", (data) => {
|
||||||
|
|
||||||
let display = data.text && data.text.trim() ? 'block' : 'none';
|
let display = data.text && data.text.trim() ? 'block' : 'none';
|
||||||
|
|
||||||
if (data.channel_uid !== channelUid) {
|
if (data.channel_uid !== channelUid) {
|
||||||
@ -221,7 +254,7 @@ document.addEventListener('keydown', function(event) {
|
|||||||
clearTimeout(keyTimeout);
|
clearTimeout(keyTimeout);
|
||||||
keyTimeout = setTimeout(() => { gPressCount = 0; }, 300);
|
keyTimeout = setTimeout(() => { gPressCount = 0; }, 300);
|
||||||
if (gPressCount === 2) {
|
if (gPressCount === 2) {
|
||||||
gPressCount = 0;
|
gPressCount = 0;
|
||||||
messagesContainer.lastElementChild?.scrollIntoView({ block: "end", inline: "nearest" });
|
messagesContainer.lastElementChild?.scrollIntoView({ block: "end", inline: "nearest" });
|
||||||
loadExtra();
|
loadExtra();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user