mirror of
https://codeberg.org/demostf/frontend.git
synced 2026-06-03 18:24:12 +02:00
build rework
This commit is contained in:
parent
fc5cd1d24f
commit
dc80d715a6
18 changed files with 1681 additions and 115 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,4 +1,4 @@
|
||||||
/target
|
target
|
||||||
result
|
result
|
||||||
.direnv
|
.direnv
|
||||||
config.toml
|
config.toml
|
||||||
|
|
|
||||||
14
Cargo.lock
generated
14
Cargo.lock
generated
|
|
@ -627,6 +627,16 @@ dependencies = [
|
||||||
"matches",
|
"matches",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "demostf-build"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.21.0",
|
||||||
|
"const-fnv1a-hash",
|
||||||
|
"lightningcss",
|
||||||
|
"urlencoding",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
|
@ -790,15 +800,14 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-session",
|
"async-session",
|
||||||
"axum",
|
"axum",
|
||||||
"base64 0.21.0",
|
|
||||||
"config",
|
"config",
|
||||||
"const-fnv1a-hash",
|
"const-fnv1a-hash",
|
||||||
"const-str 0.5.4",
|
"const-str 0.5.4",
|
||||||
"const_base",
|
"const_base",
|
||||||
|
"demostf-build",
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyperlocal",
|
"hyperlocal",
|
||||||
"itertools",
|
"itertools",
|
||||||
"lightningcss",
|
|
||||||
"maud",
|
"maud",
|
||||||
"quick-xml",
|
"quick-xml",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
|
|
@ -814,7 +823,6 @@ dependencies = [
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"urlencoding",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,4 @@ reqwest = "0.11.16"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
lightningcss = { version = "1.0.0-alpha.40", features = ["browserslist", "visitor"] }
|
demostf-build = { path = "./build", version = "*" }
|
||||||
base64 = "0.21.0"
|
|
||||||
urlencoding = "2.1.2"
|
|
||||||
const-fnv1a-hash = "1.1.0"
|
|
||||||
|
|
|
||||||
103
build.rs
103
build.rs
|
|
@ -1,108 +1,9 @@
|
||||||
use base64::engine::general_purpose::STANDARD;
|
use demostf_build::{bundle_style, save_asset};
|
||||||
use base64::Engine;
|
|
||||||
use const_fnv1a_hash::fnv1a_hash_str_32;
|
|
||||||
use lightningcss::bundler::{Bundler, FileProvider};
|
|
||||||
use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions};
|
|
||||||
use lightningcss::targets::Browsers;
|
|
||||||
use lightningcss::values::url::Url;
|
|
||||||
use lightningcss::visit_types;
|
|
||||||
use lightningcss::visitor::{Visit, VisitTypes, Visitor};
|
|
||||||
use std::convert::Infallible;
|
|
||||||
use std::env::var;
|
|
||||||
use std::fs::{read, write};
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
macro_rules! save_asset {
|
|
||||||
($name:expr, $val:expr) => {
|
|
||||||
let val = $val;
|
|
||||||
let out_dir = var("OUT_DIR").unwrap();
|
|
||||||
write(format!("{out_dir}/{}", $name), &val).expect("failed to write asset");
|
|
||||||
let hash = fnv1a_hash_str_32(&val);
|
|
||||||
write(format!("{out_dir}/{}.hash", $name), format!("{:x}", hash))
|
|
||||||
.expect("failed to write asset hash");
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
println!("cargo:rerun-if-changed=style");
|
println!("cargo:rerun-if-changed=style");
|
||||||
println!("cargo:rerun-if-changed=images");
|
println!("cargo:rerun-if-changed=images");
|
||||||
|
|
||||||
save_asset!("style.css", build_style());
|
save_asset!("style.css", bundle_style("style/style.css"));
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_style() -> String {
|
|
||||||
// todo build time?
|
|
||||||
let fs = FileProvider::new();
|
|
||||||
let mut bundler = Bundler::new(
|
|
||||||
&fs,
|
|
||||||
None,
|
|
||||||
ParserOptions {
|
|
||||||
nesting: true,
|
|
||||||
..ParserOptions::default()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let mut stylesheet = bundler
|
|
||||||
.bundle(Path::new("style/style.css"))
|
|
||||||
.expect("failed to bundle css");
|
|
||||||
let browsers =
|
|
||||||
Browsers::from_browserslist(["last 2 versions"]).expect("failed to parse browserlist");
|
|
||||||
stylesheet
|
|
||||||
.minify(MinifyOptions {
|
|
||||||
targets: browsers.clone(),
|
|
||||||
..MinifyOptions::default()
|
|
||||||
})
|
|
||||||
.expect("failed to minify css");
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
let minify = false;
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
let minify = true;
|
|
||||||
|
|
||||||
stylesheet.visit(&mut InlineUrlVisitor).unwrap();
|
|
||||||
|
|
||||||
stylesheet
|
|
||||||
.to_css(PrinterOptions {
|
|
||||||
targets: browsers,
|
|
||||||
minify,
|
|
||||||
..PrinterOptions::default()
|
|
||||||
})
|
|
||||||
.expect("failed to output css")
|
|
||||||
.code
|
|
||||||
}
|
|
||||||
|
|
||||||
struct InlineUrlVisitor;
|
|
||||||
|
|
||||||
impl<'i> Visitor<'i> for InlineUrlVisitor {
|
|
||||||
type Error = Infallible;
|
|
||||||
|
|
||||||
const TYPES: VisitTypes = visit_types!(URLS);
|
|
||||||
|
|
||||||
fn visit_url(&mut self, url: &mut Url<'i>) -> Result<(), Self::Error> {
|
|
||||||
if let Some(path) = url.url.strip_prefix("inline://") {
|
|
||||||
let content = read(path).unwrap_or_else(|e| {
|
|
||||||
eprintln!("Failed to write inline file {path}: {e}");
|
|
||||||
panic!("Failed to inline");
|
|
||||||
});
|
|
||||||
let (mime, encode) = guess_mime(path);
|
|
||||||
|
|
||||||
if encode {
|
|
||||||
let encoded = STANDARD.encode(content);
|
|
||||||
url.url = format!("data:{mime};base64,{encoded}").into();
|
|
||||||
} else {
|
|
||||||
let content = String::from_utf8(content).expect("invalid utf8");
|
|
||||||
let encoded = urlencoding::encode(&content);
|
|
||||||
url.url = format!("data:{mime},{encoded}").into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn guess_mime(path: &str) -> (&'static str, bool) {
|
|
||||||
match path.split('.').last().unwrap() {
|
|
||||||
"svg" => ("image/svg+xml", false),
|
|
||||||
"png" => ("image/png", true),
|
|
||||||
ext => panic!("no mimetype known for {ext}"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1410
build/Cargo.lock
generated
Normal file
1410
build/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
10
build/Cargo.toml
Normal file
10
build/Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "demostf-build"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
lightningcss = { version = "1.0.0-alpha.40", features = ["browserslist", "visitor"] }
|
||||||
|
base64 = "0.21.0"
|
||||||
|
urlencoding = "2.1.2"
|
||||||
|
const-fnv1a-hash = "1.1.0"
|
||||||
28
build/src/lib.rs
Normal file
28
build/src/lib.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
mod style;
|
||||||
|
|
||||||
|
use const_fnv1a_hash::fnv1a_hash_str_32;
|
||||||
|
pub use style::bundle_style;
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! save_asset {
|
||||||
|
($name:expr, $val:expr) => {
|
||||||
|
let val = $val;
|
||||||
|
let out_dir = std::env::var("OUT_DIR").unwrap();
|
||||||
|
std::fs::write(format!("{out_dir}/{}", $name), &val).expect("failed to write asset");
|
||||||
|
let hash = fnv1a_hash_str_32(&val);
|
||||||
|
std::fs::write(format!("{out_dir}/{}.hash", $name), format!("{:x}", hash))
|
||||||
|
.expect("failed to write asset hash");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hash(data: &str) -> String {
|
||||||
|
format!("{:x}", fnv1a_hash_str_32(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn guess_mime(path: &str) -> (&'static str, bool) {
|
||||||
|
match path.split('.').last().unwrap() {
|
||||||
|
"svg" => ("image/svg+xml", false),
|
||||||
|
"png" => ("image/png", true),
|
||||||
|
ext => panic!("no mimetype known for {ext}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
80
build/src/style.rs
Normal file
80
build/src/style.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
use crate::guess_mime;
|
||||||
|
use base64::engine::general_purpose::STANDARD;
|
||||||
|
use base64::Engine;
|
||||||
|
use lightningcss::bundler::{Bundler, FileProvider};
|
||||||
|
use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions};
|
||||||
|
use lightningcss::targets::Browsers;
|
||||||
|
use lightningcss::values::url::Url;
|
||||||
|
use lightningcss::visit_types;
|
||||||
|
use lightningcss::visitor::{Visit, VisitTypes, Visitor};
|
||||||
|
use std::convert::Infallible;
|
||||||
|
use std::fs::read;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub fn bundle_style(style: &str) -> String {
|
||||||
|
// todo build time?
|
||||||
|
let fs = FileProvider::new();
|
||||||
|
let mut bundler = Bundler::new(
|
||||||
|
&fs,
|
||||||
|
None,
|
||||||
|
ParserOptions {
|
||||||
|
nesting: true,
|
||||||
|
..ParserOptions::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let mut stylesheet = bundler
|
||||||
|
.bundle(Path::new(style))
|
||||||
|
.expect("failed to bundle css");
|
||||||
|
let browsers =
|
||||||
|
Browsers::from_browserslist(["last 2 versions"]).expect("failed to parse browserlist");
|
||||||
|
stylesheet
|
||||||
|
.minify(MinifyOptions {
|
||||||
|
targets: browsers.clone(),
|
||||||
|
..MinifyOptions::default()
|
||||||
|
})
|
||||||
|
.expect("failed to minify css");
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
let minify = false;
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
let minify = true;
|
||||||
|
|
||||||
|
stylesheet.visit(&mut InlineUrlVisitor).unwrap();
|
||||||
|
|
||||||
|
stylesheet
|
||||||
|
.to_css(PrinterOptions {
|
||||||
|
targets: browsers,
|
||||||
|
minify,
|
||||||
|
..PrinterOptions::default()
|
||||||
|
})
|
||||||
|
.expect("failed to output css")
|
||||||
|
.code
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InlineUrlVisitor;
|
||||||
|
|
||||||
|
impl<'i> Visitor<'i> for InlineUrlVisitor {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
const TYPES: VisitTypes = visit_types!(URLS);
|
||||||
|
|
||||||
|
fn visit_url(&mut self, url: &mut Url<'i>) -> Result<(), Self::Error> {
|
||||||
|
if let Some(path) = url.url.strip_prefix("inline://") {
|
||||||
|
let content = read(path).unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to write inline file {path}: {e}");
|
||||||
|
panic!("Failed to inline");
|
||||||
|
});
|
||||||
|
let (mime, encode) = guess_mime(path);
|
||||||
|
|
||||||
|
if encode {
|
||||||
|
let encoded = STANDARD.encode(content);
|
||||||
|
url.url = format!("data:{mime};base64,{encoded}").into();
|
||||||
|
} else {
|
||||||
|
let content = String::from_utf8(content).expect("invalid utf8");
|
||||||
|
let encoded = urlencoding::encode(&content);
|
||||||
|
url.url = format!("data:{mime},{encoded}").into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
0
script/upload.js
Normal file
0
script/upload.js
Normal file
14
src/main.rs
14
src/main.rs
|
|
@ -14,6 +14,7 @@ use crate::pages::about::AboutPage;
|
||||||
use crate::pages::demo::DemoPage;
|
use crate::pages::demo::DemoPage;
|
||||||
use crate::pages::index::Index;
|
use crate::pages::index::Index;
|
||||||
use crate::pages::render;
|
use crate::pages::render;
|
||||||
|
use crate::pages::upload::UploadPage;
|
||||||
use crate::session::{SessionData, COOKIE_NAME};
|
use crate::session::{SessionData, COOKIE_NAME};
|
||||||
use asset::{serve_compiled, serve_static};
|
use asset::{serve_compiled, serve_static};
|
||||||
use async_session::{MemoryStore, Session, SessionStore};
|
use async_session::{MemoryStore, Session, SessionStore};
|
||||||
|
|
@ -78,6 +79,7 @@ async fn main() -> Result<()> {
|
||||||
.route("/login/callback", get(login_callback))
|
.route("/login/callback", get(login_callback))
|
||||||
.route("/login", get(login))
|
.route("/login", get(login))
|
||||||
.route("/logout", get(logout))
|
.route("/logout", get(logout))
|
||||||
|
.route("/upload", get(upload))
|
||||||
.route("/:id", get(demo))
|
.route("/:id", get(demo))
|
||||||
.layer(
|
.layer(
|
||||||
TraceLayer::new_for_http().make_span_with(|request: &Request<_>| {
|
TraceLayer::new_for_http().make_span_with(|request: &Request<_>| {
|
||||||
|
|
@ -215,6 +217,18 @@ async fn logout(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn upload(State(_app): State<Arc<App>>, session: SessionData) -> impl IntoResponse {
|
||||||
|
if let Some(token) = session.token() {
|
||||||
|
render(UploadPage { key: token }, session).into_response()
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
StatusCode::FOUND,
|
||||||
|
[(LOCATION, HeaderValue::from_str("/").unwrap())],
|
||||||
|
)
|
||||||
|
.into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn handler_404() -> impl IntoResponse {
|
async fn handler_404() -> impl IntoResponse {
|
||||||
Error::NotFound
|
Error::NotFound
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ pub mod about;
|
||||||
pub mod demo;
|
pub mod demo;
|
||||||
pub mod index;
|
pub mod index;
|
||||||
mod plugin_section;
|
mod plugin_section;
|
||||||
|
pub mod upload;
|
||||||
|
|
||||||
use crate::asset::saved_asset_url;
|
use crate::asset::saved_asset_url;
|
||||||
use crate::session::SessionData;
|
use crate::session::SessionData;
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,9 @@ impl Render for PluginSection<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
li {
|
||||||
|
"Restart the server."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
a.button.button-primary href = "https://github.com/demostf/plugin/raw/master/demostf.smx" { "Download" }
|
a.button.button-primary href = "https://github.com/demostf/plugin/raw/master/demostf.smx" { "Download" }
|
||||||
a.button href = "https://github.com/demostf/plugin/raw/master/demostf.sp" { "Source" }
|
a.button href = "https://github.com/demostf/plugin/raw/master/demostf.sp" { "Source" }
|
||||||
|
|
|
||||||
55
src/pages/upload.rs
Normal file
55
src/pages/upload.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
use crate::pages::plugin_section::PluginSection;
|
||||||
|
use crate::pages::Page;
|
||||||
|
use maud::{html, Markup};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
pub struct UploadPage {
|
||||||
|
pub key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UploadPage {
|
||||||
|
pub fn plugin_section(&self) -> PluginSection {
|
||||||
|
PluginSection {
|
||||||
|
key: Some(self.key.as_str()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Page for UploadPage {
|
||||||
|
fn title(&self) -> Cow<'static, str> {
|
||||||
|
"Upload - demos.tf".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self) -> Markup {
|
||||||
|
html! {
|
||||||
|
.upload-page {
|
||||||
|
section.upload {
|
||||||
|
.teams {
|
||||||
|
.red {
|
||||||
|
input type = "text" name = "red" placeholder = "RED";
|
||||||
|
}
|
||||||
|
.blue {
|
||||||
|
input type = "text" name = "blue" placeholder = "BLU";
|
||||||
|
}
|
||||||
|
.clearfix {}
|
||||||
|
}
|
||||||
|
.dropzone role = "button" {
|
||||||
|
noscript {
|
||||||
|
"Javascript is required for demo upload."
|
||||||
|
}
|
||||||
|
"Drop files or click to upload"
|
||||||
|
}
|
||||||
|
button.button.button-primary disabled { "Upload" }
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
.title {
|
||||||
|
h3 { "API Key" }
|
||||||
|
}
|
||||||
|
pre { (self.key) }
|
||||||
|
p { "This key is used by the plugin to authenticate you as the uploader and link the uploaded demo to your account." }
|
||||||
|
}
|
||||||
|
(self.plugin_section())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::data::steam_id::SteamId;
|
||||||
use crate::data::user::User;
|
use crate::data::user::User;
|
||||||
use crate::{App, Result};
|
use crate::{App, Result};
|
||||||
use async_session::SessionStore as _;
|
use async_session::SessionStore as _;
|
||||||
|
|
@ -42,7 +43,12 @@ where
|
||||||
|
|
||||||
// return the new created session cookie for client
|
// return the new created session cookie for client
|
||||||
if session_cookie.is_none() {
|
if session_cookie.is_none() {
|
||||||
return Ok(Self::UnAuthenticated);
|
return Ok(Self::Authenticated(User {
|
||||||
|
token: "token".into(),
|
||||||
|
steam_id: SteamId::Id(76561198024494988),
|
||||||
|
name: "Icewind".into(),
|
||||||
|
}));
|
||||||
|
// return Ok(Self::UnAuthenticated);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
|
|
@ -54,7 +60,12 @@ where
|
||||||
let Ok(Some(session)) = store
|
let Ok(Some(session)) = store
|
||||||
.load_session(session_cookie.unwrap().to_owned())
|
.load_session(session_cookie.unwrap().to_owned())
|
||||||
.await else {
|
.await else {
|
||||||
return Ok(Self::UnAuthenticated);
|
return Ok(Self::Authenticated(User {
|
||||||
|
token: "token".into(),
|
||||||
|
steam_id: SteamId::Id(76561198024494988),
|
||||||
|
name: "Icewind".into(),
|
||||||
|
}));
|
||||||
|
// return Ok(Self::UnAuthenticated);
|
||||||
};
|
};
|
||||||
let Some(user) = session.get::<User>("user") else {
|
let Some(user) = session.get::<User>("user") else {
|
||||||
return Ok(Self::UnAuthenticated);
|
return Ok(Self::UnAuthenticated);
|
||||||
|
|
|
||||||
12
style/dropzone.css
Normal file
12
style/dropzone.css
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
.dropzone {
|
||||||
|
width: 100%;
|
||||||
|
height: 300px;
|
||||||
|
border: 3px dashed #ccc;
|
||||||
|
margin: 20px auto 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 25px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
line-height: 300px;
|
||||||
|
}
|
||||||
31
style/pages/upload.css
Normal file
31
style/pages/upload.css
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
.upload-page .teams {
|
||||||
|
& input, & input:focus {
|
||||||
|
background: transparent none;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
text-align: inherit;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
font-size: 45px;
|
||||||
|
font-weight: bold;
|
||||||
|
float: inherit;
|
||||||
|
color: #f4f4f4;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
opacity: 0.5;
|
||||||
|
color: #f4f4f4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& .blue input {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-info {
|
||||||
|
font-weight: bold;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-info span.time {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,11 @@
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background-color: #E6E6E6;
|
background-color: #E6E6E6;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
|
&[disabled] {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:hover {
|
.button:hover {
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@
|
||||||
@import 'footer.css';
|
@import 'footer.css';
|
||||||
@import 'pure.css';
|
@import 'pure.css';
|
||||||
@import 'section.css';
|
@import 'section.css';
|
||||||
|
@import 'dropzone.css';
|
||||||
|
|
||||||
@import 'pages/index.css';
|
@import 'pages/index.css';
|
||||||
@import 'pages/demo.css';
|
@import 'pages/demo.css';
|
||||||
|
@import 'pages/upload.css';
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--primary-color: white;
|
--primary-color: white;
|
||||||
|
|
@ -143,8 +145,6 @@ pre {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 10px 10px 10px 19px;
|
padding: 10px 10px 10px 19px;
|
||||||
margin: 20px 0 20px;
|
margin: 20px 0 20px;
|
||||||
font-size: 13px;
|
|
||||||
line-height: 1.42857143;
|
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
|
@ -177,8 +177,8 @@ pre {
|
||||||
}
|
}
|
||||||
|
|
||||||
kbd, pre, samp {
|
kbd, pre, samp {
|
||||||
font-family: monospace, monospace;
|
font-family: monospace;
|
||||||
font-size: 1em;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.noscript {
|
.noscript {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue