WIP: Complete minijinja support
This commit is contained in:
parent
6eaad79f9a
commit
4fa9f08cc3
13 changed files with 240 additions and 22 deletions
18
Cargo.lock
generated
18
Cargo.lock
generated
|
@ -1201,6 +1201,7 @@ dependencies = [
|
||||||
"datastar",
|
"datastar",
|
||||||
"maud",
|
"maud",
|
||||||
"mime_guess",
|
"mime_guess",
|
||||||
|
"minijinja",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"serde",
|
"serde",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -3405,6 +3406,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memo-map"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38d1115007560874e373613744c6fba374c17688327a71c1476d1a5954cc857b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memoffset"
|
name = "memoffset"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
|
@ -3430,6 +3437,17 @@ dependencies = [
|
||||||
"unicase",
|
"unicase",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minijinja"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e36f1329330bb1614c94b78632b9ce45dd7d761f3304a1bed07b2990a7c5097"
|
||||||
|
dependencies = [
|
||||||
|
"memo-map",
|
||||||
|
"self_cell",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
|
|
@ -15,6 +15,7 @@ axum-typed-routing = { git = "https://github.com/jvdwrf/axum-typed-routing", ver
|
||||||
datastar = { git = "https://github.com/starfederation/datastar.git", version = "0.1.0" }
|
datastar = { git = "https://github.com/starfederation/datastar.git", version = "0.1.0" }
|
||||||
maud = { version = "0.27.0", features = ["axum"] }
|
maud = { version = "0.27.0", features = ["axum"] }
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
|
minijinja = { version = "2.7.0", features = ["loader"] }
|
||||||
rust-embed = { version = "8.5.0", features = ["axum", "compression"] }
|
rust-embed = { version = "8.5.0", features = ["axum", "compression"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
tokio = { version = "1.43", features = ["full", "tracing"] }
|
tokio = { version = "1.43", features = ["full", "tracing"] }
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
* Planning (newest)
|
||||||
|
- maud vs hypertext vs minijinja
|
||||||
|
- try porting to hypertext ?
|
||||||
* Todos
|
* Todos
|
||||||
** Starter boilerplate
|
** Starter boilerplate
|
||||||
*** [X] Server: Axum
|
*** [X] Server: Axum
|
||||||
|
@ -5,7 +8,7 @@
|
||||||
**** [X] Nested Router
|
**** [X] Nested Router
|
||||||
**** [X] Fallbacks
|
**** [X] Fallbacks
|
||||||
*** [X] "embd tmpl": Maud
|
*** [X] "embd tmpl": Maud
|
||||||
*** [ ] "html tmpl": minijinja
|
*** [X] "html tmpl": minijinja
|
||||||
- only index yaml for now, test in app wether inline or external templates feel better
|
- only index yaml for now, test in app wether inline or external templates feel better
|
||||||
*** [ ] CSS Basic
|
*** [ ] CSS Basic
|
||||||
- UnoCSS
|
- UnoCSS
|
||||||
|
|
2
darm_test/public/datastar.min.js
vendored
2
darm_test/public/datastar.min.js
vendored
|
@ -1 +1 @@
|
||||||
css/datastar-1-0-0-beta-7.js
|
js/datastar-1-0-0-beta-7.js
|
BIN
darm_test/public/favicon.ico
Normal file
BIN
darm_test/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
2
darm_test/public/styles.min.css
vendored
2
darm_test/public/styles.min.css
vendored
|
@ -1 +1 @@
|
||||||
*,::before,::after{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgba(0,0,0,0);--un-ring-shadow:0 0 rgba(0,0,0,0);--un-shadow-inset: ;--un-shadow:0 0 rgba(0,0,0,0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,0.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }::backdrop{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgba(0,0,0,0);--un-ring-shadow:0 0 rgba(0,0,0,0);--un-shadow-inset: ;--un-shadow:0 0 rgba(0,0,0,0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,0.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }.static{position:static}.m-1,.m1,[m-1=""],m1{margin:.25rem}.ms,[ms=""]{margin-inline-start:1rem}contents{display:contents}.h1{height:.25rem}.b,[b=""]{border-width:1px}[pe=""]{padding-inline-end:1rem}
|
*,::before,::after{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgba(0,0,0,0);--un-ring-shadow:0 0 rgba(0,0,0,0);--un-shadow-inset: ;--un-shadow:0 0 rgba(0,0,0,0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,0.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }::backdrop{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgba(0,0,0,0);--un-ring-shadow:0 0 rgba(0,0,0,0);--un-shadow-inset: ;--un-shadow:0 0 rgba(0,0,0,0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,0.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }.static{position:static}.m-1,.m1,[m-1=""],m1{margin:.25rem}m2{margin:.5rem}m3{margin:.75rem}.ms,[ms=""]{margin-inline-start:1rem}.block{display:block}contents{display:contents}.h1{height:.25rem}.b,[b=""]{border-width:1px}[pe=""]{padding-inline-end:1rem}
|
||||||
|
|
|
@ -1,30 +1,23 @@
|
||||||
use std::sync::Once;
|
use std::sync::{Arc, Once};
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
body::Body,
|
body::Body,
|
||||||
extract::State,
|
extract::State,
|
||||||
http::{header, Response, StatusCode, Uri},
|
http::{header, Response, StatusCode, Uri},
|
||||||
response::IntoResponse,
|
response::{Html, IntoResponse},
|
||||||
routing::get,
|
routing::get,
|
||||||
};
|
};
|
||||||
use axum_typed_routing::{route, TypedRouter};
|
use axum_typed_routing::{route, TypedRouter};
|
||||||
use maud::{html, Markup};
|
use maud::{html, Markup};
|
||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
|
|
||||||
#[route(GET "/hello")]
|
|
||||||
async fn hello_world(State(_): State<String>) -> Markup {
|
|
||||||
html! {
|
|
||||||
h1 { "Hello, World!" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[route(GET "/item/:id?amount&offset")]
|
#[route(GET "/item/:id?amount&offset")]
|
||||||
async fn item_handler(
|
async fn item_handler(
|
||||||
id: u32,
|
id: u32,
|
||||||
amount: Option<u32>,
|
amount: Option<u32>,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
State(state): State<String>,
|
State(state): State<AppState>,
|
||||||
// Json(json): Json<u32>,
|
// Json(json): Json<u32>,
|
||||||
) -> Markup {
|
) -> Markup {
|
||||||
// todo!("handle request")
|
// todo!("handle request")
|
||||||
|
@ -41,7 +34,7 @@ async fn item_handler(
|
||||||
// within our defined assets directory. This is the directory on our Asset
|
// within our defined assets directory. This is the directory on our Asset
|
||||||
// struct below, where folder = "examples/public/".
|
// struct below, where folder = "examples/public/".
|
||||||
#[route(GET "/dist/*path")]
|
#[route(GET "/dist/*path")]
|
||||||
async fn static_handler(path: String, _: State<String>) -> impl IntoResponse {
|
async fn static_handler(path: String, _: State<AppState>) -> impl IntoResponse {
|
||||||
let path = path.trim_start_matches('/').to_string();
|
let path = path.trim_start_matches('/').to_string();
|
||||||
|
|
||||||
StaticFile(path)
|
StaticFile(path)
|
||||||
|
@ -51,6 +44,9 @@ fn markup_404(uri: String) -> Markup {
|
||||||
html! {
|
html! {
|
||||||
h1 { "404" }
|
h1 { "404" }
|
||||||
p { (format!("{uri:?} Not Found")) }
|
p { (format!("{uri:?} Not Found")) }
|
||||||
|
@for i in 0..5 {
|
||||||
|
div .{"m-" (i)} { (i) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +54,9 @@ fn markup_405() -> Markup {
|
||||||
html! {
|
html! {
|
||||||
h1 { "404" }
|
h1 { "404" }
|
||||||
p { "Method not allowed!" }
|
p { "Method not allowed!" }
|
||||||
|
@for i in 1..3 {
|
||||||
|
div .{"m-" (i)} {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,6 +81,7 @@ where
|
||||||
{
|
{
|
||||||
fn into_response(self) -> Response<Body> {
|
fn into_response(self) -> Response<Body> {
|
||||||
let path = self.0.into();
|
let path = self.0.into();
|
||||||
|
tracing::debug!(?path);
|
||||||
|
|
||||||
match Asset::get(path.as_str()) {
|
match Asset::get(path.as_str()) {
|
||||||
Some(content) => {
|
Some(content) => {
|
||||||
|
@ -109,24 +109,185 @@ pub fn initialize_logger() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use minijinja::{Environment, Template};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct AppState {
|
||||||
|
_field: String,
|
||||||
|
mj_env: Arc<minijinja::Environment<'static>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[route(GET "/")]
|
||||||
|
async fn jinja_index_handler(state: State<AppState>) -> impl IntoResponse {
|
||||||
|
RenderTemplate::new("/".to_string(), state.mj_env.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[route(GET "/*path")]
|
||||||
|
async fn jinja_index_handler_path(path: String, state: State<AppState>) -> impl IntoResponse {
|
||||||
|
RenderTemplate::new(path, state.mj_env.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RenderTemplate {
|
||||||
|
tmpl_name: String,
|
||||||
|
env: Arc<Environment<'static>>,
|
||||||
|
ctx: minijinja::Value,
|
||||||
|
block: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderTemplate {
|
||||||
|
fn new(tmpl_name: String, env: Arc<Environment<'static>>) -> Self {
|
||||||
|
Self {
|
||||||
|
tmpl_name,
|
||||||
|
env,
|
||||||
|
ctx: minijinja::Value::from(()),
|
||||||
|
block: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _new_with_ctx(
|
||||||
|
tmpl_name: String,
|
||||||
|
env: Arc<Environment<'static>>,
|
||||||
|
ctx: minijinja::Value,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
tmpl_name,
|
||||||
|
env,
|
||||||
|
ctx,
|
||||||
|
block: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoResponse for RenderTemplate {
|
||||||
|
fn into_response(self) -> axum::response::Response {
|
||||||
|
let path = self.tmpl_name;
|
||||||
|
let env = self.env;
|
||||||
|
let ctx = self.ctx;
|
||||||
|
let block = self.block;
|
||||||
|
|
||||||
|
let res = env.get_template(&path);
|
||||||
|
|
||||||
|
let render = move |template: Template| match block {
|
||||||
|
None => match template.render(ctx) {
|
||||||
|
Ok(html) => Html(html).into_response(),
|
||||||
|
Err(_) => (
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
"Failed to render template",
|
||||||
|
)
|
||||||
|
.into_response(),
|
||||||
|
},
|
||||||
|
Some(block) => match template.eval_to_state(ctx).unwrap().render_block(&block) {
|
||||||
|
Ok(html) => Html(html).into_response(),
|
||||||
|
Err(_) => (
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
"Failed to render block template",
|
||||||
|
)
|
||||||
|
.into_response(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(template) => render(template),
|
||||||
|
Err(_) => {
|
||||||
|
let template = env.get_template("404").unwrap();
|
||||||
|
let html = render(template);
|
||||||
|
(StatusCode::NOT_FOUND, html).into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Template loader for embedded templates. Uses Symbol’s value as variable is void: rust-embed to embed templates into the binary.
|
||||||
|
/// For input path example/template
|
||||||
|
/// it looks up:
|
||||||
|
/// - $template_dir/example/template/index.html
|
||||||
|
/// - $template_dir/example/template.html
|
||||||
|
///
|
||||||
|
/// Instead of example/template
|
||||||
|
/// any of:
|
||||||
|
/// - example//template/
|
||||||
|
/// - example/template///
|
||||||
|
/// - example/template.html
|
||||||
|
/// - example/template/index.html
|
||||||
|
/// would also resolve to the same 2 templates above
|
||||||
|
/// TODO Canonilize path with a middleware that redirects any weird variants to the canonical path?
|
||||||
|
fn template_loader(name: &str) -> Result<Option<String>, minijinja::Error> {
|
||||||
|
#[derive(RustEmbed)]
|
||||||
|
#[folder = "src/templates/"]
|
||||||
|
struct Templ;
|
||||||
|
// lets extract the "clean path" (i.e. '.html' or '/index.html' suffix)
|
||||||
|
let mut clean_path = name
|
||||||
|
.to_string()
|
||||||
|
.chars()
|
||||||
|
.fold(String::new(), |mut acc, c| {
|
||||||
|
if c == '/' && acc.chars().last() != Some('/') {
|
||||||
|
acc.push(c);
|
||||||
|
} else if c != '/' {
|
||||||
|
acc.push(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
acc
|
||||||
|
})
|
||||||
|
.to_string();
|
||||||
|
clean_path = clean_path
|
||||||
|
.strip_suffix("index.html")
|
||||||
|
.unwrap_or(&clean_path)
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
clean_path = clean_path
|
||||||
|
.strip_suffix(".html")
|
||||||
|
.unwrap_or(&clean_path)
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let cleaned_path = clean_path.to_string();
|
||||||
|
|
||||||
|
let files_to_try = vec![
|
||||||
|
format!("./{}.html", cleaned_path),
|
||||||
|
format!("./{}/index.html", cleaned_path),
|
||||||
|
];
|
||||||
|
|
||||||
|
let found_template = files_to_try.into_iter().find_map(|path| {
|
||||||
|
tracing::info!(?path);
|
||||||
|
match Templ::get(path.as_str()) {
|
||||||
|
Some(content) => {
|
||||||
|
let content = std::str::from_utf8(&content.data).unwrap().to_string();
|
||||||
|
Some(content)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(found_template)
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
initialize_logger();
|
initialize_logger();
|
||||||
|
|
||||||
|
let mut mj_env = Environment::new();
|
||||||
|
|
||||||
|
mj_env.set_loader(template_loader);
|
||||||
|
|
||||||
|
let app_state = AppState {
|
||||||
|
_field: "".to_string(),
|
||||||
|
mj_env: Arc::new(mj_env),
|
||||||
|
};
|
||||||
|
|
||||||
// TODO pick free port/config
|
// TODO pick free port/config
|
||||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:8000").await.unwrap();
|
let listener = tokio::net::TcpListener::bind("0.0.0.0:8000").await.unwrap();
|
||||||
|
|
||||||
let ui_router = axum::Router::new()
|
let ui_router = axum::Router::new().typed_route(item_handler);
|
||||||
.typed_route(item_handler)
|
|
||||||
.typed_route(hello_world);
|
|
||||||
|
|
||||||
let router: axum::Router = axum::Router::new()
|
let router: axum::Router = axum::Router::new()
|
||||||
|
// .route("/", get(jinja_index_handler))
|
||||||
.merge(ui_router.clone())
|
.merge(ui_router.clone())
|
||||||
.nest("/ui", ui_router)
|
.nest("/ui", ui_router)
|
||||||
|
.typed_route(jinja_index_handler)
|
||||||
|
.typed_route(jinja_index_handler_path)
|
||||||
.typed_route(static_handler)
|
.typed_route(static_handler)
|
||||||
.fallback_service(get(handle_404))
|
.fallback_service(get(handle_404))
|
||||||
.method_not_allowed_fallback(handle_405)
|
.method_not_allowed_fallback(handle_405)
|
||||||
.with_state("state".to_string());
|
.with_state(app_state);
|
||||||
|
|
||||||
let _ = axum::serve(listener, router.into_make_service()).await;
|
let _ = axum::serve(listener, router.into_make_service()).await;
|
||||||
}
|
}
|
||||||
|
|
1
darm_test/src/templates/404.html
Normal file
1
darm_test/src/templates/404.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<h1>Not Found! </h1>
|
4
darm_test/src/templates/asd/def.html
Normal file
4
darm_test/src/templates/asd/def.html
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<h1>Def</h1>
|
||||||
|
{% block sidebar %}
|
||||||
|
"Sidebar test"
|
||||||
|
{% endblock sidebar %}
|
26
darm_test/src/templates/index.html
Normal file
26
darm_test/src/templates/index.html
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>My Website</title>
|
||||||
|
<!-- TODO!!!: Add favico -->
|
||||||
|
<!-- <link rel="icon" href="./favicon.ico" type="image/x-icon"> -->
|
||||||
|
<script type="module" src="/dist/datastar.min.js"></script>
|
||||||
|
<link rel="stylesheet" href="/dist/styles.min.css">
|
||||||
|
<link rel="icon" href="/dist/favicon.ico" />
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<h1>Welcome to My Website</h1>
|
||||||
|
<div m-1>div</div>
|
||||||
|
<div class="m-1">div</div>
|
||||||
|
<div class="m1">div</div>
|
||||||
|
<m1>m1</m1>
|
||||||
|
<m2>m2</m2>
|
||||||
|
<m3>m3</m3>
|
||||||
|
</main>
|
||||||
|
<!-- <script src="index.js"></script> -->
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -8,7 +8,8 @@
|
||||||
<!-- TODO!!!: Add favico -->
|
<!-- TODO!!!: Add favico -->
|
||||||
<!-- <link rel="icon" href="./favicon.ico" type="image/x-icon"> -->
|
<!-- <link rel="icon" href="./favicon.ico" type="image/x-icon"> -->
|
||||||
<script type="module" src="/dist/datastar.min.js"></script>
|
<script type="module" src="/dist/datastar.min.js"></script>
|
||||||
<link rel="stylesheet" href="./dist/styles.min.css">
|
<link rel="stylesheet" href="/dist/styles.min.css">
|
||||||
|
<link rel="icon" href="/dist/favicon.ico" />
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -19,6 +20,6 @@
|
||||||
<div class="m1">div</div>
|
<div class="m1">div</div>
|
||||||
<m1>div</m1>
|
<m1>div</m1>
|
||||||
</main>
|
</main>
|
||||||
<script src="index.js"></script>
|
<!-- <script src="index.js"></script> -->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -6,8 +6,11 @@
|
||||||
.m1,
|
.m1,
|
||||||
[m-1=""],
|
[m-1=""],
|
||||||
m1{margin:0.25rem;}
|
m1{margin:0.25rem;}
|
||||||
|
m2{margin:0.5rem;}
|
||||||
|
m3{margin:0.75rem;}
|
||||||
.ms,
|
.ms,
|
||||||
[ms=""]{margin-inline-start:1rem;}
|
[ms=""]{margin-inline-start:1rem;}
|
||||||
|
.block{display:block;}
|
||||||
contents{display:contents;}
|
contents{display:contents;}
|
||||||
.h1{height:0.25rem;}
|
.h1{height:0.25rem;}
|
||||||
.b,
|
.b,
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "oeko-mono",
|
"name": "redvault-ai",
|
||||||
"description": "",
|
"description": "",
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"author": "Tristan Druyen <tristan@vault81.mozmail.com>",
|
"author": "Tristan Druyen <tristan@vault81.mozmail.com>",
|
||||||
"license": "AGPL",
|
"license": "AGPL",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch-unocss": "cd ./darm_test && unocss --watch",
|
"watch-unocss": "cd ./darm_test && unocss --watch",
|
||||||
"watch-bundle": "npm run build-bundle",
|
"watch-bundle": "npm run build-bundle; sleep 5; npm run watch-bundle",
|
||||||
"build-unocss": "cd ./darm_test && unocss",
|
"build-unocss": "cd ./darm_test && unocss",
|
||||||
"build-bundle": "cd ./darm_test && sass -scompressed style/main.scss > public/styles.min.css",
|
"build-bundle": "cd ./darm_test && sass -scompressed style/main.scss > public/styles.min.css",
|
||||||
"watch-all": "npm run watch-unocss & npm run watch-bundle",
|
"watch-all": "npm run watch-unocss & npm run watch-bundle",
|
||||||
|
|
Loading…
Add table
Reference in a new issue