squash and nuke dev

This commit is contained in:
outfoxxed 2026-02-18 02:40:40 -08:00
parent 1f1444eb65
commit e42985d6e6
Signed by: outfoxxed
GPG key ID: 4C88A185FB89301E
93 changed files with 33825 additions and 7830 deletions

View file

@ -0,0 +1,69 @@
export function initAnimations() {
const observerOptions = {
root: null,
rootMargin: "0px",
threshold: 0.1,
};
const observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
observer.unobserve(entry.target);
}
});
},
observerOptions
);
const animatedElements = document.querySelectorAll(
".animate-fade-up, .stagger-parent"
);
animatedElements.forEach(el => observer.observe(el));
}
export function initTOCHighlighting() {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
const heading = entry.target.querySelector(
"h1, h2, h3, h4, h5, h6"
);
if (heading) {
const id = heading.id;
const desktopElement = document.querySelector(
`.toc-wrapper li a[href="#${id}"]`
);
const mobileElement = document.querySelector(
`.toc-wrapper-mobile li a[href="#${id}"]`
);
if (entry.isIntersecting) {
desktopElement?.parentElement?.classList.add("active");
mobileElement?.parentElement?.classList.add("active");
} else {
desktopElement?.parentElement?.classList.remove(
"active"
);
mobileElement?.parentElement?.classList.remove(
"active"
);
}
}
});
});
document
.querySelectorAll("section[data-heading-rank]")
.forEach(section => {
observer.observe(section);
});
}
// auto-init on DOMContentLoaded
if (typeof document !== "undefined") {
document.addEventListener("DOMContentLoaded", () => {
initAnimations();
initTOCHighlighting();
});
}

View file

@ -0,0 +1,260 @@
document.addEventListener("DOMContentLoaded", () => {
const container = document.querySelector(
".marquee"
) as HTMLDivElement;
const scroller = document.querySelector(
".marquee-content"
) as HTMLDivElement;
const btnLeft = document.getElementById("marquee-scroll-left");
const btnRight = document.getElementById(
"marquee-scroll-right"
);
if (!container || !scroller) return;
const bufferSize = 2;
let items = Array.from(
scroller.querySelectorAll(".marquee-item")
) as HTMLDivElement[];
const originalCount = items.length;
if (originalCount === 0) return;
let itemWidth = 0;
let sequenceWidth = 0;
let targetScrollX = 0;
let currentScrollX = 0;
let isAnimating = false;
let isDown = false;
let lastTouchX = 0;
let touchVelocity = 0;
let lastTouchTime = 0;
const smoothFactor = 0.1;
const snapThreshold = 0.1;
// setup clones
const setupClones = () => {
// remove existing clones
scroller.querySelectorAll(".clone").forEach(c => c.remove());
const originals = Array.from(
scroller.querySelectorAll(".marquee-item")
) as HTMLDivElement[];
// add clones after
for (let i = 0; i < bufferSize; i++) {
originals.forEach(item => {
const clone = item.cloneNode(true) as HTMLDivElement;
clone.classList.add("clone");
scroller.appendChild(clone);
});
}
// add clones before
const beforeContainer = document.createDocumentFragment();
for (let i = 0; i < bufferSize; i++) {
originals.forEach(item => {
const clone = item.cloneNode(true) as HTMLDivElement;
clone.classList.add("clone");
beforeContainer.appendChild(clone);
});
}
scroller.insertBefore(beforeContainer, scroller.firstChild);
items = Array.from(
scroller.querySelectorAll(".marquee-item")
) as HTMLDivElement[];
};
const updateDimensions = () => {
itemWidth = container.clientWidth;
if (itemWidth === 0) return;
sequenceWidth = originalCount * itemWidth;
// standardize width
scroller.style.width = `${items.length * itemWidth}px`;
items.forEach(item => {
item.style.width = `${itemWidth}px`;
item.style.flex = `0 0 ${itemWidth}px`;
item.style.maxWidth = `${itemWidth}px`;
});
targetScrollX =
bufferSize * sequenceWidth +
(targetScrollX % sequenceWidth);
currentScrollX = targetScrollX;
scroller.style.transform = `translateX(-${currentScrollX}px)`;
};
const lerp = (start: number, end: number, factor: number) =>
start + (end - start) * factor;
const animate = () => {
if (!isDown && Math.abs(touchVelocity) < 0.1) {
// snap to nearest item if not interacting and close to one
const nearestItemScroll =
Math.round(targetScrollX / itemWidth) * itemWidth;
if (
Math.abs(targetScrollX - nearestItemScroll) <
itemWidth * 0.5
) {
targetScrollX = lerp(
targetScrollX,
nearestItemScroll,
0.1
);
}
}
currentScrollX = lerp(
currentScrollX,
targetScrollX,
smoothFactor
);
// boundary reset
if (currentScrollX > (bufferSize + 1) * sequenceWidth) {
currentScrollX -= sequenceWidth;
targetScrollX -= sequenceWidth;
} else if (
currentScrollX <
(bufferSize - 1) * sequenceWidth
) {
currentScrollX += sequenceWidth;
targetScrollX += sequenceWidth;
}
scroller.style.transform = `translateX(-${currentScrollX}px)`;
// fade in-out and scale items based on distance from center
items.forEach((item, index) => {
const itemCenter = index * itemWidth;
const distance = Math.abs(currentScrollX - itemCenter);
const progress = Math.min(distance / itemWidth, 1); // 0 at center, 1 at edge
const opacity = 1 - progress;
const scale = 1 - progress * 0.1; // scale down as it leaves
const yOffset = progress * 20; // slide down as it leaves
item.style.opacity = opacity.toString();
// NOTE: apply transform to the video container specifically
// to keep layout stable
const content = item.querySelector(
".marquee-item-content"
) as HTMLElement;
if (content) {
content.style.transform = `scale(${scale}) translateY(${yOffset}px)`;
}
});
const diff = Math.abs(targetScrollX - currentScrollX);
const interaction = isDown || Math.abs(touchVelocity) > 0.1;
if (diff > snapThreshold || interaction) {
requestAnimationFrame(animate);
} else {
isAnimating = false;
currentScrollX = targetScrollX;
scroller.style.transform = `translateX(-${currentScrollX}px)`;
}
};
const startAnimation = () => {
if (!isAnimating) {
isAnimating = true;
requestAnimationFrame(animate);
}
};
// video handling
const videos = scroller.querySelectorAll("video");
const observerOptions = {
root: container,
threshold: 0.5,
};
const videoObserver = new IntersectionObserver(entries => {
entries.forEach(entry => {
const video = entry.target as HTMLVideoElement;
if (entry.isIntersecting) {
video.play().catch(() => {}); // Handle potential autoplay blocks
} else {
video.pause();
}
});
}, observerOptions);
videos.forEach(v => {
videoObserver.observe(v);
v.addEventListener("ended", () => {
targetScrollX += itemWidth;
startAnimation();
});
});
// events
btnLeft?.addEventListener("click", () => {
targetScrollX -= itemWidth;
startAnimation();
});
btnRight?.addEventListener("click", () => {
targetScrollX += itemWidth;
startAnimation();
});
container.addEventListener(
"wheel",
e => {
e.preventDefault();
targetScrollX += e.deltaY;
startAnimation();
},
{ passive: false }
);
container.addEventListener("touchstart", e => {
isDown = true;
lastTouchX = e.touches[0].clientX;
lastTouchTime = Date.now();
touchVelocity = 0;
});
container.addEventListener("touchmove", e => {
if (!isDown) return;
const currentTouchX = e.touches[0].clientX;
const deltaX = lastTouchX - currentTouchX;
targetScrollX += deltaX * 1.5;
const now = Date.now();
const dt = now - lastTouchTime;
if (dt > 0) touchVelocity = deltaX / dt;
lastTouchX = currentTouchX;
lastTouchTime = now;
startAnimation();
});
container.addEventListener("touchend", () => {
isDown = false;
targetScrollX += touchVelocity * 100; // Momentum
touchVelocity = 0;
startAnimation();
});
window.addEventListener("resize", updateDimensions);
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
videos.forEach(v => v.pause());
}
});
// init
setupClones();
setTimeout(() => {
updateDimensions();
container.classList.add("initialized");
startAnimation();
}, 50);
});

View file

@ -0,0 +1,89 @@
interface ThemeProps {
theme: "light" | "dark";
system: "light" | "dark";
}
export const getCurrentTheme = (): ThemeProps => {
const isDarkSystem = window.matchMedia(
"(prefers-color-scheme: dark)"
).matches;
const systemTheme = isDarkSystem ? "dark" : "light";
if (typeof localStorage !== "undefined") {
if (localStorage.theme === "dark") {
return { theme: "dark", system: systemTheme };
}
if (localStorage.theme === "light") {
return { theme: "light", system: systemTheme };
}
}
return { theme: systemTheme, system: systemTheme };
};
export const updateTheme = (transition = true) => {
const theme = getCurrentTheme();
const toggle = document.getElementById(
"theme-manual-toggle"
) as HTMLInputElement;
if (transition) {
document.documentElement.classList.add("changing-theme");
document.documentElement.style.setProperty(
"--theme-transition",
"0.3s var(--ease-in-out)"
);
} else {
document.documentElement.style.removeProperty(
"--theme-transition"
);
}
if (theme.theme === "dark") {
document.documentElement.classList.add("dark");
if (toggle) toggle.checked = true;
} else {
document.documentElement.classList.remove("dark");
if (toggle) toggle.checked = false;
}
if (transition) {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
document.documentElement.classList.remove(
"changing-theme"
);
});
});
}
};
export const initTheme = () => {
const toggle = document.getElementById(
"theme-manual-toggle"
) as HTMLInputElement;
if (toggle) {
toggle.addEventListener("change", () => {
localStorage.theme = toggle.checked ? "dark" : "light";
updateTheme();
});
}
window
.matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", () => updateTheme());
window.addEventListener("storage", () => updateTheme());
// initial sync
updateTheme(false);
};
// auto-init on client
if (typeof document !== "undefined") {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initTheme);
} else {
initTheme();
}
}