Browse Source

Initial commit

main
Charlotte Som 7 months ago
commit
62c50d368b
  1. 12
      .editorconfig
  2. 2
      .gitignore
  3. 1280
      Cargo.lock
  4. 16
      Cargo.toml
  5. 38
      LICENSE.md
  6. 3
      build.rs
  7. 24
      build_src/assets.rs
  8. 65
      build_src/main.rs
  9. 42
      build_src/main_page.rs
  10. BIN
      src/assets/logo.png
  11. 3
      src/assets/main_page/confetti.js
  12. 46
      src/assets/styles.css
  13. 46
      templates/main_page.html.j2

12
.editorconfig

@ -0,0 +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

2
.gitignore

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

1280
Cargo.lock

File diff suppressed because it is too large

16
Cargo.toml

@ -0,0 +1,16 @@
[package]
name = "lavender_software"
version = "0.1.0"
authors = ["videogame hacker <half-kh-hacker@hackery.site>"]
edition = "2018"
build = "build.rs"
[[bin]]
name = "lavender_software"
path = "build_src/main.rs"
[dependencies]
askama = "0.10.5"
notify = "4.0.17"
siru = { git = "https://github.com/videogame-hacker/siru.git" }

38
LICENSE.md

@ -0,0 +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.

3
build.rs

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

24
build_src/assets.rs

@ -0,0 +1,24 @@
use std::{fs, path::Path};
use crate::*;
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(())
}
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(())
}

65
build_src/main.rs

@ -0,0 +1,65 @@
use siru::prelude::*;
use std::{convert::TryInto, path::PathBuf, sync::Arc, time::Duration};
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
mod assets;
mod main_page;
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
}
}
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]
.iter()
.map(|f| {
let ctx = Arc::clone(&ctx);
std::thread::spawn(move || f(&ctx).unwrap())
})
.for_each(|t| t.join().unwrap());
}
fn main() {
println!("Watching...\n");
use notify::*;
let (tx, rx) = std::sync::mpsc::channel();
let mut watcher = watcher(tx, Duration::from_millis(100)).unwrap();
build();
watcher.watch("./src", RecursiveMode::Recursive).unwrap();
loop {
match rx.recv() {
Ok(_) => build(),
Err(_) => break,
}
}
}

42
build_src/main_page.rs

@ -0,0 +1,42 @@
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"),
];
ctx.write(
"index.html",
MainPageTemplate {
members: members.iter().map(|x| x.into()).collect(),
}
.render()?,
)?;
Ok(())
}

BIN
src/assets/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

3
src/assets/main_page/confetti.js

@ -0,0 +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);

46
src/assets/styles.css

@ -0,0 +1,46 @@
: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);
}
header, main, footer {
max-width: 90ch;
margin: 0 auto;
}
header {
display: flex;
flex-direction: row;
}
header img {
display: inline-block;
margin-right: 2em;
width: 8em;
}
#purple {
font-style: normal;
color: var(--bg);
background-color: var(--accent);
border-radius: 2px;
padding: 0.25em;
}
li {
line-height: 1.6em;
}

46
templates/main_page.html.j2

@ -0,0 +1,46 @@
<!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.png"> <!-- TODO: We should compress this image -->
<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>
</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>
</main>
<script async src="/assets/main_page/confetti.js"></script>
</body>
</html>
Loading…
Cancel
Save