File size: 3,032 Bytes
08fc97e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | // GSAP-powered interactions:
// 1. Hero fade-in on initial page load
// 2. Results card + chunk-card stagger on every HTMX swap
// 3. Citation hover/focus β glow + scale the matching chunk card and
// smooth-scroll it into view; a click locks the highlight briefly
const { gsap } = window;
const LOCK_MS = 1400;
// βββ 1. Page-load entrance ββββββββββββββββββββββββββββββββββββββββββββββββ
gsap.from("#hero", { opacity: 0, y: -16, duration: 0.8, ease: "power2.out" });
gsap.from("#search-form", { opacity: 0, y: 20, duration: 0.7, delay: 0.2, ease: "power2.out" });
// βββ 2. Results swap-in animation βββββββββββββββββββββββββββββββββββββββββ
document.body.addEventListener("htmx:afterSwap", (e) => {
if (e.detail.target.id !== "results") return;
const shell = e.detail.target.querySelector(".results-shell");
if (!shell) return;
gsap.from(shell, { opacity: 0, y: 20, duration: 0.5, ease: "power2.out" });
gsap.from(shell.querySelectorAll(".chunk-card"), {
opacity: 0, y: 14, duration: 0.45, stagger: 0.06, delay: 0.15, ease: "power2.out",
});
hookCitations(shell);
});
// βββ 3. Citation β chunk-card linking βββββββββββββββββββββββββββββββββββββ
function hookCitations(root) {
const cards = new Map();
root.querySelectorAll(".chunk-card").forEach((c) => {
cards.set(c.dataset.chunkId, c);
});
root.querySelectorAll(".citation").forEach((cite) => {
const cid = cite.dataset.chunk;
const target = cards.get(cid);
if (!target) {
// Cited ID not in retrieved set β flag as a hallucinated citation.
cite.classList.add("citation-invalid");
return;
}
let lockTimer = null;
const highlight = (lock = false) => {
gsap.to(target, { scale: 1.03, duration: 0.25, ease: "power2.out", overwrite: true });
target.classList.add("chunk-glow");
cite.classList.add("citation-active");
if (lock) {
clearTimeout(lockTimer);
lockTimer = setTimeout(() => unhighlight(), LOCK_MS);
}
};
const unhighlight = () => {
gsap.to(target, { scale: 1, duration: 0.25, ease: "power2.out", overwrite: true });
target.classList.remove("chunk-glow");
cite.classList.remove("citation-active");
};
cite.addEventListener("mouseenter", () => highlight(false));
cite.addEventListener("mouseleave", () => { if (!lockTimer) unhighlight(); });
cite.addEventListener("focus", () => highlight(false));
cite.addEventListener("blur", () => { if (!lockTimer) unhighlight(); });
cite.addEventListener("click", (ev) => {
ev.preventDefault();
highlight(true);
gsap.to(window, {
duration: 0.6,
scrollTo: { y: target, offsetY: 80 },
ease: "power2.inOut",
});
});
});
}
|