This commit is contained in:
Denis Manherz 2024-08-11 20:13:35 +02:00
parent da28216938
commit 8d58f50c03
4 changed files with 765 additions and 262 deletions

View file

@ -74,7 +74,7 @@ style-file = "style/output.css"
# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.
#
# Optional. Env: LEPTOS_ASSETS_DIR.
assets-dir = "public"
assets-dir = "/home/denis/wallpaper-gen-data"
# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.
site-addr = "0.0.0.0:3000"

View file

@ -21,6 +21,29 @@ body {
align-content: flex-start;
}
.hosting {
@apply inline-flex items-center justify-between;
@apply w-full;
@apply p-2;
@apply text-gray-500;
@apply bg-white;
@apply border;
@apply border-gray-200;
@apply rounded-lg;
@apply cursor-pointer;
@apply dark:hover:text-gray-300;
@apply dark:border-gray-700;
@apply dark:peer-checked:text-blue-500;
@apply peer-checked:border-blue-600;
@apply peer-checked:text-blue-600;
@apply hover:text-gray-600;
@apply hover:bg-gray-100;
@apply dark:text-gray-400;
@apply dark:bg-gray-800;
@apply dark:hover:bg-gray-700;
@apply text-center;
}
.pic-outer {
display: flex;
justify-content: center;
@ -41,7 +64,7 @@ body {
}
.pic-inner:hover {
transform: scale(0.98);
transform: scale(0.99);
}
#info-button {
@ -73,6 +96,7 @@ body {
/* The container <div> - needed to position the dropdown content */
.dropdown {
position: relative;
width: 20px;
}
/* Dropdown Content (Hidden by Default) */

View file

@ -3,6 +3,7 @@ use cfg_if::*;
use leptos::*;
use leptos_meta::*;
use leptos_router::*;
use serde::{Deserialize, Serialize};
cfg_if! {
if #[cfg(feature="ssr")]{
@ -48,14 +49,14 @@ pub fn App() -> impl IntoView {
fn Home() -> impl IntoView {
use leptos::html::Div;
use leptos_use::*;
let (size, set_size) = create_signal(1.);
let (size, set_size) = create_signal(1);
let (page, set_page) = create_signal(0);
//let res = create_local_resource(page, get_pics2);
let (all, set_all) = create_signal(Vec::<PicStruct>::new());
let res = create_local_resource(
move || (page.get(), size.get()),
move |d| get_pics2(d.0, d.1),
);
let (all, set_all) = create_signal(Vec::<Wallpaper>::new());
let (search_string, set_search_string) = create_signal("".to_string());
let res = create_local_resource(page, get_pics2);
let s_res = create_local_resource(search_string, get_pics);
let el = create_node_ref::<Div>();
let _ = use_infinite_scroll_with_options(
@ -67,294 +68,220 @@ fn Home() -> impl IntoView {
res.and_then(move |_| {
set_all.update(|dat| dat.extend(res().unwrap().unwrap()));
});
/*if !res.loading().get() {
set_page.update(|page| *page += 1);
} else if res().is_some() {
set_all.update(|dat| dat.extend(res().unwrap().unwrap()));
// println!("{:?}", all().len())
}*/
},
UseInfiniteScrollOptions::default().distance(10.0),
UseInfiniteScrollOptions::default().distance(512.),
);
view! {
<h1>"Home"</h1>
<div>
<input
on:input=move |ev| {
set_size(event_target_value(&ev).parse::<f64>().unwrap());
}
<div class="text-center m-3">
<div class="w-72">
<div class="relative w-full min-w-[200px] h-10">
<input
class="peer w-full h-full bg-transparent text-blue-gray-700 font-sans font-normal outline outline-0 focus:outline-0 disabled:bg-blue-gray-50 disabled:border-0 transition-all placeholder-shown:border placeholder-shown:border-blue-gray-200 placeholder-shown:border-t-blue-gray-200 border focus:border-2 border-t-transparent focus:border-t-transparent text-sm px-3 py-2.5 rounded-[7px] border-blue-gray-200 focus:border-gray-900"
placeholder=" "
on:input=move |ev| {
set_all.update(|all| all.clear());
set_search_string(event_target_value(&ev));
}
/>
prop:value=size
type="range"
min="0.5"
max="2"
step="0.5"
value="1"
/>
<label class="flex w-full h-full select-none pointer-events-none absolute left-0 font-normal !overflow-visible truncate peer-placeholder-shown:text-blue-gray-500 leading-tight peer-focus:leading-tight peer-disabled:text-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500 transition-all -top-1.5 peer-placeholder-shown:text-sm text-[11px] peer-focus:text-[11px] before:content[' '] before:block before:box-border before:w-2.5 before:h-1.5 before:mt-[6.5px] before:mr-1 peer-placeholder-shown:before:border-transparent before:rounded-tl-md before:border-t peer-focus:before:border-t-2 before:border-l peer-focus:before:border-l-2 before:pointer-events-none before:transition-all peer-disabled:before:border-transparent after:content[' '] after:block after:flex-grow after:box-border after:w-2.5 after:h-1.5 after:mt-[6.5px] after:ml-1 peer-placeholder-shown:after:border-transparent after:rounded-tr-md after:border-t peer-focus:after:border-t-2 after:border-r peer-focus:after:border-r-2 after:pointer-events-none after:transition-all peer-disabled:after:border-transparent peer-placeholder-shown:leading-[3.75] text-gray-500 peer-focus:text-gray-900 before:border-blue-gray-200 peer-focus:before:!border-gray-900 after:border-blue-gray-200 peer-focus:after:!border-gray-900">
"Username"
</label>
<p>"Input is: " {search_string}</p>
</div>
</div>
<ul class="grid w-1/3 gap-3 md:grid-cols-3 m-auto">
<li>
<input
type="radio"
id="hosting-small"
name="hosting"
value="hosting-small"
class="hidden peer"
required
on:input=move |_| {
set_size(0);
}
/>
<label for="hosting-small" class="hosting">
<div class="block">
<div class="w-full text-lg font-semibold">"256"</div>
</div>
</label>
</li>
<li>
<input
type="radio"
id="hosting-big"
name="hosting"
value="hosting-big"
class="hidden peer"
checked="checked"
on:input=move |_| {
set_size(1);
}
/>
<label for="hosting-big" class="hosting">
<div class="block">
<div class="w-full text-lg font-semibold">"512"</div>
</div>
</label>
</li>
<li>
<input
type="radio"
id="hosting-biger"
name="hosting"
value="hosting-biger"
class="hidden peer"
on:input=move |_| {
set_size(2);
}
/>
<label for="hosting-biger" class="hosting">
<div class="block">
<div class="w-full text-lg font-semibold">"1024"</div>
</div>
</label>
</li>
</ul>
</div>
<div class="main overflow-y-auto max-h-300" node_ref=el>
<For each=move || all.get() key=|state| state.key.clone() let:child>
<Pic width=child.width height=child.height scale=size url=child.url/>
<For
each=move || all.get()
key=move |state| state.thumbs[size() as usize].clone()
let:child
>
<Pic size=size.get() url=child.thumbs[size() as usize].clone() wp=child/>
</For>
</div>
<div class="w-full">
<p class="text-center">{move || page()}</p>
</div>
}
}
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PicStruct {
key: String,
width: i32,
height: i32,
url: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct Wallpaper {
pub struct Wallpaper {
file_name: String,
info: String,
date_added: String,
height: u64,
width: u64,
height: i32,
width: i32,
path: String,
thumbs: Vec<String>,
}
#[derive(Debug, Serialize)]
struct Name<'a> {
first: &'a str,
last: &'a str,
}
#[derive(Debug, Serialize)]
struct Person<'a> {
title: &'a str,
name: Name<'a>,
marketing: bool,
}
#[derive(Debug, Serialize)]
struct Responsibility {
marketing: bool,
}
#[derive(Debug, Deserialize)]
struct Record {
#[allow(dead_code)]
id: i32,
}
use std::env;
#[server(GetPics, "/api")]
pub async fn get_pics2(page: i32, size: f64) -> Result<Vec<PicStruct>, ServerFnError> {
use image::GenericImageView;
use imageinfo::ImageInfo;
use std::fs;
use std::path::PathBuf;
#[server(GetPicsSearch, "/api")]
pub async fn get_pics(search_string: String) -> Result<Vec<Wallpaper>, ServerFnError> {
let query = "
SELECT * FROM wallpapers LIMIT 10;
DEFINE ANALYZER book_analyzer TOKENIZERS blank,class,punct FILTERS snowball(english);
DEFINE INDEX info_s ON wallpapers FIELDS info SEARCH ANALYZER book_analyzer BM25(1.2,0.75);
SELECT *
FROM wallpapers
WHERE info @0@ 'asdasd'
LIMIT 10;
";
let db = Surreal::new::<Ws>("127.0.0.1:8000")
.await
.expect("Couldnt connect to db server");
println!("{search_string:#?}");
db.set("page", search_string)
.await
.expect("Couldnt set war");
// Select a specific namespace / database
db.use_ns("test")
.use_db("test")
.await
.expect("Couldnt find db");
//let papers: Vec<Wallpaper> = db.select("wallpapers").await.expect("Err");
//println!("{papers:#?}");
let mut res = db.query(query).await?;
let pap: Vec<Wallpaper> = res.take(0)?;
println!("{pap:#?}");
let mut res = db.query(query).await.expect("Something");
let pap: Vec<Wallpaper> = res.take(0).expect("Something");
//println!("{pap:#?}");
let mut pics = Vec::new();
Ok(pap)
}
let folder_path = "./public";
#[server(GetPics, "/api")]
pub async fn get_pics2(page: i32) -> Result<Vec<Wallpaper>, ServerFnError> {
let query = "
SELECT * FROM wallpapers LIMIT 20 START $page;
";
// Define the number of pictures per page
let pics_per_page = 10;
let db = Surreal::new::<Ws>("127.0.0.1:8000")
.await
.expect("Couldnt connect to db server");
println!("{page:#?}");
db.set("page", page * 20).await.expect("Couldnt set war");
// Select a specific namespace / database
db.use_ns("test")
.use_db("test")
.await
.expect("Couldnt find db");
// Calculate the index to start from based on the page number
let start_index = (page) * pics_per_page;
let end_index = start_index + pics_per_page;
let mut res = db.query(query).await.expect("Something");
let pap: Vec<Wallpaper> = res.take(0).expect("Something");
//println!("{pap:#?}");
// manage a vector with all the image paths
let mut image_file_paths = Vec::<PathBuf>::new();
for entry in fs::read_dir(folder_path)? {
let entry = entry?;
let file_path = entry.path();
let file_name_str = file_path.file_name().unwrap_or_default().to_str().unwrap();
if size == 0.5 {
//skip 1024 and 512
if file_name_str.contains("512x512") || file_name_str.contains("1024x1024") {
continue;
}
} else if size == 1.0 {
if file_name_str.contains("256x256") || file_name_str.contains("1024x1024") {
println!("sdfasdf");
continue;
}
} else if size == 1.5 {
if file_name_str.contains("256x256") || file_name_str.contains("512x512") {
continue;
}
}
image_file_paths.push(file_path.clone());
}
for (i, file_path) in image_file_paths.iter().enumerate() {
if file_path.is_file() {
if i < start_index as usize {
continue;
}
let file = std::fs::File::open(file_path)?;
let mut bufreader = std::io::BufReader::new(&file);
let exifreader = exif::Reader::new();
let exif = exifreader.read_from_container(&mut bufreader)?;
for f in exif.fields() {
println!(
"{} {} {}",
f.tag,
f.ifd_num,
f.display_value().with_unit(&exif)
);
}
let file_name = file_path.file_name().unwrap_or_default();
let file_name_str = file_name.to_str().unwrap();
// Check if the file has a supported image extension
if let Some(extension) = file_path.extension() {
if let Some(extension_str) = extension.to_str() {
if image::ImageFormat::from_extension(extension_str).is_some() {
// Process the image
let img = image::open(&file_path).expect("Failed to open image");
let (x, y) = img.dimensions();
pics.push(PicStruct {
key: file_name_str.to_string(), // Could not be unique if two images were created at exactly
// the same time
width: x as i32,
height: y as i32,
url: file_name.to_string_lossy().into(),
});
if i + 1 >= end_index as usize {
break;
}
}
}
}
}
}
// Send different picture resolutions depending on selected size
// sizes 256 512 1024
// Iterate over the entries in the folder
/*for (index, entry) in fs::read_dir(folder_path)
.expect("Failed to read directory")
.enumerate()
{
if let Ok(entry) = entry {
if index < start_index as usize {
// Skip pictures before the start index
continue;
}
let file_path = entry.path();
let file_name = file_path.file_name().unwrap_or_default();
let file_name_str = file_name.to_str().unwrap();
// Skip the wrong sizes
if size == 0.5 {
//skip 1024 and 512
if file_name_str.contains("512x512") || file_name_str.contains("1024x1024") {
continue;
}
} else if size == 1.0 {
if file_name_str.contains("256x256") || file_name_str.contains("1024x1024") {
continue;
}
} else if size == 1.5 {
if file_name_str.contains("256x256") || file_name_str.contains("512x512") {
continue;
}
}
// Check if the entry is a file
if file_path.is_file() {
// Check if the file has a supported image extension
if let Some(extension) = file_path.extension() {
if let Some(extension_str) = extension.to_str() {
if image::ImageFormat::from_extension(extension_str).is_some() {
// Process the image
let img = image::open(&file_path).expect("Failed to open image");
let (x, y) = img.dimensions();
pics.push(PicStruct {
key: file_name_str.to_string(), // Could not be unique if two images were created at exactly
// the same time
width: x as i32,
height: y as i32,
url: file_name.to_string_lossy().into(),
});
// Break if we have reached the end index
if index + 1 >= end_index as usize {
break;
}
}
}
}
}
}
}*/
println!("{:#?}", pics);
Ok(pics)
Ok(pap)
}
#[component]
fn Info(show: ReadSignal<bool>, set_show: WriteSignal<bool>) -> impl IntoView {
fn Info(show: ReadSignal<bool>, set_show: WriteSignal<bool>, wp: Wallpaper) -> impl IntoView {
view! {
<Show when=move || show()>
<div id="info-main" class="rounded">
<button type="button" on:click=move |_| set_show(false)>
<div id="info-main" class="rounded text-center">
<button
on:click=move |_| set_show(false)
class="bg-blue-500 hover:bg-blue-400 text-white font-bold py-2 px-4 border-b-4 border-blue-700 hover:border-blue-500 rounded"
>
"Close"
</button>
<input type="text" id="country" name="country" value="Norway" readonly/>
<br/>
<br/>
<div class="w-96">
<div class="relative w-full min-w-[200px]">
<textarea
class="peer h-full min-h-[100px] w-full resize-none rounded-[7px] border border-blue-gray-200 border-t-transparent bg-transparent px-3 py-2.5 font-sans text-sm font-normal text-blue-gray-700 outline outline-0 transition-all placeholder-shown:border placeholder-shown:border-blue-gray-200 placeholder-shown:border-t-blue-gray-200 focus:border-2 focus:border-gray-900 focus:border-t-transparent focus:outline-0 disabled:resize-none disabled:border-0 disabled:bg-blue-gray-50"
placeholder=" "
prop:value=&wp.info
></textarea>
<label class="before:content[' '] after:content[' '] pointer-events-none absolute left-0 -top-1.5 flex h-full w-full select-none text-[11px] font-normal leading-tight text-blue-gray-400 transition-all before:pointer-events-none before:mt-[6.5px] before:mr-1 before:box-border before:block before:h-1.5 before:w-2.5 before:rounded-tl-md before:border-t before:border-l before:border-blue-gray-200 before:transition-all after:pointer-events-none after:mt-[6.5px] after:ml-1 after:box-border after:block after:h-1.5 after:w-2.5 after:flex-grow after:rounded-tr-md after:border-t after:border-r after:border-blue-gray-200 after:transition-all peer-placeholder-shown:text-sm peer-placeholder-shown:leading-[3.75] peer-placeholder-shown:text-blue-gray-500 peer-placeholder-shown:before:border-transparent peer-placeholder-shown:after:border-transparent peer-focus:text-[11px] peer-focus:leading-tight peer-focus:text-gray-900 peer-focus:before:border-t-2 peer-focus:before:border-l-2 peer-focus:before:border-gray-900 peer-focus:after:border-t-2 peer-focus:after:border-r-2 peer-focus:after:border-gray-900 peer-disabled:text-transparent peer-disabled:before:border-transparent peer-disabled:after:border-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500">
Message
</label>
</div>
</div>
</div>
</Show>
}
}
#[component]
fn Pic(width: i32, height: i32, url: String, scale: ReadSignal<f64>) -> impl IntoView {
fn Pic(url: String, size: i32, wp: Wallpaper) -> impl IntoView {
let (showInfo, set_showInfo) = create_signal(false);
let mut w_url = url.clone();
let ww_url = w_url.split_off(31);
let asd = &url.chars().position(|c| c == 'x').unwrap();
let wh = &url[(asd - 3)..(asd + 4)];
println!("{wh:#?}");
//let w: i32 = (&wh[..3]).parse().unwrap_or(333);
//let h: i32 = (&wh[4..]).parse().unwrap_or(333);
let w: i32 = 256 as i32 * (2 as i32).pow(size as u32);
let h: i32 = (&wh[4..]).parse().unwrap_or(333);
view! {
<div
class="pic-outer"
// style="border:1px solid black;"
style:width=move || format!("{}px", f64::from(width + 10))
style:height=move || format!("{}px", f64::from(height + 10))
style:width=move || format!("{}px", w + 10)
style:height=move || format!("{}px", h + 10)
>
<div
class="pic-inner"
style=format!("background-image: url('{}'); background-size: cover;", url)
style:width=move || format!("{}px", f64::from(width))
style:height=move || format!("{}px", f64::from(height))
style=format!("background-image: url('{}'); background-size: cover;", ww_url)
style:width=move || format!("{}px", w)
style:height=move || format!("{}px", h)
>
<div class="dropdown">
<div class="dropdown w-25 relative">
<button class="dropbtn">
<i class="fa-solid fa-download"></i>
</button>
@ -364,12 +291,18 @@ fn Pic(width: i32, height: i32, url: String, scale: ReadSignal<f64>) -> impl Int
<a href="#">4:3</a>
</div>
</div>
<button id="info-button" on:click=move |_| set_showInfo(true)>
<button
id="info-button"
on:click=move |_| {
if showInfo.get() { set_showInfo(false) } else { set_showInfo(true) }
}
>
<i class="fa-regular fa-circle-question"></i>
</button>
</div>
</div>
<Info show=showInfo set_show=set_showInfo/>
<Info show=showInfo set_show=set_showInfo wp=wp/>
}
}

View file

@ -22,7 +22,7 @@
::before,
::after {
--tw-content: "";
--tw-content: '';
}
/*
@ -44,10 +44,9 @@ html,
-moz-tab-size: 4;
/* 3 */
-o-tab-size: 4;
tab-size: 4;
tab-size: 4;
/* 3 */
font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
/* 4 */
font-feature-settings: normal;
/* 5 */
@ -90,7 +89,7 @@ Add the correct text decoration in Chrome, Edge, and Safari.
abbr:where([title]) {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
text-decoration: underline dotted;
}
/*
@ -136,8 +135,7 @@ code,
kbd,
samp,
pre {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
/* 1 */
font-feature-settings: normal;
/* 2 */
@ -236,9 +234,9 @@ select {
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
[type='button'],
[type='reset'],
[type='submit'] {
-webkit-appearance: button;
/* 1 */
background-color: transparent;
@ -285,7 +283,7 @@ Correct the cursor style of increment and decrement buttons in Safari.
2. Correct the outline style in Safari.
*/
[type="search"] {
[type='search'] {
-webkit-appearance: textfield;
/* 1 */
outline-offset: -2px;
@ -378,8 +376,7 @@ textarea {
2. Set the default placeholder color to the user's configured gray 400 color.
*/
input::-moz-placeholder,
textarea::-moz-placeholder {
input::-moz-placeholder, textarea::-moz-placeholder {
opacity: 1;
/* 1 */
color: #9ca3af;
@ -447,9 +444,7 @@ video {
display: none;
}
*,
::before,
::after {
*, ::before, ::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
@ -549,26 +544,216 @@ video {
--tw-backdrop-sepia: ;
}
.pointer-events-none {
pointer-events: none;
}
.absolute {
position: absolute;
}
.relative {
position: relative;
}
.-top-1 {
top: -0.25rem;
}
.-top-1\.5 {
top: -0.375rem;
}
.left-0 {
left: 0px;
}
.m-3 {
margin: 0.75rem;
}
.m-auto {
margin: auto;
}
.block {
display: block;
}
.flex {
display: flex;
}
.grid {
display: grid;
}
.hidden {
display: none;
}
.h-10 {
height: 2.5rem;
}
.h-full {
height: 100%;
}
.min-h-\[100px\] {
min-height: 100px;
}
.w-1\/3 {
width: 33.333333%;
}
.w-72 {
width: 18rem;
}
.w-96 {
width: 24rem;
}
.w-full {
width: 100%;
}
.min-w-\[200px\] {
min-width: 200px;
}
.select-none {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
.resize-none {
resize: none;
}
.gap-3 {
gap: 0.75rem;
}
.\!overflow-visible {
overflow: visible !important;
}
.overflow-y-auto {
overflow-y: auto;
}
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.rounded {
border-radius: 0.25rem;
}
.rounded-\[7px\] {
border-radius: 7px;
}
.border {
border-width: 1px;
}
.border-b-4 {
border-bottom-width: 4px;
}
.border-blue-700 {
--tw-border-opacity: 1;
border-color: rgb(29 78 216 / var(--tw-border-opacity));
}
.border-t-transparent {
border-top-color: transparent;
}
.bg-blue-500 {
--tw-bg-opacity: 1;
background-color: rgb(59 130 246 / var(--tw-bg-opacity));
}
.bg-transparent {
background-color: transparent;
}
.px-3 {
padding-left: 0.75rem;
padding-right: 0.75rem;
}
.px-4 {
padding-left: 1rem;
padding-right: 1rem;
}
.py-2 {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
.py-2\.5 {
padding-top: 0.625rem;
padding-bottom: 0.625rem;
}
.text-center {
text-align: center;
}
.text-\[11px\] {
font-size: 11px;
}
.text-sm {
font-size: 0.750rem;
}
.font-bold {
font-weight: 700;
}
.font-normal {
font-weight: 400;
}
.leading-tight {
line-height: 1.25;
}
.text-gray-500 {
--tw-text-opacity: 1;
color: rgb(107 114 128 / var(--tw-text-opacity));
}
.text-white {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
}
.outline {
outline-style: solid;
}
.outline-0 {
outline-width: 0px;
}
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
* {
font-family: Noto Sans Tai Tham;
color: #eaf0f0;
@ -588,6 +773,72 @@ body {
align-content: flex-start;
}
.hosting {
display: inline-flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 0.5rem;
--tw-text-opacity: 1;
color: rgb(107 114 128 / var(--tw-text-opacity));
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
border-width: 1px;
--tw-border-opacity: 1;
border-color: rgb(229 231 235 / var(--tw-border-opacity));
border-radius: 0.5rem;
cursor: pointer;
}
@media (prefers-color-scheme: dark) {
.hosting:hover {
--tw-text-opacity: 1;
color: rgb(209 213 219 / var(--tw-text-opacity));
}
.hosting {
--tw-border-opacity: 1;
border-color: rgb(55 65 81 / var(--tw-border-opacity));
}
.peer:checked ~ .hosting {
--tw-text-opacity: 1;
color: rgb(59 130 246 / var(--tw-text-opacity));
}
}
.peer:checked ~ .hosting {
--tw-border-opacity: 1;
border-color: rgb(37 99 235 / var(--tw-border-opacity));
--tw-text-opacity: 1;
color: rgb(37 99 235 / var(--tw-text-opacity));
}
.hosting:hover {
--tw-text-opacity: 1;
color: rgb(75 85 99 / var(--tw-text-opacity));
--tw-bg-opacity: 1;
background-color: rgb(243 244 246 / var(--tw-bg-opacity));
}
@media (prefers-color-scheme: dark) {
.hosting {
--tw-text-opacity: 1;
color: rgb(156 163 175 / var(--tw-text-opacity));
--tw-bg-opacity: 1;
background-color: rgb(31 41 55 / var(--tw-bg-opacity));
}
.hosting:hover {
--tw-bg-opacity: 1;
background-color: rgb(55 65 81 / var(--tw-bg-opacity));
}
}
.hosting {
text-align: center;
}
.pic-outer {
display: flex;
justify-content: center;
@ -603,13 +854,13 @@ body {
#country {
background-color: rgba(0, 0, 0, 0.61);
-webkit-backdrop-filter: blur(5px);
backdrop-filter: blur(5px);
backdrop-filter: blur(5px);
padding: 4px;
margin: 5px;
}
.pic-inner:hover {
transform: scale(0.98);
transform: scale(0.99);
}
#info-button {
@ -634,7 +885,7 @@ body {
position: absolute;
background-color: rgba(0, 0, 0, 0.5);
-webkit-backdrop-filter: blur(5px);
backdrop-filter: blur(5px);
backdrop-filter: blur(5px);
overflow: auto;
z-index: 2;
}
@ -643,7 +894,7 @@ body {
.dropdown {
position: relative;
width: 40px;
width: 20px;
}
/* Dropdown Content (Hidden by Default) */
@ -655,7 +906,7 @@ body {
border-radius: 0.25rem;
background-color: rgba(0, 0, 0, 0.5);
-webkit-backdrop-filter: blur(5px);
backdrop-filter: blur(5px);
backdrop-filter: blur(5px);
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
@ -682,7 +933,7 @@ body {
.dropdown-content a:hover {
background-color: rgba(0, 0, 0, 0.5);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
}
/* Show the dropdown menu on hover */
@ -690,3 +941,298 @@ body {
.dropdown:hover .dropdown-content {
display: block;
}
.before\:pointer-events-none::before {
content: var(--tw-content);
pointer-events: none;
}
.before\:mr-1::before {
content: var(--tw-content);
margin-right: 0.25rem;
}
.before\:mt-\[6\.5px\]::before {
content: var(--tw-content);
margin-top: 6.5px;
}
.before\:box-border::before {
content: var(--tw-content);
box-sizing: border-box;
}
.before\:block::before {
content: var(--tw-content);
display: block;
}
.before\:h-1::before {
content: var(--tw-content);
height: 0.25rem;
}
.before\:h-1\.5::before {
content: var(--tw-content);
height: 0.375rem;
}
.before\:w-2::before {
content: var(--tw-content);
width: 0.5rem;
}
.before\:w-2\.5::before {
content: var(--tw-content);
width: 0.625rem;
}
.before\:rounded-tl-md::before {
content: var(--tw-content);
border-top-left-radius: 0.375rem;
}
.before\:border-l::before {
content: var(--tw-content);
border-left-width: 1px;
}
.before\:border-t::before {
content: var(--tw-content);
border-top-width: 1px;
}
.before\:transition-all::before {
content: var(--tw-content);
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.after\:pointer-events-none::after {
content: var(--tw-content);
pointer-events: none;
}
.after\:ml-1::after {
content: var(--tw-content);
margin-left: 0.25rem;
}
.after\:mt-\[6\.5px\]::after {
content: var(--tw-content);
margin-top: 6.5px;
}
.after\:box-border::after {
content: var(--tw-content);
box-sizing: border-box;
}
.after\:block::after {
content: var(--tw-content);
display: block;
}
.after\:h-1::after {
content: var(--tw-content);
height: 0.25rem;
}
.after\:h-1\.5::after {
content: var(--tw-content);
height: 0.375rem;
}
.after\:w-2::after {
content: var(--tw-content);
width: 0.5rem;
}
.after\:w-2\.5::after {
content: var(--tw-content);
width: 0.625rem;
}
.after\:flex-grow::after {
content: var(--tw-content);
flex-grow: 1;
}
.after\:rounded-tr-md::after {
content: var(--tw-content);
border-top-right-radius: 0.375rem;
}
.after\:border-r::after {
content: var(--tw-content);
border-right-width: 1px;
}
.after\:border-t::after {
content: var(--tw-content);
border-top-width: 1px;
}
.after\:transition-all::after {
content: var(--tw-content);
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.placeholder-shown\:border:-moz-placeholder-shown {
border-width: 1px;
}
.placeholder-shown\:border:placeholder-shown {
border-width: 1px;
}
.hover\:border-blue-500:hover {
--tw-border-opacity: 1;
border-color: rgb(59 130 246 / var(--tw-border-opacity));
}
.hover\:bg-blue-400:hover {
--tw-bg-opacity: 1;
background-color: rgb(96 165 250 / var(--tw-bg-opacity));
}
.focus\:border-2:focus {
border-width: 2px;
}
.focus\:border-gray-900:focus {
--tw-border-opacity: 1;
border-color: rgb(17 24 39 / var(--tw-border-opacity));
}
.focus\:border-t-transparent:focus {
border-top-color: transparent;
}
.focus\:outline-0:focus {
outline-width: 0px;
}
.disabled\:resize-none:disabled {
resize: none;
}
.disabled\:border-0:disabled {
border-width: 0px;
}
.peer:-moz-placeholder-shown ~ .peer-placeholder-shown\:text-sm {
font-size: 0.750rem;
}
.peer:placeholder-shown ~ .peer-placeholder-shown\:text-sm {
font-size: 0.750rem;
}
.peer:-moz-placeholder-shown ~ .peer-placeholder-shown\:leading-\[3\.75\] {
line-height: 3.75;
}
.peer:placeholder-shown ~ .peer-placeholder-shown\:leading-\[3\.75\] {
line-height: 3.75;
}
.peer:-moz-placeholder-shown ~ .peer-placeholder-shown\:before\:border-transparent::before {
content: var(--tw-content);
border-color: transparent;
}
.peer:placeholder-shown ~ .peer-placeholder-shown\:before\:border-transparent::before {
content: var(--tw-content);
border-color: transparent;
}
.peer:-moz-placeholder-shown ~ .peer-placeholder-shown\:after\:border-transparent::after {
content: var(--tw-content);
border-color: transparent;
}
.peer:placeholder-shown ~ .peer-placeholder-shown\:after\:border-transparent::after {
content: var(--tw-content);
border-color: transparent;
}
.peer:focus ~ .peer-focus\:text-\[11px\] {
font-size: 11px;
}
.peer:focus ~ .peer-focus\:leading-tight {
line-height: 1.25;
}
.peer:focus ~ .peer-focus\:text-gray-900 {
--tw-text-opacity: 1;
color: rgb(17 24 39 / var(--tw-text-opacity));
}
.peer:focus ~ .peer-focus\:before\:border-l-2::before {
content: var(--tw-content);
border-left-width: 2px;
}
.peer:focus ~ .peer-focus\:before\:border-t-2::before {
content: var(--tw-content);
border-top-width: 2px;
}
.peer:focus ~ .peer-focus\:before\:\!border-gray-900::before {
content: var(--tw-content);
--tw-border-opacity: 1 !important;
border-color: rgb(17 24 39 / var(--tw-border-opacity)) !important;
}
.peer:focus ~ .peer-focus\:before\:border-gray-900::before {
content: var(--tw-content);
--tw-border-opacity: 1;
border-color: rgb(17 24 39 / var(--tw-border-opacity));
}
.peer:focus ~ .peer-focus\:after\:border-r-2::after {
content: var(--tw-content);
border-right-width: 2px;
}
.peer:focus ~ .peer-focus\:after\:border-t-2::after {
content: var(--tw-content);
border-top-width: 2px;
}
.peer:focus ~ .peer-focus\:after\:\!border-gray-900::after {
content: var(--tw-content);
--tw-border-opacity: 1 !important;
border-color: rgb(17 24 39 / var(--tw-border-opacity)) !important;
}
.peer:focus ~ .peer-focus\:after\:border-gray-900::after {
content: var(--tw-content);
--tw-border-opacity: 1;
border-color: rgb(17 24 39 / var(--tw-border-opacity));
}
.peer:disabled ~ .peer-disabled\:text-transparent {
color: transparent;
}
.peer:disabled ~ .peer-disabled\:before\:border-transparent::before {
content: var(--tw-content);
border-color: transparent;
}
.peer:disabled ~ .peer-disabled\:after\:border-transparent::after {
content: var(--tw-content);
border-color: transparent;
}
@media (min-width: 768px) {
.md\:grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}