Compare commits

29 Commits

Author SHA1 Message Date
35aa3b076f add iocaine blog post 2025-08-25 21:26:10 +00:00
2e397c5be3 finish blog
 add first blog entry
🎨 rework ui+ux
 add freemono
2025-08-24 03:46:08 +00:00
f1c471839f readd btns
🐛 fixed some blog stuff
2025-08-21 02:19:36 +00:00
bfd8c187c6 🔥 rm bulma
 rework ui+ux
 update shader
 update profile.avif
 update 404
2025-08-20 08:38:03 +00:00
510897d94b push changes 2025-08-14 15:19:35 +02:00
078634b664 sloppy blog impl from example 2025-08-10 18:28:27 +02:00
6a5176caab 🐛 fix layout
🍱 update 88x31s
🔥 rm landing
2025-08-07 02:27:39 +02:00
i0uring
282c67b1fc update bg.frag 2025-07-30 15:24:38 +02:00
74224bd648 🐛 fix btn sizes 2025-05-08 22:12:02 +02:00
8f217ec82b 🐛 fix scrollbar
🔥 rm disclaimer in footer
2025-05-08 00:19:54 +02:00
0e1055b1a6 📝 change text
 add buttons
🔥 rm socials
2025-05-07 05:31:46 +02:00
94c56d7405 update justfile
🐛 fix theming
📝 change some wording
2025-04-23 21:23:33 +02:00
Lucielle Rosalia Hoerner
0a203fdb65 Merge pull request 'rust rewrite' (#4) from rewrite/rust into develop
Reviewed-on: https://git.gay/luciel/web_luciel/pulls/4
2025-03-19 01:07:42 +00:00
3788bf81b5 🐛 fixed footer in mobile view
 added own button :3
2025-03-14 03:33:03 +01:00
b8325accb2 add mnet project 2025-03-13 22:23:51 +01:00
f8241695c9 🐛 fix routing
🐛 fix project urls
2025-03-13 02:34:38 +01:00
2e8ecfddd8 add socials
 add padding for hotlink btns
♻️ moved some contents
2025-03-13 01:06:13 +01:00
fd6367eb2d added monocraft font 2025-03-13 00:43:55 +01:00
fbb76210ef 🔥 mov home => about
 add hotlink btns :3
 clean main.scss
🐛 fix height of footer
🐛 fix landing.rs
♻️ refactored code
2025-03-13 00:37:05 +01:00
5797baa64e updated infos in about.rs 2025-03-12 20:50:01 +01:00
ebcc54cc1e 🐛 fixed margins in projects.rs 2025-03-12 19:19:08 +01:00
9d6f05cace add proper header with menu
 add projects tab
 add selection color
♻️ mov home => about
2025-03-12 19:11:45 +01:00
3e4ca7fcf7 updated favicon.ico 2025-03-11 04:05:33 +01:00
d68b9915c0 🎨 new layout
 added landing.rs
 added profile.avif
 added post.bloom.svg
2025-03-11 03:23:49 +01:00
lunarydess
c7b262cd49 add routing
 add bulma
 add favicon
 add justfile
🔥 rm navbar
2025-03-10 14:45:13 +01:00
lunarydess
9a9be89650 🔥 removed redundant draw-calls 2025-03-03 18:39:34 +01:00
lunarydess
af25035bec somewhat fixed shader loading 2025-03-03 14:58:59 +01:00
lunarydess
822725267e 🔥 removed tmp files 2025-03-03 02:13:31 +01:00
lunarydess
6428bcb799 :rocket deploy rewrite 2025-03-03 02:11:41 +01:00
74 changed files with 2002 additions and 2071 deletions

8
.gitignore vendored
View File

@@ -1,2 +1,6 @@
.idea
simplewebserver-*
.idea/
dist/
pkg/
target/
Cargo.lock
static/

37
Cargo.toml Normal file
View File

@@ -0,0 +1,37 @@
[package]
name = "web_iouring"
version = "0.0.0-develop"
authors = ["iouring"]
license = "Apache-2.0"
edition = "2024"
rust-version = "1.91"
publish = false
[dependencies]
js-sys = "0.3"
yew = { git = "https://github.com/yewstack/yew.git", rev = "21f373b", features = [
"csr",
] }
yew-router = { git = "https://github.com/yewstack/yew.git", rev = "21f373b" }
wasm-bindgen = { version = "0.2" }
log = { version = "0.4" }
wasm-logger = { version = "0.2" }
serde = { version = "1.0" }
wasm-bindgen-futures = "0.4.50"
serde_json = "1.0.143"
lsp-types = "0.97"
chrono = "0.4"
yew-markdown = { git = "https://git.celesteflare.cc/i0uring/page_md.git", rev = "1e9840" }
yew-hooks = "0.3"
[patch.crates-io]
yew = { git = "https://github.com/yewstack/yew.git", rev = "21f373b", features = [
"csr",
], optional = false }
[dependencies.web-sys]
version = "0.3"
features = ["HtmlCanvasElement", "WebGlBuffer", "WebGlProgram", "WebGlRenderingContext", "WebGl2RenderingContext", "WebGlShader", "WebGlUniformLocation", "Navigator", "Clipboard"]

2
Trunk.toml Normal file
View File

@@ -0,0 +1,2 @@
[tools]
wasm_opt = "version_122"

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 570 KiB

View File

@@ -1,11 +0,0 @@
{
"name": "Application ~ Chatty",
"link": "https://github.com/lunarydess/Application-Chatty",
"image": "1.png",
"desc": [
"A simple chat-server written in Java 21.",
"I am using Netty 4 for Networking.",
"It has a modern UI built with Swing and FlatLaf. ♡",
"The app is free-to-use but still w.i.p."
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -1,10 +0,0 @@
{
"name": "Discord ~ Ashuramaru",
"link": "https://github.com/lunarydess/Discord-Ashuramaru",
"image": "2.png",
"desc": [
"This is a discord bot I wrote in Java.",
"It has basic functions and an advanced captcha system.",
"Built with JavaCord, picocli and Jackson."
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -1,11 +0,0 @@
{
"name": "Library ~ TinyEvents",
"link": "https://github.com/lunarydess/Library-TinyEvents",
"image": "3.png",
"desc": [
"A event-system written in Java 17.",
"It's easy-to-use and blazingly fast.",
"Well-performing benchmarks and reliable error-handling.",
"Usable in any imaginable infra."
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -1,284 +0,0 @@
#version 100
precision lowp float;
const float CAM_FAR = 20.0;
const vec3 BACKGROUND = vec3(0.0, 0.0, 0.0);
const float PI = radians(180.0);
uniform float time;
uniform vec2 resolution;
uniform bool layer_snow;
vec4 vColor;
vec3 artifactOffset;
mat3 artifactRotation;
vec3 artifactAxis;
vec3 camFwd;
vec3 camUp;
float smootherstep(float edge0, float edge1, float x) {
x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return x * x * x * (x * (x * 6.0 - 15.0) + 10.0);
}
float rand(float n) {
n = fract(n * 43758.5453);
n *= n;
return fract(n * 43758.5453);
}
float hash(float n) {
return fract(abs(fract(n) * 43758.5453));
}
float noise(float x) {
float i = floor(x);
float f = fract(x);
float u = f * f * (3.0 - 2.0 * f);
return mix(hash(i), hash(i + 1.0), u);
}
mat4 viewMatrix(vec3 dir, vec3 up) {
vec3 f = normalize(dir);
vec3 s = normalize(cross(f, up));
return mat4(vec4(s, 0.0), vec4(cross(s, f), 0.0), vec4(-f, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
}
mat3 rotationAlign(vec3 d, vec3 z) {
vec3 v = cross(z, d);
float c = dot(z, d);
float k = 1.0 / (1.0 + c);
return mat3(
v.x * v.x * k + c,
v.y * v.x * k - v.z,
v.z * v.x * k + v.y,
v.x * v.y * k + v.z,
v.y * v.y * k + c,
v.z * v.y * k - v.x,
v.x * v.z * k - v.y,
v.y * v.z * k + v.x,
v.z * v.z * k + c
);
}
float intersectPlane(vec3 origin, vec3 direction, vec3 point, vec3 normal) {
return clamp(dot(point - origin, normal) / dot(direction, normal), -1.0, 9991999.0);
}
vec3 calcRay(vec2 uv, float fov, float aspect) {
uv = uv * 2.0 - 1.0;
float d = 1.0 / tan(radians(fov) * 0.5);
return normalize(vec3(aspect * uv.x, uv.y, d));
}
vec2 getWave(vec2 position, vec2 dir, float speed, float frequency, float iTimeshift) {
float x = dot(dir, position) * frequency + iTimeshift * speed;
float wave = exp(sin(x) - 1.0);
float dist = wave * cos(x);
return vec2(wave, -dist);
}
float heightmap(vec2 worldPos) {
const float scale = 0.06;
vec2 p = worldPos * scale;
vec2 p2 = (artifactOffset.xz - vec2(0.0, 1.0)) * scale;
float d = (1.0 - smootherstep(0.0, 1.0, clamp(length(p2 - p) * 1.25, 0.0, 1.0))) * 0.87;
float angle = 0.0;
float freq = 5.0;
float speed = 2.0;
float weight = 1.9;
float wave = 0.0;
float waveScale = 0.0;
vec2 dir;
vec2 res;
for (int i = 0; i < 5; i++) {
dir = vec2(cos(angle), sin(angle));
res = getWave(p, dir, speed, freq, time);
p += dir * res.y * weight * 0.05;
wave += res.x * weight - d;
angle += 12.0;
waveScale += weight;
weight = mix(weight, 0.0, 0.2);
freq *= 1.18;
speed *= 1.06;
}
return wave * (1.0 / waveScale);
}
float octahedron(vec3 p, float s) {
p = abs(p);
return (p.x + p.y + p.z - s) * 0.57735027;
}
void artifact(vec3 p, inout float currDist, inout vec3 glowColor, inout int id) {
p -= artifactOffset;
p = artifactRotation * p;
float dist = octahedron(p, 0.8);
const float glowDist = 4.8;
if (dist < glowDist) {
float d = dist + rand(dist) * 1.7;
glowColor += vec3(0.75, 0.55, 0.45) * clamp(1.0 - pow((d * (1.0 / glowDist)), 5.0), 0.0, 1.0) * 0.035;
}
if (dist < currDist) {
currDist = dist;
id = 1;
}
}
float objects(vec3 p, inout vec3 glowColor, inout int objId) {
float dist = CAM_FAR;
artifact(p, dist, glowColor, objId);
return dist;
}
float artifactDist(vec3 p) {
p -= artifactOffset;
p = artifactRotation * p;
return octahedron(p, 1.2);
}
float objectsDist(vec3 p) {
return artifactDist(p);
}
vec3 objectsNormal(vec3 p, float eps) {
vec2 h = vec2(eps, 0);
return normalize(vec3(artifactDist(p + h.xyy) - artifactDist(p - h.xyy), eps * 2.0, artifactDist(p + h.yyx) - artifactDist(p - h.yyx)));
}
vec3 objectsColor(int id, vec3 normal, vec3 ray) {
return id == 1 ? vec3(0.85, 0.65, 0.55) * mix(0.8, 1.5, dot(normal, normalize(vec3(0.0, 1.0, 0.5))) * 0.5 + 0.5) :
id == 2 ? vec3(0.85, 0.65, 0.55) * 1.5 :
vec3(1.0, 1.0, 0.0);
}
void marchObjects(vec3 eye, vec3 ray, float wDepth, inout vec4 color) {
float dist = 0.0;
int id;
vec3 rayPos = eye;
float depth = CAM_FAR;
for (int i = 0; i < 30; i++) {
dist = objects(rayPos, color.rgb, id);
depth = distance(rayPos, eye);
if (depth > wDepth || dist < 0.01) break;
rayPos += ray * dist;
}
color = dist < 0.01 ? vec4(objectsColor(id, objectsNormal(rayPos, 0.01), ray), depth) : color;
}
vec3 waterColor(vec3 ray, vec3 normal, vec3 p) {
vec3 color = vec3(0.0);
float fogDist = length(p - vec3(0.0, 0.0, -6.0));
float dist = 0.0;
int objId = 0;
vec3 refl = reflect(ray, normal);
vec3 rayPos = p + refl * dist;
if (length(p.xz - artifactOffset.xz) < 8.5 && dot(refl, normalize(artifactOffset - p)) > -0.25) {
for (int i = 0; i < 40; i++) {
dist = objects(rayPos, color, objId);
if (dist < 0.01) {
color = objectsColor(objId, objectsNormal(rayPos, 0.001), rayPos);
break;
}
rayPos += refl * dist;
}
}
float fresnel = 0.04 + 0.9 * pow(1.0 - max(0.0, dot(-normal, ray)), 7.0);
float d = length(artifactOffset - p);
const float r = 14.0;
float atten = clamp(1.0 - (d * d) / (r * r), 0.0, 1.0);
atten *= atten;
vec3 point = vec3(0.75, 0.55, 0.45) * atten * (1.0 + fresnel) * 0.07;
vec3 ambient = dot(normal, normalize(vec3(0.0, 1.0, 0.5))) * max(fresnel, 0.06) * vec3(0.1, 0.5, 1.0) * 0.85;
float fog = smootherstep(25.0, 6.0, fogDist) * (1.0 / (fogDist * 0.1));
return color + (point + ambient) * fog;
}
vec3 waterNormal(vec2 p, float eps) {
vec2 h = vec2(eps, 0.0);
return normalize(vec3(heightmap(p - h.xy) - heightmap(p + h.xy), eps * 2.0, heightmap(p - h.yx) - heightmap(p + h.yx)));
}
void marchWater(vec3 eye, vec3 ray, inout vec4 color) {
const vec3 planeNorm = vec3(0.0, 1.0, 0.0);
const float depth = 3.0;
float ceilDist = intersectPlane(eye, ray, vec3(0.0, 0.0, 0.0), planeNorm);
vec3 normal = vec3(0.0);
if (dot(planeNorm, ray) > -0.05) {
color = vec4(vec3(0.0), CAM_FAR);
return;
}
float height = 0.0;
vec3 rayPos = eye + ray * ceilDist;
for (int i = 0; i < 30; i++) {
height = heightmap(rayPos.xz) * depth - depth;
if (rayPos.y - height < 0.1) {
color.w = distance(rayPos, eye);
vec3 normPos = (eye + ray * color.w);
color.rgb = waterColor(ray, waterNormal(normPos.xz, 0.005), normPos);
return;
}
rayPos += ray * max(rayPos.y - height, 0.1);
}
color = vec4(vec3(0.0), CAM_FAR);
}
vec3 march(vec2 uv, vec3 camPos) {
mat4 vm = viewMatrix(camFwd, camUp);
vec3 ray = (vm * vec4(calcRay(uv, 80.0, resolution.x / resolution.y), 1.0)).xyz;
vec4 color = vec4(BACKGROUND, CAM_FAR);
vec3 waterColor;
marchWater(camPos, ray, color);
marchObjects(camPos, ray, color.w, color);
return color.rgb;
}
float snow(vec2 uv, float scale) {
float w = smootherstep(1.0, 0.0, -uv.y * (scale * 0.01));
if (w < 0.1) return 0.0;
uv += time / scale;
uv.y += time / scale;
uv.x += sin(uv.y + time * 0.125) / scale;
uv *= scale;
vec2 s = floor(uv), f = fract(uv);
return smootherstep(0.0, min(length(0.5 + 0.5 * sin(11.0 * fract(sin((s + scale) * mat2(7.0, 3.0, 6.0, 5.0)) * 5.0)) - f), 3.0), sin(f.x + f.y) * 0.01) * w;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord * (vec2(1.0) / resolution.xy);
float s = sin(time);
float c = cos(time);
artifactRotation = mat3(c, 0, s, 0, 1, 0, -s, 0, c) * rotationAlign(vec3(0.0, 1.0, 0.0), vec3(s * 0.2, 1.0, c * 0.2 + 0.3));
artifactOffset = vec3(s * 0.4, c * 0.3 - 1.7, -6.0);
camFwd = vec3(0.0, 0.7 + noise(time * 0.8 + 4.0) * 0.08 - 0.04, 1.0);
camUp = vec3(noise(time * 1.2) * 0.02 - 0.01, 1.0, 0.0);
fragColor = vec4(march(uv, vec3(0.0, 1.9, 1.0)) - (length(uv - 0.5) - 0.3) * 0.05, 1.0);
if (layer_snow) {
vec2 p = fragCoord.xy / resolution.xy;
vec2 uvSnow = (fragCoord.xy * 2.0 - resolution.xy) / min(resolution.x, resolution.y);
fragColor += mix(vec4(vec3(snow(uvSnow, 4.0)), 0.5) + vec4(vec3(snow(uvSnow, 3.0)), 0.5), vec4(0.0), vec4(0.7));
}
}
void main(void) {
gl_FragColor = vColor;
mainImage(gl_FragColor, gl_FragCoord.xy);
}

View File

@@ -1,12 +0,0 @@
#version 100
attribute vec3 position;
attribute vec4 color;
uniform mat4 model;
varying vec4 vColor;
void main(void) {
vColor = color;
gl_Position = model * vec4(position, 1.0);
}

View File

@@ -1,285 +0,0 @@
#version 300 es
precision lowp float;
const float CAM_FAR = 20.0;
const vec3 BACKGROUND = vec3(0.0, 0.0, 0.0);
const float PI = radians(180.0);
uniform float time;
uniform vec2 resolution;
uniform bool layer_snow;
in vec4 vColor;
layout (location = 0) out vec4 fragColor;
vec3 artifactOffset;
mat3 artifactRotation;
vec3 artifactAxis;
vec3 camFwd;
vec3 camUp;
float smootherstep(float edge0, float edge1, float x) {
x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return x * x * x * (x * (x * 6.0 - 15.0) + 10.0);
}
float rand(float n) {
n = fract(n * 43758.5453);
n *= n;
return fract(n * 43758.5453);
}
float hash(float n) {
return fract(abs(fract(n) * 43758.5453));
}
float noise(float x) {
float i = floor(x);
float f = fract(x);
float u = f * f * (3.0 - 2.0 * f);
return mix(hash(i), hash(i + 1.0), u);
}
mat4 viewMatrix(vec3 dir, vec3 up) {
vec3 f = normalize(dir);
vec3 s = normalize(cross(f, up));
return mat4(vec4(s, 0.0), vec4(cross(s, f), 0.0), vec4(-f, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
}
mat3 rotationAlign(vec3 d, vec3 z) {
vec3 v = cross(z, d);
float c = dot(z, d);
float k = 1.0 / (1.0 + c);
return mat3(
v.x * v.x * k + c,
v.y * v.x * k - v.z,
v.z * v.x * k + v.y,
v.x * v.y * k + v.z,
v.y * v.y * k + c,
v.z * v.y * k - v.x,
v.x * v.z * k - v.y,
v.y * v.z * k + v.x,
v.z * v.z * k + c
);
}
float intersectPlane(vec3 origin, vec3 direction, vec3 point, vec3 normal) {
return clamp(dot(point - origin, normal) / dot(direction, normal), -1.0, 9991999.0);
}
vec3 calcRay(vec2 uv, float fov, float aspect) {
uv = uv * 2.0 - 1.0;
float d = 1.0 / tan(radians(fov) * 0.5);
return normalize(vec3(aspect * uv.x, uv.y, d));
}
vec2 getWave(vec2 position, vec2 dir, float speed, float frequency, float iTimeshift) {
float x = dot(dir, position) * frequency + iTimeshift * speed;
float wave = exp(sin(x) - 1.0);
float dist = wave * cos(x);
return vec2(wave, -dist);
}
float heightmap(vec2 worldPos) {
const float scale = 0.06;
vec2 p = worldPos * scale;
vec2 p2 = (artifactOffset.xz - vec2(0.0, 1.0)) * scale;
float d = (1.0 - smootherstep(0.0, 1.0, clamp(length(p2 - p) * 1.25, 0.0, 1.0))) * 0.87;
float angle = 0.0;
float freq = 5.0;
float speed = 2.0;
float weight = 1.9;
float wave = 0.0;
float waveScale = 0.0;
vec2 dir;
vec2 res;
for (int i = 0; i < 5; i++) {
dir = vec2(cos(angle), sin(angle));
res = getWave(p, dir, speed, freq, time);
p += dir * res.y * weight * 0.05;
wave += res.x * weight - d;
angle += 12.0;
waveScale += weight;
weight = mix(weight, 0.0, 0.2);
freq *= 1.18;
speed *= 1.06;
}
return wave * (1.0 / waveScale);
}
float octahedron(vec3 p, float s) {
p = abs(p);
return (p.x + p.y + p.z - s) * 0.57735027;
}
void artifact(vec3 p, inout float currDist, inout vec3 glowColor, inout int id) {
p -= artifactOffset;
p = artifactRotation * p;
float dist = octahedron(p, 0.8);
const float glowDist = 4.8;
if (dist < glowDist) {
float d = dist + rand(dist) * 1.7;
glowColor += vec3(0.75, 0.55, 0.45) * clamp(1.0 - pow((d * (1.0 / glowDist)), 5.0), 0.0, 1.0) * 0.035;
}
if (dist < currDist) {
currDist = dist;
id = 1;
}
}
float objects(vec3 p, inout vec3 glowColor, inout int objId) {
float dist = CAM_FAR;
artifact(p, dist, glowColor, objId);
return dist;
}
float artifactDist(vec3 p) {
p -= artifactOffset;
p = artifactRotation * p;
return octahedron(p, 1.2);
}
float objectsDist(vec3 p) {
return artifactDist(p);
}
vec3 objectsNormal(vec3 p, float eps) {
vec2 h = vec2(eps, 0);
return normalize(vec3(artifactDist(p + h.xyy) - artifactDist(p - h.xyy), eps * 2.0, artifactDist(p + h.yyx) - artifactDist(p - h.yyx)));
}
vec3 objectsColor(int id, vec3 normal, vec3 ray) {
return id == 1 ? vec3(0.85, 0.65, 0.55) * mix(0.8, 1.5, dot(normal, normalize(vec3(0.0, 1.0, 0.5))) * 0.5 + 0.5) :
id == 2 ? vec3(0.85, 0.65, 0.55) * 1.5 :
vec3(1.0, 1.0, 0.0);
}
void marchObjects(vec3 eye, vec3 ray, float wDepth, inout vec4 color) {
float dist = 0.0;
int id;
vec3 rayPos = eye;
float depth = CAM_FAR;
for (int i = 0; i < 30; i++) {
dist = objects(rayPos, color.rgb, id);
depth = distance(rayPos, eye);
if (depth > wDepth || dist < 0.01) break;
rayPos += ray * dist;
}
color = dist < 0.01 ? vec4(objectsColor(id, objectsNormal(rayPos, 0.01), ray), depth) : color;
}
vec3 waterColor(vec3 ray, vec3 normal, vec3 p) {
vec3 color = vec3(0.0);
float fogDist = length(p - vec3(0.0, 0.0, -6.0));
float dist = 0.0;
int objId = 0;
vec3 refl = reflect(ray, normal);
vec3 rayPos = p + refl * dist;
if (length(p.xz - artifactOffset.xz) < 8.5 && dot(refl, normalize(artifactOffset - p)) > -0.25) {
for (int i = 0; i < 40; i++) {
dist = objects(rayPos, color, objId);
if (dist < 0.01) {
color = objectsColor(objId, objectsNormal(rayPos, 0.001), rayPos);
break;
}
rayPos += refl * dist;
}
}
float fresnel = 0.04 + 0.9 * pow(1.0 - max(0.0, dot(-normal, ray)), 7.0);
float d = length(artifactOffset - p);
const float r = 14.0;
float atten = clamp(1.0 - (d * d) / (r * r), 0.0, 1.0);
atten *= atten;
vec3 point = vec3(0.75, 0.55, 0.45) * atten * (1.0 + fresnel) * 0.07;
vec3 ambient = dot(normal, normalize(vec3(0.0, 1.0, 0.5))) * max(fresnel, 0.06) * vec3(0.1, 0.5, 1.0) * 0.85;
float fog = smootherstep(25.0, 6.0, fogDist) * (1.0 / (fogDist * 0.1));
return color + (point + ambient) * fog;
}
vec3 waterNormal(vec2 p, float eps) {
vec2 h = vec2(eps, 0.0);
return normalize(vec3(heightmap(p - h.xy) - heightmap(p + h.xy), eps * 2.0, heightmap(p - h.yx) - heightmap(p + h.yx)));
}
void marchWater(vec3 eye, vec3 ray, inout vec4 color) {
const vec3 planeNorm = vec3(0.0, 1.0, 0.0);
const float depth = 3.0;
float ceilDist = intersectPlane(eye, ray, vec3(0.0, 0.0, 0.0), planeNorm);
vec3 normal = vec3(0.0);
if (dot(planeNorm, ray) > -0.05) {
color = vec4(vec3(0.0), CAM_FAR);
return;
}
float height = 0.0;
vec3 rayPos = eye + ray * ceilDist;
for (int i = 0; i < 30; i++) {
height = heightmap(rayPos.xz) * depth - depth;
if (rayPos.y - height < 0.1) {
color.w = distance(rayPos, eye);
vec3 normPos = (eye + ray * color.w);
color.rgb = waterColor(ray, waterNormal(normPos.xz, 0.005), normPos);
return;
}
rayPos += ray * max(rayPos.y - height, 0.1);
}
color = vec4(vec3(0.0), CAM_FAR);
}
vec3 march(vec2 uv, vec3 camPos) {
mat4 vm = viewMatrix(camFwd, camUp);
vec3 ray = (vm * vec4(calcRay(uv, 80.0, resolution.x / resolution.y), 1.0)).xyz;
vec4 color = vec4(BACKGROUND, CAM_FAR);
vec3 waterColor;
marchWater(camPos, ray, color);
marchObjects(camPos, ray, color.w, color);
return color.rgb;
}
float snow(vec2 uv, float scale) {
float w = smootherstep(1.0, 0.0, -uv.y * (scale * 0.01));
if (w < 0.1) return 0.0;
uv += time / scale;
uv.y += time / scale;
uv.x += sin(uv.y + time * 0.125) / scale;
uv *= scale;
vec2 s = floor(uv), f = fract(uv);
return smootherstep(0.0, min(length(0.5 + 0.5 * sin(11.0 * fract(sin((s + scale) * mat2(7.0, 3.0, 6.0, 5.0)) * 5.0)) - f), 3.0), sin(f.x + f.y) * 0.01) * w;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord * (vec2(1.0) / resolution.xy);
float s = sin(time);
float c = cos(time);
artifactRotation = mat3(c, 0, s, 0, 1, 0, -s, 0, c) * rotationAlign(vec3(0.0, 1.0, 0.0), vec3(s * 0.2, 1.0, c * 0.2 + 0.3));
artifactOffset = vec3(s * 0.4, c * 0.3 - 1.7, -6.0);
camFwd = vec3(0.0, 0.7 + noise(time * 0.8 + 4.0) * 0.08 - 0.04, 1.0);
camUp = vec3(noise(time * 1.2) * 0.02 - 0.01, 1.0, 0.0);
fragColor = vec4(march(uv, vec3(0.0, 1.9, 1.0)) - (length(uv - 0.5) - 0.3) * 0.05, 1.0);
if (layer_snow) {
vec2 p = fragCoord.xy / resolution.xy;
vec2 uvSnow = (fragCoord.xy * 2.0 - resolution.xy) / min(resolution.x, resolution.y);
fragColor += mix(vec4(vec3(snow(uvSnow, 4.0)), 0.5) + vec4(vec3(snow(uvSnow, 3.0)), 0.5), vec4(0.0), vec4(0.7));
}
}
void main() {
fragColor = vColor;
mainImage(fragColor, gl_FragCoord.xy);
}

View File

@@ -1,268 +0,0 @@
@group(0) @binding(0)
var<uniform> time: f32;
@group(0) @binding(0)
var<uniform> resolution: vec2<f32>;
@fragment
// fn mainImage(@builtin(frag_coord) fragCoord: vec4<f32>) -> @location(0) vec4<f32>
fn fragment_main(fragData: VertexOut) -> @location(0) vec4f {
var uv = fragCoord.xy / resolution;
var s = sin(time);
var c = cos(time);
var artifactRotation: mat3x3<f32> = mat3x3<f32>(
vec3<f32>(c, 0.0, s),
vec3<f32>(0.0, 1.0, 0.0),
vec3<f32>(-s, 0.0, c)
) * rotationAlign(vec3<f32>(0.0, 1.0, 0.0), vec3<f32>(s * 0.2, 1.0, c * 0.2 + 0.3));
var artifactOffset = vec3<f32>(s * 0.4, c * 0.3 - 1.7, -6.0);
var camFwd: vec3<f32> = vec3<f32>(0.0, 0.7 + noise(time * 0.8 + 4.0) * 0.08 - 0.04, 1.0);
var camUp: vec3<f32> = vec3<f32>(noise(time * 1.2) * 0.02 - 0.01, 1.0, 0.0);
var color: vec3<f32> = march(uv, vec3<f32>(0.0, 1.9, 1.0)) - (length(uv - vec2<f32>(0.5)) - 0.3) * 0.05;
return vec4<f32>(color, 1.0);
}
fn smootherstep(edge0: f32, edge1: f32, x: f32) -> f32 {
var t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
}
fn rand(n: f32) -> f32 {
var n1 = fract(n * 43758.5453);
n1 = n1 * n1;
return fract(n1 * 43758.5453);
}
fn hash(n: f32) -> f32 {
return fract(abs(fract(n) * 43758.5453));
}
fn noise(x: f32) -> f32 {
var i = floor(x);
var f = fract(x);
var u = f * f * (3.0 - 2.0 * f);
return mix(hash(i), hash(i + 1.0), u);
}
fn viewMatrix(dir: vec3<f32>, up: vec3<f32>) -> mat4x4<f32> {
var f = normalize(dir);
var s = normalize(cross(f, up));
return mat4x4<f32>(
vec4<f32>(s, 0.0),
vec4<f32>(cross(s, f), 0.0),
vec4<f32>(-f, 0.0),
vec4<f32>(0.0, 0.0, 0.0, 1.0)
);
}
fn rotationAlign(d: vec3<f32>, z: vec3<f32>) -> mat3x3<f32> {
var v = cross(z, d);
var c = dot(z, d);
var k = 1.0 / (1.0 + c);
return mat3x3<f32>(
vec3<f32>(v.x * v.x * k + c, v.y * v.x * k - v.z, v.z * v.x * k + v.y),
vec3<f32>(v.x * v.y * k + v.z, v.y * v.y * k + c, v.z * v.y * k - v.x),
vec3<f32>(v.x * v.z * k - v.y, v.y * v.z * k + v.x, v.z * v.z * k + c)
);
}
fn intersectPlane(origin: vec3<f32>, direction: vec3<f32>, pointt: vec3<f32>, normal: vec3<f32>) -> f32 {
return clamp(dot(pointt - origin, normal) / dot(direction, normal), -1.0, 9991999.0);
}
fn calcRay(uv: vec2<f32>, fov: f32, aspect: f32) -> vec3<f32> {
var uv1 = uv * 2.0 - 1.0;
var d = 1.0 / tan(radians(fov) * 0.5);
return normalize(vec3<f32>(aspect * uv1.x, uv1.y, d));
}
fn getWave(position: vec2<f32>, dir: vec2<f32>, speed: f32, frequency: f32, iTimeshift: f32) -> vec2<f32> {
var x = dot(dir, position) * frequency + iTimeshift * speed;
var wave = exp(sin(x) - 1.0);
var dist = wave * cos(x);
return vec2<f32>(wave, -dist);
}
fn heightmap(worldPos: vec2<f32>) -> f32 {
let scale: f32 = 0.06;
var p = worldPos * scale;
var p2 = (artifactOffset.xz - vec2<f32>(0.0, 1.0)) * scale;
var d = (1.0 - smootherstep(0.0, 1.0, clamp(length(p2 - p) * 1.25, 0.0, 1.0))) * 0.87;
var angle: f32 = 0.0;
var freq: f32 = 5.0;
var speed: f32 = 2.0;
var weight: f32 = 1.9;
var wave: f32 = 0.0;
var waveScale: f32 = 0.0;
var dir: vec2<f32>;
var res: vec2<f32>;
for (var i: i32 = 0; i < 5; i = i + 1) {
dir = vec2<f32>(cos(angle), sin(angle));
res = getWave(p, dir, speed, freq, time);
p = p + dir * res.y * weight * 0.05;
wave = wave + res.x * weight - d;
angle = angle + 12.0;
waveScale = waveScale + weight;
weight = mix(weight, 0.0, 0.2);
freq = freq * 1.18;
speed = speed * 1.06;
}
return wave * (1.0 / waveScale);
}
fn octahedron(p: vec3<f32>, s: f32) -> f32 {
p = abs(p);
return (p.x + p.y + p.z - s) * 0.57735027;
}
fn artifact(p: vec3<f32>, currDist: f32, glowColor: vec3<f32>, id: i32) {
p = p - artifactOffset;
p = artifactRotation * p;
var dist = octahedron(p, 0.8);
let glowDist: f32 = 4.8;
if (dist < glowDist) {
var d = dist + rand(dist) * 1.7;
glowColor = glowColor + vec3<f32>(0.75, 0.55, 0.45) * clamp(1.0 - pow((d * (1.0 / glowDist)), 5.0), 0.0, 1.0) * 0.035;
}
if (dist < currDist) {
currDist = dist;
id = 1;
}
}
fn objects(p: vec3<f32>, glowColor: vec3<f32>, objId: i32) -> f32 {
var dist: f32 = CAM_FAR;
artifact(p, dist, glowColor, objId);
return dist;
}
fn artifactDist(p: vec3<f32>) -> f32 {
p = p - artifactOffset;
p = artifactRotation * p;
return octahedron(p, 1.2);
}
fn objectsDist(p: vec3<f32>) -> f32 {
return artifactDist(p);
}
fn objectsNormal(p: vec3<f32>, eps: f32) -> vec3<f32> {
var h = vec2<f32>(eps, 0.0);
return normalize(vec3<f32>(
artifactDist(p + h.xxx) - artifactDist(p - h.xxx),
eps * 2.0,
artifactDist(p + h.yyy) - artifactDist(p - h.yyy)
));
}
fn objectsColor(id: i32, normal: vec3<f32>, ray: vec3<f32>) -> vec3<f32> {
if (id == 1) { return vec3<f32>(0.85, 0.65, 0.55) * mix(0.8, 1.5, dot(normal, normalize(vec3<f32>(0.0, 1.0, 0.5))) * 0.5 + 0.5); }
if (id == 2) { return vec3<f32>(0.85, 0.65, 0.55) * 1.5; }
return vec3<f32>(1.0, 1.0, 0.0);
}
fn marchObjects(eye: vec3<f32>, ray: vec3<f32>, wDepth: f32, color: vec4<f32>) -> vec4<f32> {
var dist: f32 = 0.0;
var id: i32;
var rayPos: vec3<f32> = eye;
var depth: f32 = CAM_FAR;
for (var i: i32 = 0; i < 30; i = i + 1) {
dist = objects(rayPos, color.rgb, id);
depth = distance(rayPos, eye);
if (depth > wDepth || dist < 0.01) {
break;
}
rayPos = rayPos + ray * dist;
}
if (dist < 0.01) {
return vec4<f32>(objectsColor(id, objectsNormal(rayPos, 0.01), ray), depth);
}
return color;
}
fn waterColor(ray: vec3<f32>, normal: vec3<f32>, p: vec3<f32>) -> vec3<f32> {
var color: vec3<f32> = vec3<f32>(0.0);
var fogDist: f32 = length(p - vec3<f32>(0.0, 0.0, -6.0));
var dist: f32 = 0.0;
var objId: i32 = 0;
var refl: vec3<f32> = reflect(ray, normal);
var rayPos: vec3<f32> = p + refl * dist;
if (length(p.xz - artifactOffset.xz) < 8.5 && dot(refl, normalize(artifactOffset - p)) > -0.25) {
for (var i: i32 = 0; i < 40; i = i + 1) {
dist = objects(rayPos, color, objId);
if (dist < 0.01) {
color = objectsColor(objId, objectsNormal(rayPos, 0.001), rayPos);
break;
}
rayPos = rayPos + refl * dist;
}
}
var fresnel: f32 = 0.04 + 0.9 * pow(1.0 - max(0.0, dot(-normal, ray)), 7.0);
var d: f32 = length(artifactOffset - p);
const r: f32 = 14.0;
var atten: f32 = clamp(1.0 - (d * d) / (r * r), 0.0, 1.0);
atten = atten * atten;
var pointt: vec3<f32> = vec3<f32>(0.75, 0.55, 0.45) * atten * (1.0 + fresnel) * 0.07;
var ambient: vec3<f32> = dot(normal, normalize(vec3<f32>(0.0, 1.0, 0.5))) * max(fresnel, 0.06) * vec3<f32>(0.1, 0.5, 1.0) * 0.85;
var fog: f32 = smootherstep(25.0, 6.0, fogDist) * (1.0 / (fogDist * 0.1));
return color + (pointt + ambient) * fog;
}
fn waterNormal(p: vec2<f32>, eps: f32) -> vec3<f32> {
var h: vec2<f32> = vec2<f32>(eps, 0.0);
return normalize(vec3<f32>(
heightmap(p - h.xy) - heightmap(p + h.xy),
eps * 2.0,
heightmap(p - h.yx) - heightmap(p + h.yx)
));
}
fn marchWater(eye: vec3<f32>, ray: vec3<f32>, color: vec4<f32>) -> vec4<f32> {
let planeNorm: vec3<f32> = vec3<f32>(0.0, 1.0, 0.0);
let depth: f32 = 3.0;
var ceilDist: f32 = intersectPlane(eye, ray, vec3<f32>(0.0, 0.0, 0.0), planeNorm);
var normal: vec3<f32> = vec3<f32>(0.0);
if (dot(planeNorm, ray) > -0.05) {
return vec4<f32>(vec3<f32>(0.0), CAM_FAR);
}
var height: f32 = 0.0;
var rayPos: vec3<f32> = eye + ray * ceilDist;
for (var i: i32 = 0; i < 30; i = i + 1) {
height = heightmap(rayPos.xz) * depth - depth;
if (rayPos.y - height < 0.1) {
color.w = distance(rayPos, eye);
var normPos: vec3<f32> = (eye + ray * color.w);
color.rgb = waterColor(ray, waterNormal(normPos.xz, 0.005), normPos);
return color;
}
rayPos = rayPos + ray * max(rayPos.y - height, 0.1);
}
return vec4<f32>(vec3<f32>(0.0), CAM_FAR);
}
fn march(uv: vec2<f32>, camPos: vec3<f32>) -> vec3<f32> {
var vm: mat4x4<f32> = viewMatrix(camFwd, camUp);
var ray: vec3<f32> = (vm * vec4<f32>(calcRay(uv, 80.0, resolution.x / resolution.y), 1.0)).xyz;
var color: vec4<f32> = vec4<f32>(BACKGROUND, CAM_FAR);
var waterColor: vec3<f32>;
color = marchWater(camPos, ray, color);
color = marchObjects(camPos, ray, color.w, color);
return color.rgb;
}

View File

@@ -1,13 +0,0 @@
struct VertexOut {
@builtin(position) position : vec4f,
@location(0) color : vec4f
};
@vertex
fn vertex_main(@location(0) position: vec4f,
@location(1) color: vec4f) -> VertexOut {
var output : VertexOut;
output.position = position;
output.color = color;
return output;
}

View File

@@ -1,90 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>celesteflare.cc ~ lunary</title>
<meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>i0ur.ing ~ welcome</title>
<link href="assets/imgs/logo.ico" rel="icon" type="image/x-icon"/>
<link href="main.css" rel="stylesheet" type="text/css"/>
<link data-trunk rel="copy-file" href="public/misc/favicon.ico" />
<link data-trunk rel="copy-file" href="public/misc/404.png" />
<link data-trunk rel="copy-file" href="public/misc/profile.avif" />
<link data-trunk rel="copy-file" href="public/misc/oneko.gif" />
<script async defer src="js/init.js" type="module"></script>
<link data-trunk rel="copy-file" href="public/misc/freemono.ttf" />
<link data-trunk rel="copy-file" href="public/misc/monocraft.ttf" />
<link data-trunk rel="copy-file" href="public/blog/testimage.jpg" />
<link data-trunk rel="copy-file" href="public/shaders/post.bloom.svg" />
<link data-trunk rel="copy-file" href="public/buttons/iouring.png" />
<link data-trunk rel="copy-file" href="public/buttons/wasm.png" />
<link data-trunk rel="copy-file" href="public/buttons/dataforest.png" />
<link data-trunk rel="copy-file" href="public/buttons/servfail.png" />
<link data-trunk rel="copy-file" href="public/buttons/rust.png" />
<link data-trunk rel="copy-file" href="public/buttons/csharp.png" />
<link data-trunk rel="copy-file" href="public/buttons/kotlin.png" />
<link data-trunk rel="copy-file" href="public/buttons/java.png" />
<link data-trunk rel="copy-file" href="public/buttons/fedora.png" />
<link data-trunk rel="copy-file" href="public/buttons/artix.png" />
<link data-trunk rel="copy-file" href="public/buttons/nixos.png" />
<link data-trunk rel="copy-file" href="public/buttons/chimera.png" />
<link data-trunk rel="copy-file" href="public/buttons/void.png" />
<link data-trunk rel="copy-file" href="public/buttons/aqueer.png" />
<link data-trunk rel="sass" href="main.scss" />
<link data-trunk rel="rust" />
<base data-trunk-public-url />
</head>
<body>
<canvas id="shader-background"></canvas>
<main>
<div class="flexcard" id="cards">
<img alt="oneko.gif" class="oneko" src="assets/imgs/oneko.gif"/>
<div class="card">
<div id="profile-card"></div>
<div id="navigation">
<button class="navi" id="home" type="button">Home</button>
<button class="navi" id="portfolio" type="button">Portfolio</button>
<button class="navi" id="blog" type="button">Blog</button>
</div>
<div class="contents" id="content"></div>
<hr/>
<div class="socials">
<p>&copy; celesteflare.cc</p>
<a href="https://bsky.app/profile/lunary.celesteflare.cc" target="_blank"><i class="bi bi-cloudy-fill"></i></a>
<a href="https://dc.celesteflare.cc" onclick="navigator.clipboard.writeText(`luzey.zip`)"
target="_blank"><i class="bi bi-discord"></i></a>
<a href="https://www.instagram.com/luzl.r.h" target="_blank"><i class="bi bi-instagram"></i></a>
<a href="https://www.github.com/lunarydess" target="_blank"><i class="bi bi-github"></i></a>
</div>
</div>
</div>
<noscript
style="
position: fixed;
top: 0;
left: 0;
z-index: 10;
height: 100%;
width: 100%;
background-color: hsl(240, 21%, 15%);
user-select: none;
"
>
<div
style="
position: fixed;
inset: 0;
width: fit-content;
height: fit-content;
margin: auto;
"
>
<center>
<h1
style="
color: hsl(226, 64%, 88%);
text-decoration-color: hsl(226, 64%, 88%);
text-align: center;
text-rendering: geometricPrecision;
top: 50%;
"
>
JavaScript is mandatory. &#58;&#39;&#40;<br/>
Did you try it yet?
</h1>
<img
alt="yippee.gif"
src="assets/imgs/yippee.gif" style="
width: 25%;
height: 25%;
image-rendering: optimizeQuality;
image-resolution: 498x;
shape-image-threshold: 10;
image-orientation: from-image;
border-radius: 8px;
outline: hsl(53, 93%, 54%) dotted 2px;
pointer-events: none;
"/>
</center>
</div>
</noscript>
</main>
</body>
</html>
</html>

View File

@@ -1,12 +0,0 @@
import {insertFileContentIn as insert} from "./navigator.js";
import {init as initGallery} from "./gallery.js";
export async function init() {
const naviElements = document.getElementsByClassName("navi");
for (let i = 0; i < naviElements.length; i++) {
const element = naviElements.item(i);
element.addEventListener("click", function () {
insert("content", element.id, element.id === "portfolio" ? initGallery : undefined);
});
}
}

View File

@@ -1,53 +0,0 @@
const maxImages = 3;
const currentImage = {
idx: 1,
name: null,
image: null,
link: null,
desc: null,
};
export async function init() {
const previewImage = document.getElementById("portfolio-preview");
const previewTitle = document.getElementById("portfolio-title");
const previewDesc = document.getElementById("portfolio-description");
document.getElementById("portfolio-preview-previous").onclick = function () {
nextImage(previewImage, previewTitle, previewDesc, false)
}
document.getElementById("portfolio-preview-next").onclick = function () {
nextImage(previewImage, previewTitle, previewDesc, true)
}
fetchImage(previewImage, previewTitle, previewDesc);
}
async function nextImage(image, title, description, forward) {
if (forward) ++currentImage.idx;
else --currentImage.idx;
if (currentImage.idx > maxImages) currentImage.idx = 1;
else if (currentImage.idx < 1) currentImage.idx = maxImages;
fetchImage(image, title, description);
}
async function fetchImage(image, title, description) {
fetch(`assets/projects/${currentImage.idx}.json`)
.then(value => value.json().then(currentImageJson => {
currentImage.name = currentImageJson.name;
currentImage.image = currentImageJson.image;
currentImage.link = currentImageJson.link;
currentImage.desc = currentImageJson.desc;
title.innerText = currentImage.name;
description.innerText = currentImage.desc.map(item => JSON.stringify(item)).join('\n').replaceAll("\"", "");
console.info(description.innerText)
image.setAttribute("src", `assets/projects/${currentImage.image}`);
image.onclick = function () {
window.open(currentImage.link, "_blank").focus();
};
}))
.catch(error => console.error(error));
}

View File

@@ -1,13 +0,0 @@
import {init as initThemes} from "./themes.js";
import {init as initShaders} from "./shaders.js";
import {init as initNavigator} from "./navigator.js";
import {init as initButtons} from "./buttons.js";
async function init() {
initThemes();
initShaders();
initNavigator();
initButtons();
}
init();

View File

@@ -1,25 +0,0 @@
import {init as initGallery} from "./gallery.js";
export async function insertContentIn(destination, content, onload = undefined) {
const element = document.getElementById(destination);
element.innerHTML = content;
if (onload) {
element.onload = onload();
}
}
export async function insertFileContentIn(destination, htmlFile, onload = undefined) {
fetch(`./router/${htmlFile}.html`)
.then(response => response.text())
.then(text => insertContentIn(destination, text, onload))
.then(_ => {
document.title = `lunary ~ ${htmlFile}`
window.history.pushState(null, 'Title', `#${htmlFile}`)
}).catch(error => console.error(error));
}
export async function init() {
let meow = window.location.href.match(/https*:\/\/.*#.+/g);
meow = meow === null || meow === undefined || meow.length === 0 ? "home" : String(meow).substring(String(meow).lastIndexOf("#") + 1);
insertFileContentIn("content", meow, meow === "portfolio" ? initGallery : undefined);
}

View File

@@ -1,234 +0,0 @@
import {isWinter, isXmas} from "./util.js";
export async function init() {
const canvas = document.querySelector("#shader-background");
/* TODO: ...
const wgpuContext = canvas.getContext("webgpu");
if (wgpuContext !== null) {
initWGPU(canvas, wgpuContext);
return;
}*/
let context;
const wglVersion =
(context = (canvas.getContext("experimental-webgl2") || canvas.getContext("webgl2"))) != null ? 2 :
(context = (canvas.getContext("experimental-webgl") || canvas.getContext("webgl"))) != null ? 1 : 0;
if (wglVersion !== 0) initGL(canvas, context, wglVersion);
else console.warn("couldn't find webgpu nor webgl, so no-no fancy shader-background. :(");
}
async function initGL(canvas, context, wglVersion) {
console.info(`detected wgl ${wglVersion} meow`);
const positions = context.createBuffer();
context.bindBuffer(context.ARRAY_BUFFER, positions);
context.bufferData(context.ARRAY_BUFFER, new Float32Array([
-1.0, 1.0, 0.0,
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0,
1.0, 1.0, 0.0
]), context.STATIC_DRAW);
const colors = context.createBuffer();
context.bindBuffer(context.ARRAY_BUFFER, colors);
context.bufferData(context.ARRAY_BUFFER, new Float32Array([
1.0, 0.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
1.0, 1.0, 0.0, 1.0
]), context.STATIC_DRAW);
const elements_indices = [3, 2, 1, 3, 1, 0];
const elements = context.createBuffer();
context.bindBuffer(context.ELEMENT_ARRAY_BUFFER, elements);
context.bufferData(context.ELEMENT_ARRAY_BUFFER, new Uint16Array(elements_indices), context.STATIC_DRAW);
const es_key = wglVersion === 2 ? "es3" : "es1";
const vertex_shader = context.createShader(context.VERTEX_SHADER);
let vertex_shader_code = "";
await fetch(`./assets/shaders/glsl/${es_key}/background/bg.vsh.glsl`)
.then(response => response.text())
.then(text => vertex_shader_code = text)
.catch(error => console.error(error));
context.shaderSource(vertex_shader, vertex_shader_code);
context.compileShader(vertex_shader);
let vertex_shader_log = context.getShaderInfoLog(vertex_shader);
if (vertex_shader_log == null || vertex_shader_log.trim().length === 0) {
console.info("vertex shader loaded.");
} else console.warn(`vertex shader couldn't be loaded! (${vertex_shader_log})`);
let fragment_shader_code = "";
await fetch(`./assets/shaders/glsl/${es_key}/background/bg.fsh.glsl`)
.then(response => response.text())
.then(text => fragment_shader_code = text)
.catch(error => console.error(error));
const fragment_shader = context.createShader(context.FRAGMENT_SHADER);
context.shaderSource(fragment_shader, fragment_shader_code);
context.compileShader(fragment_shader);
let fragment_shader_log = context.getShaderInfoLog(fragment_shader);
if (fragment_shader_log == null || fragment_shader_log.trim().length === 0) {
console.info("fragment shader loaded.");
} else console.warn(`fragment shader couldn't be loaded! (${fragment_shader_log})`);
const shaderProgram = context.createProgram();
context.attachShader(shaderProgram, vertex_shader);
context.attachShader(shaderProgram, fragment_shader);
context.linkProgram(shaderProgram);
if (!context.getProgramParameter(shaderProgram, context.LINK_STATUS))
console.warn("shaders couldn't be linked! :(");
context.useProgram(shaderProgram);
const position = context.getAttribLocation(shaderProgram, "position");
context.enableVertexAttribArray(position);
context.bindBuffer(context.ARRAY_BUFFER, positions);
context.vertexAttribPointer(position, 3, context.FLOAT, false, 0, 0);
const color = context.getAttribLocation(shaderProgram, "color");
context.enableVertexAttribArray(color);
context.bindBuffer(context.ARRAY_BUFFER, colors);
context.vertexAttribPointer(color, 4, context.FLOAT, false, 0, 0);
context.bindBuffer(context.ELEMENT_ARRAY_BUFFER, elements);
context.clearColor(0.0, 0.0, 0.0, 1.0);
context.clear(context.COLOR_BUFFER_BIT);
context.enable(context.DEPTH_TEST);
const unimloc_matrix = context.getUniformLocation(shaderProgram, "model");
const default_matrix = new Float32Array([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]);
const unimloc_time = context.getUniformLocation(shaderProgram, "time");
const unimloc_res = context.getUniformLocation(shaderProgram, "resolution");
const unimloc_overlay_snow = context.getUniformLocation(shaderProgram, "layer_snow");
function renderLoop(time) {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
context.uniformMatrix4fv(unimloc_matrix, false, default_matrix);
context.uniform1f(unimloc_time, time / 1000.0);
context.uniform2fv(unimloc_res, [canvas.width, canvas.height]);
context.uniform1i(unimloc_overlay_snow, isXmas() || isWinter());
context.viewport(0, 0, canvas.width, canvas.height);
context.clear(context.COLOR_BUFFER_BIT | context.DEPTH_BUFFER_BIT);
context.drawElements(context.TRIANGLES, elements_indices.length, context.UNSIGNED_SHORT, 0);
window.requestAnimationFrame(renderLoop);
};
window.requestAnimationFrame(renderLoop);
}
async function initWGPU(canvas, context) {
console.info("detected wgpu");
async function fallback() {
console.info("falling back to gl");
let context;
const wglVersion = (context = (canvas.getContext("experimental-webgl2") || canvas.getContext("webgl2"))) != null ? 2 : (context = (canvas.getContext("experimental-webgl") || canvas.getContext("webgl"))) != null ? 1 : 0;
if (wglVersion !== 0) initGL(canvas, context, wglVersion);
else console.warn("couldn't find webgl, so no-no fancy shader-background. :(");
}
if (!navigator.gpu) {
console.info("wgpu context exists but is not supported.");
fallback();
return;
}
const adapter = await navigator.gpu.requestAdapter();
if (!adapter) {
console.warn("couldn't find any adapters");
fallback();
return;
}
const device = await adapter.requestDevice();
if (!device) {
console.warn("couldn't find any devices");
fallback();
return;
}
let shaders = "";
await fetch("./assets/shaders/wgsl/background/bg.vsh.wgsl")
.then(response => response.text())
.then(data => shaders += data)
.catch(error => console.error(error));
shaders += '\n';
await fetch("./assets/shaders/wgsl/background/bg.fsh.wgsl")
.then(response => response.text())
.then(data => shaders += data)
.catch(error => console.error(error));
const shaderModule = device.createShaderModule({code: shaders});
context.configure({
device: device,
format: navigator.gpu.getPreferredCanvasFormat(),
alphaMode: "premultiplied",
});
const vertices = new Float32Array([1.0, 1.0, 1.0, 1.0]);
const vertexBuffer = device.createBuffer({
size: vertices.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(vertexBuffer, 0, vertices, 0, vertices.length);
const renderPipeline = device.createRenderPipeline({
vertex: {
module: shaderModule,
entryPoint: 'vertex_main',
buffers: [{
attributes: [{
shaderLocation: 0, // position
offset: 0,
format: 'float32x4'
}, {
shaderLocation: 1, // color
offset: 16,
format: 'float32x4'
}],
arrayStride: 32,
stepMode: 'vertex'
}]
},
fragment: {
module: shaderModule,
entryPoint: 'fragment_main',
targets: [{
format: navigator.gpu.getPreferredCanvasFormat()
}]
},
primitive: {
topology: 'triangle-list'
},
layout: 'auto'
});
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginRenderPass({
colorAttachments: [{
clearValue: {r: 0.0, g: 0.0, b: 0.0, a: 1.0},
loadOp: 'clear',
storeOp: 'store',
view: context.getCurrentTexture().createView()
}]
});
passEncoder.setPipeline(renderPipeline);
passEncoder.setVertexBuffer(0, vertexBuffer);
passEncoder.draw(3);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
}

View File

@@ -1,321 +0,0 @@
import {hexToRgb} from "./util.js";
const constants = {
palettes: {
"Catppuccin": {
"Latte": {
"base": "#EFF1F5",
"mantle": "#E6E9EF",
"crust": "#DCE0E8",
"text": "#4C4F69",
"pink": "#EA76CB",
"purple": "#8839EF",
"red": "#D20F39",
"light_red": "#E64553",
"orange": "#FE640B",
"yellow": "#DF8E1D",
"green": "#40A02B",
"light_green": "#179299",
"blue": "#1E66F5",
"light_blue": "#7287FD"
},
"Frappe": {
"base": "#303446",
"mantle": "#292C3C",
"crust": "#232634",
"text": "#C6D0F5",
"pink": "#F4B8E4",
"purple": "#CA9EE6",
"red": "#E78284",
"light_red": "#EA999C",
"orange": "#EF9F76",
"yellow": "#E5C890",
"green": "#A6D189",
"light_green": "#81C8BE",
"blue": "#8CAAEE",
"light_blue": "#85C1DC"
},
"Macchiato": {
"base": "#24273A",
"mantle": "#1E2030",
"crust": "#181926",
"text": "#CAD3F5",
"pink": "#F5bDE6",
"purple": "#C6A0F6",
"red": "#ED8796",
"light_red": "#EE99A0",
"orange": "#F5A97F",
"yellow": "#EED49F",
"green": "#A6DA95",
"light_green": "#8BD5CA",
"blue": "#8AADF4",
"light_blue": "#7DC4E4"
},
"Mocha": {
"base": "#1E1E2E",
"mantle": "#181825",
"crust": "#11111b",
"text": "#CDD6F4",
"pink": "#F5C2E7",
"purple": "#CBA6F7",
"red": "#F38BA8",
"light_red": "#EBA0AC",
"orange": "#FAB387",
"yellow": "#F9E2AF",
"green": "#A6E3A1",
"light_green": "#94E2D5",
"blue": "#89B4FA",
"light_blue": "#74C7EC"
},
},
"Arc": {
"Dark": {
"base": "#282C34",
"mantle": "#333842",
"crust": "#2C313A",
"text": "#ABB2BF",
"pink": "#FF6AC1",
"purple": "#D38AEA",
"red": "#E06C75",
"light_red": "#BE5046",
"orange": "#DA8548",
"yellow": "#E5D07b",
"green": "#98C379",
"light_green": "#87BF70",
"blue": "#61AFEF",
"light_blue": "#4DB5BD"
},
"Light": {
"base": "#FAFAFA",
"mantle": "#EFF0EB",
"crust": "#F2F3F7",
"text": "#545862",
"pink": "#FF75A0",
"purple": "#BF9EEE",
"red": "#F47067",
"light_red": "#F47067",
"orange": "#FF9F50",
"yellow": "#F7BB47",
"green": "#6BC46D",
"light_green": "#42B983",
"blue": "#58A6FF",
"light_blue": "#73D0FF"
}
},
"Material": {
"Oceanic": {
"base": "#015970",
"mantle": "#0a2540",
"crust": "#0c3b62",
"text": "#ECEFF4",
"pink": "#FF3366",
"purple": "#AD82EC",
"red": "#FF5370",
"light_red": "#F78C6C",
"orange": "#FFCB6B",
"yellow": "#FAB795",
"green": "#C3E88D",
"light_green": "#A3BE8C",
"blue": "#82AAFF",
"light_blue": "#80CBC4",
},
"Darker": {
"base": "#1B262C",
"mantle": "#0F4C75",
"crust": "#3282B8",
"text": "#F7F7FF",
"pink": "#FEB236",
"purple": "#6B5B95",
"red": "#D81159",
"light_red": "#DB504A",
"orange": "#FFBC42",
"yellow": "#F0F66E",
"green": "#0EAD69",
"light_green": "#07BEB8",
"blue": "#0A9396",
"light_blue": "#00B2CA",
},
"Lighter": {
"base": "#EAEAEA",
"mantle": "#DADADA",
"crust": "#C4C4C4",
"text": "#141414",
"pink": "#FFB7C5",
"purple": "#D7A9E3",
"red": "#FF6363",
"light_red": "#FF9A8B",
"orange": "#FFD56D",
"yellow": "#FAED26",
"green": "#A8DF65",
"light_green": "#5DD39E",
"blue": "#7FD1B9",
"light_blue": "#68E8A0",
},
"Palenight": {
"base": "#292D3E",
"mantle": "#444267",
"crust": "#444267",
"text": "#ECEFF4",
"pink": "#FF5370",
"purple": "#C792EA",
"red": "#FF5370",
"light_red": "#F07178",
"orange": "#FFCB6B",
"yellow": "#FFCB6B",
"green": "#C3E88D",
"light_green": "#AED581",
"blue": "#82AAFF",
"light_blue": "#80CBC4",
},
"Deep Ocean": {
"base": "#0D2C54",
"mantle": "#144668",
"crust": "#1A5684",
"text": "#CCE7E9",
"pink": "#F77FBE",
"purple": "#B041FF",
"red": "#FF4E50",
"light_red": "#FF6B6B",
"orange": "#FFA07A",
"yellow": "#FFD97D",
"green": "#00CC99",
"light_green": "#00E0B7",
"blue": "#6D5DFC",
"light_blue": "#1EAE98",
},
"Forest": {
"base": "#0C351E",
"mantle": "#1A512D",
"crust": "#247035",
"text": "#E6F1F2",
"pink": "#FF80AB",
"purple": "#8E44AD",
"red": "#FF5252",
"light_red": "#FF7F7F",
"orange": "#FFB74D",
"yellow": "#FFEE58",
"green": "#2ECC71",
"light_green": "#2BD6A6",
"blue": "#3498DB",
"light_blue": "#5BC0EB",
},
"Sky Blue": {
"base": "#2C3E50",
"mantle": "#3F5772",
"crust": "#4D6E88",
"text": "#EAECEE",
"pink": "#FF6B6B",
"purple": "#A37ACC",
"red": "#FF7F50",
"light_red": "#FF8C68",
"orange": "#FFAB70",
"yellow": "#FFD570",
"green": "#1ABC9C",
"light_green": "#20C9A6",
"blue": "#3498DB",
"light_blue": "#5BC0DE",
},
"Sandy Beach": {
"base": "#FFF1E6",
"mantle": "#FFD9B3",
"crust": "#FFC196",
"text": "#4A4A4A",
"pink": "#FF7675",
"purple": "#D6A2E8",
"red": "#FDAC53",
"light_red": "#FFC85B",
"orange": "#F39C12",
"yellow": "#FFD200",
"green": "#91E763",
"light_green": "#A2DE67",
"blue": "#69C9D0",
"light_blue": "#5ECCDD",
},
"Volcano": {
"base": "#1C1116",
"mantle": "#452B30",
"crust": "#752F35",
"text": "#ECEBE4",
"pink": "#FF7582",
"purple": "#D5A5D1",
"red": "#FF3F34",
"light_red": "#FF6F59",
"orange": "#FF974F",
"yellow": "#FFD166",
"green": "#00818A",
"light_green": "#00A69D",
"blue": "#1B9AAA",
"light_blue": "#6AB187",
},
"Space": {
"base": "#2B2D42",
"mantle": "#3C415C",
"crust": "#495A75",
"text": "#D9F0FF",
"pink": "#FF6A88",
"purple": "#D16DFF",
"red": "#FF7171",
"light_red": "#FF8E8E",
"orange": "#FFAF7B",
"yellow": "#FFD066",
"green": "#76E3D6",
"light_green": "#7EE8FA",
"blue": "#3282B8",
"light_blue": "#74B9FF",
}
}
}
};
export let current = constants.palettes["Arc"]["Dark"];
export async function init() {
document.documentElement.style.setProperty("--background-color", "#000000ff");
// TODO: ... | add theme cookie
set("Catppuccin", "Mocha");
update();
}
export async function set(theme, variant) {
current = constants.palettes[theme][variant];
}
export async function update() {
document.documentElement.style.setProperty('--background-color', current.base);
for (let key in current) {
document.documentElement.style.setProperty(`--${key}`, hexToRgb(current[key]).rgb);
}
}

View File

@@ -1,45 +0,0 @@
export function hexToRgb(hex) {
const
red = parseInt(hex.slice(1, 3), 16),
green = parseInt(hex.slice(3, 5), 16),
blue = parseInt(hex.slice(5, 7), 16);
return {
red: red,
green: green,
blue: blue,
rgb: `${red}, ${green}, ${blue}`,
hex: hex
};
}
export function hexToRgba(hex) {
const
red = parseInt(hex.slice(1, 3), 16),
green = parseInt(hex.slice(3, 5), 16),
blue = parseInt(hex.slice(5, 7), 16),
alpha = parseInt(hex.slice(7, 9), 16);
return {
red: red,
green: green,
blue: blue,
alpha: alpha,
rgb: `${red}, ${green}, ${blue}`,
rgba: `${red}, ${green}, ${blue}, ${alpha}`,
hex: hex
};
}
export function isWinter(date = new Date()) {
const month = date.getMonth();
return month === 11 ? date.getDay() >= 21 : month === 2 ? date.getDay() < 21 : month < 2;
}
export function isXmas(date = new Date()) {
return date.getMonth() === 11 && (date.getDay() >= 24 && date.getDay() <= 26);
}
export function isHalloween(date = new Date()) {
return date.getMonth() === 10 && date.getDay() === 31;
}

13
justfile Normal file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env just --justfile
setup:
rustup target add wasm32-unknown-unknown
install:
cargo install --locked wasm-pack trunk --force
debug:
trunk serve
build:
trunk build --release

237
main.css
View File

@@ -1,237 +0,0 @@
@charset "UTF-8";
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.min.css");
@font-face {
font-family: Comfortaa;
src: url("assets/fonts/comfortaa.ttf");
}
:root {
--base: 0, 0, 0;
--mantle: 0, 0, 0;
--crust: 0, 0, 0;
--text: 0, 0, 0;
--pink: 0, 0, 0;
--purple: 0, 0, 0;
--red: 0, 0, 0;
--light_red: 0, 0, 0;
--orange: 0, 0, 0;
--yellow: 0, 0, 0;
--green: 0, 0, 0;
--light_green: 0, 0, 0;
--blue: 0, 0, 0;
--light_blue: 0, 0, 0;
}
@keyframes home {
/* @formatter:off */
from { opacity: 0 !important; }
to { opacity: 1 !important; }
/* @formatter:on */
}
#profile-card {
margin: 0;
image-orientation: from-image;
image-rendering: optimizeQuality;
image-resolution: from-image;
vertical-align: top;
}
#navigation {
text-align: center;
justify-content: center;
justify-items: center;
justify-self: center;
}
#navigation button {
backdrop-filter: blur(4px);
background-color: rgba(var(--base), 0.6125);
border-color: rgba(var(--blue), 0.0);
border-style: inset !important;
border-width: thin !important;
border-radius: 4px;
text-decoration-color: rgba(var(--text), 1.0);
padding: 2px 4px;
margin-top: 12px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
}
.oneko {
image-rendering: optimizeQuality;
image-orientation: from-image;
image-resolution: from-image;
flex-direction: column;
flex-flow: wrap;
text-align: left;
margin-left: 8px;
align-self: self-start;
justify-content: left;
justify-items: left;
justify-self: left;
float: left;
}
#navigation button:hover {
border-color: rgba(var(--text), 1.0);
}
.card {
backdrop-filter: blur(2px);
background-color: rgba(var(--base), 0.4125);
border-radius: 12px;
}
.card hr {
margin: 0.5em auto !important;
border-style: inset !important;
border-width: thin !important;
}
.socials {
text-align: right;
justify-content: right;
justify-items: right;
justify-self: right;
margin-right: 6px;
}
.socials p {
text-align: left;
font-style: italic;
display: inline;
float: left;
margin-top: 0;
margin-left: 6px;
margin-bottom: 8px;
justify-items: left;
justify-self: left;
}
.socials i {
font-size: large;
margin: 0;
font-kerning: normal;
text-align: center;
text-rendering: optimizeLegibility;
text-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
transition: text-shadow 0.6s cubic-bezier(0.76, 0, 0.24, 1);
}
.socials i:hover {
animation: socials-in 0.6s forwards;
}
.socials i:not(:hover) {
animation: socials-out 0.6s backwards;
}
html * {
font-family: Comfortaa, "Verdana Pro", "Verdana", "Courier New", Courier, monospace;
color: rgb(var(--text));
scrollbar-color: rgb(var(--blue)) rgb(var(--mantle));
scrollbar-width: auto;
z-index: 1 !important;
}
.profile-bio {
text-align: left;
}
canvas {
background-color: var(--base);
display: block !important;
position: absolute !important;
left: 0 !important;
top: 0 !important;
z-index: 0 !important;
}
rect {
shape-rendering: geometricPrecision;
fill-opacity: 0.1;
stroke: black;
stroke-width: 2px;
stroke-opacity: 1;
}
::-webkit-scrollbar-track {
background-color: rgb(var(--mantle));
border-radius: 50%;
}
::-webkit-scrollbar-thumb {
background-color: rgb(var(--blue));
border-radius: 50%;
}
body {
background-color: rgba(var(--base), 1);
z-index: -1;
}
main {
display: flex;
flex-wrap: wrap;
justify-content: center;
flex-direction: column;
align-items: center;
width: auto;
height: max-content;
}
::-moz-selection {
background: rgba(var(--blue), 0.3125);
color: rgb(var(--text));
}
::selection {
background: rgba(var(--blue), 0.3125);
color: rgb(var(--text));
}
.flexcard {
display: flex;
padding: 0;
margin: 0;
height: calc(100vh - 16px);
align-items: center;
justify-content: center;
}
@keyframes socials-in {
from {
text-shadow: 0 0 0 #fff,
0 0 1px #fff;
}
to {
text-shadow: 0 0 4px #fff,
0 0 4px #fff;
}
}
@keyframes socials-out {
from {
text-shadow: 0 0 4px #fff,
0 0 4px #fff;
}
to {
text-shadow: 0 0 0 #fff,
0 0 1px #fff;
}
}
#cards {
flex-direction: column;
flex-flow: column;
}
#contents {
width: 100%;
height: auto;
}

81
main.scss Normal file
View File

@@ -0,0 +1,81 @@
@charset "UTF-8";
@font-face {
font-family: freemono;
src: url("freemono.ttf");
}
@font-face {
font-family: monocraft;
src: url("monocraft.ttf");
}
html * {
font-family: freemono, monocraft, monospace !important;
scrollbar-color: hsla(232, 97%, 85%, 0.5) hsla(240, 21.05%, 14.9%, 0.7) !important;
scrollbar-width: thin !important;
scroll-behavior: smooth !important;
}
::-webkit-scrollbar {
display: none;
}
.textarea {
-ms-overflow-style: none !important;
scrollbar-width: none !important;
}
.textarea::-webkit-scrollbar {
display: none !important;
}
.box {
backdrop-filter: blur(4px) !important;
}
.card {
backdrop-filter: blur(4px) !important;
}
.input,
.select select,
.textarea {
backdrop-filter: blur(4px);
background-color: white, 0.213125 !important;
border-color: white !important;
}
a.navbar-item,
a.navbar-item:hover {
background-color: transparent !important;
}
::-moz-selection {
background: #F5C2E740 !important;
}
::selection {
background: #F5C2E740 !important;
}
a {
color: #F5C2E7FF !important;
text-decoration: none !important;
}
a:hover {
text-decoration: underline !important;
filter: url("post.bloom.svg#process") !important;
}
button {
background-color: transparent !important;
border-color: transparent !important;
color: white !important;
}
button:hover {
text-decoration: underline !important;
filter: url("post.bloom.svg#process") !important;
}

BIN
public/blog/testimage.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

BIN
public/buttons/aqueer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
public/buttons/artix.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
public/buttons/chimera.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
public/buttons/csharp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
public/buttons/fedora.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
public/buttons/iouring.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
public/buttons/java.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
public/buttons/kotlin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
public/buttons/nixos.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
public/buttons/rust.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
public/buttons/servfail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
public/buttons/void.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
public/buttons/wasm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
public/misc/404.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
public/misc/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

BIN
public/misc/freemono.ttf Normal file

Binary file not shown.

BIN
public/misc/monocraft.ttf Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 B

After

Width:  |  Height:  |  Size: 315 B

BIN
public/misc/profile.avif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

179
public/shaders/es3/bg.frag Normal file
View File

@@ -0,0 +1,179 @@
#version 300 es
precision highp float;
const float CAM_FAR = 20.0;
const vec3 BACKGROUND = vec3(0.0, 0.0, 0.0);
uniform float time;
uniform vec2 resolution;
uniform bool layer_snow;
in vec4 vColor;
layout (location = 0) out vec4 fragColor;
vec3 artifactOffset;
mat3 artifactRotation;
vec3 artifactAxis;
vec3 camFwd;
vec3 camUp;
float rand(float n) {
return fract(sin(n) * 43758.5453);
}
float noise(float x) {
float i = floor(x);
float f = fract(x);
return mix(rand(i), rand(i + 1.0), f * f * (3.0 - 2.0 * f));
}
mat4 viewMatrix(vec3 dir, vec3 up) {
vec3 f = normalize(dir);
vec3 s = normalize(cross(f, up));
vec3 u = cross(s, f);
return mat4(
vec4(s, 0.0),
vec4(u, 0.0),
vec4(-f, 0.0),
vec4(0.0, 0.0, 0.0, 1.0)
);
}
mat3 rotationAlign(vec3 d, vec3 z) {
vec3 v = cross(z, d);
float c = dot(z, d);
float k = 1.0 / (1.0 + c);
float k_vx = v.x * k;
float k_vy = v.y * k;
float k_vz = v.z * k;
return mat3(
k_vx * v.x + c, k_vx * v.y - v.z, k_vx * v.z + v.y,
k_vy * v.x + v.z, k_vy * v.y + c, k_vy * v.z - v.x,
k_vz * v.x - v.y, k_vz * v.y + v.x, k_vz * v.z + c
);
}
float intersectPlane(vec3 origin, vec3 direction, vec3 point, vec3 normal) {
return clamp(dot(point - origin, normal) / dot(direction, normal), -1.0, 9991999.0);
}
vec3 calcRay(vec2 uv, float fov, float aspect) {
uv = uv * 2.0 - 1.0;
float d = 1.0 / tan(radians(fov) * 0.5);
return normalize(vec3(aspect * uv.x, uv.y, d));
}
float octahedron(vec3 p, float s) {
const float factor = 0.57735027;
p = abs(p);
return (p.x + p.y + p.z - s) * factor;
}
void artifact(vec3 p, inout float currDist, inout vec3 glowColor, inout int id) {
p -= artifactOffset;
p = artifactRotation * p;
float dist = octahedron(p, 0.8);
const float glowDist = 4.8;
if (dist < glowDist) {
float d = dist + rand(dist) * 1.7;
glowColor += vec3(0.75, 0.55, 0.45) * clamp(1.0 - pow((d * (1.0 / glowDist)), 5.0), 0.0, 1.0) * 0.035;
}
if (dist < currDist) {
currDist = dist;
id = 1;
}
}
float objects(vec3 p, inout vec3 glowColor, inout int objId) {
float dist = CAM_FAR;
artifact(p, dist, glowColor, objId);
return dist;
}
float artifactDist(vec3 p) {
p -= artifactOffset;
p = artifactRotation * p;
return octahedron(p, 1.2);
}
vec3 objectsNormal(vec3 p, float eps) {
vec2 h = vec2(eps, 0);
return normalize(vec3(artifactDist(p + h.xyy) - artifactDist(p - h.xyy), eps * 2.0, artifactDist(p + h.yyx) - artifactDist(p - h.yyx)));
}
vec3 objectsColor(int id, vec3 normal, vec3 ray) {
return id == 1 ? vec3(0.85, 0.65, 0.55) * mix(0.8, 1.5, dot(normal, normalize(vec3(0.0, 1.0, 0.5))) * 0.5 + 0.5) :
id == 2 ? vec3(0.85, 0.65, 0.55) * 1.5 :
vec3(1.0, 1.0, 0.0);
}
void marchObjects(vec3 eye, vec3 ray, float wDepth, inout vec4 color) {
float dist = 0.0;
int id;
vec3 rayPos = eye;
float depth = CAM_FAR;
for (int i = 0; i < 30; i++) {
dist = objects(rayPos, color.rgb, id);
depth = distance(rayPos, eye);
if (depth > wDepth || dist < 0.01) break;
rayPos += ray * dist;
}
color = dist < 0.01 ? vec4(objectsColor(id, objectsNormal(rayPos, 0.01), ray), depth) : color;
}
vec3 march(vec2 uv, vec3 camPos) {
mat4 vm = viewMatrix(camFwd, camUp);
vec3 ray = (vm * vec4(calcRay(uv, 80.0, resolution.x / resolution.y), 1.0)).xyz;
vec4 color = vec4(BACKGROUND, CAM_FAR);
marchObjects(camPos, ray, color.w, color);
return color.rgb;
}
float snow(vec2 uv, float scale) {
float w = smoothstep(1.0, 0.0, -uv.y * (scale * 0.01));
if (w < 0.1) return 0.0;
float timeScale = time / scale;
uv += vec2(timeScale, timeScale);
uv.x += sin(uv.y + time * 0.125) / scale;
uv *= scale;
vec2 s = floor(uv);
vec2 f = fract(uv);
float sinValueX = sin((s.x + scale) * 7.0) * 5.0;
float sinValueY = sin((s.y + scale) * 5.0) * 5.0;
float combinedSinValue = sinValueX + sinValueY;
float lengthValue = length(0.5 + 0.5 * sin(11.0 * fract(combinedSinValue)) - f);
return smoothstep(0.0, min(lengthValue, 3.0), sin(f.x + f.y) * 0.01) * w;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / resolution.xy;
float s = sin(time);
float c = cos(time);
artifactRotation = mat3(c, 0, s, 0, 1, 0, -s, 0, c) * rotationAlign(vec3(0.0, 1.0, 0.0), vec3(s * 0.2, 1.0, c * 0.2 + 0.3));
artifactOffset = vec3(s * 0.4, c * 0.3 - 1.7, -6.0);
camFwd = vec3(0.0, 0.7 + noise(time * 0.8 + 4.0) * 0.08 - 0.04, 1.0);
camUp = vec3(noise(time * 1.2) * 0.02 - 0.01, 1.0, 0.0);
fragColor = vec4(march(uv, vec3(0.0, 1.9, 1.0)) - (length(uv - 0.5) - 0.3) * 0.05, 1.0);
if (layer_snow) {
vec2 uvSnow = (fragCoord * 2.0 - resolution.xy) / min(resolution.x, resolution.y);
fragColor += vec4(vec3(snow(uvSnow, 4.0)), 0.5) * 0.7;
}
}
void main() {
fragColor = vColor;
mainImage(fragColor, gl_FragCoord.xy);
}

View File

@@ -1,5 +1,4 @@
#version 300 es
#version 300 es
layout(location = 0) in vec3 position;
layout(location = 1) in vec4 color;
@@ -7,6 +6,6 @@ uniform mat4 model;
out vec4 vColor;
void main(void) {
vColor = color;
gl_Position = model * vec4(position, 1.0);
vColor = color;
gl_Position = model * vec4(position, 1.0);
}

View File

@@ -0,0 +1,25 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.2" width="0" height="0">
<filter id="process">
<feGaussianBlur stdDeviation="1"/>
<feComponentTransfer>
<feFuncR type="identity"/>
<feFuncG type="identity"/>
<feFuncB type="identity"/>
<feFuncA type="identity"/>
</feComponentTransfer>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
<feGaussianBlur stdDeviation="2"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
<feGaussianBlur stdDeviation="3"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</svg>

After

Width:  |  Height:  |  Size: 691 B

View File

@@ -1,28 +0,0 @@
<div class="home" id="intro"><p>
todo! :)
</p></div>
<style>
@import url(main.css);
.home {
display: flex;
padding: 0;
margin: 0;
text-align: center;
flex-wrap: wrap;
flex-flow: row;
animation: home 1s forwards;
justify-content: center;
justify-items: center;
justify-self: center;
}
#intro p {
background: linear-gradient(135deg,
#55cdfc, #55cdfc,
#f7a8b8, #f7a8b8,
#ffffff, #ffffff);
background-clip: text;
color: transparent;
}
</style>

View File

@@ -1,29 +0,0 @@
<div class="home" id="intro"><p>
Hey, I am Lucielle, welcome. &#9825;<br>
she/her &#8226; developer &#8226; artist<br>
</p></div>
<style>
@import url(main.css);
.home {
display: flex;
padding: 0;
margin: 0;
text-align: center;
flex-wrap: wrap;
flex-flow: row;
animation: home 1s forwards;
justify-content: center;
justify-items: center;
justify-self: center;
}
#intro p {
background: linear-gradient(135deg,
#55cdfc, #55cdfc,
#f7a8b8, #f7a8b8,
#ffffff, #ffffff);
background-clip: text;
color: transparent;
}
</style>

View File

@@ -1,91 +0,0 @@
<div class="portfolio">
<img alt="waow.png" height="180" id="portfolio-preview" src="" width="320"/>
<div id="portfolio-navi">
<p id="portfolio-title"></p>
<div id="portfolio-navi-btns">
<button id="portfolio-preview-previous" type="button">&lt;</button>
<button id="portfolio-preview-next" type="button">&gt;</button>
</div>
</div>
<p id="portfolio-description"></p>
</div>
<style>
@import url(main.css);
.portfolio {
display: flex;
padding: 0;
margin: 0;
text-align: center;
flex-direction: column;
flex-flow: column;
animation: home 1s forwards;
justify-content: center;
justify-items: center;
justify-self: center;
}
#portfolio-preview {
image-rendering: optimizeQuality;
image-orientation: from-image;
image-resolution: from-image;
margin-top: 12px;
}
.portfolio button {
backdrop-filter: blur(4px);
background-color: rgba(var(--base), 0.6125);
border-color: rgba(var(--blue), 0.0);
border-style: inset !important;
border-width: thin !important;
border-radius: 4px;
text-decoration-color: rgba(var(--text), 1.0);
padding: 2px 4px;
margin-top: 12px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
}
.portfolio button:hover {
border-color: rgba(var(--text), 1.0);
}
#portfolio-title {
text-align: left;
margin-left: 12px;
}
#portfolio-description {
flex-direction: row;
flex-flow: wrap;
justify-content: space-around;
text-align: left;
margin-top: 0;
max-width: 320px;
}
#portfolio-navi {
display: flex;
flex-direction: column;
flex-flow: wrap;
justify-content: space-between;
}
#portfolio-navi-btns {
display: flex;
flex-direction: column;
flex-flow: wrap;
justify-content: right;
}
#portfolio-navi-btns button {
margin-left: 2px;
}
.portfolio button {
width: 20px;
height: 20px;
}
</style>

0
src/lib.rs Normal file
View File

480
src/main.rs Normal file
View File

@@ -0,0 +1,480 @@
#![feature(iter_intersperse)]
extern crate core;
use js_sys::Float32Array;
use log::{info, warn};
use std::cell::RefCell;
use std::sync::Arc;
use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*;
use web_sys::{HtmlCanvasElement, WebGl2RenderingContext as GL2, window};
use yew::prelude::*;
use yew_router::prelude::*;
mod pages;
use crate::pages::about::About;
use crate::pages::blog::author::Author;
use crate::pages::blog::authors::Authors;
use crate::pages::blog::entries::Entries;
use crate::pages::blog::entry::Entry;
use crate::pages::findme::FindMe;
use crate::pages::projects::projects::Projects;
use pages::not_found::PageNotFound;
#[derive(Routable, PartialEq, Eq, Clone, Debug)]
pub enum Route {
#[at("/")]
About,
#[at("/blog/entries/:id")]
Entry { id: u8 },
#[at("/blog/entries")]
Entries,
#[at("/blog/authors/:id")]
Author { id: u8 },
#[at("/blog/authors")]
Authors,
#[at("/projects")]
Projects,
#[at("/findme")]
FindMe,
#[not_found]
#[at("/404")]
NotFound,
}
pub struct App {
node_ref: NodeRef,
}
impl Component for App {
type Message = ();
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self {
node_ref: NodeRef::default(),
}
}
fn view(&self, _ctx: &Context<Self>) -> Html {
html! {
<>
<canvas width="100%" height="100%" style="
background-color: black !important;
position: fixed !important;
left: 0 !important;
top: 0 !important;
width: 100vw !important;
height: 100vh !important;
z-index: 0 !important;
" ref={self.node_ref.clone()} />
<BrowserRouter>
<header style="
z-index: 2 !important;
display: flex !important;
color: white !important;
flex-direction: row !important;
align-items: center !important;
justify-content: center !important;
max-width: 100vw !important;
min-height: 20px !important;
height: 20px !important;
max-height: 20px !important;
filter: url(post.bloom.svg) !important;
">
<h3>{r"»[ "}</h3>
<h3><Link<Route> to={Route::About}>{r"about"}</Link<Route>></h3>
<h3>{r"|"}</h3>
<h3><Link<Route> to={Route::Entries}>{r"blog"}</Link<Route>></h3>
<h3>{r"|"}</h3>
<h3><Link<Route> to={Route::FindMe}>{r"findme"}</Link<Route>></h3>
<h3>{r"|"}</h3>
<h3><Link<Route> to={Route::Projects}>{r"projects"}</Link<Route>></h3>
<h3>{r" ]«"}</h3>
</header>
<main style="
z-index: 1 !important;
background-color: transparent !important;
color: white !important;
display: flex !important;
flex-direction: column !important;
min-width: fit-content !important;
width: calc(100vw - 18 px) !important;
flex-wrap: nowrap !important;
justify-content: space-between !important;
align-items: center !important;
justify-content: center !important;
align-self: center !important;
margin-left: auto !important;
margin-right: auto !important;
overflow-x: hidden !important;
overflow-y: auto !important;
height: fit-content !important;
max-height: calc(100vh - 42px) !important;
">
/*
min-height: 70vh !important;
height: 80vw !important;
*/
<div style="
background-color: rgba(0, 0, 0, 0.3125) !important;
backdrop-filter: blur(6px) !important;
padding: 4px 0px 0px 4px !important;
border-radius: 1.0rem !important;
border-style: solid !important;
border-color: black !important;
border-width: 1px !important;
z-index: 1 !important;
overflow-x: hidden !important;
overflow-y: auto !important;
min-width: 70vw !important;
width: 70vh !important;
max-width: calc(100vw - 18px) !important;
height: 100% !important;
margin-top: auto !important;
margin-bottom: auto !important;
scrollbar-width: thin !important;
scrollbar-color: transparent transparent !important;
"><Switch<Route> render={switch} />
{self.view_buttons()}
</div>
</main>
</BrowserRouter>
</>
}
}
fn rendered(&mut self, _ctx: &Context<Self>, first_render: bool) {
if first_render {
self.view_canvas();
}
}
}
fn switch(routes: Route) -> Html {
match routes {
Route::About => {
html! { <About /> }
}
Route::Entries => {
html! { <Entries /> }
}
Route::Entry { id } => {
html! { <Entry id={id as u8} /> }
}
Route::Authors => {
html! { <Authors /> }
}
Route::Author { id } => {
html! { <Author id={id as u8} /> }
}
Route::FindMe => {
html! { <FindMe /> }
}
Route::Projects => {
html! { <Projects /> }
}
Route::NotFound => {
html! { <PageNotFound /> }
}
}
}
impl App {
fn view_buttons(&self) -> Html {
html! {
<div style="
z-index: 1 !important;
display: flex !important;
flex-wrap: wrap !important;
flex-direction: row !important;
justify-content: center !important;
min-width: 70vw !important;
width: 70vh !important;
max-width: calc(100vw - 18px) !important;
height: fit-content !important;
overflow: hidden !important;
margin-right: auto !important;
margin-left: auto !important;
">
<a style="
width: 88px !important;
height: 31px !important;
margin-right: 0.2rem !important;
image-rendering: pixelated !important;
image-resolution: from-image !important;
image-orientation: from-image !important;
" href="https://i0ur.ing/"><img loading="eager" alt="iouring" src="iouring.png" /></a>
<a style="
width: 88px !important;
height: 31px !important;
margin-right: 0.2rem !important;
image-rendering: pixelated !important;
image-resolution: from-image !important;
image-orientation: from-image !important;
" href="https://webassembly.org/"><img loading="eager" alt="wasm" src="wasm.png" /></a>
<a style="
width: 88px !important;
height: 31px !important;
margin-right: 0.2rem !important;
image-rendering: pixelated !important;
image-resolution: from-image !important;
image-orientation: from-image !important;
" href="https://beta.servfail.network/"><img loading="eager" alt="servfail" src="servfail.png" /></a>
<a style="
width: 88px !important;
height: 31px !important;
margin-right: 0.2rem !important;
image-rendering: pixelated !important;
image-resolution: from-image !important;
image-orientation: from-image !important;
" href="https://www.dataforest.net/en/"><img loading="eager" alt="dataforest" src="dataforest.png" /></a>
<a style="
width: 88px !important;
height: 31px !important;
margin-right: 0.2rem !important;
image-rendering: pixelated !important;
image-resolution: from-image !important;
image-orientation: from-image !important;
" href="https://fedoraproject.org/"><img loading="eager" alt="fedora" src="fedora.png" /></a>
<a style="
width: 88px !important;
height: 31px !important;
margin-right: 0.2rem !important;
image-rendering: pixelated !important;
image-resolution: from-image !important;
image-orientation: from-image !important;
" href="https://artixlinux.org/"><img loading="eager" alt="artix" src="artix.png" /></a>
<a style="
width: 88px !important;
height: 31px !important;
margin-right: 0.2rem !important;
image-rendering: pixelated !important;
image-resolution: from-image !important;
image-orientation: from-image !important;
" href="https://nixos.org/"><img loading="eager" alt="nixos" src="nixos.png" /></a>
<a style="
width: 88px !important;
height: 31px !important;
margin-right: 0.2rem !important;
image-rendering: pixelated !important;
image-resolution: from-image !important;
image-orientation: from-image !important;
" href="https://chimera-linux.org/"><img loading="eager" alt="void" src="chimera.png" /></a>
<a style="
width: 88px !important;
height: 31px !important;
margin-right: 0.2rem !important;
image-rendering: pixelated !important;
image-resolution: from-image !important;
image-orientation: from-image !important;
" href="https://voidlinux.org/"><img loading="eager" alt="void" src="void.png" /></a>
<a style="
width: 88px !important;
height: 31px !important;
margin-right: 0.2rem !important;
image-rendering: pixelated !important;
image-resolution: from-image !important;
image-orientation: from-image !important;
" href="https://www.rust-lang.org/"><img loading="eager" alt="rust" src="rust.png" /></a>
<a style="
width: 88px !important;
height: 31px !important;
margin-right: 0.2rem !important;
image-rendering: pixelated !important;
image-resolution: from-image !important;
image-orientation: from-image !important;
" href="https://dotnet.microsoft.com/en-us/"><img loading="eager" alt="csharp" src="csharp.png" /></a>
<a style="
width: 88px !important;
height: 31px !important;
margin-right: 0.2rem !important;
image-rendering: pixelated !important;
image-resolution: from-image !important;
image-orientation: from-image !important;
" href="https://kotlinlang.org/"><img loading="eager" alt="kotlin" src="kotlin.png" /></a>
<a style="
width: 88px !important;
height: 31px !important;
margin-right: 0.2rem !important;
image-rendering: pixelated !important;
image-resolution: from-image !important;
image-orientation: from-image !important;
" href="https://www.java.com/"><img loading="eager" alt="java" src="java.png" /></a>
<a style="
width: 88px !important;
height: 31px !important;
image-rendering: pixelated !important;
image-resolution: from-image !important;
image-orientation: from-image !important;
"><img loading="eager" alt="aqueer" src="aqueer.png" /></a>
</div>
}
}
fn view_canvas(&self) {
let canvas = self.node_ref.cast::<HtmlCanvasElement>().unwrap();
let gl = canvas
.get_context("webgl2")
.unwrap_or(canvas.get_context("webgl").unwrap())
.unwrap()
.dyn_into::<GL2>()
.unwrap();
let vertex_buffer = gl.create_buffer().unwrap();
gl.bind_buffer(GL2::ARRAY_BUFFER, Some(&vertex_buffer));
gl.buffer_data_with_array_buffer_view(
GL2::ARRAY_BUFFER,
&Float32Array::from(
vec![
-1.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0,
]
.as_slice(),
),
GL2::STATIC_DRAW,
);
let color_buffer = gl.create_buffer().unwrap();
gl.bind_buffer(GL2::ARRAY_BUFFER, Some(&color_buffer));
gl.buffer_data_with_array_buffer_view(
GL2::ARRAY_BUFFER,
&Float32Array::from(
vec![
1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0,
]
.as_slice(),
),
GL2::STATIC_DRAW,
);
let element_buffer = gl.create_buffer().unwrap();
gl.bind_buffer(GL2::ELEMENT_ARRAY_BUFFER, Some(&element_buffer));
let element_indices = vec![3, 2, 1, 3, 1, 0];
gl.buffer_data_with_array_buffer_view(
GL2::ELEMENT_ARRAY_BUFFER,
&js_sys::Uint16Array::from(element_indices.as_slice()),
GL2::STATIC_DRAW,
);
let vertex_shader_code = include_str!("../public/shaders/es3/bg.vert");
// info!("{}", vertex_shader_code);
let vertex_shader = gl.create_shader(GL2::VERTEX_SHADER).unwrap();
gl.shader_source(&vertex_shader, vertex_shader_code);
gl.compile_shader(&vertex_shader);
let vertex_shader_log = gl.get_shader_info_log(&vertex_shader);
if !vertex_shader_log.as_ref().unwrap().is_empty() {
info!("{}", vertex_shader_log.as_ref().unwrap());
}
let fragment_shader_code = include_str!("../public/shaders/es3/bg.frag");
let fragment_shader = gl.create_shader(GL2::FRAGMENT_SHADER).unwrap();
gl.shader_source(&fragment_shader, fragment_shader_code);
gl.compile_shader(&fragment_shader);
let fragment_shader_log = gl.get_shader_info_log(&fragment_shader);
if !fragment_shader_log.as_ref().unwrap().is_empty() {
info!("{}", vertex_shader_log.as_ref().unwrap());
}
let shader_program = gl.create_program().unwrap();
gl.attach_shader(&shader_program, &vertex_shader);
gl.attach_shader(&shader_program, &fragment_shader);
gl.link_program(&shader_program);
if !gl
.get_program_parameter(&shader_program, GL2::LINK_STATUS)
.as_bool()
.unwrap()
{
warn!("shader couldn't be linked!");
}
gl.use_program(Some(&shader_program));
let position = gl.get_attrib_location(&shader_program, "position") as u32;
gl.enable_vertex_attrib_array(position);
gl.bind_buffer(GL2::ARRAY_BUFFER, Some(&vertex_buffer));
gl.vertex_attrib_pointer_with_i32(position, 3, GL2::FLOAT, false, 0, 0);
let color = gl.get_attrib_location(&shader_program, "color") as u32;
gl.enable_vertex_attrib_array(color);
gl.bind_buffer(GL2::ARRAY_BUFFER, Some(&color_buffer));
gl.vertex_attrib_pointer_with_i32(color, 4, GL2::FLOAT, false, 0, 0);
gl.bind_buffer(GL2::ELEMENT_ARRAY_BUFFER, Some(&element_buffer));
gl.enable(GL2::DEPTH_TEST);
gl.clear_color(0.0, 0.0, 0.0, 1.0);
gl.clear(GL2::COLOR_BUFFER_BIT | GL2::DEPTH_BUFFER_BIT);
let default_matrix = vec![
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
];
let unim_matrix = gl.get_uniform_location(&shader_program, "model");
let unim_time = gl.get_uniform_location(&shader_program, "time");
let unim_res = gl.get_uniform_location(&shader_program, "resolution");
let unim_overlay_time = gl.get_uniform_location(&shader_program, "layer_snow");
let mut timestamp = 0.0;
gl.uniform1f(unim_time.as_ref(), timestamp / 2000.0);
gl.draw_elements_with_i32(
GL2::TRIANGLES,
element_indices.len() as i32,
GL2::UNSIGNED_SHORT,
0,
);
fn request_animation_frame(f: &Closure<dyn FnMut()>) {
window()
.unwrap()
.request_animation_frame(f.as_ref().unchecked_ref())
.expect("should register `requestAnimationFrame` OK");
}
let cb = Arc::new(RefCell::new(None));
*cb.borrow_mut() = Some(Closure::wrap(Box::new({
let cb = cb.clone();
move || {
timestamp += 20.0;
canvas.set_width(window().unwrap().inner_width().unwrap().as_f64().unwrap() as u32);
canvas.set_height(window().unwrap().inner_height().unwrap().as_f64().unwrap() as u32);
gl.uniform_matrix4fv_with_f32_array(unim_matrix.as_ref(), false, default_matrix.as_slice());
gl.uniform1f(unim_time.as_ref(), timestamp / 2000.0);
gl.uniform2fv_with_f32_array(
unim_res.as_ref(),
vec![canvas.width() as f32, canvas.height() as f32].as_slice(),
);
gl.uniform1i(unim_overlay_time.as_ref(), 0); // TODO: ... | add is xmas check
gl.viewport(0, 0, canvas.width() as i32, canvas.height() as i32);
gl.clear(GL2::COLOR_BUFFER_BIT | GL2::DEPTH_BUFFER_BIT);
gl.draw_elements_with_i32(
GL2::TRIANGLES,
element_indices.len() as i32,
GL2::UNSIGNED_SHORT,
0,
);
request_animation_frame(cb.borrow().as_ref().unwrap());
}
}) as Box<dyn FnMut()>));
request_animation_frame(cb.borrow().as_ref().unwrap());
}
}
fn main() {
wasm_logger::init(wasm_logger::Config::new(log::Level::Trace));
yew::Renderer::<App>::new().render();
}

95
src/pages/about.rs Normal file
View File

@@ -0,0 +1,95 @@
use yew::prelude::*;
pub struct About;
impl Component for About {
type Message = ();
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, _ctx: &Context<Self>) -> Html {
html! {
<>
<div>
<img alt="insert pfp here" style="
float: left !important;
margin-right: 10px !important;
border-radius: 0.5rem !important;
" loading="eager" width="96px" height="96px" src="profile.avif" />
<div style="
display: flex !important;
flex-direction: column !important;
max-height: 96px !important;
">
<p style="
display: flex !important;
color: transparent !important;
flex-direction: row !important;
align-items: center !important;
width: fit-content !important;
background-clip: text !important;
background-image: linear-gradient(45deg, #5BCEFAFF, #F5A9B8FF, #FFFFFFFF) !important;
-webkit-background-clip: text !important;
margin: unset !important;
filter: url(post.bloom.svg#process) !important;
">
<img loading="eager" alt="ket" src="oneko.gif" />{r" lia · they/she · transfemby"}</p>
<p style="margin: unset !important;">
<p style="margin: unset !important;">{r"certified catgirl™"}</p>
<p style="margin: unset !important;">{r"software engineer"}</p>
<p style="margin: unset !important;">{r"professional yapper"}</p>
<p style="margin: unset !important;">{r"neurospicy and disabled"}</p>
</p>
</div>
<p style="
font-weight: bold !important;
font-style: italic !important;
font-size: xx-large !important;
">{r">intro_"}<br /><p style="
font-weight: normal !important;
font-style: normal !important;
font-size: medium !important;
">{r#"
name's lia or lucy. we are creating.
trying to provide a safe space in the internet.
our goal's to make this world a better ~ less trashy ~ place.
may sound naive as fuck but fuck it we ball.
audhd (certified) and probably bpd or ptsd. i'm not a psychologist tho.
interact with caution, may bite.
political view's prolly very obvious. should be enough to mention i'm social.
figure it yourself. use your brain.
"#}</p>{r">hobbies_"}<br /><p style="
font-weight: normal !important;
font-style: normal !important;
font-size: medium !important;
">{r#"
watching anime, designing something kewl, playing games, starting a new project or just yapping with friends, nearly everything's included.
i especially enjoy cooking or even baking if enough spoons are to avail.
which anime? has to be very specific, i'm really picky and judgy regarding them.
i uh - design stuff when enough creativity is there. nothing specific. maybe just a button, banner, icon or just anything.
has to be something i can somewhat imagine in my fucky-wucky head.
gaymes? gay! no uhh - i play ranging from minecraft and stardew, to puzzly metroidvanias like hollow knight and pseudoregalia, or challenging platformers like celeste, nearly anything.
i also enjoy playing rhythm games like osu! or muse dash. returning to terraria sometimes and also some old-ish games.
projects? you can find public ones in my projects tab.
"#}/*</p>{r"hobbies"}<br /><p style="
font-weight: normal !important;
font-style: normal !important;
font-size: medium !important;
">{r#"
watching anime, designing something kewl, playing games, starting a new project or just yapping/meeting up with friends, nearly everything's included.
i especially enjoy cooking or even baking if enough spoons are to avail.
which anime? has to be very specific, i'm really picky and judgy regarding them.
i uh - design stuff when enough creativity is there. nothing specific. maybe just a button, banner, icon or just anything.
has to be something i can somewhat imagine in my fucky-wucky head.
gaymes? gay! no uhh - i play ranging from minecraft and stardew, to puzzly metroidvanias like hollow knight and pseudoregalia, or challenging platformers like celeste, nearly anything.
i also enjoy playing rhythm games like osu! or muse dash. returning to terraria sometimes and also some old-ish games.
projects? you can find public ones in my projects tab.
"#}*/</p>
</p>
</div>
</>
}
}
}

71
src/pages/blog/author.rs Normal file
View File

@@ -0,0 +1,71 @@
use crate::pages::blog::content;
use crate::pages::blog::content::{BlogEntry, Post};
use yew::prelude::*;
use crate::pages::blog::authorcard::AuthorCard;
use crate::pages::blog::entrycard::EntryCard;
#[derive(Clone, Debug, Eq, PartialEq, Properties)]
pub struct Props {
pub id: u8,
}
pub struct Author {
author: content::Author,
}
impl Component for Author {
type Message = ();
type Properties = Props;
fn create(ctx: &Context<Self>) -> Self {
Self {
author: content::Author::from_id(ctx.props().id),
}
}
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
self.author = content::Author::from_id(ctx.props().id);
true
}
fn view(&self, _ctx: &Context<Self>) -> Html {
let Self { author } = self;
let id = author.id;
let cards: Vec<_> = (0..Post::POSTS.len()) // TODO: ... | add var
.filter(|&id_offset| {
Post::from_id(id_offset as u8)
.authors
.iter()
.any(|id1| *id1 == id)
})
.map(|id_offset| html! {<EntryCard id={id_offset as u8} />})
.collect();
html! {
<div style="
display: flex !important;
width: 100% !important;
flex-direction: column !important;
flex-wrap: wrap !important;
justify-content: center !important;
margin-bottom: 2rem !important;
">
<h2 style="text-align: center !important;">{ "about me" }</h2>
<div style="
display: flex !important;
width: 100% !important;
flex-direction: row !important;
flex-wrap: wrap !important;
justify-content: center !important;
margin-bottom: 1rem !important;
"><AuthorCard {id} /></div>
<h2 style="text-align: center !important;">{ "my posts" }</h2>
<div style="
display: flex !important;
width: 100% !important;
flex-direction: row !important;
flex-wrap: wrap !important;
justify-content: center !important;
">{ for cards }</div>
</div>
}
}
}

View File

@@ -0,0 +1,72 @@
use yew::prelude::*;
use yew_router::components::Link;
use crate::pages::blog::content::Author;
use crate::pages::blog::content::BlogEntry;
use crate::Route;
#[derive(Clone, Debug, PartialEq, Eq, Properties)]
pub struct PropsAuthorCard {
pub id: u8,
}
pub struct AuthorCard {
author: Author,
}
impl Component for AuthorCard {
type Message = ();
type Properties = PropsAuthorCard;
fn create(ctx: &Context<Self>) -> Self {
Self {
author: Author::from_id(ctx.props().id),
}
}
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
self.author = Author::from_id(ctx.props().id);
true
}
fn view(&self, _ctx: &Context<Self>) -> Html {
let Self { author } = self;
let keywords = author.keywords.iter().map(|keyword| html! {
<p style="margin: unset !important;">{*keyword}</p>
}).collect::<Html>();
html! {
<div style="
display: flex !important;
flex-direction: row !important;
flex-wrap: wrap !important;
background-color: #ECBEE130 !important;
border-radius: 0.5rem !important;
width: fit-content !important;
height: fit-content !important;
text-align: left !important;
margin: 1rem 1rem 0 0 !important;
">
<img style="
float: left !important;
margin-right: 0.3rem !important;
border-radius: 0.5rem !important;
" alt="insert pfp here" width="96" height="96" src={author.image_url} />
<div style="
display: flex !important;
flex-direction: column !important;
flex-wrap: wrap !important;
margin-top: 0.3rem !important;
margin-right: 0.3rem !important;
">
<h3 style="margin: unset !important;">
<Link<Route> to={Route::Author { id: author.id }}>
{ &*author.name }
</Link<Route>>
</h3>
{ keywords }
</div>
</div>
}
}
}

