This commit is contained in:
retoor 2025-05-19 01:07:11 +02:00
parent 527b010b24
commit db5431d77d
5 changed files with 101 additions and 53 deletions

View File

@ -55,6 +55,7 @@ from snek.view.upload import UploadView
from snek.view.user import UserView from snek.view.user import UserView
from snek.view.web import WebView from snek.view.web import WebView
from snek.view.channel import ChannelAttachmentView from snek.view.channel import ChannelAttachmentView
from snek.view.settings.containers import ContainersIndexView, ContainersCreateView, ContainersUpdateView, ContainersDeleteView
from snek.webdav import WebdavApplication from snek.webdav import WebdavApplication
from snek.sgit import GitApplication from snek.sgit import GitApplication
SESSION_KEY = b"c79a0c5fda4b424189c427d28c9f7c34" SESSION_KEY = b"c79a0c5fda4b424189c427d28c9f7c34"
@ -208,6 +209,10 @@ class Application(BaseApplication):
self.router.add_view("/settings/repositories/create.html", RepositoriesCreateView) self.router.add_view("/settings/repositories/create.html", RepositoriesCreateView)
self.router.add_view("/settings/repositories/repository/{name}/update.html", RepositoriesUpdateView) self.router.add_view("/settings/repositories/repository/{name}/update.html", RepositoriesUpdateView)
self.router.add_view("/settings/repositories/repository/{name}/delete.html", RepositoriesDeleteView) self.router.add_view("/settings/repositories/repository/{name}/delete.html", RepositoriesDeleteView)
self.router.add_view("/settings/containers/index.html", ContainersIndexView)
self.router.add_view("/settings/containers/create.html", ContainersCreateView)
self.router.add_view("/settings/containers/container/{uid}/update.html", ContainersUpdateView)
self.router.add_view("/settings/containers/container/{uid}/delete.html", ContainersDeleteView)
self.webdav = WebdavApplication(self) self.webdav = WebdavApplication(self)
self.git = GitApplication(self) self.git = GitApplication(self)
self.add_subapp("/webdav", self.webdav) self.add_subapp("/webdav", self.webdav)

View File

