Initial commit
This commit is contained in:
		
						commit
						62c50d368b
					
				
					 13 changed files with 1577 additions and 0 deletions
				
			
		
							
								
								
									
										12
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							|  | @ -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
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | ||||||
|  | /target | ||||||
|  | /dist | ||||||
							
								
								
									
										1280
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1280
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										16
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							|  | @ -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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								LICENSE.md
									
									
									
									
									
										Normal file
									
								
							|  | @ -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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								build.rs
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | fn main() { | ||||||
|  |     println!("cargo:rerun-if-changed=build_src"); | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								build_src/assets.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								build_src/assets.rs
									
									
									
									
									
										Normal file
									
								
							|  | @ -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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								build_src/main.rs
									
									
									
									
									
										Normal file
									
								
							|  | @ -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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								build_src/main_page.rs
									
									
									
									
									
										Normal file
									
								
							|  | @ -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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/logo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 108 KiB | 
							
								
								
									
										3
									
								
								src/assets/main_page/confetti.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/assets/main_page/confetti.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/assets/styles.css
									
									
									
									
									
										Normal file
									
								
							|  | @ -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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								templates/main_page.html.j2
									
									
									
									
									
										Normal file
									
								
							|  | @ -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…
	
		Reference in a new issue