Account for remaining site changes

This commit is contained in:
Zachery Aaron Shores-Chmielewski 2026-07-24 12:51:40 +04:00
parent 0717383be9
commit 8ce7b655bc
8 changed files with 123 additions and 36 deletions

View file

@ -7,7 +7,9 @@ use crate::newton::compute;
const MAX_ITERATIONS: u32 = 32; const MAX_ITERATIONS: u32 = 32;
/// Get the canvas element and 2D context /// Get the canvas element and 2D context
fn get_canvas_and_context(canvas_id: &str) -> Result<(HtmlCanvasElement, CanvasRenderingContext2d), JsValue> { fn get_canvas_and_context(
canvas_id: &str,
) -> Result<(HtmlCanvasElement, CanvasRenderingContext2d), JsValue> {
let window = web_sys::window().ok_or("no window")?; let window = web_sys::window().ok_or("no window")?;
let document = window.document().ok_or("no document")?; let document = window.document().ok_or("no document")?;
let canvas = document let canvas = document
@ -66,7 +68,8 @@ pub fn render_fractal_animated(canvas_id: &str, time: f64, scale: u32) -> Result
// Create ImageData at reduced size // Create ImageData at reduced size
let clamped = wasm_bindgen::Clamped(&pixels[..]); let clamped = wasm_bindgen::Clamped(&pixels[..]);
let image_data = ImageData::new_with_u8_clamped_array_and_sh(clamped, width as u32, height as u32)?; let image_data =
ImageData::new_with_u8_clamped_array_and_sh(clamped, width as u32, height as u32)?;
// Scale up when drawing to canvas // Scale up when drawing to canvas
if scale > 1 { if scale > 1 {
@ -89,8 +92,10 @@ pub fn render_fractal_animated(canvas_id: &str, time: f64, scale: u32) -> Result
)?; )?;
context.draw_image_with_html_canvas_element_and_dw_and_dh( context.draw_image_with_html_canvas_element_and_dw_and_dh(
&temp_canvas, &temp_canvas,
0.0, 0.0, 0.0,
canvas_width as f64, canvas_height as f64 0.0,
canvas_width as f64,
canvas_height as f64,
)?; )?;
} else { } else {
context.put_image_data(&image_data, 0.0, 0.0)?; context.put_image_data(&image_data, 0.0, 0.0)?;

View file

@ -2,17 +2,18 @@
WEB_UI_PORT=3001 WEB_UI_PORT=3001
SSH_PORT=2223 SSH_PORT=2223
DATA_DIR=/home/deploy/forgejo/data
docker run -d \ docker run -d \
--name forgejo \ --name forgejo \
-p ${WEB_UI_PORT}:3000 \ -p 127.0.0.1:${WEB_UI_PORT}:3000 \
-p ${SSH_PORT}:22 \ -p ${SSH_PORT}:22 \
-v ~/forgejo/data:/data \ -v ${DATA_DIR}:/data \
-v /etc/timezone:/etc/timezone:ro \ -v /etc/timezone:/etc/timezone:ro \
-v /etc/localtime:/etc/localtime:ro \ -v /etc/localtime:/etc/localtime:ro \
--restart always \ --restart always \
codeberg.org/forgejo/forgejo:9 codeberg.org/forgejo/forgejo:9
echo "Forgejo started on:" echo "Forgejo started on:"
echo " Web UI: http://zachery.lol:${WEB_UI_PORT}" echo " Web UI: http://127.0.0.1:${WEB_UI_PORT} (proxied at https://zachery.lol/code/)"
echo " ssh port: ${SSH_PORT}" echo " ssh port: ${SSH_PORT}"

View file

@ -33,8 +33,8 @@ server {
listen 443 ssl; # managed by Certbot listen 443 ssl; # managed by Certbot
listen [::]:443 ssl ipv6only=on; # managed by Certbot listen [::]:443 ssl ipv6only=on; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/zachery.lol/fullchain.pem; # managed by Certbot ssl_certificate /etc/letsencrypt/live/zachery.lol-0001/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/zachery.lol/privkey.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/zachery.lol-0001/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
} }

View file

@ -117,6 +117,8 @@
} }
} }
</style> </style>
<link rel="preload" href="/routes/root/dist-strips.bin?v=1" as="fetch" crossorigin />
<script defer src="/routes/root/fractal-gl.js?v=12"></script>
</head> </head>
<body> <body>
<canvas id="fractal-canvas"></canvas> <canvas id="fractal-canvas"></canvas>
@ -135,15 +137,7 @@
</nav> </nav>
</div> </div>
<script> <!-- fractal background loaded via deferred <script> in <head> -->
requestAnimationFrame(function () {
requestAnimationFrame(function () {
var s = document.createElement('script');
s.src = '/routes/root/fractal-gl.js?v=9';
s.async = true;
document.body.appendChild(s);
});
});
</script>
</body> </body>
</html> </html>

View file

@ -279,6 +279,8 @@
} }
} }
</script> </script>
<link rel="preload" href="/routes/root/dist-strips.bin?v=1" as="fetch" crossorigin />
<script defer src="/routes/root/fractal-gl.js?v=12"></script>
</head> </head>
<body> <body>
<canvas id="fractal-canvas"></canvas> <canvas id="fractal-canvas"></canvas>
@ -323,15 +325,7 @@
<a href="/contact">back to contact</a> <a href="/contact">back to contact</a>
</nav> </nav>
<script> <!-- fractal background loaded via deferred <script> in <head> -->
requestAnimationFrame(function () {
requestAnimationFrame(function () {
var s = document.createElement('script');
s.src = '/routes/root/fractal-gl.js?v=9';
s.async = true;
document.body.appendChild(s);
});
});
</script>
</body> </body>
</html> </html>

93
tools/gen-strips.py Normal file
View file

@ -0,0 +1,93 @@
# /// script
# requires-python = ">=3.9"
# dependencies = ["pillow"]
# ///
"""
Generate routes/root/dist-strips.bin from routes/root/dist.png.
Splits the 512x512 grayscale Julia distance field into N horizontal bands,
re-encodes each band as its own WebP, and concatenates them in CENTER-OUT order
(the band the pan/zoom animation shows first) so a streaming client can upload
the visible region before the outer bands have finished downloading. WebP q90 is
~half the size of PNG with no visible loss after the color-band mapping.
File layout (little-endian):
u32 headerLen
bytes headerLen -- UTF-8 JSON: {fullW, fullH, bands:[{y,h,off,len}, ...]}
bytes payload -- concatenated band WebPs in the order listed in `bands`
(off/len are byte ranges within payload, stream order)
Re-run only when dist.png changes; the output is committed.
Usage: uv run tools/gen-strips.py
"""
import io
import json
import os
import struct
from PIL import Image
HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(HERE)
SRC = os.path.join(ROOT, "routes", "root", "dist.png")
OUT = os.path.join(ROOT, "routes", "root", "dist-strips.bin")
NBANDS = 8
WEBP_QUALITY = 90 # lossy; ~half of PNG, visually identical. Bump to 95 / use lossless if needed.
def center_out(n):
"""Band indices ordered from the middle outward: e.g. n=8 -> [4,3,5,2,6,1,7,0]."""
order = []
lo, hi = n // 2 - 1, n // 2
while lo >= 0 or hi < n:
if hi < n:
order.append(hi)
hi += 1
if lo >= 0:
order.append(lo)
lo -= 1
return order
def main():
img = Image.open(SRC).convert("L")
w, h = img.size
# Row boundaries (robust if h is not divisible by NBANDS).
bounds = [round(i * h / NBANDS) for i in range(NBANDS + 1)]
bands_by_index = []
for i in range(NBANDS):
y0, y1 = bounds[i], bounds[i + 1]
crop = img.crop((0, y0, w, y1))
buf = io.BytesIO()
crop.save(buf, format="WEBP", quality=WEBP_QUALITY, method=6)
bands_by_index.append((y0, y1 - y0, buf.getvalue()))
payload = bytearray()
meta = []
order = center_out(NBANDS)
for idx in order:
y, bh, png = bands_by_index[idx]
meta.append({"y": y, "h": bh, "off": len(payload), "len": len(png)})
payload += png
header = json.dumps(
{"fullW": w, "fullH": h, "bands": meta}, separators=(",", ":")
).encode("utf-8")
with open(OUT, "wb") as f:
f.write(struct.pack("<I", len(header)))
f.write(header)
f.write(payload)
total = 4 + len(header) + len(payload)
src_size = os.path.getsize(SRC)
print(f"{w}x{h} -> {NBANDS} bands, center-out order {order}")
print(
f"header {len(header)}B, payload {len(payload)}B, total {total}B "
f"({total / 1024:.1f} KB) vs dist.png {src_size}B ({src_size / 1024:.1f} KB)"
)
print(f"wrote {OUT}")
if __name__ == "__main__":
main()