Merge remote-tracking branch 'upstream/main' into svg-masks

This commit is contained in:
ygg2 2024-08-30 05:18:38 -04:00
commit 616c60ba04
13 changed files with 468 additions and 28 deletions

62
.dist/home.js Normal file
View file

@ -0,0 +1,62 @@
import { mobile } from "./login.js";
console.log(mobile);
const serverbox = document.getElementById("instancebox");
fetch("/instances.json").then(_ => _.json()).then((json) => {
console.warn(json);
for (const instance of json) {
if (instance.display === false) {
continue;
}
const div = document.createElement("div");
div.classList.add("flexltr", "instance");
if (instance.image) {
const img = document.createElement("img");
img.src = instance.image;
div.append(img);
}
const statbox = document.createElement("div");
statbox.classList.add("flexttb");
{
const textbox = document.createElement("div");
textbox.classList.add("flexttb", "instatancetextbox");
const title = document.createElement("h2");
title.innerText = instance.name;
if (instance.online !== undefined) {
const status = document.createElement("span");
status.innerText = instance.online ? "Online" : "Offline";
status.classList.add("instanceStatus");
title.append(status);
}
textbox.append(title);
if (instance.description || instance.descriptionLong) {
const p = document.createElement("p");
if (instance.descriptionLong) {
p.innerText = instance.descriptionLong;
}
else {
p.innerText = instance.description;
}
textbox.append(p);
}
statbox.append(textbox);
}
if (instance.uptime) {
const stats = document.createElement("div");
stats.classList.add("flexltr");
const span = document.createElement("span");
span.innerText = `Uptime: All time: ${Math.floor(instance.uptime.alltime * 100)}% This week: ${Math.floor(instance.uptime.weektime * 100)}% Today: ${Math.floor(instance.uptime.daytime * 100)}%`;
stats.append(span);
statbox.append(stats);
}
div.append(statbox);
div.onclick = _ => {
if (instance.online) {
window.location.href = "/register.html?instance=" + encodeURI(instance.name);
}
else {
alert("Instance is offline, can't connect");
}
};
serverbox.append(div);
}
});

View file

@ -419,13 +419,13 @@ if (datalist) {
console.warn(json); console.warn(json);
if (instancein && instancein.value === "") { if (instancein && instancein.value === "") {
instancein.value = json[0].name; instancein.value = json[0].name;
setTimeout(checkInstance, 10);
} }
for (const instance of json) { for (const instance of json) {
if (instance.display === false) { if (instance.display === false) {
continue; continue;
} }
const option = document.createElement("option"); const option = document.createElement("option");
option.disabled = !instance.online;
option.value = instance.name; option.value = instance.name;
if (instance.url) { if (instance.url) {
stringURLMap.set(option.value, instance.url); stringURLMap.set(option.value, instance.url);
@ -447,5 +447,6 @@ if (datalist) {
} }
datalist.append(option); datalist.append(option);
} }
checkInstance("");
}); });
} }

2
.gitignore vendored
View file

@ -132,3 +132,5 @@ dist
#Data file for tomoato #Data file for tomoato
testAccount.json testAccount.json
CC CC
uptime.json
.directory

View file

@ -31,6 +31,7 @@ anything with a `?` in-front of its `:` are optional, though you must either inc
Some of these values may not be used right now, though they will likely be used in the future, so feel free to fill out what you like, though the more you fill out the more information we can give the users about your instance in the future. Some of these values may not be used right now, though they will likely be used in the future, so feel free to fill out what you like, though the more you fill out the more information we can give the users about your instance in the future.
language should be [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1_codes). language should be [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1_codes).
Country should be [ISO 8166-2 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Country should be [ISO 8166-2 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2).
You can also add yourself to [this](https://github.com/spacebarchat/spacebarchat/tree/master/instances) list, and you should, though there are some disadvantages to only being in that list
# Questions # Questions
## Do I have to do this to let Jank Client connect to my server? ## Do I have to do this to let Jank Client connect to my server?
No, you may choose to not do this, this just makes it easier for people using Jank Client to find and use your instance as it's in the dropdown menu for instances, though the user may enter any instance they please. No, you may choose to not do this, this just makes it easier for people using Jank Client to find and use your instance as it's in the dropdown menu for instances, though the user may enter any instance they please.
@ -38,3 +39,5 @@ No, you may choose to not do this, this just makes it easier for people using Ja
If it's spacebar compatable, yes it may be entered, though if there are too many incompatablities, it may not be included, or may need a warning of sorts. If it's spacebar compatable, yes it may be entered, though if there are too many incompatablities, it may not be included, or may need a warning of sorts.
## I'm hosting my own instance of spacebar and would like to change the defualt instance on my instance of Jank Client to my own instance. ## I'm hosting my own instance of spacebar and would like to change the defualt instance on my instance of Jank Client to my own instance.
Just change the first entry in the list to your own, and it should connect without issue. Just change the first entry in the list to your own, and it should connect without issue.
## Why would I put my instance in this list over the official spacebar list?
While putting your instance in the other list will get it to show up on jank client, this list does have more settings, and will show up earlier in the results, though either list will work to get in the dropdown menu

View file

@ -4,7 +4,8 @@ const compression = require('compression')
const express = require('express'); const express = require('express');
const fs = require('fs'); const fs = require('fs');
const app = express(); const app = express();
const instances=require("./webpage/instances.json") const instances=require("./webpage/instances.json");
const stats=require("./stats.js");
const instancenames=new Map(); const instancenames=new Map();
for(const instance of instances){ for(const instance of instances){
instancenames.set(instance.name,instance); instancenames.set(instance.name,instance);
@ -12,11 +13,8 @@ for(const instance of instances){
app.use(compression()) app.use(compression())
fetch("https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/instances/instances.json").then(_=>_.json()).then(json=>{ fetch("https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/instances/instances.json").then(_=>_.json()).then(json=>{
for(const instance of json){ for(const instance of json){
console.log(instance);
if(!instancenames.has(instance.name)){ if(!instancenames.has(instance.name)){
console.log("pushed");
instances.push(instance); instances.push(instance);
console.log(instances)
}else{ }else{
const ofinst=instancenames.get(instance.name) const ofinst=instancenames.get(instance.name)
for(const key of Object.keys(instance)){ for(const key of Object.keys(instance)){
@ -26,6 +24,7 @@ fetch("https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/instan
} }
} }
} }
stats.observe(instances)
}) })
app.use("/getupdates",(req, res) => { app.use("/getupdates",(req, res) => {
@ -147,6 +146,12 @@ async function inviteres(req,res){
app.use('/services/oembed', (req, res) => { app.use('/services/oembed', (req, res) => {
inviteres(req, res); inviteres(req, res);
}) })
app.use("/uptime",(req,res)=>{
console.log(req.query.name)
const uptime=stats.uptime[req.query.name];
console.log(req.query.name,uptime,stats.uptime)
res.send(uptime);
})
app.use('/', async (req, res) => { app.use('/', async (req, res) => {
const scheme = req.secure ? 'https' : 'http'; const scheme = req.secure ? 'https' : 'http';
const host=`${scheme}://${req.get("Host")}`; const host=`${scheme}://${req.get("Host")}`;
@ -158,13 +163,15 @@ app.use('/', async (req, res) => {
}else{ }else{
console.log(req); console.log(req);
} }
if(req.path==="/"){
res.sendFile(`./webpage/home.html`, {root: __dirname});
return;
}
if(debugging&&req.path.startsWith("/service.js")){ if(debugging&&req.path.startsWith("/service.js")){
res.send("dud"); res.send("dud");
return; return;
} }
if(req.path.startsWith("/instances.json")){ if(req.path.startsWith("/instances.json")){
console.log("grabbed")
res.send(JSON.stringify(instances)); res.send(JSON.stringify(instances));
return; return;
} }
@ -191,3 +198,5 @@ app.use('/', async (req, res) => {
const PORT = process.env.PORT || +process.argv[1] || 8080; const PORT = process.env.PORT || +process.argv[1] || 8080;
app.listen(PORT, () => {}); app.listen(PORT, () => {});
console.log("this ran :P"); console.log("this ran :P");
exports.getapiurls=getapiurls;

152
stats.js Normal file
View file

@ -0,0 +1,152 @@
const index = require('./index.js');
const fs=require("fs");
let uptimeObject={};
if(fs.existsSync("./uptime.json")){
try{
uptimeObject=JSON.parse(fs.readFileSync('./uptime.json', 'utf8'));
}catch{
uptimeObject={};
}
}
if(uptimeObject["undefined"]){
delete uptimeObject["undefined"];
updatejson();
}
async function observe(instances){
const active=new Set();
async function resolveinstance(instance){
calcStats(instance);
let api;
if(instance.urls){
api=instance.urls.api;
}else if(instance.url){
const urls=await index.getapiurls(instance.url);
if(urls){
api=urls.api;
}
}
if(!api||api===""){
setStatus(instance,false);
console.warn(instance.name+" does not resolve api URL");
setTimeout(_=>{resolveinstance(instance)},1000*60*30,);
return
}
active.add(instance.name);
api+=api.endsWith("/")?"":"/"
function check(){
fetch(api+"ping").then(_=>{
setStatus(instance,_.ok);
})
}
setTimeout(
_=>{
check();
setInterval(_=>{
check();
},1000*60*30)
},Math.random()*1000*60*10
)
}
const promlist=[];
for(const instance of instances){
promlist.push(resolveinstance(instance));
}
await Promise.allSettled(promlist);
for(const key of Object.keys(uptimeObject)){
if(!active.has(key)){
setStatus(key,false);
}
}
}
function calcStats(instance){
let obj=uptimeObject[instance.name];
if(!obj) return;
const day=Date.now()-1000*60*60*24;
const week=Date.now()-1000*60*60*24*7;
let alltime=-1;
let totalTimePassed=0;
let laststamp=0;
let daytime=-1;
let weektime=-1;
let online=false;
for(const thing of obj){
const stamp=thing.time;
if(alltime===-1){
laststamp=stamp;
alltime=0;
}
const timepassed=stamp-laststamp;
totalTimePassed+=timepassed;
alltime+=online*timepassed;
if(stamp>week){
if(weektime===-1){
weektime=online*(stamp-week);
}else{
weektime+=online*timepassed;
}
if(stamp>day){
if(daytime===-1){
daytime=online*(stamp-day);
}else{
daytime+=online*timepassed;
}
}
}
online=thing.online;
}
instance.online=online;
const timepassed=Date.now()-laststamp;
totalTimePassed+=timepassed;
daytime+=online*Math.min(timepassed,1000*60*60*24);
weektime+=online*Math.min(timepassed,1000*60*60*24*7);
alltime+=online*timepassed;
alltime/=totalTimePassed;
if(timepassed>1000*60*60*24){
daytime/=1000*60*60*24;
if(timepassed>1000*60*60*24*7){
weektime/=1000*60*60*24*7;
}else{
weektime=alltime;
}
}else{
weektime=alltime
daytime=alltime;
}
instance.uptime={daytime,weektime,alltime}
}
/**
* @param {string|Object} instance
* @param {boolean} status
*/
function setStatus(instance,status){
let name=instance.name;
if(typeof instance==="string" ){
name=instance;
}
let obj=uptimeObject[name];
let needSetting=false;
if(!obj){
obj=[];
uptimeObject[name]=obj;
needSetting=true;
}else{
if(obj[obj.length-1].online!==status){
needSetting=true;
}
}
if(needSetting){
obj.push({time:Date.now(),online:status});
updatejson();
}
if(typeof instance!=="string" ){
calcStats(instance);
}
}
function updatejson(){
fs.writeFile('./uptime.json',JSON.stringify(uptimeObject),_=>{});
}
exports.observe=observe;
exports.uptime=uptimeObject;

48
webpage/home.html Normal file
View file

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jank Client</title>
<meta content="Jank Client" property="og:title" />
<meta content="A spacebar client that has DMs, replying and more" property="og:description" />
<meta content="/logo.webp" property="og:image" />
<meta content="#4b458c" data-react-helmet="true" name="theme-color" />
<link href="/style.css" rel="stylesheet" type="text/css" />
<link href="/themes.css" rel="stylesheet" type="text/css" id="lightcss"/>
</head>
<body class="Dark-theme">
<div id="titleDiv">
<img src="/logo.svg" width="40">
<h1 id="pageTitle">Jank Client</h1>
<a href="https://sb-jankclient.vanillaminigames.net/invite/USgYJo?instance=https%3A%2F%2Fspacebar.chat" class="TitleButtons"><h1>Spacebar Guild</h1></a>
<a href="https://github.com/MathMan05/JankClient" class="TitleButtons"><h1>Github</h1></a>
</div>
<div class="flexttb">
<div class="flexttb pagehead"><h1>Welcome to Jank Client</h1></div>
<div class="pagebox">
<p>Jank Client is a spacebar compatible client seeking to be as good as it can be with many features including:</p>
<ul>
<li>Direct Messaging</li>
<li>Reactions support</li>
<li>Invites</li>
<li>Account switching</li>
<li>User settings</li>
</ul>
</div>
<div class="pagebox">
<h2>Spacebar compatible Instances:</h2>
<div id="instancebox">
</div>
</div>
<div class="pagebox">
<h2>Contribute to Jank Client</h2>
<p>We always appreciate some help, wether that be in the form of bug reports, or code, or even just pointing out some typos.</p><br>
</a><a href="https://github.com/MathMan05/JankClient" class="TitleButtons"><h1>Github</h1></a>
</div>
</div>
</body>
<script src="/home.js" type="module" ></script>
</html>

64
webpage/home.ts Normal file
View file

@ -0,0 +1,64 @@
import {mobile} from "./login.js";
console.log(mobile);
const serverbox=document.getElementById("instancebox") as HTMLDivElement;
fetch("/instances.json").then(_=>_.json()).then((json:{name:string,description?:string,descriptionLong?:string,image?:string,url?:string,display?:boolean,online?:boolean,
uptime:{alltime:number,daytime:number,weektime:number},
urls:{wellknown:string,api:string,cdn:string,gateway:string,login?:string}}[])=>{
console.warn(json);
for(const instance of json){
if(instance.display===false){
continue;
}
const div=document.createElement("div");
div.classList.add("flexltr","instance");
if(instance.image){
const img=document.createElement("img");
img.src=instance.image;
div.append(img);
}
const statbox=document.createElement("div");
statbox.classList.add("flexttb");
{
const textbox=document.createElement("div");
textbox.classList.add("flexttb","instatancetextbox");
const title=document.createElement("h2");
title.innerText=instance.name;
if(instance.online!==undefined){
const status=document.createElement("span");
status.innerText=instance.online?"Online":"Offline";
status.classList.add("instanceStatus");
title.append(status);
}
textbox.append(title);
if(instance.description||instance.descriptionLong){
const p=document.createElement("p");
if(instance.descriptionLong){
p.innerText=instance.descriptionLong;
}else{
p.innerText=instance.description;
}
textbox.append(p);
}
statbox.append(textbox)
}
if(instance.uptime){
const stats=document.createElement("div");
stats.classList.add("flexltr");
const span=document.createElement("span");
span.innerText=`Uptime: All time: ${Math.floor(instance.uptime.alltime*100)}% This week: ${Math.floor(instance.uptime.weektime*100)}% Today: ${Math.floor(instance.uptime.daytime*100)}%`
stats.append(span);
statbox.append(stats);
}
div.append(statbox);
div.onclick=_=>{
if(instance.online){
window.location.href="/register.html?instance="+encodeURI(instance.name);
}else{
alert("Instance is offline, can't connect");
}
}
serverbox.append(div);
}
})

View file

@ -1,4 +1,4 @@
<body> <body class="Dark-theme">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">

View file

@ -1,4 +1,4 @@
<body> <body class="Dark-theme">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">

View file

@ -405,17 +405,17 @@ export {mobile, getBulkUsers,getBulkInfo,setTheme,Specialuser,getapiurls,adduser
const datalist=document.getElementById("instances"); const datalist=document.getElementById("instances");
console.warn(datalist); console.warn(datalist);
if(datalist){ if(datalist){
fetch("/instances.json").then(_=>_.json()).then((json:{name:string,description?:string,src?:string,url?:string,display?:boolean,urls:{wellknown:string,api:string,cdn:string,gateway:string,login?:string}}[])=>{ fetch("/instances.json").then(_=>_.json()).then((json:{name:string,online:boolean,description?:string,src?:string,url?:string,display?:boolean,urls:{wellknown:string,api:string,cdn:string,gateway:string,login?:string}}[])=>{
console.warn(json); console.warn(json);
if(instancein&&instancein.value===""){ if(instancein&&instancein.value===""){
instancein.value=json[0].name; instancein.value=json[0].name;
setTimeout(checkInstance,10);
} }
for(const instance of json){ for(const instance of json){
if(instance.display===false){ if(instance.display===false){
continue; continue;
} }
const option=document.createElement("option"); const option=document.createElement("option");
option.disabled=!instance.online;
option.value=instance.name; option.value=instance.name;
if(instance.url){ if(instance.url){
stringURLMap.set(option.value,instance.url); stringURLMap.set(option.value,instance.url);
@ -434,5 +434,6 @@ if(datalist){
} }
datalist.append(option); datalist.append(option);
} }
checkInstance("");
}) })
} }

View file

@ -1,4 +1,4 @@
<body> <body class="Dark-theme">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">

View file

@ -1,6 +1,3 @@
html{
height:100%;
}
body { body {
font-family: "acumin-pro", "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "acumin-pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight: 400; font-weight: 400;
@ -539,7 +536,7 @@ hr {
flex-direction: column; flex-direction: column;
flex-shrink: 1; flex-shrink: 1;
min-height: 0; min-height: 0;
height: 100%; height: 100vh;
/* width: 100%; */ /* width: 100%; */
position: relative; position: relative;
} }
@ -707,7 +704,7 @@ textarea:focus-visible,
} }
.servertd { .servertd {
background-color: var(--channel-name-bg); background-color: var(--server-bg);
height: .55in; height: .55in;
width:100%; width:100%;
display: flex; display: flex;
@ -797,7 +794,7 @@ textarea:focus-visible,
button { button {
transition: background .1s ease-in-out; transition: background .1s ease-in-out;
background-color: var(--button-bg); background-color: var(--message-bg-hover);
color: var(--primary-text); color: var(--primary-text);
border-color:var(--timestamp-color); border-color:var(--timestamp-color);
font-weight: bold; font-weight: bold;
@ -822,11 +819,11 @@ button:disabled:hover{
color: var(--primary-text); color: var(--primary-text);
} }
button:hover { button:hover {
background-color: var(--button-hover); background-color: var(--primary-bg);
} }
input::file-selector-button { input::file-selector-button {
transition: background .1s ease-in-out; transition: background .1s ease-in-out;
background-color: var(--button-bg); background-color: var(--message-bg-hover);
border-color:var(--timestamp-color); border-color:var(--timestamp-color);
color: var(--primary-text); color: var(--primary-text);
font-weight: bold; font-weight: bold;
@ -845,7 +842,7 @@ input[type="file"] {
} }
select{ select{
transition: background .1s ease-in-out; transition: background .1s ease-in-out;
background-color: var(--button-bg); background-color: var(--message-bg-hover);
color: var(--primary-text); color: var(--primary-text);
font-weight: bold; font-weight: bold;
text-align: left; text-align: left;
@ -996,7 +993,7 @@ span {
user-select: none; user-select: none;
} }
.filename a{ .filename {
color: var(--filename); color: var(--filename);
} }
@ -1251,6 +1248,7 @@ span {
flex-shrink: 1; flex-shrink: 1;
flex-grow: 1; flex-grow: 1;
align-items: stretch; align-items: stretch;
align-content: stretch;
} }
.flexttb{ .flexttb{
display: flex; display: flex;
@ -1261,6 +1259,7 @@ span {
/* padding-bottom: .1in; */ /* padding-bottom: .1in; */
align-items: flex-start; align-items: flex-start;
width: 100%; width: 100%;
/* height: 100%; */
} }
.settingbuttons{ .settingbuttons{
padding-top:.075in; padding-top:.075in;
@ -1289,9 +1288,6 @@ span {
padding: .05in; padding: .05in;
box-sizing: border-box; box-sizing: border-box;
} }
.flexspace:has(.Buttons){
padding:0;
}
.titlediv{ .titlediv{
height:100%; height:100%;
display: flex; display: flex;
@ -1302,7 +1298,6 @@ span {
font-weight:900; font-weight:900;
font-size:.25in; font-size:.25in;
border-bottom: solid .03in var(--message-bg-hover); border-bottom: solid .03in var(--message-bg-hover);
background: var(--primary-bg);
padding: .06in .2in; padding: .06in .2in;
background: var(--channel-name-bg); background: var(--channel-name-bg);
box-sizing: border-box; box-sizing: border-box;
@ -1344,13 +1339,15 @@ span {
.channelflex{ .channelflex{
flex-shrink:0; flex-shrink:0;
flex-grow:0; flex-grow: 1;
width:2.5in; width:2.5in;
display: flex; display: flex;
justify-content: space-evenly; justify-content: space-evenly;
align-content: stretch; align-content: stretch;
flex-direction: column; flex-direction: column;
overflow: hidden; overflow: hidden;
/* height: 100%; */
height: 100vh;
} }
.messageflex{ .messageflex{
display:flex; display:flex;
@ -1404,6 +1401,7 @@ span {
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
flex-shrink: 0; flex-shrink: 0;
position: relative;
} }
.discovery-guild .flex { .discovery-guild .flex {
@ -1465,6 +1463,7 @@ span {
width: 100%; width: 100%;
flex-direction: column; flex-direction: column;
max-height:100in; max-height:100in;
justify-content: flex-end;
} }
#connection-container, #app-list-container { #connection-container, #app-list-container {
@ -1853,7 +1852,7 @@ form div{
} }
.banner{ .banner{
position:absolute; position:absolute;
z-index:0; z-index: 0;
top:0; top:0;
left:0; left:0;
width:100%; width:100%;
@ -1984,3 +1983,102 @@ form div{
.clickable:hover{ .clickable:hover{
text-decoration:underline; text-decoration:underline;
} }
#titleDiv{
box-sizing: border-box;
display:flex;
flex-direction: row;
align-items: center;
width: 100%;
padding: .03in .1in;
background: var(--profile-bg);
border-bottom: solid var(--black);
justify-content: center;
}
.TitleButtons{
color:var(--primary-text);
margin-left: .03in;
padding: .05in;
border-radius:.075in;
border:solid .03in var(--black);
box-shadow: 0 0 .07in var(--shadow);
transition: box-shadow .3s;
display: block;
width: fit-content;
}
.TitleButtons:hover{
box-shadow: 0 0 .01in var(--shadow);
}
#pageTitle{
margin-right:.1in;
}
.pagehead{
background: var(--channel-hover);
height:1in;
display: flex;
align-items: center;
border-bottom: solid var(--black);
justify-content: center;
font-size: .25in;
flex-shrink: 0;
}
.pagebox{
padding: .06in;
background: var(--channel-hover);
margin: .1in;
min-height:2in;
border-radius:.1in;
box-sizing: border-box;
border: solid .04in black;
border-left: solid .05in darkblue;
box-shadow: .03in .04in .1in var(--black);
flex-shrink: 0;
}
#instancebox{
display:flex;
flex-direction: row;
flex-wrap: wrap;
width: fit-content;
}
.instance{
img{
width:.6in;
height:.6in;
margin-right:.1in;
border-radius:.2in;
border:solid .03in var(--black);
}
h2{
font-size:.25in;
}
flex-grow:0;
width:4.8in;
height: 1.6in;
background:var(--button-bg);
border-radius:.1in;
padding:.06in;
margin:.05in;
border:solid .04in var(--black);
box-shadow:0 0 .1in var(--shadow);
display: flex;
justify-content: center;
cursor:pointer;
user-select:none;
}
.instatancetextbox{
background:var(--message-bg-hover);
border-radius:.1in;
padding:0.03in .05in;
border:solid .03in var(--black);
height: 1.2in;
display:flex;
justify-content: center;
width:3.9in;
margin-bottom:.1in;
}
.instanceStatus{
font-size:.125in;
font-weight:100;
margin-left:.05in;
}