@ -53,7 +53,8 @@ class ChatInputComponent extends HTMLElement {
this.textarea.focus(); this.textarea.focus();
} }
connectedCallback() { async connectedCallback() {
this.user = await app.rpc.getUser(null);
this.liveType = this.getAttribute("live-type") === "true"; this.liveType = this.getAttribute("live-type") === "true";
this.liveTypeInterval = parseInt(this.getAttribute("live-type-interval")) || 3; this.liveTypeInterval = parseInt(this.getAttribute("live-type-interval")) || 3;
this.channelUid = this.getAttribute("channel"); this.channelUid = this.getAttribute("channel");
@ -193,7 +194,7 @@ class ChatInputComponent extends HTMLElement {
if (this.trackSecondsBetweenEvents(this.lastUpdateEvent, new Date()) > 1) { if (this.trackSecondsBetweenEvents(this.lastUpdateEvent, new Date()) > 1) {
this.lastUpdateEvent = new Date(); this.lastUpdateEvent = new Date();
if (typeof app !== "undefined" && app.rpc && typeof app.rpc.set_typing === "function") { if (typeof app !== "undefined" && app.rpc && typeof app.rpc.set_typing === "function") {
app.rpc.set_typing(this.channelUid); app.rpc.set_typing(this.channelUid,this.user.color);
} }
} }
} }

View File

@ -1,28 +1,42 @@
/* each star */
.star { .star {
position: absolute; position: absolute;
width: 2px; width: 2px;
height: 2px; height: 2px;
background: #fff; background: var(--star-color, #fff);
border-radius: 50%; border-radius: 50%;
opacity: 0; opacity: 0;
/* flicker animation */ transition: background 0.5s ease;
animation: twinkle ease-in-out infinite; animation: twinkle ease-in-out infinite;
} }
@keyframes twinkle { @keyframes twinkle {
0%, 100% { opacity: 0; } 0%, 100% { opacity: 0; }
50% { opacity: 1; } 50% { opacity: 1; }
} }
/* optional page content */ @keyframes star-glow-frames {
.content { 0% {
box-shadow: 0 0 5px --star-color;
}
50% {
box-shadow: 0 0 20px --star-color, 0 0 30px --star-color;
}
100% {
box-shadow: 0 0 5px --star-color;
}
}
.star-glow {
animation: star-glow-frames 1s;
}
.content {
position: relative; position: relative;
z-index: 1; z-index: 1;
color: #eee; color: var(--star-content-color, #eee);
font-family: sans-serif; font-family: sans-serif;
text-align: center; text-align: center;
top: 40%; top: 40%;
transform: translateY(-40%); transform: translateY(-40%);
} }

View File

@ -1,31 +1,56 @@
<script> <script type="module">
import { app } from "/app.js";
// number of stars you want const STAR_COUNT = 200;
const STAR_COUNT = 200; const body = document.body;
const body = document.body;
for (let i = 0; i < STAR_COUNT; i++) { function createStar() {
const star = document.createElement('div'); const star = document.createElement('div');
star.classList.add('star'); star.classList.add('star');
star.style.left = `${Math.random() * 100}%`;
// random position within the viewport star.style.top = `${Math.random() * 100}%`;
star.style.left = Math.random() * 100 + '%'; const size = Math.random() * 2 + 1;
star.style.top = Math.random() * 100 + '%'; star.style.width = `${size}px`;
star.style.height = `${size}px`;
// random size (optional) const duration = Math.random() * 3 + 2;
const size = Math.random() * 2 + 1; // between 1px and 3px const delay = Math.random() * 5;
star.style.width = size + 'px'; star.style.animationDuration = `${duration}s`;
star.style.height = size + 'px'; star.style.animationDelay = `${delay}s`;
// random animation timing for natural flicker
const duration = Math.random() * 3 + 2; // 2s5s
const delay = Math.random() * 5; // 0s5s
star.style.animationDuration = duration + 's';
star.style.animationDelay = delay + 's';
body.appendChild(star); body.appendChild(star);
} }
Array.from({ length: STAR_COUNT }, createStar);
</script> function lightenColor(hex, percent) {
const num = parseInt(hex.replace("#", ""), 16);
let r = (num >> 16) + Math.round(255 * percent / 100);
let g = ((num >> 8) & 0x00FF) + Math.round(255 * percent / 100);
let b = (num & 0x0000FF) + Math.round(255 * percent / 100);
r = Math.min(255, r);
g = Math.min(255, g);
b = Math.min(255, b);
return `#${(1 << 24 | r << 16 | g << 8 | b).toString(16).slice(1)}`;
}
const originalColor = document.documentElement.style.getPropertyValue("--star-color").trim();
function glowCSSVariable(varName, glowColor, duration = 500) {
const root = document.documentElement;
//igetComputedStyle(root).getPropertyValue(varName).trim();
glowColor = lightenColor(glowColor, 20);
root.style.setProperty(varName, glowColor);
setTimeout(() => {
root.style.setProperty(varName, originalColor);
}, duration);
}
function updateStarColorDelayed(color) {
glowCSSVariable('--star-color', color, 2500);
}
app.ws.addEventListener("set_typing", (data) => {
updateStarColorDelayed(data.data.color);
});
</script>

View File

@ -36,9 +36,11 @@ class RPCView(BaseView):
async def db_update(self, table_name, record): async def db_update(self, table_name, record):
self._require_login() self._require_login()
return await self.services.db.update(self.user_uid, table_name, record) return await self.services.db.update(self.user_uid, table_name, record)
async def set_typing(self,channel_uid): async def set_typing(self,channel_uid,color=None):
self._require_login() self._require_login()
user = await self.services.user.get(self.user_uid) user = await self.services.user.get(self.user_uid)
if not color:
color = user["color"]
return await self.services.socket.broadcast(channel_uid, { return await self.services.socket.broadcast(channel_uid, {
"channel_uid": "293ecf12-08c9-494b-b423-48ba1a2d12c2", "channel_uid": "293ecf12-08c9-494b-b423-48ba1a2d12c2",
"event": "set_typing", "event": "set_typing",
@ -47,7 +49,8 @@ class RPCView(BaseView):
"user_uid": user['uid'], "user_uid": user['uid'],
"username": user["username"], "username": user["username"],
"nick": user["nick"], "nick": user["nick"],
"channel_uid": channel_uid "channel_uid": channel_uid,
"color": color
} }
}) })