Compare commits
	
		
			No commits in common. "main" and "main" have entirely different histories.
		
	
	
		
	
		
					 21 changed files with 569 additions and 746 deletions
				
			
		|  | @ -3,7 +3,7 @@ root = true | ||||||
| [*] | [*] | ||||||
| indent_style = space | indent_style = space | ||||||
| indent_size = 2 | indent_size = 2 | ||||||
| end_of_line = lf | end_of_line = crlf | ||||||
| charset = utf-8 | charset = utf-8 | ||||||
| trim_trailing_whitespace = false | trim_trailing_whitespace = false | ||||||
| insert_final_newline = true | insert_final_newline = true | ||||||
|  |  | ||||||
							
								
								
									
										63
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										63
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							|  | @ -89,15 +89,6 @@ version = "1.0.1" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "base64" |  | ||||||
| version = "0.10.1" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" |  | ||||||
| dependencies = [ |  | ||||||
|  "byteorder", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "bitflags" | name = "bitflags" | ||||||
| version = "1.2.1" | version = "1.2.1" | ||||||
|  | @ -425,12 +416,6 @@ dependencies = [ | ||||||
|  "libc", |  "libc", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "hex" |  | ||||||
| version = "0.3.2" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "html5ever" | name = "html5ever" | ||||||
| version = "0.25.1" | version = "0.25.1" | ||||||
|  | @ -507,7 +492,6 @@ dependencies = [ | ||||||
|  "askama", |  "askama", | ||||||
|  "notify", |  "notify", | ||||||
|  "siru", |  "siru", | ||||||
|  "ssri", |  | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -989,18 +973,6 @@ dependencies = [ | ||||||
|  "opaque-debug", |  "opaque-debug", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "sha2" |  | ||||||
| version = "0.8.2" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" |  | ||||||
| dependencies = [ |  | ||||||
|  "block-buffer", |  | ||||||
|  "digest", |  | ||||||
|  "fake-simd", |  | ||||||
|  "opaque-debug", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "shell-words" | name = "shell-words" | ||||||
| version = "1.0.0" | version = "1.0.0" | ||||||
|  | @ -1034,21 +1006,6 @@ version = "0.4.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" | checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "ssri" |  | ||||||
| version = "7.0.0" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "a9cec0d388f39fbe79d7aa600e8d38053bf97b1bc8d350da7c0ba800d0f423f2" |  | ||||||
| dependencies = [ |  | ||||||
|  "base64", |  | ||||||
|  "digest", |  | ||||||
|  "hex", |  | ||||||
|  "serde", |  | ||||||
|  "sha-1", |  | ||||||
|  "sha2", |  | ||||||
|  "thiserror", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "static_assertions" | name = "static_assertions" | ||||||
| version = "1.1.0" | version = "1.1.0" | ||||||
|  | @ -1123,26 +1080,6 @@ dependencies = [ | ||||||
|  "unicode-width", |  "unicode-width", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "thiserror" |  | ||||||
| version = "1.0.30" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" |  | ||||||
| dependencies = [ |  | ||||||
|  "thiserror-impl", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "thiserror-impl" |  | ||||||
| version = "1.0.30" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" |  | ||||||
| dependencies = [ |  | ||||||
|  "proc-macro2", |  | ||||||
|  "quote", |  | ||||||
|  "syn", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "time" | name = "time" | ||||||
| version = "0.1.44" | version = "0.1.44" | ||||||
|  |  | ||||||
|  | @ -14,4 +14,3 @@ path = "build_src/main.rs" | ||||||
| askama = "0.10.5" | askama = "0.10.5" | ||||||
| notify = "4.0.17" | notify = "4.0.17" | ||||||
| siru = { git = "https://github.com/videogame-hacker/siru.git" } | siru = { git = "https://github.com/videogame-hacker/siru.git" } | ||||||
| ssri = "7.0.0" |  | ||||||
|  |  | ||||||
							
								
								
									
										17
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								README.md
									
									
									
									
									
								
							|  | @ -2,23 +2,8 @@ | ||||||
| 
 | 
 | ||||||
| Static site generated using [siru](https://github.com/videogame-hacker/siru) and hosted at [lavender.software](https://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 | ## Deploying | ||||||
| 
 | 
 | ||||||
| **Note:** You don't need to do this unless you're the one deploying the site to the production environment. |  | ||||||
| 
 |  | ||||||
| ```shell | ```shell | ||||||
| $ # To set up, ensure that the 'dist' folder reflects the VPS | $ # To set up, ensure that the 'dist' folder reflects the VPS | ||||||
| $ git clone 'root@lavender.software:/srv/http/lavender.software' dist | $ git clone 'root@lavender.software:/srv/http/lavender.software' dist | ||||||
|  | @ -30,5 +15,3 @@ dist/ $ git pull --rebase | ||||||
| dist/ $ git push | dist/ $ git push | ||||||
| dist/ $ # Your changes should now be live at lavender.software | dist/ $ # Your changes should now be live at lavender.software | ||||||
| ``` | ``` | ||||||
| 
 |  | ||||||
| If deploying this site, you may also want to examine the sample `nginx-site.conf` file. |  | ||||||
|  |  | ||||||
|  | @ -74,8 +74,11 @@ fn main() { | ||||||
|         let mut watcher = watcher(tx, Duration::from_millis(100)).unwrap(); |         let mut watcher = watcher(tx, Duration::from_millis(100)).unwrap(); | ||||||
|         watcher.watch("./src", RecursiveMode::Recursive).unwrap(); |         watcher.watch("./src", RecursiveMode::Recursive).unwrap(); | ||||||
| 
 | 
 | ||||||
|         while rx.recv().is_ok() { |         loop { | ||||||
|             build() |             match rx.recv() { | ||||||
|  |                 Ok(_) => build(), | ||||||
|  |                 Err(_) => break, | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,13 +1,58 @@ | ||||||
| use crate::*; | 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)] | #[derive(Template)] | ||||||
| #[template(path = "main_page.html.j2")] | #[template(path = "main_page.html.j2")] | ||||||
| struct MainPageTemplate {} | struct MainPageTemplate { | ||||||
|  |     members: Vec<Member>, | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| pub fn main_page(ctx: &BuildContext) -> Result<()> { | pub fn main_page(ctx: &BuildContext) -> Result<()> { | ||||||
|     log_info("Rendering main page…"); |     log_info("Rendering main page…"); | ||||||
| 
 | 
 | ||||||
|     ctx.write("index.html", MainPageTemplate {}.render()?)?; |     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(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,7 +1,5 @@ | ||||||
| use crate::*; | use crate::*; | ||||||
| 
 | 
 | ||||||
| use ssri::{Algorithm, IntegrityOpts}; |  | ||||||
| 
 |  | ||||||
| pub fn copy_webring(ctx: &BuildContext) -> Result<()> { | pub fn copy_webring(ctx: &BuildContext) -> Result<()> { | ||||||
|     log_info("Copying webring…"); |     log_info("Copying webring…"); | ||||||
|     copy_dir_recursive( |     copy_dir_recursive( | ||||||
|  | @ -9,15 +7,5 @@ pub fn copy_webring(ctx: &BuildContext) -> Result<()> { | ||||||
|         ctx.output_dir.join("webring"), |         ctx.output_dir.join("webring"), | ||||||
|     )?; |     )?; | ||||||
| 
 | 
 | ||||||
|     log_info("Calculating webring integrity…"); |  | ||||||
|     let webring_content = ctx.read_bin("webring/webring-0.2.0.js")?; |  | ||||||
|     let integrity = IntegrityOpts::new() |  | ||||||
|         .algorithm(Algorithm::Sha512) |  | ||||||
|         .algorithm(Algorithm::Sha1) |  | ||||||
|         .chain(&webring_content) |  | ||||||
|         .result(); |  | ||||||
| 
 |  | ||||||
|     ctx.write("webring/webring-0.2.0.js.integ.txt", integrity.to_string())?; |  | ||||||
| 
 |  | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,11 +0,0 @@ | ||||||
| # assuming the dist repo lives at /srv/http/lavender.software |  | ||||||
| 
 |  | ||||||
| location / { |  | ||||||
|   root /srv/http/lavender.software/; |  | ||||||
| 
 |  | ||||||
|   location = /webring/data.json { |  | ||||||
|     add_header Access-Control-Allow-Origin *; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| error_page 404 /404.html |  | ||||||
|  | @ -9,29 +9,9 @@ | ||||||
|         "name": "charlotte", |         "name": "charlotte", | ||||||
|         "url": "https://char.lt/" |         "url": "https://char.lt/" | ||||||
|     }, |     }, | ||||||
|   { |  | ||||||
|     "id": "milse113", |  | ||||||
|     "name": "milse113", |  | ||||||
|     "url": "https://milse113.github.io/lavender.html" |  | ||||||
|   }, |  | ||||||
|     { |     { | ||||||
|         "id": "mira", |         "id": "mira", | ||||||
|         "name": "mira", |         "name": "mira", | ||||||
|         "url": "https://boxin.space/" |         "url": "https://boxin.space/" | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     "id": "oatmealine", |  | ||||||
|     "name": "oatmealine", |  | ||||||
|     "url": "https://oat.zone/" |  | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     "id": "maia", |  | ||||||
|     "name": "maia", |  | ||||||
|     "url": "https://maia.crimew.gay/" |  | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     "id": "easrng", |  | ||||||
|     "name": "easrng", |  | ||||||
|     "url": "https://easrng.net/" |  | ||||||
|     } |     } | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | @ -1,162 +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..."); |  | ||||||
|   } |  | ||||||
| })(); |  | ||||||
|  | @ -1,52 +0,0 @@ | ||||||
| /* Feel free to style the webring however you like! */ |  | ||||||
| 
 |  | ||||||
| .lavender-webring-container { |  | ||||||
|   all: unset; |  | ||||||
| 
 |  | ||||||
|   /* |  | ||||||
|     assuming Linux users will have a preferable sans-serif font set in their browser, |  | ||||||
|     everyone else gets a nice default |  | ||||||
|   */ |  | ||||||
|   font-family: -apple-system, BlinkMacSystemFont, "SF Pro", "Segoe UI", |  | ||||||
|     "Helvetica Neue", sans-serif; |  | ||||||
| 
 |  | ||||||
|   display: flex; |  | ||||||
|   flex-direction: column; |  | ||||||
| 
 |  | ||||||
|   background-color: rgb(28, 23, 36); |  | ||||||
|   color: rgb(234, 234, 248); |  | ||||||
| 
 |  | ||||||
|   padding: 1em; |  | ||||||
| 
 |  | ||||||
|   text-align: center; |  | ||||||
|   font-size: 1.125rem; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .lavender-webring-description { |  | ||||||
|   margin-block-end: 0.5em; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .lavender-webring-container a { |  | ||||||
|   color: hsl(275, 57%, 68%); |  | ||||||
|   text-decoration: none; |  | ||||||
|   border-bottom: 1px solid hsl(275, 57%, 68%); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .lavender-webring-site-links { |  | ||||||
|   display: grid; |  | ||||||
|   grid-template-columns: repeat(3, minmax(0, 1fr)); |  | ||||||
| 
 |  | ||||||
|   list-style: none; |  | ||||||
|   margin: 0; |  | ||||||
|   padding: 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .lavender-webring-prev-site a::before { |  | ||||||
|   content: "←"; |  | ||||||
|   margin-inline-end: 1ch; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .lavender-webring-next-site a::after { |  | ||||||
|   content: "→"; |  | ||||||
|   margin-inline-start: 1ch; |  | ||||||
| } |  | ||||||
|  | @ -1,54 +0,0 @@ | ||||||
| const currentScript = document.currentScript; |  | ||||||
| const ctx = currentScript.dataset; |  | ||||||
| 
 |  | ||||||
| // charlotte's ad-hoc terse javascript framework!
 |  | ||||||
| const CSS_PREFIX = "lavender-webring"; |  | ||||||
| const e = (tag, props = {}, children = []) => { |  | ||||||
|   let element = Object.assign(document.createElement(tag), props); |  | ||||||
|   element.append(...children); |  | ||||||
|   return element; |  | ||||||
| }; |  | ||||||
| const t = (text) => document.createTextNode(text); |  | ||||||
| const c = (className) => ({ className: `${CSS_PREFIX}-${className}` }); |  | ||||||
| const h = (href) => ({ href }); |  | ||||||
| 
 |  | ||||||
| const createDescriptionContent = () => |  | ||||||
|   ctx.description != null |  | ||||||
|     ? [t(ctx.description)] |  | ||||||
|     : [ |  | ||||||
|         t("This site is part of the "), |  | ||||||
|         e("a", h("https://lavender.software"), [t("lavender.software")]), |  | ||||||
|         t(" webring!"), |  | ||||||
|       ]; |  | ||||||
| 
 |  | ||||||
| const renderWebring = (currSite, prevSite, nextSite) => { |  | ||||||
|   currentScript.replaceWith( |  | ||||||
|     e("aside", c("container"), [ |  | ||||||
|       e("section", c("description"), createDescriptionContent()), |  | ||||||
|       e("ul", c("site-links"), [ |  | ||||||
|         e("li", c("prev-site"), [e("a", h(prevSite.url), [t(prevSite.name)])]), |  | ||||||
|         e("li", c("curr-site"), [e("a", h(currSite.url), [t(currSite.name)])]), |  | ||||||
|         e("li", c("next-site"), [e("a", h(nextSite.url), [t(nextSite.name)])]), |  | ||||||
|       ]), |  | ||||||
|     ]) |  | ||||||
|   ); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| (async () => { |  | ||||||
|   const data = await fetch("https://lavender.software/webring/data.json").then( |  | ||||||
|     (r) => r.json() |  | ||||||
|   ); |  | ||||||
| 
 |  | ||||||
|   let thisSiteIdx = data.findIndex((site) => site.id == ctx.siteId); |  | ||||||
|   if (thisSiteIdx === -1) { |  | ||||||
|     throw new Error( |  | ||||||
|       `Could not find site by id '${ctx.siteId}' in the webring!` |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   let currSite = data[thisSiteIdx]; |  | ||||||
|   let prevSite = data[(thisSiteIdx + data.length - 1) % data.length]; |  | ||||||
|   let nextSite = data[(thisSiteIdx + 1) % data.length]; |  | ||||||
| 
 |  | ||||||
|   renderWebring(currSite, prevSite, nextSite); |  | ||||||
| })(); |  | ||||||
							
								
								
									
										157
									
								
								src/webring/webring.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								src/webring/webring.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,157 @@ | ||||||
|  | (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..."); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | })(); | ||||||
|  | @ -25,13 +25,23 @@ | ||||||
| 
 | 
 | ||||||
|         <ul> |         <ul> | ||||||
|           <li><a href="https://git.lain.faith/videogame-hacker/discord-css-injector">lavender cord</a> - a theming platform for Discord</li> |           <li><a href="https://git.lain.faith/videogame-hacker/discord-css-injector">lavender cord</a> - a theming platform for Discord</li> | ||||||
|           <li><a href="https://git.lavender.software/lavender/watch-party">watch-party</a> - a webapp to allow for synced video playback among friends</li> |           <li>more soon!</li> | ||||||
|           <li>catsette <em>(upcoming)</em> - an independent music marketplace platform for artists</li> |  | ||||||
|           <li>Hermes <em>(upcoming)</em> - a native <a href="https://scuttlebutt.nz/">Secure Scuttlebutt</a> client for Linux</li> |  | ||||||
|           <li>… and more soon!</li> |  | ||||||
|         </ul> |         </ul> | ||||||
|       </section> |       </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> |       <section> | ||||||
|         <h2>services</h2> |         <h2>services</h2> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue