Compare commits
No commits in common. "f192ac2a8ec18ac05164d6cf8828d03c0b89ace1" and "c87662419a8805831aa65fd6e113debc5e53e734" have entirely different histories.
f192ac2a8e
...
c87662419a
|
@ -81,7 +81,7 @@ pub struct DirEntry {
|
|||
pub path: VPathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ImageSize {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
|
|
|
@ -11,7 +11,7 @@ use super::{walk_dir_rec, Dir, DirEntry, EditPath, ImageSize, VPath, VPathBuf};
|
|||
|
||||
pub struct ContentCache<T> {
|
||||
inner: T,
|
||||
cache: DashMap<VPathBuf, Vec<u8>>,
|
||||
cache: DashMap<VPathBuf, Option<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl<T> ContentCache<T> {
|
||||
|
@ -48,13 +48,10 @@ where
|
|||
}
|
||||
|
||||
fn content(&self, path: &VPath) -> Option<Vec<u8>> {
|
||||
self.cache.get(path).map(|x| x.clone()).or_else(|| {
|
||||
let content = self.inner.content(path);
|
||||
if let Some(content) = &content {
|
||||
self.cache.insert(path.to_owned(), content.clone());
|
||||
}
|
||||
content
|
||||
})
|
||||
self.cache
|
||||
.entry(path.to_owned())
|
||||
.or_insert_with(|| self.inner.content(path))
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn content_version(&self, path: &VPath) -> Option<String> {
|
||||
|
|
|
@ -6,7 +6,7 @@ use super::{Dir, DirEntry, EditPath, ImageSize, VPath, VPathBuf};
|
|||
|
||||
pub struct Blake3ContentVersionCache<T> {
|
||||
inner: T,
|
||||
cache: DashMap<VPathBuf, String>,
|
||||
cache: DashMap<VPathBuf, Option<String>>,
|
||||
}
|
||||
|
||||
impl<T> Blake3ContentVersionCache<T> {
|
||||
|
@ -31,16 +31,15 @@ where
|
|||
}
|
||||
|
||||
fn content_version(&self, path: &VPath) -> Option<String> {
|
||||
self.cache.get(path).map(|x| x.clone()).or_else(|| {
|
||||
let version = self.inner.content(path).map(|content| {
|
||||
self.cache
|
||||
.entry(path.to_owned())
|
||||
.or_insert_with(|| {
|
||||
self.content(path).map(|content| {
|
||||
let hash = blake3::hash(&content).to_hex();
|
||||
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> {
|
||||
|
|
|
@ -10,7 +10,7 @@ use super::{Dir, DirEntry, EditPath, ImageSize, VPath, VPathBuf};
|
|||
|
||||
pub struct ImageSizeCache<T> {
|
||||
inner: T,
|
||||
cache: DashMap<VPathBuf, ImageSize>,
|
||||
cache: DashMap<VPathBuf, Option<ImageSize>>,
|
||||
}
|
||||
|
||||
impl<T> ImageSizeCache<T> {
|
||||
|
@ -59,17 +59,15 @@ where
|
|||
}
|
||||
|
||||
fn image_size(&self, path: &VPath) -> Option<ImageSize> {
|
||||
self.cache.get(path).map(|x| *x).or_else(|| {
|
||||
let image_size = self
|
||||
.compute_image_size(path)
|
||||
self.cache
|
||||
.entry(path.to_owned())
|
||||
.or_insert_with(|| {
|
||||
self.compute_image_size(path)
|
||||
.inspect_err(|err| warn!("compute_image_size({path}) failed: {err:?}"))
|
||||
.ok()
|
||||
.flatten();
|
||||
if let Some(image_size) = image_size {
|
||||
self.cache.insert(path.to_owned(), image_size);
|
||||
}
|
||||
image_size
|
||||
.flatten()
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn anchor(&self, path: &VPath) -> Option<VPathBuf> {
|
||||
|
|
|
@ -147,6 +147,20 @@ addSpell("b-linked", LinkedBranch);
|
|||
|
||||
/* 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) {
|
||||
while (element && element.tagName != "MAIN") {
|
||||
if (element.tagName == "DETAILS") {
|
||||
|
@ -156,6 +170,69 @@ 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() {
|
||||
if (window.location.pathname == "/b" && window.location.search.length > 0) {
|
||||
let shortID = window.location.search.substring(1);
|
||||
|
@ -165,12 +242,33 @@ 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() {
|
||||
let branch = document.getElementById(getCurrentlyHighlightedBranch());
|
||||
if (branch != null) {
|
||||
expandDetailsRecursively(branch);
|
||||
branch.classList.add("target");
|
||||
branch.scrollIntoView();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue