disable inter's on-by-default ligatures, redo icon/emote CSS
also: * proper styling for auth page * clean-up of html, css, js * fix scroll indicator eating mouse events * fix scroll indicator on (mobile?) chrome * re-calculate timestamp width on each new message, to account for unknown/unexpected formats like the inclusion of AM/PM
This commit is contained in:
@@ -50,7 +50,7 @@ public class Processing
|
|||||||
if (chunk is IconChunk { } icon)
|
if (chunk is IconChunk { } icon)
|
||||||
{
|
{
|
||||||
return IconUtil.GfdFileView.TryGetEntry((uint) icon.Icon, out _)
|
return IconUtil.GfdFileView.TryGetEntry((uint) icon.Icon, out _)
|
||||||
? $"<span class=\"gfd-icon gfd-icon-hq-{(uint)icon.Icon}\" style=\"zoom:calc(16 * 4 / 3 / 40 * 1.4)\"></span>"
|
? $"<span class=\"gfd-icon gfd-icon-hq-{(uint)icon.Icon}\"></span>"
|
||||||
: "";
|
: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ public class Processing
|
|||||||
// The emote name should be safe, it is checked against a list from BTTV.
|
// The emote name should be safe, it is checked against a list from BTTV.
|
||||||
// Still sanitizing it for the extra safety.
|
// Still sanitizing it for the extra safety.
|
||||||
if (image is { Failed: false })
|
if (image is { Failed: false })
|
||||||
return $"<span style=\"zoom:calc(16 * 4 / 3 / 40 * 1.4)\"><img class=\"emote-icon emote-icon-hq\" src=\"/emote/{Sanitizer.Sanitize(emotePayload.Code)}\"></span>";
|
return $"<span class=\"emote-icon\"><img src=\"/emote/{Sanitizer.Sanitize(emotePayload.Code)}\"></span>";
|
||||||
}
|
}
|
||||||
|
|
||||||
var colour = text.Foreground;
|
var colour = text.Foreground;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
* {
|
* {
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
font-family: Lodestone, 'Inter var', sans-serif;
|
font-family: Lodestone, 'Inter var', sans-serif;
|
||||||
font-feature-settings: 'tnum';
|
font-feature-settings: 'tnum', 'calt' 0; /* calt appears to be on by default */
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
@@ -52,17 +52,58 @@ body {
|
|||||||
background-color: var(--bg-input-hover);
|
background-color: var(--bg-input-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > main {
|
body > main.chat {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: var(--bg);
|
background-color: var(--bg);
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
|
|
||||||
box-shadow: 0 0 50px 0 rgba(0, 0, 0, 0.5);
|
box-shadow: 0 0 50px 0 rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body > main.auth {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 20px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
input { width: 150px; }
|
||||||
|
|
||||||
|
input, button {
|
||||||
|
padding: 5px 20px;
|
||||||
|
font-size: 1rem;
|
||||||
|
border: 3px solid transparent;
|
||||||
|
border-radius: 20px;
|
||||||
|
background-color: var(--bg-input);
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 2px solid var(--focus-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 5px 15px;
|
||||||
|
border: 3px solid var(--bg-input);
|
||||||
|
background-image: var(--gradient-clickable);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--bg-input-hover);
|
||||||
|
background-color: var(--bg-input-hover);
|
||||||
|
background-image: var(--gradient-clickable-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* message list */
|
/* message list */
|
||||||
section#messages {
|
section#messages {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -84,6 +125,7 @@ section#messages {
|
|||||||
width: calc(100% - 200px);
|
width: calc(100% - 200px);
|
||||||
height: 200px;
|
height: 200px;
|
||||||
background-image: radial-gradient(50% 20% at 50% 100%, #60a0ff40, transparent);
|
background-image: radial-gradient(50% 20% at 50% 100%, #60a0ff40, transparent);
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,6 +143,10 @@ section#messages {
|
|||||||
color: var(--fg-faint);
|
color: var(--fg-faint);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,18 +262,54 @@ section#input {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* icons, emotes */
|
||||||
|
.gfd-icon {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: middle;
|
||||||
|
zoom: 0.75;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emote-icon {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 2rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
img {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 2rem;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*** mobile ***/
|
/*** mobile ***/
|
||||||
@media ((max-width: 600px) and (orientation: portrait)) or (max-width: 400px) {
|
@media ((max-width: 600px) and (orientation: portrait)) or (max-width: 400px) {
|
||||||
body {
|
body {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
body > main {
|
body > main {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
section#input {
|
section#input {
|
||||||
button {
|
button {
|
||||||
max-width: 0;
|
max-width: 0;
|
||||||
padding-left: 1.5rem;
|
padding-left: 1.5rem;
|
||||||
@@ -239,5 +321,5 @@ section#input {
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%) translateY(-50%);
|
transform: translateX(-50%) translateY(-50%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
// websocket connection
|
// websocket connection
|
||||||
class SSEConnection {
|
class SSEConnection {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.socket = new EventSource('/sse', );
|
this.socket = new EventSource('/sse');
|
||||||
|
|
||||||
this.socket.addEventListener('close', (event) => {
|
this.socket.addEventListener('close', () => {
|
||||||
console.log("Closing SSE connection.")
|
console.log('Closing SSE connection.');
|
||||||
this.socket.close()
|
this.socket.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.addEventListener('switch-channel', (event) => {
|
this.socket.addEventListener('switch-channel', (event) => {
|
||||||
updateChannelHint(JSON.parse(event.data).channel)
|
updateChannelHint(JSON.parse(event.data).channel);
|
||||||
});
|
});
|
||||||
|
|
||||||
// New messages that are able to be directly processed
|
// New messages that are able to be directly processed
|
||||||
this.socket.addEventListener('new-message', (event) => {
|
this.socket.addEventListener('new-message', (event) => {
|
||||||
for (let message of JSON.parse(event.data).messages)
|
for (let message of JSON.parse(event.data).messages) {
|
||||||
{
|
|
||||||
addMessage(message);
|
addMessage(message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -23,15 +22,13 @@ class SSEConnection {
|
|||||||
// New messages, that require a clean message list before processing
|
// New messages, that require a clean message list before processing
|
||||||
this.socket.addEventListener('bulk-messages', (event) => {
|
this.socket.addEventListener('bulk-messages', (event) => {
|
||||||
clearMessages();
|
clearMessages();
|
||||||
|
for (let message of JSON.parse(event.data).messages) {
|
||||||
for (let message of JSON.parse(event.data).messages)
|
|
||||||
{
|
|
||||||
addMessage(message);
|
addMessage(message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.addEventListener('channel-list', (event) => {
|
this.socket.addEventListener('channel-list', (event) => {
|
||||||
updateChannelOptions(JSON.parse(event.data).channels)
|
updateChannelOptions(JSON.parse(event.data).channels);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +53,7 @@ document.getElementById('channel-select').addEventListener('change', (event) =>
|
|||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({channel: event.target.value})
|
body: JSON.stringify({ channel: event.target.value })
|
||||||
});
|
});
|
||||||
const content = await rawResponse.json();
|
const content = await rawResponse.json();
|
||||||
|
|
||||||
@@ -71,30 +68,39 @@ function updateChannelOptions(channels) {
|
|||||||
// clear existing channels
|
// clear existing channels
|
||||||
select.innerHTML = '';
|
select.innerHTML = '';
|
||||||
|
|
||||||
for (let [ name, channel ] of Object.entries(channels))
|
for (const [ name, channel ] of Object.entries(channels)) {
|
||||||
{
|
|
||||||
let option = document.createElement('option');
|
let option = document.createElement('option');
|
||||||
option.text = name;
|
option.text = name;
|
||||||
option.value = channel;
|
option.value = channel;
|
||||||
|
|
||||||
select.appendChild(option)
|
select.appendChild(option)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// functions for handling the message list
|
// functions for handling the message list
|
||||||
function scrollMessagesToBottom() {
|
|
||||||
const messagesContainer = document.querySelector('#messages > .scroll-container');
|
|
||||||
messagesContainer.scrollTop = messagesContainer.scrollHeight - messagesContainer.offsetHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
function messagesContainerIsScrolledToBottom() {
|
function messagesContainerIsScrolledToBottom() {
|
||||||
const messagesContainer = document.querySelector('#messages > .scroll-container');
|
const messagesContainer = document.querySelector('#messages > .scroll-container');
|
||||||
return messagesContainer.scrollTop == messagesContainer.scrollHeight - messagesContainer.offsetHeight;
|
return messagesContainer.scrollTop >= messagesContainer.scrollHeight - messagesContainer.offsetHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate timestamp width
|
||||||
|
// to ensure that all timestamps have the same width. some typefaces have the same width across
|
||||||
|
// all number glyphs, others do not. the solution below is very rudimentary; at the very least,
|
||||||
|
// delaying it to account for font loading might make sense. perhaps there's an even better way?
|
||||||
|
let maxTimestampWidth = 0;
|
||||||
|
function calculateTimestampWidth(timestamp) {
|
||||||
|
const widthProbe = document.getElementById('timestamp-width-probe');
|
||||||
|
widthProbe.innerText = timestamp;
|
||||||
|
|
||||||
|
if (widthProbe.clientWidth > maxTimestampWidth) {
|
||||||
|
maxTimestampWidth = widthProbe.clientWidth;
|
||||||
|
document.body.style.setProperty('--timestamp-width', (Math.ceil(maxTimestampWidth) + 1) + 'px');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addMessage(messageData) {
|
function addMessage(messageData) {
|
||||||
const scrolledToBottom = messagesContainerIsScrolledToBottom();
|
const scrolledToBottom = messagesContainerIsScrolledToBottom();
|
||||||
|
calculateTimestampWidth(messageData.timestamp);
|
||||||
|
|
||||||
const liMessage = document.createElement('li');
|
const liMessage = document.createElement('li');
|
||||||
const spanTimestamp = document.createElement('span');
|
const spanTimestamp = document.createElement('span');
|
||||||
@@ -102,16 +108,15 @@ function addMessage(messageData) {
|
|||||||
const spanMessage = document.createElement('span');
|
const spanMessage = document.createElement('span');
|
||||||
spanMessage.classList.add('message');
|
spanMessage.classList.add('message');
|
||||||
|
|
||||||
// suggestions, update with actual data model
|
|
||||||
spanTimestamp.innerText = messageData.timestamp;
|
spanTimestamp.innerText = messageData.timestamp;
|
||||||
spanMessage.innerHTML = messageData.messageHTML; // or build HTML in here
|
spanMessage.innerHTML = messageData.messageHTML;
|
||||||
|
|
||||||
liMessage.appendChild(spanTimestamp);
|
liMessage.appendChild(spanTimestamp);
|
||||||
liMessage.appendChild(spanMessage);
|
liMessage.appendChild(spanMessage);
|
||||||
document.getElementById('messages-list').appendChild(liMessage);
|
document.getElementById('messages-list').appendChild(liMessage);
|
||||||
|
|
||||||
if (scrolledToBottom) {
|
if (scrolledToBottom) {
|
||||||
scrollMessagesToBottom();
|
liMessage.scrollIntoView();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +124,7 @@ function clearMessages() {
|
|||||||
document.getElementById('messages-list').innerHTML = '';
|
document.getElementById('messages-list').innerHTML = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// add indicator signaling more messages
|
||||||
document.querySelector('#messages > .scroll-container').addEventListener('scroll', () => {
|
document.querySelector('#messages > .scroll-container').addEventListener('scroll', () => {
|
||||||
const messagesContainer = document.querySelector('#messages > .scroll-container');
|
const messagesContainer = document.querySelector('#messages > .scroll-container');
|
||||||
if (!messagesContainerIsScrolledToBottom()) {
|
if (!messagesContainerIsScrolledToBottom()) {
|
||||||
@@ -137,14 +142,6 @@ document.querySelector('#input > form').addEventListener('submit', (event) => {
|
|||||||
const chatInput = document.getElementById('chat-input');
|
const chatInput = document.getElementById('chat-input');
|
||||||
const message = chatInput.value;
|
const message = chatInput.value;
|
||||||
|
|
||||||
fetch('/send', {
|
|
||||||
method: 'POST',
|
|
||||||
body: message,
|
|
||||||
headers: {
|
|
||||||
'Content-type': 'application/txt; charset=UTF-8'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const rawResponse = await fetch('/send', {
|
const rawResponse = await fetch('/send', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -152,7 +149,7 @@ document.querySelector('#input > form').addEventListener('submit', (event) => {
|
|||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({message: message})
|
body: JSON.stringify({ message: message })
|
||||||
});
|
});
|
||||||
const content = await rawResponse.json();
|
const content = await rawResponse.json();
|
||||||
|
|
||||||
@@ -164,26 +161,14 @@ document.querySelector('#input > form').addEventListener('submit', (event) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// calculate timestamp width
|
// from kizer, gfd icons
|
||||||
// to ensure that all timestamps have the same width. some typefaces have the same width across
|
|
||||||
// all number glyphs, others do not. the solution below is very rudimentary; at the very least,
|
|
||||||
// delaying it to account for font loading might make sense. perhaps there's an even better way?
|
|
||||||
window.setTimeout(() => {
|
|
||||||
const widthProbe = document.getElementById('timestamp-width-probe');
|
|
||||||
widthProbe.innerText = '88:88'; // assume 8 to be widest glyph
|
|
||||||
document.body.style.setProperty('--timestamp-width', Math.ceil(widthProbe.clientWidth) + 'px');
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
// From kizer
|
|
||||||
async function AddGfdStylesheet(gfdPath, texPath) {
|
async function AddGfdStylesheet(gfdPath, texPath) {
|
||||||
const texPromise = LoadTexAsBlob(texPath);
|
const texPromise = LoadTexAsBlob(texPath);
|
||||||
const gfdPromise = LoadGfd(gfdPath);
|
const gfdPromise = LoadGfd(gfdPath);
|
||||||
const texUrl = URL.createObjectURL(await texPromise);
|
const texUrl = URL.createObjectURL(await texPromise);
|
||||||
const gfd = await gfdPromise;
|
const gfd = await gfdPromise;
|
||||||
let width = 0;
|
|
||||||
let height = 0;
|
|
||||||
|
|
||||||
let stylesheets = [];
|
const stylesheets = [];
|
||||||
for (const entry of gfd) {
|
for (const entry of gfd) {
|
||||||
if (entry.width * entry.height <= 0)
|
if (entry.width * entry.height <= 0)
|
||||||
continue;
|
continue;
|
||||||
@@ -202,32 +187,30 @@ async function AddGfdStylesheet(gfdPath, texPath) {
|
|||||||
`background-position: -${entry.left}px -${entry.top}px`,
|
`background-position: -${entry.left}px -${entry.top}px`,
|
||||||
`background-image: url('${texUrl}')`,
|
`background-image: url('${texUrl}')`,
|
||||||
`width: ${entry.width}px`,
|
`width: ${entry.width}px`,
|
||||||
`height: ${entry.height}px`,
|
`height: ${entry.height}px`
|
||||||
`margin-bottom: -20px`
|
].join(';'),
|
||||||
].join(";"),
|
|
||||||
[
|
[
|
||||||
`background-position: -${entry.left * 2}px -${entry.top * 2 + 341}px`,
|
`background-position: -${entry.left * 2}px -${entry.top * 2 + 341}px`,
|
||||||
`background-image: url('${texUrl}')`,
|
`background-image: url('${texUrl}')`,
|
||||||
`width: ${entry.width * 2}px`,
|
`width: ${entry.width * 2}px`,
|
||||||
`height: ${entry.height * 2}px`,
|
`height: ${entry.height * 2}px`
|
||||||
`margin-bottom: -40px`
|
].join(';'),
|
||||||
].join(";")
|
entry.width
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
let stylesheet = ".gfd-icon::before { content: ''; display: inline-block; overflow: hidden; vertical-align: top; height:0; }\n";
|
let stylesheet = '';
|
||||||
for (const entry of stylesheets) {
|
for (const entry of stylesheets) {
|
||||||
if (!entry)
|
if (!entry)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
stylesheet += `\n${entry[0].map(x => `.gfd-icon.gfd-icon-${x}::before`).join(', ')}{${entry[1]};}`;
|
stylesheet += `\n${entry[0].map(x => `.gfd-icon.gfd-icon-${x}::before`).join(', ')}{${entry[1]};}`;
|
||||||
stylesheet += `\n${entry[0].map(x => `.gfd-icon.gfd-icon-hq-${x}::before`).join(', ')}{${entry[2]};}`;
|
stylesheet += `\n${entry[0].map(x => `.gfd-icon.gfd-icon-hq-${x}::before`).join(', ')}{${entry[2]};}`;
|
||||||
|
stylesheet += `\n${entry[0].map(x => `.gfd-icon.gfd-icon-${x}`).join(', ')}{width:${entry[3]}px;}`;
|
||||||
|
stylesheet += `\n${entry[0].map(x => `.gfd-icon.gfd-icon-hq-${x}`).join(', ')}{width:${entry[3] * 2}px;}`;
|
||||||
}
|
}
|
||||||
stylesheet += "\n.emote-icon { content: ''; display: inline-block; overflow: hidden; vertical-align: top; }";
|
|
||||||
stylesheet += `\n.emote-icon.emote-icon-hq {width: ${width * 2}px; height: ${height * 2}px; margin-bottom: -40px}`;
|
|
||||||
|
|
||||||
const styleNode = document.createElement("style");
|
const styleNode = document.createElement('style');
|
||||||
styleNode.type = "text/css";
|
|
||||||
styleNode.appendChild(document.createTextNode(stylesheet));
|
styleNode.appendChild(document.createTextNode(stylesheet));
|
||||||
document.head.appendChild(styleNode);
|
document.head.appendChild(styleNode);
|
||||||
}
|
}
|
||||||
@@ -235,7 +218,7 @@ async function AddGfdStylesheet(gfdPath, texPath) {
|
|||||||
async function LoadTexAsBlob(path) {
|
async function LoadTexAsBlob(path) {
|
||||||
const tex = ParseTex(await (await fetch(path)).arrayBuffer());
|
const tex = ParseTex(await (await fetch(path)).arrayBuffer());
|
||||||
if (tex.format !== 0x1450) // B8G8R8A8
|
if (tex.format !== 0x1450) // B8G8R8A8
|
||||||
throw "Not supported";
|
throw 'Not supported';
|
||||||
|
|
||||||
const dataArray = new Uint8ClampedArray(tex.buffer, tex.offsetToSurface[0], tex.width * tex.height * 4);
|
const dataArray = new Uint8ClampedArray(tex.buffer, tex.offsetToSurface[0], tex.width * tex.height * 4);
|
||||||
for (let i = 0; i < dataArray.length; i += 4) {
|
for (let i = 0; i < dataArray.length; i += 4) {
|
||||||
@@ -297,4 +280,4 @@ function ParseTex(arrayBuffer) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
AddGfdStylesheet("/files/gfdata.gfd", "/files/fonticon_ps5.tex");
|
AddGfdStylesheet('/files/gfdata.gfd', '/files/fonticon_ps5.tex');
|
||||||
|
|||||||
@@ -3,17 +3,20 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>Authentication</title>
|
<title>Authentication</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="/static/start.css">
|
<meta charset="utf-8">
|
||||||
</head>
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<body>
|
|
||||||
<div style="text-align: center;">
|
|
||||||
<h3 style="color: #fff">Authcode</h3>
|
|
||||||
<form action="/auth" method="POST">
|
|
||||||
<input style="color: #fff; background: #121212" type="password" id="authcode" name="authcode">
|
|
||||||
<input style="color: #fff; background: #121212" type="submit" value="Submit">
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="static/start.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<main class="auth">
|
||||||
|
<h1>Authcode</h1>
|
||||||
|
<form action="/auth" method="POST">
|
||||||
|
<input type="password" name="authcode">
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
<img src="/emote/Sure">
|
<img src="/emote/Sure">
|
||||||
</div>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Binary file not shown.
Reference in New Issue
Block a user