lavender.software webring #2

Merged
char merged 5 commits from patch/webring into main 2022-03-05 15:00:48 +00:00
16 changed files with 589 additions and 569 deletions
Showing only changes of commit 5e29b82c8d - Show all commits

View file

@ -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

4
.gitignore vendored
View file

@ -1,2 +1,2 @@
/target
/dist
/target
/dist

View file

@ -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.

View file

@ -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
```

View file

@ -1,3 +1,3 @@
fn main() {
println!("cargo:rerun-if-changed=build_src");
}
fn main() {
println!("cargo:rerun-if-changed=build_src");
}

View file

@ -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(())
}

View file

@ -1,84 +1,84 @@
use siru::prelude::*;
use std::{convert::TryInto, fs, path::Path, path::PathBuf, sync::Arc, time::Duration};
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
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<Path>, dst: impl AsRef<Path>) -> 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<T> = std::result::Result<T, Box<dyn std::error::Error>>;
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<Path>, dst: impl AsRef<Path>) -> 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,
}
}
}
}

View file

@ -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<Member>,
}
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<Member>,
}
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(())
}

View file

@ -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(())
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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/"
}
]

View file

@ -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 = `
<div id="--lavender-cssreset-${UID}">
<div id="--lavender-webring-container-${UID}">
<p id="--lavender-webring-title-${UID}">This website is a part of the Lavender Software webring</p>
<div id="--lavender-webring-item-container-${UID}">
<a class="--lavender-webring-items-${UID}" id="--lavender-webring-item-1-${UID}"></a>
<a class="--lavender-webring-items-${UID}" id="--lavender-webring-item-2-${UID}"></a>
<a class="--lavender-webring-items-${UID}" id="--lavender-webring-item-3-${UID}"></a>
</div>
</div>
</div>
`;
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...");
}
})();

View file

@ -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 = `
<div id="--lavender-cssreset-${UID}">
<div id="--lavender-webring-container-${UID}">
<p id="--lavender-webring-title-${UID}">This website is a part of the Lavender Software webring</p>
<div id="--lavender-webring-item-container-${UID}">
<a class="--lavender-webring-items-${UID}" id="--lavender-webring-item-1-${UID}"></a>
<a class="--lavender-webring-items-${UID}" id="--lavender-webring-item-2-${UID}"></a>
<a class="--lavender-webring-items-${UID}" id="--lavender-webring-item-3-${UID}"></a>
</div>
</div>
</div>
`;
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...");
}
})();

View file

@ -1,9 +1,9 @@
<footer>
<nav>
<ul>
<li><strong class="brand">lavender software ltd</strong></li>
<li><a href="https://git.lavender.software/lavender/lavender.software">source code</a></li>
<!-- <li><a href="/about/">about us</a></li> -->
</ul>
</nav>
</footer>
<footer>
<nav>
<ul>
<li><strong class="brand">lavender software ltd</strong></li>
<li><a href="https://git.lavender.software/lavender/lavender.software">source code</a></li>
<!-- <li><a href="/about/">about us</a></li> -->
</ul>
</nav>
</footer>

View file

@ -1,62 +1,62 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lavender software | digital product studio</title>
<link rel="stylesheet" href="/assets/styles.css">
</head>
<body>
<header>
<img alt="Lavender Logo" src="/assets/logo.svg">
<div>
<h1>lavender software</h1>
<p>the kind of software that we make is … <em id="purple">purple</em></p>
</div>
</header>
<main>
<section>
<h2>projects</h2>
<ul>
<li><a href="https://git.lain.faith/videogame-hacker/discord-css-injector">lavender cord</a> - a theming platform for Discord</li>
<li>more soon!</li>
</ul>
</section>
<!--
<section>
<h2>who are we?</h2>
<ul>
{% for member in members %}
<li>
<a href="{{ member.website }}">{{ member.name }}</a> - {{ member.title }}
</li>
{% endfor %}
</ul>
</section> -->
<section>
<h2>services</h2>
<p>we offer:</p>
<ul>
<li>individual software consulting</li>
<li>system operations for-hire</li>
<li>contractual project work</li>
</ul>
</section>
</main>
{% include "_footer.html.j2" %}
<script async src="/assets/main_page/confetti.js"></script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lavender software | digital product studio</title>
<link rel="stylesheet" href="/assets/styles.css">
</head>
<body>
<header>
<img alt="Lavender Logo" src="/assets/logo.svg">
<div>
<h1>lavender software</h1>
<p>the kind of software that we make is … <em id="purple">purple</em></p>
</div>
</header>
<main>
<section>
<h2>projects</h2>
<ul>
<li><a href="https://git.lain.faith/videogame-hacker/discord-css-injector">lavender cord</a> - a theming platform for Discord</li>
<li>more soon!</li>
</ul>
</section>
<!--
<section>
<h2>who are we?</h2>
<ul>
{% for member in members %}
<li>
<a href="{{ member.website }}">{{ member.name }}</a> - {{ member.title }}
</li>
{% endfor %}
</ul>
</section> -->
<section>
<h2>services</h2>
<p>we offer:</p>
<ul>
<li>individual software consulting</li>
<li>system operations for-hire</li>
<li>contractual project work</li>
</ul>
</section>
</main>
{% include "_footer.html.j2" %}
<script async src="/assets/main_page/confetti.js"></script>
</body>
</html>