diff --git a/.editorconfig b/.editorconfig index dc35434..bcb5235 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,12 +1,12 @@ -root = true - -[*] -indent_style = space -indent_size = 2 -end_of_line = crlf -charset = utf-8 -trim_trailing_whitespace = false -insert_final_newline = true - -[*.rs] -indent_size = 4 +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = true + +[*.rs] +indent_size = 4 diff --git a/.gitignore b/.gitignore index bc01a9e..4f96631 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -/target -/dist +/target +/dist diff --git a/LICENSE.md b/LICENSE.md index 015dca4..dc0100d 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,38 +1,38 @@ -The Charlotte Public License version 0.1 - -Copyright 2021, Lavender Software (collectively, the "Author" henceforth). - -This license gives everyone permission to examine, modify, and use this software -and the associated documentation (the "Inator"), without patent obstacles, while protecting -the Author and any contributors (the "Composers") from liability. - -Each Composer permits you to examine, modify, utilize, and distribute the Inator -where it would otherwise infringe upon that Composer's copyright or any patent claims that -they hold. - -No contributor may revoke this license, but the Author may choose to release the Inator -(including the contributed works of any other Composer) under a different license. - -You may not use the Inator to accrue revenue without explicit permission from the Author. - -You may not use the Inator to do Malevolence. If you are -notified that you have committed a Malevolence instrumented by the Inator, your license is -terminated unless you take all practical steps to comply within a reasonable timeframe. - -The definition of Malevolence is at the discretion of the Author. It may include, but is not -limited to: - -- The promotion of bigotry, including: sexism, transphobia, homophobia, ableism, or the -perpetuation of racial oppression. -- Causing a detriment to public health. -- Instigating political, economic, or corporeal violence. -- Entrenching an empire. -- Where applicable, use of the Inator without the informed consent of a second party who may -object to its use. - -The Inator is provided without any warranty, "as-is". No Composer is liable for any damages -related to the Inator. - -In order to receive this license, you must agree to the terms set out in this document. -This license, authorial attribution, and copyright notice must be distributed with -any copies or large portions of the Inator. +The Charlotte Public License version 0.1 + +Copyright 2021, Lavender Software (collectively, the "Author" henceforth). + +This license gives everyone permission to examine, modify, and use this software +and the associated documentation (the "Inator"), without patent obstacles, while protecting +the Author and any contributors (the "Composers") from liability. + +Each Composer permits you to examine, modify, utilize, and distribute the Inator +where it would otherwise infringe upon that Composer's copyright or any patent claims that +they hold. + +No contributor may revoke this license, but the Author may choose to release the Inator +(including the contributed works of any other Composer) under a different license. + +You may not use the Inator to accrue revenue without explicit permission from the Author. + +You may not use the Inator to do Malevolence. If you are +notified that you have committed a Malevolence instrumented by the Inator, your license is +terminated unless you take all practical steps to comply within a reasonable timeframe. + +The definition of Malevolence is at the discretion of the Author. It may include, but is not +limited to: + +- The promotion of bigotry, including: sexism, transphobia, homophobia, ableism, or the +perpetuation of racial oppression. +- Causing a detriment to public health. +- Instigating political, economic, or corporeal violence. +- Entrenching an empire. +- Where applicable, use of the Inator without the informed consent of a second party who may +object to its use. + +The Inator is provided without any warranty, "as-is". No Composer is liable for any damages +related to the Inator. + +In order to receive this license, you must agree to the terms set out in this document. +This license, authorial attribution, and copyright notice must be distributed with +any copies or large portions of the Inator. diff --git a/README.md b/README.md index c2d42b4..e7c7db1 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,32 @@ -# lavender.software - -Static site generated using [siru](https://github.com/videogame-hacker/siru) and hosted at [lavender.software](https://lavender.software/). - -## Deploying - -```shell -$ # To set up, ensure that the 'dist' folder reflects the VPS -$ git clone 'root@lavender.software:/srv/http/lavender.software' dist -$ -$ cargo run # Build the site -$ cd dist/ -dist/ $ git add -A . && git commit -m "Deploying: $(date)" -dist/ $ git pull --rebase -dist/ $ git push -dist/ $ # Your changes should now be live at lavender.software -``` +# lavender.software + +Static site generated using [siru](https://github.com/videogame-hacker/siru) and hosted at [lavender.software](https://lavender.software/). + +## Setting Up + +```shell +$ git clone 'git@lavender.software:lavender/lavender.software.git' +$ cd lavender.software/ +lavender.software/ $ mkdir dist # or follow instructions in 'Deploying' +lavender.software/ $ cargo run +... +lavender.software/ $ # Built files are in dist/ +``` + +You may want to `cd dist && python -m http.server` to get a local HTTP server. + +## Deploying + +**Note:** You don't need to do this unless you're th eone deploying the site to the production environment. + +```shell +$ # To set up, ensure that the 'dist' folder reflects the VPS +$ git clone 'root@lavender.software:/srv/http/lavender.software' dist +$ +$ cargo run # Build the site +$ cd dist/ +dist/ $ git add -A . && git commit -m "Deploying: $(date)" +dist/ $ git pull --rebase +dist/ $ git push +dist/ $ # Your changes should now be live at lavender.software +``` diff --git a/build.rs b/build.rs index 4573f07..49f210e 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,3 @@ -fn main() { - println!("cargo:rerun-if-changed=build_src"); -} +fn main() { + println!("cargo:rerun-if-changed=build_src"); +} diff --git a/build_src/assets.rs b/build_src/assets.rs index fb9e906..77ecb6b 100644 --- a/build_src/assets.rs +++ b/build_src/assets.rs @@ -1,8 +1,8 @@ -use crate::*; - -pub fn copy_assets(ctx: &BuildContext) -> Result<()> { - log_info("Copying assets…"); - copy_dir_recursive(ctx.source_dir.join("assets"), ctx.output_dir.join("assets"))?; - - Ok(()) -} +use crate::*; + +pub fn copy_assets(ctx: &BuildContext) -> Result<()> { + log_info("Copying assets…"); + copy_dir_recursive(ctx.source_dir.join("assets"), ctx.output_dir.join("assets"))?; + + Ok(()) +} diff --git a/build_src/main.rs b/build_src/main.rs index c2a3b10..8716f44 100644 --- a/build_src/main.rs +++ b/build_src/main.rs @@ -1,84 +1,84 @@ -use siru::prelude::*; -use std::{convert::TryInto, fs, path::Path, path::PathBuf, sync::Arc, time::Duration}; - -type Result = std::result::Result>; - -mod assets; -mod main_page; -mod webring; - -pub struct BuildContext { - source_dir: PathBuf, - output_dir: PathBuf, - write_pipeline: WritePipeline, -} - -impl SiruFS for BuildContext { - fn get_source_dir(&self) -> &PathBuf { - &self.source_dir - } - - fn get_output_dir(&self) -> &PathBuf { - &self.output_dir - } - - fn get_write_pipeline(&self) -> &WritePipeline { - &self.write_pipeline - } -} - -pub fn copy_dir_recursive(src: impl AsRef, dst: impl AsRef) -> std::io::Result<()> { - fs::create_dir_all(&dst)?; - for entry in fs::read_dir(src)? { - let entry = entry?; - let ty = entry.file_type()?; - if ty.is_dir() { - copy_dir_recursive(entry.path(), dst.as_ref().join(entry.file_name()))?; - } else { - fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; - } - } - Ok(()) -} - -fn build() { - let ctx = BuildContext { - source_dir: "src".try_into().unwrap(), - output_dir: "dist".try_into().unwrap(), - write_pipeline: WritePipeline::new(), - }; - - let ctx = Arc::new(ctx); - - [ - main_page::main_page, - assets::copy_assets, - webring::copy_webring, - ] - .iter() - .map(|f| { - let ctx = Arc::clone(&ctx); - std::thread::spawn(move || f(&ctx).unwrap()) - }) - .for_each(|t| t.join().unwrap()); -} - -fn main() { - build(); - - if matches!(std::env::args().nth(1).as_deref(), Some("watch")) { - use notify::*; - - let (tx, rx) = std::sync::mpsc::channel(); - - let mut watcher = watcher(tx, Duration::from_millis(100)).unwrap(); - watcher.watch("./src", RecursiveMode::Recursive).unwrap(); - - loop { - match rx.recv() { - Ok(_) => build(), - Err(_) => break, - } - } - } -} +use siru::prelude::*; +use std::{convert::TryInto, fs, path::Path, path::PathBuf, sync::Arc, time::Duration}; + +type Result = std::result::Result>; + +mod assets; +mod main_page; +mod webring; + +pub struct BuildContext { + source_dir: PathBuf, + output_dir: PathBuf, + write_pipeline: WritePipeline, +} + +impl SiruFS for BuildContext { + fn get_source_dir(&self) -> &PathBuf { + &self.source_dir + } + + fn get_output_dir(&self) -> &PathBuf { + &self.output_dir + } + + fn get_write_pipeline(&self) -> &WritePipeline { + &self.write_pipeline + } +} + +pub fn copy_dir_recursive(src: impl AsRef, dst: impl AsRef) -> std::io::Result<()> { + fs::create_dir_all(&dst)?; + for entry in fs::read_dir(src)? { + let entry = entry?; + let ty = entry.file_type()?; + if ty.is_dir() { + copy_dir_recursive(entry.path(), dst.as_ref().join(entry.file_name()))?; + } else { + fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; + } + } + Ok(()) +} + +fn build() { + let ctx = BuildContext { + source_dir: "src".try_into().unwrap(), + output_dir: "dist".try_into().unwrap(), + write_pipeline: WritePipeline::new(), + }; + + let ctx = Arc::new(ctx); + + [ + main_page::main_page, + assets::copy_assets, + webring::copy_webring, + ] + .iter() + .map(|f| { + let ctx = Arc::clone(&ctx); + std::thread::spawn(move || f(&ctx).unwrap()) + }) + .for_each(|t| t.join().unwrap()); +} + +fn main() { + build(); + + if matches!(std::env::args().nth(1).as_deref(), Some("watch")) { + use notify::*; + + let (tx, rx) = std::sync::mpsc::channel(); + + let mut watcher = watcher(tx, Duration::from_millis(100)).unwrap(); + watcher.watch("./src", RecursiveMode::Recursive).unwrap(); + + loop { + match rx.recv() { + Ok(_) => build(), + Err(_) => break, + } + } + } +} diff --git a/build_src/main_page.rs b/build_src/main_page.rs index 012d5b7..086bb22 100644 --- a/build_src/main_page.rs +++ b/build_src/main_page.rs @@ -1,58 +1,58 @@ -use crate::*; - -struct Member { - name: String, - website: String, - title: String, -} - -impl From<&(&str, &str, &str)> for Member { - fn from(tuple: &(&str, &str, &str)) -> Self { - Member { - name: tuple.0.to_string(), - website: tuple.1.to_string(), - title: tuple.2.to_string(), - } - } -} - -#[derive(Template)] -#[template(path = "main_page.html.j2")] -struct MainPageTemplate { - members: Vec, -} - -pub fn main_page(ctx: &BuildContext) -> Result<()> { - log_info("Rendering main page…"); - - let members = [ - ("charlotte som", "https://som.codes/", "founder"), - ("agatha rose", "https://agatharose.dev/", "meow"), - ("maya", "https://1312.gay/", "chief director of maya"), - ( - "Luna Lulu", - "https://lunaisa.dev", - "critically acclaimed website maker", - ), - ( - "annie versario", - "https://annie.kitty.lgbt", - "regional marquee technician", - ), - ( - "The System", - "https://the-system.eu.org", - "lead systems specialist", - ), - ]; - - ctx.write( - "index.html", - MainPageTemplate { - members: members.iter().map(|x| x.into()).collect(), - } - .render()?, - )?; - - Ok(()) -} +use crate::*; + +struct Member { + name: String, + website: String, + title: String, +} + +impl From<&(&str, &str, &str)> for Member { + fn from(tuple: &(&str, &str, &str)) -> Self { + Member { + name: tuple.0.to_string(), + website: tuple.1.to_string(), + title: tuple.2.to_string(), + } + } +} + +#[derive(Template)] +#[template(path = "main_page.html.j2")] +struct MainPageTemplate { + members: Vec, +} + +pub fn main_page(ctx: &BuildContext) -> Result<()> { + log_info("Rendering main page…"); + + let members = [ + ("charlotte som", "https://som.codes/", "founder"), + ("agatha rose", "https://agatharose.dev/", "meow"), + ("maya", "https://1312.gay/", "chief director of maya"), + ( + "Luna Lulu", + "https://lunaisa.dev", + "critically acclaimed website maker", + ), + ( + "annie versario", + "https://annie.kitty.lgbt", + "regional marquee technician", + ), + ( + "The System", + "https://the-system.eu.org", + "lead systems specialist", + ), + ]; + + ctx.write( + "index.html", + MainPageTemplate { + members: members.iter().map(|x| x.into()).collect(), + } + .render()?, + )?; + + Ok(()) +} diff --git a/build_src/webring.rs b/build_src/webring.rs index 8a2184b..7088e07 100644 --- a/build_src/webring.rs +++ b/build_src/webring.rs @@ -1,11 +1,11 @@ -use crate::*; - -pub fn copy_webring(ctx: &BuildContext) -> Result<()> { - log_info("Copying webring…"); - copy_dir_recursive( - ctx.source_dir.join("webring"), - ctx.output_dir.join("webring"), - )?; - - Ok(()) -} +use crate::*; + +pub fn copy_webring(ctx: &BuildContext) -> Result<()> { + log_info("Copying webring…"); + copy_dir_recursive( + ctx.source_dir.join("webring"), + ctx.output_dir.join("webring"), + )?; + + Ok(()) +} diff --git a/src/assets/main_page/confetti.js b/src/assets/main_page/confetti.js index f6f47cc..55275a5 100644 --- a/src/assets/main_page/confetti.js +++ b/src/assets/main_page/confetti.js @@ -1,3 +1,3 @@ -/* confettijs.org MIT */ const Confetti = (() => { "use strict"; const e = 10; let t, o, n = 75, i = 25, r = 1, s = !1, l = !0, a = [], d = (new Date).getTime(); function p(e) { if (t = document.createElement("canvas"), o = t.getContext("2d"), t.width = 2 * window.innerWidth, t.height = 2 * window.innerHeight, t.style.position = "fixed", t.style.top = 0, t.style.left = 0, t.style.width = "calc(100%)", t.style.height = "calc(100%)", t.style.margin = 0, t.style.padding = 0, t.style.zIndex = 999999999, t.style.pointerEvents = "none", document.body.appendChild(t), null != e) { let t = document.getElementById(e); null != t && t.addEventListener("click", e => { !function (e, t) { let o = []; for (let i = 0; i < n; i++)o.push(c(e, t)); a.push({ particles: o }) }(2 * e.clientX, 2 * e.clientY), l && (e.target.style.visibility = "hidden") }) } window.addEventListener("resize", () => { t.width = 2 * window.innerWidth, t.height = 2 * window.innerHeight }) } function y(e) { return e.pos.y - 2 * e.size.x > 2 * window.innerHeight } function c(e, t) { let o = (16 * Math.random() + 4) * r, n = (4 * Math.random() + 4) * r; return { pos: { x: e - o / 2, y: t - n / 2 }, vel: h(), size: { x: o, y: n }, rotation: 360 * Math.random(), rotation_speed: 10 * (Math.random() - .5), hue: 360 * Math.random(), opacity: 100, lifetime: Math.random() + .25 } } function h() { let e = Math.random() - .5, t = Math.random() - .7, o = Math.sqrt(e * e + t * t); return t /= o, { x: (e /= o) * (Math.random() * i), y: t * (Math.random() * i) } } return p.prototype.setCount = (e => { "number" == typeof e ? n = e : console.error("[ERROR] Confetti.setCount() - Input needs to be of type 'number'") }), p.prototype.setPower = (e => { "number" == typeof e ? i = e : console.error("[ERROR] Confetti.setPower() - Input needs to be of type 'number'") }), p.prototype.setSize = (e => { "number" == typeof e ? r = e : console.error("[ERROR] Confetti.setSize() - Input needs to be of type 'number'") }), p.prototype.setFade = (e => { "boolean" == typeof e ? s = e : console.error("[ERROR] Confetti.setFade() - Input needs to be of type 'boolean'") }), p.prototype.destroyTarget = (e => { "boolean" == typeof e ? l = e : console.error("[ERROR] Confetti.destroyTarget() - Input needs to be of type 'boolean'") }), window.requestAnimationFrame(function t(n) { let i = (n - d) / 1e3; d = n; for (let t = a.length - 1; t >= 0; t--) { let o = a[t]; for (let t = o.particles.length - 1; t >= 0; t--) { let n = o.particles[t]; n.vel.y += e * (n.size.y / (10 * r)) * i, n.vel.x += 25 * (Math.random() - .5) * i, n.vel.x *= .98, n.vel.y *= .98, n.pos.x += n.vel.x, n.pos.y += n.vel.y, n.rotation += n.rotation_speed, s && (n.opacity -= n.lifetime), y(n) && o.particles.splice(t, 1) } 0 == o.particles.length && a.splice(t, 1) } !function () { o.clearRect(0, 0, 2 * window.innerWidth, 2 * window.innerHeight); for (const d of a) for (const a of d.particles) e = a.pos.x, t = a.pos.y, n = a.size.x, i = a.size.y, r = a.rotation, s = a.hue, l = a.opacity, o.save(), o.beginPath(), o.translate(e + n / 2, t + i / 2), o.rotate(r * Math.PI / 180), o.rect(-n / 2, -i / 2, n, i), o.fillStyle = `hsla(275deg, ${s}%, ${s / 3.6}%, ${l}%)`, o.fill(), o.restore(); var e, t, n, i, r, s, l }(), window.requestAnimationFrame(t) }), p })(); -const c = new Confetti("purple"); -c.destroyTarget(false); +/* confettijs.org MIT */ const Confetti = (() => { "use strict"; const e = 10; let t, o, n = 75, i = 25, r = 1, s = !1, l = !0, a = [], d = (new Date).getTime(); function p(e) { if (t = document.createElement("canvas"), o = t.getContext("2d"), t.width = 2 * window.innerWidth, t.height = 2 * window.innerHeight, t.style.position = "fixed", t.style.top = 0, t.style.left = 0, t.style.width = "calc(100%)", t.style.height = "calc(100%)", t.style.margin = 0, t.style.padding = 0, t.style.zIndex = 999999999, t.style.pointerEvents = "none", document.body.appendChild(t), null != e) { let t = document.getElementById(e); null != t && t.addEventListener("click", e => { !function (e, t) { let o = []; for (let i = 0; i < n; i++)o.push(c(e, t)); a.push({ particles: o }) }(2 * e.clientX, 2 * e.clientY), l && (e.target.style.visibility = "hidden") }) } window.addEventListener("resize", () => { t.width = 2 * window.innerWidth, t.height = 2 * window.innerHeight }) } function y(e) { return e.pos.y - 2 * e.size.x > 2 * window.innerHeight } function c(e, t) { let o = (16 * Math.random() + 4) * r, n = (4 * Math.random() + 4) * r; return { pos: { x: e - o / 2, y: t - n / 2 }, vel: h(), size: { x: o, y: n }, rotation: 360 * Math.random(), rotation_speed: 10 * (Math.random() - .5), hue: 360 * Math.random(), opacity: 100, lifetime: Math.random() + .25 } } function h() { let e = Math.random() - .5, t = Math.random() - .7, o = Math.sqrt(e * e + t * t); return t /= o, { x: (e /= o) * (Math.random() * i), y: t * (Math.random() * i) } } return p.prototype.setCount = (e => { "number" == typeof e ? n = e : console.error("[ERROR] Confetti.setCount() - Input needs to be of type 'number'") }), p.prototype.setPower = (e => { "number" == typeof e ? i = e : console.error("[ERROR] Confetti.setPower() - Input needs to be of type 'number'") }), p.prototype.setSize = (e => { "number" == typeof e ? r = e : console.error("[ERROR] Confetti.setSize() - Input needs to be of type 'number'") }), p.prototype.setFade = (e => { "boolean" == typeof e ? s = e : console.error("[ERROR] Confetti.setFade() - Input needs to be of type 'boolean'") }), p.prototype.destroyTarget = (e => { "boolean" == typeof e ? l = e : console.error("[ERROR] Confetti.destroyTarget() - Input needs to be of type 'boolean'") }), window.requestAnimationFrame(function t(n) { let i = (n - d) / 1e3; d = n; for (let t = a.length - 1; t >= 0; t--) { let o = a[t]; for (let t = o.particles.length - 1; t >= 0; t--) { let n = o.particles[t]; n.vel.y += e * (n.size.y / (10 * r)) * i, n.vel.x += 25 * (Math.random() - .5) * i, n.vel.x *= .98, n.vel.y *= .98, n.pos.x += n.vel.x, n.pos.y += n.vel.y, n.rotation += n.rotation_speed, s && (n.opacity -= n.lifetime), y(n) && o.particles.splice(t, 1) } 0 == o.particles.length && a.splice(t, 1) } !function () { o.clearRect(0, 0, 2 * window.innerWidth, 2 * window.innerHeight); for (const d of a) for (const a of d.particles) e = a.pos.x, t = a.pos.y, n = a.size.x, i = a.size.y, r = a.rotation, s = a.hue, l = a.opacity, o.save(), o.beginPath(), o.translate(e + n / 2, t + i / 2), o.rotate(r * Math.PI / 180), o.rect(-n / 2, -i / 2, n, i), o.fillStyle = `hsla(275deg, ${s}%, ${s / 3.6}%, ${l}%)`, o.fill(), o.restore(); var e, t, n, i, r, s, l }(), window.requestAnimationFrame(t) }), p })(); +const c = new Confetti("purple"); +c.destroyTarget(false); diff --git a/src/assets/styles.css b/src/assets/styles.css index 6ccd6ab..7a28e86 100644 --- a/src/assets/styles.css +++ b/src/assets/styles.css @@ -1,88 +1,88 @@ -:root { - --bg: rgb(28, 23, 36); - --fg: rgb(234, 234, 248); - --accent: hsl(275, 57%, 68%); -} - -html { - background-color: var(--bg); - color: var(--fg); - font-size: 1.125em; - font-family: sans-serif; -} - -a { - color: var(--accent); - text-decoration: none; - border-bottom: 1px solid var(--accent); -} - -html, -body { - margin: 0; - padding: 0; -} - -body { - display: flex; - flex-direction: column; - min-height: 100vh; -} - -header, -main, -footer { - width: 100%; - max-width: 90ch; - margin: 0 auto; - padding: 1em; -} - -main { - flex: 1; - margin-bottom: 2em; -} - -header { - display: flex; - flex-direction: row; -} - -header img { - display: inline-block; - margin-right: 2em; - height: 8em; -} - -#purple { - font-style: normal; - color: var(--bg); - background-color: var(--accent); - border-radius: 2px; - padding: 0.25em; -} - -li { - line-height: 1.6em; -} - -footer nav ul { - list-style: none; - display: flex; - flex-direction: row; - margin: 0; - padding: 0; -} - -footer nav ul > li { - display: inline; - line-height: 1rem; - padding-bottom: 0.15em; -} - -footer nav ul > li + li { - border-inline-start: 1px solid var(--fg); - - padding-inline-start: 1ch; - margin-inline-start: 1ch; -} +:root { + --bg: rgb(28, 23, 36); + --fg: rgb(234, 234, 248); + --accent: hsl(275, 57%, 68%); +} + +html { + background-color: var(--bg); + color: var(--fg); + font-size: 1.125em; + font-family: sans-serif; +} + +a { + color: var(--accent); + text-decoration: none; + border-bottom: 1px solid var(--accent); +} + +html, +body { + margin: 0; + padding: 0; +} + +body { + display: flex; + flex-direction: column; + min-height: 100vh; +} + +header, +main, +footer { + width: 100%; + max-width: 90ch; + margin: 0 auto; + padding: 1em; +} + +main { + flex: 1; + margin-bottom: 2em; +} + +header { + display: flex; + flex-direction: row; +} + +header img { + display: inline-block; + margin-right: 2em; + height: 8em; +} + +#purple { + font-style: normal; + color: var(--bg); + background-color: var(--accent); + border-radius: 2px; + padding: 0.25em; +} + +li { + line-height: 1.6em; +} + +footer nav ul { + list-style: none; + display: flex; + flex-direction: row; + margin: 0; + padding: 0; +} + +footer nav ul > li { + display: inline; + line-height: 1rem; + padding-bottom: 0.15em; +} + +footer nav ul > li + li { + border-inline-start: 1px solid var(--fg); + + padding-inline-start: 1ch; + margin-inline-start: 1ch; +} diff --git a/src/webring/data.json b/src/webring/data.json index 52620a0..1843063 100644 --- a/src/webring/data.json +++ b/src/webring/data.json @@ -1,17 +1,17 @@ -[ - { - "id": "annie", - "name": "annieversary", - "url": "https://versary.town/" - }, - { - "id": "charlotte", - "name": "charlotte", - "url": "https://char.lt/" - }, - { - "id": "mira", - "name": "mira", - "url": "https://boxin.space/" - } -] +[ + { + "id": "annie", + "name": "annieversary", + "url": "https://versary.town/" + }, + { + "id": "charlotte", + "name": "charlotte", + "url": "https://char.lt/" + }, + { + "id": "mira", + "name": "mira", + "url": "https://boxin.space/" + } +] diff --git a/src/webring/webring-0.1.0.js b/src/webring/webring-0.1.0.js new file mode 100644 index 0000000..77c80de --- /dev/null +++ b/src/webring/webring-0.1.0.js @@ -0,0 +1,162 @@ +(function () { + function getUID() { + var array = new Uint8Array(8); + window.crypto.getRandomValues(array); + return Array.from(array) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + } + + const params = document.currentScript.dataset; + const UID = getUID(); + + const css = ` +#--lavender-cssreset-${UID} { + all: unset; +} + +#--lavender-webring-container-${UID} { + background-color: rgb(28, 23, 36); + background-blend-mode: multiply; + position: relative; + + background-color: ${params.backgroundColor || "purple"}; + color: ${params.textColor || "white"}; + text-shadow: 0px 1px 1px ${params.textShadowColor || "black"}; + padding: 1px; + font-size: 16px; + font-family: sans-serif; + + margin: 10px 0px; +} + +#--lavender-webring-container-${UID} a { + color: inherit !important; +} + +#--lavender-webring-container-${UID}::before { + display: block; + content: ' '; + top: 0; left: 0; bottom: 0; right: 0; + position: absolute; + box-sizing: border-box; + pointer-events: none; + border: 5px outset; + opacity: 0.25; + border-color: white black black white; +} + +#--lavender-webring-container-${UID} #--lavender-webring-title-${UID} { + margin: 10px 10px 0px; + text-align: center; + font-style: italic; +} + +#--lavender-webring-container-${UID} #--lavender-webring-item-container-${UID} { + display: flex; + justify-content: space-around; +} + +@media (max-width: 600px) { + #--lavender-webring-container-${UID} #--lavender-webring-item-container-${UID} { + flex-direction: column; + } +} + +#--lavender-webring-container-${UID} .--lavender-webring-items-${UID} { + font-size: 14px; + display: block; + text-align: center; + padding: 0px; + margin: 10px 10px 8px; + flex: 1; +} + +#--lavender-webring-container-${UID} #--lavender-webring-item-1-${UID}::before { + display: inline; + content: '\\2190\\00a0'; +} +#--lavender-webring-container-${UID} #--lavender-webring-item-3-${UID}::after { + display: inline; + content: '\\00a0\\2192'; +} +`; + + const webring_content = ` +
+
+

This website is a part of the Lavender Software webring

+
+ + + +
+
+
+`; + + function renderIdx(data, element, idx) { + idx = (idx + data.length) % data.length; + + datum = data[idx]; + element.textContent = datum.name; + element.href = datum.url; + } + + function getElementByPostfixedId(path) { + return document.getElementById(path + "-" + UID); + } + + function renderContent(currentScript, data) { + const params = currentScript.dataset; + + var headstyle = document.createElement("style"); + headstyle.innerHTML = css; + document.head.appendChild(headstyle); + + currentScript.insertAdjacentHTML("afterend", webring_content); + + const container = getElementByPostfixedId("--lavender-webring-container"); + + const item1 = getElementByPostfixedId("--lavender-webring-item-1"); + const item2 = getElementByPostfixedId("--lavender-webring-item-2"); + const item3 = getElementByPostfixedId("--lavender-webring-item-3"); + + const id = params.siteId; + var idindex = -1; + + for (var i = 0; i < data.length; i++) { + if (data[i].id == id) { + idindex = i; + } + } + + if (idindex == -1) { + item2.textContent = + "this site was not found in the list. please check that you don't have any typos in the id!"; + return; + } + + if ("hideTitle" in params) { + getElementByPostfixedId("--lavender-webring-title").style.display = + "none"; + } + + renderIdx(data, item1, idindex - 1); + renderIdx(data, item2, idindex); + renderIdx(data, item3, idindex + 1); + } + + const currentScript = document.currentScript; + if (currentScript) { + fetch("https://lavender.software/webring/data.json") + .then(function (response) { + return response.json(); + }) + .then(function (data) { + renderContent(currentScript, data); + }); + } else { + console.log("cannot locate document.currentScript element. aborting..."); + } +})(); diff --git a/src/webring/webring.js b/src/webring/webring.js deleted file mode 100644 index cef8050..0000000 --- a/src/webring/webring.js +++ /dev/null @@ -1,157 +0,0 @@ -(function() { - function getUID() { - var array = new Uint8Array(8); - window.crypto.getRandomValues(array); - return Array.from(array).map(b => b.toString(16).padStart(2, "0")).join(""); - } - - const params = document.currentScript.dataset; - const UID = getUID(); - - const css = ` -#--lavender-cssreset-${UID} { - all: unset; -} - -#--lavender-webring-container-${UID} { - background-color: rgb(28, 23, 36); - background-blend-mode: multiply; - position: relative; - - background-color: ${params.backgroundColor || "purple"}; - color: ${params.textColor || "white"}; - text-shadow: 0px 1px 1px ${params.textShadowColor || "black"}; - padding: 1px; - font-size: 16px; - font-family: sans-serif; - - margin: 10px 0px; -} - -#--lavender-webring-container-${UID} a { - color: inherit !important; -} - -#--lavender-webring-container-${UID}::before { - display: block; - content: ' '; - top: 0; left: 0; bottom: 0; right: 0; - position: absolute; - box-sizing: border-box; - pointer-events: none; - border: 5px outset; - opacity: 0.25; - border-color: white black black white; -} - -#--lavender-webring-container-${UID} #--lavender-webring-title-${UID} { - margin: 10px 10px 0px; - text-align: center; - font-style: italic; -} - -#--lavender-webring-container-${UID} #--lavender-webring-item-container-${UID} { - display: flex; - justify-content: space-around; -} - -@media (max-width: 600px) { - #--lavender-webring-container-${UID} #--lavender-webring-item-container-${UID} { - flex-direction: column; - } -} - -#--lavender-webring-container-${UID} .--lavender-webring-items-${UID} { - font-size: 14px; - display: block; - text-align: center; - padding: 0px; - margin: 10px 10px 8px; - flex: 1; -} - -#--lavender-webring-container-${UID} #--lavender-webring-item-1-${UID}::before { - display: inline; - content: '\\2190\\00a0'; -} -#--lavender-webring-container-${UID} #--lavender-webring-item-3-${UID}::after { - display: inline; - content: '\\00a0\\2192'; -} -`; - - const webring_content = ` -
-
-

This website is a part of the Lavender Software webring

-
- - - -
-
-
-`; - - function renderIdx(data, element, idx) { - idx = (idx + data.length) % data.length; - - datum = data[idx]; - element.textContent = datum.name; - element.href = datum.url; - } - - function getElementByPostfixedId(path) { - return document.getElementById(path + "-" + UID); - } - - function renderContent(currentScript, data) { - const params = currentScript.dataset; - - var headstyle = document.createElement('style'); - headstyle.innerHTML = css; - document.head.appendChild(headstyle); - - currentScript.insertAdjacentHTML("afterend", webring_content); - - const container = getElementByPostfixedId('--lavender-webring-container'); - - const item1 = getElementByPostfixedId('--lavender-webring-item-1'); - const item2 = getElementByPostfixedId('--lavender-webring-item-2'); - const item3 = getElementByPostfixedId('--lavender-webring-item-3'); - - const id = params.siteId; - var idindex = -1; - - for (var i = 0; i < data.length; i++) { - if (data[i].id == id) { - idindex = i; - } - } - - if (idindex == -1) { - item2.textContent = "this site was not found in the list. please check that you don't have any typos in the id!"; - return; - } - - if ("hideTitle" in params) { - getElementByPostfixedId('--lavender-webring-title').style.display = 'none'; - } - - renderIdx(data, item1, idindex - 1); - renderIdx(data, item2, idindex); - renderIdx(data, item3, idindex + 1); - } - - const currentScript = document.currentScript; - if (currentScript) { - fetch("https://lavender.software/webring/data.json").then(function(response) { - return response.json(); - }).then(function(data) { - renderContent(currentScript, data); - }); - } else { - console.log("cannot locate document.currentScript element. aborting..."); - } - -})(); diff --git a/templates/_footer.html.j2 b/templates/_footer.html.j2 index 9112d95..b22213f 100644 --- a/templates/_footer.html.j2 +++ b/templates/_footer.html.j2 @@ -1,9 +1,9 @@ - + diff --git a/templates/main_page.html.j2 b/templates/main_page.html.j2 index 18abeef..0d7c3b0 100644 --- a/templates/main_page.html.j2 +++ b/templates/main_page.html.j2 @@ -1,62 +1,62 @@ - - - - - - - lavender software | digital product studio - - - - - -
- Lavender Logo - -
-

lavender software

-

the kind of software that we make is … purple

-
-
- -
-
-

projects

- -
    -
  • lavender cord - a theming platform for Discord
  • -
  • more soon!
  • -
-
- - - -
-

services

- -

we offer:

- -
    -
  • individual software consulting
  • -
  • system operations for-hire
  • -
  • contractual project work
  • -
-
-
- - {% include "_footer.html.j2" %} - - - - + + + + + + + lavender software | digital product studio + + + + + +
+ Lavender Logo + +
+

lavender software

+

the kind of software that we make is … purple

+
+
+ +
+
+

projects

+ +
    +
  • lavender cord - a theming platform for Discord
  • +
  • more soon!
  • +
+
+ + + +
+

services

+ +

we offer:

+ +
    +
  • individual software consulting
  • +
  • system operations for-hire
  • +
  • contractual project work
  • +
+
+
+ + {% include "_footer.html.j2" %} + + + +