31
src/pages/blog/authors.rs Normal file
View File

@@ -0,0 +1,31 @@
use yew::prelude::*;
use crate::pages::blog::authorcard::AuthorCard;
use crate::pages::blog::content::{Author, BlogEntry};
pub struct Authors;
impl Component for Authors {
type Message = ();
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, _: &Context<Self>) -> Html {
html! {
<div style="
display: flex !important;
width: 100% !important;
flex-direction: row !important;
flex-wrap: wrap !important;
justify-content: center !important;
margin-top: 2rem !important;
margin-bottom: 2rem !important;
">{ for Author::AUTHORS.map(|author| {
let id = author.id;
html! { <AuthorCard {id} /> }
}) }</div>
}
}
}

299
src/pages/blog/content.rs Normal file
View File

@@ -0,0 +1,299 @@
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Author {
pub id: u8,
pub image_url: &'static str,
pub name: &'static str,
pub keywords: &'static [&'static str],
pub about: &'static str,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Post {
pub id: u8,
pub authors: &'static [u8],
pub title: &'static str,
pub utcdate: &'static str,
pub content: &'static str,
}
impl BlogEntry for Author {
fn from_entry(entry: &mut Entry) -> Self {
*Self::AUTHORS
.get(entry.id as usize)
.cloned()
.unwrap_or(&Self {
id: u8::MAX,
image_url: "",
name: "not found",
keywords: [].as_slice(),
about: "",
})
}
}
impl BlogEntry for Post {
fn from_entry(entry: &mut Entry) -> Self {
*Self::POSTS
.get(entry.id as usize)
.cloned()
.unwrap_or(&Self {
id: u8::MAX,
authors: &[u8::MAX],
title: "not found",
utcdate: "1970-01-01",
content: "",
})
}
}
pub struct Entry {
pub id: u8,
}
impl Entry {
pub fn from_id(seed: u8) -> Self {
Self { id: seed }
}
}
pub trait BlogEntry: Sized {
fn from_entry(entry: &mut Entry) -> Self;
fn from_id(id: u8) -> Self {
Self::from_entry(&mut Entry::from_id(id))
}
const AUTHORS: [&'static Author; 2] = [
&Author {
id: 0,
image_url: "profile.avif",
name: "lia",
keywords: &[
"certified catgirl™",
"software engineer",
"professional yapper",
"neurospicy and disabled",
],
about: "",
},
&Author {
id: 1,
image_url: "",
name: "mreowww",
keywords: &["meow", "mrrp", "mew"],
about: "",
},
];
const POSTS: [&'static Post; 2] = [
&Post {
id: 0,
authors: &[0],
title: "haj, world!",
utcdate: "2025-08-24",
content: r#"
### my first blogpost
haj! and welcome to my first blogpost.
this blogpost won't have that much content.
fun fact: this post becomes html from markdown :3
pretty cool, ain't it? we think so too.
it's for including images, formatted text, code and content much easier.
i also added a too overcomplicated blog backend that allows authors and posts by u8::MAX, cause why not.
won't post that much either way, so why bother with higher values.
nonetheless... have a great day, evening, whatever daytime it's for you rn and stay safe. ♡
you may take a look at my other blogposts. this one was basically practially just a test.
"#,
},
&Post {
id: 1,
authors: &[0],
title: "sweet little poison",
utcdate: "2025-08-25",
content: r#"
### > setting up iocaine with docker and caddy. ~
sup, today we're going to give ya a crash course on how to homebrew poison for ai. ^.^
we'll be utilizing docker for containerization and caddy as a reverse-proxy.
#### > preparing docker and files ~
first, we're going to create a new external network and recreate our file struct.
in this example we're going to name it "caddy". very creative, i know.
we achieve this by running following pile of commands:
```sh
docker network create caddy && \
mkdir -p ./caddy/pages/example.com ./iocaine ./socks && \
touch ./docker-compose.yml ./caddy/proxy ./iocaine/config.toml && \
touch ./caddy/pages/example.com/index.html && \
printf '<html><body><h1>hellow body consisting of blood and flesh</h1></body></html>' \
> ./caddy/pages/example.com/index.html
```
after running that we should be set and ready for the next few steps.
if you're curious, this is how it should look like right now:
```sh
Documents/example » tree .
.
├── caddy
│   ├── pages
│   │   └── example.com
│   │   └── index.html
│   └── proxy
├── docker-compose.yml
├── iocaine
│   └── config.toml
└── socks
```
#### > downloading data and configuring iocaine ~
next things next: we're going to download a robots.json, some markov-chain-stuffies and a words.txt.
for now, we will be using the default stuff from the official docs cause why not:
```sh
curl --proto '=https' --tlsv1.3 \
-L https://archive.org/download/GeorgeOrwells1984/1984_djvu.txt \
-o ./iocaine/1984.txt && \
curl --proto '=https' --tlsv1.3 \
-L https://archive.org/download/ost-english-brave_new_world_aldous_huxley/Brave_New_World_Aldous_Huxley_djvu.txt \
-o ./iocaine/brave-new-world.txt && \
curl --proto '=https' --tlsv1.2 \
-L https://git.savannah.gnu.org/cgit/miscfiles.git/plain/web2 \
-o ./iocaine/words.txt && \
curl --proto '=https' --tlsv1.3 \
-L https://github.com/ai-robots-txt/ai.robots.txt/raw/refs/heads/main/robots.json \
-o ./iocaine/robots.json
```
most importantly, we have to scrape nam-shub-of-enki from gergely's git.
here's a way to curl and extract it automatically in the correct folder:
```sh
curl --proto '=https' --tlsv1.3 \
-L https://git.madhouse-project.org/api/packages/iocaine/generic/nam-shub-of-enki/20250711.0/nam-shub-of-enki-20250711.0.tar.zst \
-o ./iocaine/nam-shub-of-enki.tar.zst && \
sudo tar -xvf ./iocaine/nam-shub-of-enki.tar.zst -C ./iocaine
```
now, we're going to prepare our iocaine/config.toml like this:
```sh
cat > ./iocaine/config.toml <<'YAML'
[server]
bind = "/run/iocaine/waow.socket"
unix_listen_access = "everybody"
[server.control]
bind = "/run/iocaine/listen.socket"
unix_listen_access = "owner"
[server.request-handler]
path = "/data"
language = "roto"
[sources]
words = "/data/words.txt"
markov = [ "/data/1984.txt", "/data/brave-new-world.txt" ]
[metrics]
enable = false
YAML
```
#### > cooking up our docker compose file for deployment ~
i have fucked around with docker a little while to make this somehow work.
here's a command which will insert needed content into our docker-compose.yml:
```sh
cat > ./docker-compose.yml <<'YAML'
services:
caddy:
image: caddy:alpine
container_name: proxy.caddy
hostname: proxy.caddy
restart: unless-stopped
cap_add:
- NET_ADMIN
ports:
- "80:80"
- "443:443"
- "443:443/udp"
networks:
- caddy
volumes:
- ./socks:/run/iocaine:ro
- ./caddy/pages:/var/www/html
- ./caddy/data:/data
- ./caddy/config:/config
- ./caddy/proxy:/etc/caddy/Caddyfile:ro
depends_on:
- iocaine
iocaine:
image: git.madhouse-project.org/iocaine/iocaine:2
container_name: proxy.iocaine
hostname: proxy.iocaine
restart: unless-stopped
ports:
- "127.0.0.1:42069:42069"
networks:
- caddy
volumes:
- ./socks:/run/iocaine:rw
- ./iocaine:/data
environment:
- IOCAINE__SERVER__BIND="/run/iocaine/waow.socket"
- IOCAINE__SERVER__UNIX_LISTEN_ACCESS="everybody"
- IOCAINE__SERVER__REQUEST_HANDLER__PATH="/data"
- IOCAINE__SERVER__REQUEST_HANDLER__LANGUAGE="roto"
- IOCAINE__SERVER__CONTROL__BIND="/run/iocaine/listen.socket"
- IOCAINE__SERVER__CONTROL__UNIX_LISTEN_ACCESS="owner"
- IOCAINE__SOURCES__WORDS="/data/words.txt"
- IOCAINE__SOURCES__MARKOV=["/data/1984.txt", "/data/brave-new-world.txt"]
- IOCAINE__METRICS__ENABLE=false
- NSOE__AI_ROBOTS_TXT_PATH=/data/robots.json
command: --config-file /data/config.toml start
networks:
caddy:
external: true
YAML
```
#### > finishing up with caddy and deploying ~
our caddyfile needs some love to work.
here's what i ripped from the official docs:
```sh
cat > ./iocaine/proxy <<'TXT'
(iocaine) {
@read method GET HEAD
@not-read not {
method GET HEAD
}
reverse_proxy @read unix//run/iocaine/waow.socket {
#reverse_proxy @read proxy.iocaine:42069 {
@fallback status 421
handle_response @fallback {
{blocks.handler}
}
}
handle @not-read {
{blocks.default}
}
}
example.com {
import iocaine {
handler {
reverse_proxy http://example:8080
}
default {
# this is the behaviour if neither a GET nor HEAD request comes in
reverse_proxy http://example:8080
}
}
}
TXT
```
now the last thing that's left is a plain and simple:
`docker compose up -d`
and we should be good to go!
i hope this post made it easier/helped you with setting up iocaine on your own server. ♡
it's good to have this set up just to fuck around with fucking parisitic generative ai stealing your things.
gatekeep your stuff from big corpos that are trying to make their business from your experience and skills.
"#,
},
];
}

