Compare commits

...

2 commits

Author SHA1 Message Date
liquidex f192ac2a8e thin down the JavaScript that scrolls to branches
_lots_ of early treehouse stuff to fix there
2024-11-23 22:43:59 +01:00
liquidex 645ae598f2 make it impossible to fill up an unbounded amount of memory with caches
yeah storing Nones there is probably not a great idea isn't it
2024-11-23 22:34:03 +01:00
5 changed files with 34 additions and 126 deletions

View file

@ -81,7 +81,7 @@ pub struct DirEntry {
pub path: VPathBuf, pub path: VPathBuf,
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct ImageSize { pub struct ImageSize {
pub width: u32, pub width: u32,
pub height: u32, pub height: u32,

View file

@ -11,7 +11,7 @@ use super::{walk_dir_rec, Dir, DirEntry, EditPath, ImageSize, VPath, VPathBuf};
pub struct ContentCache<T> { pub struct ContentCache<T> {
inner: T, inner: T,
cache: DashMap<VPathBuf, Option<Vec<u8>>>, cache: DashMap<VPathBuf, Vec<u8>>,
} }
impl<T> ContentCache<T> { impl<T> ContentCache<T> {
@ -48,10 +48,13 @@ where
} }
fn content(&self, path: &VPath) -> Option<Vec<u8>> { fn content(&self, path: &VPath) -> Option<Vec<u8>> {
self.cache self.cache.get(path).map(|x| x.clone()).or_else(|| {
.entry(path.to_owned()) let content = self.inner.content(path);
.or_insert_with(|| self.inner.content(path)) if let Some(content) = &content {
.clone() self.cache.insert(path.to_owned(), content.clone());
}
content
})
} }
fn content_version(&self, path: &VPath) -> Option<String> { fn content_version(&self, path: &VPath) -> Option<String> {

View file

@ -6,7 +6,7 @@ use super::{Dir, DirEntry, EditPath, ImageSize, VPath, VPathBuf};
pub struct Blake3ContentVersionCache<T> { pub struct Blake3ContentVersionCache<T> {
inner: T, inner: T,
cache: DashMap<VPathBuf, Option<String>>, cache: DashMap<VPathBuf, String>,
} }
impl<T> Blake3ContentVersionCache<T> { impl<T> Blake3ContentVersionCache<T> {
@ -31,15 +31,16 @@ where
} }
fn content_version(&self, path: &VPath) -> Option<String> { fn content_version(&self, path: &VPath) -> Option<String> {
self.cache self.cache.get(path).map(|x| x.clone()).or_else(|| {
.entry(path.to_owned()) let version = self.inner.content(path).map(|content| {
.or_insert_with(|| {
self.content(path).map(|content| {
let hash = blake3::hash(&content).to_hex(); let hash = blake3::hash(&content).to_hex();
format!("b3-{}", &hash[0..8]) format!("b3-{}", &hash[0..8])
});
if let Some(version) = &version {
self.cache.insert(path.to_owned(), version.clone());
}
version
}) })
})
.clone()
} }
fn image_size(&self, path: &VPath) -> Option<ImageSize> { fn image_size(&self, path: &VPath) -> Option<ImageSize> {

View file

@ -10,7 +10,7 @@ use super::{Dir, DirEntry, EditPath, ImageSize, VPath, VPathBuf};
pub struct ImageSizeCache<T> { pub struct ImageSizeCache<T> {
inner: T, inner: T,
cache: DashMap<VPathBuf, Option<ImageSize>>, cache: DashMap<VPathBuf, ImageSize>,
} }
impl<T> ImageSizeCache<T> { impl<T> ImageSizeCache<T> {
@ -59,15 +59,17 @@ where
} }
fn image_size(&self, path: &VPath) -> Option<ImageSize> { fn image_size(&self, path: &VPath) -> Option<ImageSize> {
self.cache self.cache.get(path).map(|x| *x).or_else(|| {
.entry(path.to_owned()) let image_size = self
.or_insert_with(|| { .compute_image_size(path)
self.compute_image_size(path)
.inspect_err(|err| warn!("compute_image_size({path}) failed: {err:?}")) .inspect_err(|err| warn!("compute_image_size({path}) failed: {err:?}"))
.ok() .ok()
.flatten() .flatten();
if let Some(image_size) = image_size {
self.cache.insert(path.to_owned(), image_size);
}
image_size
}) })
.clone()
} }
fn anchor(&self, path: &VPath) -> Option<VPathBuf> { fn anchor(&self, path: &VPath) -> Option<VPathBuf> {

View file

@ -147,20 +147,6 @@ addSpell("b-linked", LinkedBranch);
/* Fragment navigation */ /* Fragment navigation */
let rehashing = false;
function rehash() {
// https://www.youtube.com/watch?v=Tv1SYqLllKI
if (!rehashing) {
rehashing = true;
let hash = window.location.hash;
if (hash.length > 0) {
window.location.hash = "";
window.location.hash = hash;
}
rehashing = false;
}
}
function expandDetailsRecursively(element) { function expandDetailsRecursively(element) {
while (element && element.tagName != "MAIN") { while (element && element.tagName != "MAIN") {
if (element.tagName == "DETAILS") { if (element.tagName == "DETAILS") {
@ -170,69 +156,6 @@ function expandDetailsRecursively(element) {
} }
} }
function navigateToPage(page) {
window.location.pathname = `${page}`;
}
async function navigateToBranch(fragment) {
if (fragment.length == 0) return;
let { navigationMap } = await import("/navmap.js");
let element = document.getElementById(fragment);
if (element !== null) {
// If the element is already loaded on the page, we're good.
expandDetailsRecursively(element);
rehash();
// NOTE(2024-03-31): Only scroll into view in the loaded case.
// This case happens very often with `/b`-navigated branches, and those serve the specific
// page that contains the provided branch.
// Hash-links are not used anymore so upgrading the second case is unnecessary.
// They were a thing before I linked to the treehouse very often so no need to update.
element.scrollIntoView();
} else {
// The element is not loaded, we need to load the tree that has it.
let parts = fragment.split(":");
if (parts.length >= 2) {
let [page, _id] = parts;
let fullPath = navigationMap[page];
if (Array.isArray(fullPath)) {
// TODO: This logic will probably need to be upgraded at some point to support
// navigation maps with roots other than index. Currently though only index is
// generated so that doesn't matter.
let [_root, ...path] = fullPath;
if (path !== undefined) {
let isNotAtIndexHtml =
window.location.pathname != "" &&
window.location.pathname != "/" &&
window.location.pathname != "/index.html";
let lastBranch = null;
for (let linked of path) {
let branch = LinkedBranch.byLink.get(linked);
if (isNotAtIndexHtml && branch === undefined) {
navigateToPage("index");
return;
}
await branch.loadTree("navigateToBranch");
lastBranch = branch;
}
if (lastBranch != null) {
expandDetailsRecursively(lastBranch.details);
}
rehash();
}
} else {
// In case the navigation map does not contain the given page, we can try
// redirecting the user to a concrete page on the site.
navigateToPage(page);
}
}
}
}
function getCurrentlyHighlightedBranch() { function getCurrentlyHighlightedBranch() {
if (window.location.pathname == "/b" && window.location.search.length > 0) { if (window.location.pathname == "/b" && window.location.search.length > 0) {
let shortID = window.location.search.substring(1); let shortID = window.location.search.substring(1);
@ -242,33 +165,12 @@ function getCurrentlyHighlightedBranch() {
} }
} }
async function navigateToCurrentBranch() {
await navigateToBranch(getCurrentlyHighlightedBranch());
}
// When you click on a link, and the destination is within a <details> that is not expanded,
// expand the <details> recursively.
window.addEventListener("popstate", navigateToCurrentBranch);
addEventListener("DOMContentLoaded", navigateToCurrentBranch);
// When you enter the website through a link someone sent you, it would be nice if the linked branch
// got expanded by default.
async function expandLinkedBranch() {
let currentlyHighlightedBranch = getCurrentlyHighlightedBranch();
if (currentlyHighlightedBranch.length > 0) {
let linkedBranch = document.getElementById(currentlyHighlightedBranch);
if (linkedBranch.children.length > 0 && linkedBranch.children[0].tagName == "DETAILS") {
expandDetailsRecursively(linkedBranch.children[0]);
}
}
}
addEventListener("DOMContentLoaded", expandLinkedBranch);
async function highlightCurrentBranch() { async function highlightCurrentBranch() {
let branch = document.getElementById(getCurrentlyHighlightedBranch()); let branch = document.getElementById(getCurrentlyHighlightedBranch());
if (branch != null) { if (branch != null) {
expandDetailsRecursively(branch);
branch.classList.add("target"); branch.classList.add("target");
branch.scrollIntoView();
} }
} }