55
src/pages/blog/entries.rs Normal file
View File

@@ -0,0 +1,55 @@
use yew::prelude::*;
use crate::pages::blog::content::BlogEntry;
use crate::pages::blog::content::Post;
use crate::pages::blog::entrycard::EntryCard;
pub struct Entries {}
impl Component for Entries {
type Message = ();
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self {}
}
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<h1 style="margin: unset !important;">{ "entries" }</h1>
<h2 style="
margin: unset !important;
margin-bottom: 1rem !important;
">{ "here are some things i yapped about" }</h2>
{ self.view_posts(ctx) }
</div>
}
}
}
impl Entries {
fn view_posts(&self, _ctx: &Context<Self>) -> Html {
let cards: Vec<_> = (0..Post::POSTS.len()) // TODO: ... | add var
.filter(|&id_offset| {
Post::from_id(id_offset as u8)
.authors
.iter()
.all(|id| *id != u8::MAX)
})
.map(|id_offset| html! {<EntryCard id={id_offset as u8} />})
.collect();
html! {
<div style="
display: flex !important;
flex-direction: row !important;
flex-wrap: wrap !important;
justify-content: center !important;
margin: unset !important;
margin-bottom: 1rem !important;
width: 100% !important;
">
{ for cards }
</div>
}
}
}

77
src/pages/blog/entry.rs Normal file
View File

@@ -0,0 +1,77 @@
use std::rc::Rc;
use yew::prelude::*;
use yew_markdown::Markdown;
use yew_router::prelude::*;
use crate::pages::blog::content;
use crate::pages::blog::content::{Author, BlogEntry};
use crate::Route;
#[derive(Clone, Debug, Eq, PartialEq, Properties)]
pub struct Props {
pub id: u8,
}
#[derive(PartialEq, Eq, Debug)]
pub struct PostState {
pub inner: content::Post,
}
impl Reducible for PostState {
type Action = u8;
fn reduce(self: Rc<Self>, action: u8) -> Rc<Self> {
Self {
inner: content::Post::from_id(action.into()),
}
.into()
}
}
#[function_component]
pub fn Entry(props: &Props) -> Html {
let id = props.id;
let post = use_reducer(|| PostState {
inner: content::Post::from_id(id.into()),
});
{
let post_dispatcher = post.dispatcher();
use_effect_with(id, move |id| {
post_dispatcher.dispatch(*id);
|| {}
});
}
let post = &post.inner;
html! {
<>
<h1 style="
color: #F5C2E7FF !important;
margin: unset !important;
filter: url(post.bloom.svg#process) !important;
">{ &*post.title }</h1>
<div style="flex-direction: column !important;">
<h2 style="margin: unset !important;">
{ "written by " } {
post.authors.iter().map(|id| {
let author = Author::from_id(*id);
html! {
<Link<Route> to={crate::Route::Author { id: author.id }}>
{ &*author.name }
</Link<Route>>
}
}).intersperse(html! { ", " }).collect::<Html>()
} { format!(" on {}", &post.utcdate) }
</h2>
</div>
<Markdown
src={post.content}
hard_line_breaks={true}
send_debug_info={Callback::noop()}
/>
</>
}
}

View File

@@ -0,0 +1,98 @@
use std::rc::Rc;
use yew::prelude::*;
use yew_router::components::Link;
use crate::pages::blog::content::{Author, BlogEntry, Post};
use crate::Route;
#[derive(Clone, Debug, PartialEq, Eq, Properties)]
pub struct PropsEntryCard {
pub id: u8,
}
#[derive(PartialEq, Eq, Debug)]
pub struct PostState {
inner: Post,
}
impl Reducible for PostState {
type Action = u8;
fn reduce(self: Rc<Self>, action: u8) -> Rc<Self> {
Self {
inner: Post::from_id(action.into()),
}
.into()
}
}
#[function_component]
pub fn EntryCard(props: &PropsEntryCard) -> Html {
let id = props.id;
let post = use_reducer_eq(|| PostState {
inner: Post::from_id(id.into()),
});
{
let post_dispatcher = post.dispatcher();
use_effect_with(id, move |id| {
post_dispatcher.dispatch(*id);
|| {}
});
}
let post = &post.inner;
html! {
<div style="
display: flex !important;
flex-direction: column !important;
flex-wrap: wrap !important;
background-color: #ECBEE130 !important;
border-radius: 0.5rem !important;
width: fit-content !important;
height: fit-content !important;
text-align: left !important;
margin: 1rem 1rem 0 0 !important;
">
<Link<Route> to={Route::Entry { id: post.id }}>
<h2 style="
margin: unset !important;
margin-top: 0.3rem !important;
margin-left: 0.3rem !important;
margin-right: 0.3rem !important;
">{ &*post.title }</h2>
</Link<Route>>
<h3 style="
margin: unset !important;
margin-left: 0.3rem !important;
margin-right: 0.3rem !important;
">
{ format!("published {}", post.utcdate) }
</h3>
<div style="flex-direction: column !important;">
<h3 style="
margin: unset !important;
margin-left: 0.3rem !important;
margin-right: 0.3rem !important;
margin-bottom: 0.3rem !important;
">
{ "written by " }
{
post.authors.iter().map(|id| {
let author = Author::from_id(*id);
html! {
<Link<Route> to={crate::Route::Author { id: author.id }}>
{ &*author.name }
</Link<Route>>
}
}).intersperse(html! { ", " }).collect::<Html>()
}
</h3>
</div>
</div>
}
}

7
src/pages/blog/mod.rs Normal file
View File

@@ -0,0 +1,7 @@
pub mod author;
pub mod authorcard;
pub mod authors;
pub mod content;
pub mod entries;
pub mod entry;
pub mod entrycard;

138
src/pages/findme.rs Normal file
View File

@@ -0,0 +1,138 @@
use web_sys::window;
use yew::prelude::*;
pub struct FindMe;
impl Component for FindMe {
type Message = ();
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, _ctx: &Context<Self>) -> Html {
let clipboard = window().expect("meow").navigator().clipboard();
let handle_copy_matrix = {
let clipboard = clipboard.clone();
Callback::from(move |_| {
let _ = clipboard.write_text(&*"@iouring:hi.stellaris.fyi".to_string());
})
};
let handle_copy_telegram = {
let clipboard = clipboard.clone();
Callback::from(move |_| {
let _ = clipboard.write_text(&*"@luc1ell3".to_string());
})
};
let handle_copy_discord = {
let clipboard = clipboard.clone();
Callback::from(move |_| {
let _ = clipboard.write_text(&*"@donotusedisc0rdkthxbye".to_string());
})
};
html! {
<div style="
display: flex !important;
flex-direction: row !mportant;
flex-wrap: wrap !important;
justify-content: center !important;
width: 100% !important;
">
<div style="
width: fit-content !important;
height: fit-content !important;
margin: 1rem 1rem 1rem 1rem !important;
">
<p style="
font-weight: bold !important;
font-style: normal !important;
font-size: xx-large !important;
text-align: center !important;
margin: unset !important;
">{r"follow us"}</p>
<p style="
font-weight: normal !important;
font-style: normal !important;
font-size: large !important;
">{" × "}<a style="
color: #F5C2E7FF !important;
" href="https://ice.stellaris.fyi/@iouring">{r"fediverse"}</a><br />
{" × "}<a style="
color: #B4BEFEFF !important;
" href="https://bsky.app/profile/i0ur.ing">{r"bluesky"}</a><br />
</p>
</div>
<div style="
width: fit-content !important;
height: fit-content !important;
margin: 1rem 1rem 1rem 1rem !important;
">
<p style="
font-weight: bold !important;
font-style: normal !important;
font-size: xx-large !important;
text-align: center !important;
margin: unset !important;
">{r"write us"}</p>
<p style="
font-weight: normal !important;
font-style: normal !important;
font-size: large !important;
">
{"× "}
<button style="
color: #94E2D5FF !important;
font-size: large !important;
" onclick={handle_copy_matrix}>{r"matrix"}</button>
<br />
{"× "}
<button style="
color: #B4BEFEFF !important;
font-size: large !important;
" onclick={handle_copy_telegram}>{r"telegr"}</button>
<br />
{"× "}
<button style="
color: #F38BA8FF !important;
font-size: large !important;
" onclick={handle_copy_discord}>{r"ewcord"}</button>
</p>
</div>
<div style="
width: fit-content !important;
height: fit-content !important;
margin: 1rem 1rem 1rem 1rem !important;
">
<p style="
font-weight: bold !important;
font-style: normal !important;
font-size: xx-large !important;
text-align: center !important;
margin: unset !important;
">{r"git gud"}</p>
<p style="
font-weight: normal !important;
font-style: normal !important;
font-size: large !important;
">{" × "}<a style="
color: #F5C2E7FF !important;
" href="https://git.celesteflare.cc/i0uring">{r"my own!!!"}</a><br />
{" × "}<a style="
color: #CBA6F7FF !important;
" href="https://git.gay/luciel">{r"the gay one"}</a><br />
{" × "}<a style="
color: #B4BEFEFF !important;
" href="https://git.rimuru.club/i0uring">{r"git of fren"}</a><br />
{" × "}<a style="
color: #89B4FAFF !important;
" href="https://codeberg.org/i0uring">{r"a mountain what"}</a>
</p>
</div>
</div>
}
}
}

6
src/pages/mod.rs Normal file
View File

@@ -0,0 +1,6 @@
pub mod about;
pub mod blog;
pub mod findme;
pub mod projects;
pub mod not_found;

31
src/pages/not_found.rs Normal file
View File

@@ -0,0 +1,31 @@
use yew::prelude::*;
pub struct PageNotFound;
impl Component for PageNotFound {
type Message = ();
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, _ctx: &Context<Self>) -> Html {
html! {
<div style="
display: flex !important;
text-align: center !important;
flex-direction: row !important;
align-items: center !important;
flex-wrap: wrap !important;
justify-content: center !important;
">
<img loading="eager" alt="huh" style="
width: 96px !important;
height: 96px !important;
" width="96" height="96" src="404.png" />
<h1>{ "what" }</h1>
</div>
}
}
}

View File

@@ -0,0 +1,51 @@
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Project {
pub url: &'static str,
pub name: &'static str,
pub desc: &'static str,
}
impl ProjectEntry for Project {}
pub trait ProjectEntry: Sized {
const PROJECTS_ONGOING: [&'static Project; 3] = [
&Project {
url: "https://git.celesteflare.cc/stellaris/mod_headsup",
name: "headsup mod",
desc: "extensible hud mc mod",
},
&Project {
url: "https://git.celesteflare.cc/i0uring/app_catnip",
name: "catnip",
desc: "all-rounder ide in the making",
},
&Project {
url: "https://git.celesteflare.cc/i0uring/app_nekochat",
name: "neko chat",
desc: "my planned matrix client",
},
];
const PROJECTS_FINISHED: [&'static Project; 4] = [
&Project {
url: "https://git.celesteflare.cc/i0uring/lib_tinyevents",
name: "tiny events",
desc: "a java 21+ event-sys",
},
&Project {
url: "https://git.celesteflare.cc/i0uring/dotfiles",
name: "dotfiles",
desc: "personal set of configurations",
},
&Project {
url: "https://git.celesteflare.cc/i0uring/lib_swingify",
name: "tiny events",
desc: "simplifies java swing window creation",
},
&Project {
url: "https://git.celesteflare.cc/i0uring/dotfiles",
name: "modern netty",
desc: "adds experimental netty handlers to mc",
},
];
}

View File

@@ -0,0 +1,3 @@
pub mod content;
pub mod projectcard;
pub mod projects;

View File

@@ -0,0 +1,54 @@
use yew::prelude::*;
use crate::pages::projects::content::Project;
#[derive(Clone, Debug, PartialEq, Eq, Properties)]
pub struct PropsAuthorCard {
pub project: Project,
}
pub struct ProjectCard {
project: Project,
}
impl Component for ProjectCard {
type Message = ();
type Properties = PropsAuthorCard;
fn create(ctx: &Context<Self>) -> Self {
Self {
project: ctx.props().project,
}
}
fn view(&self, _ctx: &Context<Self>) -> Html {
let Self { project } = self;
html! {
<div style="
display: flex !important;
flex-direction: row !important;
flex-wrap: wrap !important;
background-color: #ECBEE130 !important;
border-radius: 0.5rem !important;
width: fit-content !important;
height: fit-content !important;
text-align: left !important;
margin: 1rem 1rem 0 0 !important;
">
<div style="
display: flex !important;
flex-direction: column !important;
flex-wrap: wrap !important;
margin: 0.3rem 0.3rem 0.3rem 0.3rem !important;
">
<h2 style="margin: unset !important;">
<a href={project.url}>{ &*project.name }</a>
</h2>
<h3 style="margin: unset !important;">
{ &*project.desc }
</h3>
</div>
</div>
}
}
}

View File

@@ -0,0 +1,54 @@
use crate::pages::projects::content::{Project, ProjectEntry};
use crate::pages::projects::projectcard::ProjectCard;
use yew::prelude::*;
pub struct Projects;
impl Component for Projects {
type Message = ();
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, _ctx: &Context<Self>) -> Html {
let projects_ongoing = Project::PROJECTS_ONGOING.map(|project| {
let project = *project;
html! { <ProjectCard {project} /> }
});
let projects_finished = Project::PROJECTS_FINISHED.map(|project| {
let project = *project;
html! { <ProjectCard {project} /> }
});
html! {
<div style="
display: flex !important;
width: 100% !important;
flex-direction: column !important;
flex-wrap: wrap !important;
justify-content: center !important;
margin-bottom: 2rem !important;
">
<h2 style="text-align: center !important;">{ "ongoing projects" }</h2>
<div style="
display: flex !important;
width: 100% !important;
flex-direction: row !important;
flex-wrap: wrap !important;
justify-content: flex-start !important;
margin-bottom: 1rem !important;
">{ for projects_ongoing }</div>
<h2 style="text-align: center !important;">{ "finished projects" }</h2>
<div style="
display: flex !important;
width: 100% !important;
flex-direction: row !important;
flex-wrap: wrap !important;
justify-content: flex-start !important;
">{ for projects_finished }</div>
</div>
}
}
}

Binary file not shown.