From b8de69dceda9486d4cc4bb75738238012f58f197 Mon Sep 17 00:00:00 2001 From: Jay V Date: Thu, 3 Jul 2025 19:15:38 -0400 Subject: [PATCH] docs: fix share page scroll performance --- packages/web/src/components/Share.tsx | 154 ++++++++++++++++++++------ 1 file changed, 119 insertions(+), 35 deletions(-) diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx index f1a1f1aa..fb9ddc99 100644 --- a/packages/web/src/components/Share.tsx +++ b/packages/web/src/components/Share.tsx @@ -597,6 +597,8 @@ export default function Share(props: { }) { let lastScrollY = 0 let scrollTimeout: number | undefined + let scrollSentinel: HTMLElement | undefined + let scrollObserver: IntersectionObserver | undefined const id = props.id const params = new URLSearchParams(window.location.search) @@ -604,6 +606,7 @@ export default function Share(props: { const [showScrollButton, setShowScrollButton] = createSignal(false) const [isButtonHovered, setIsButtonHovered] = createSignal(false) + const [isNearBottom, setIsNearBottom] = createSignal(false) const [store, setStore] = createStore<{ info?: Session.Info @@ -713,10 +716,9 @@ export default function Share(props: { const currentScrollY = window.scrollY const isScrollingDown = currentScrollY > lastScrollY const scrolled = currentScrollY > 200 // Show after scrolling 200px - const isNearBottom = window.innerHeight + currentScrollY >= document.body.scrollHeight - 100 // Only show when scrolling down, scrolled enough, and not near bottom - const shouldShow = isScrollingDown && scrolled && !isNearBottom + const shouldShow = isScrollingDown && scrolled && !isNearBottom() // Update last scroll position lastScrollY = currentScrollY @@ -732,7 +734,7 @@ export default function Share(props: { if (!isButtonHovered()) { setShowScrollButton(false) } - }, 3000) + }, 1500) } else if (!isButtonHovered()) { // Only hide if not hovered (to prevent disappearing while user is about to click) setShowScrollButton(false) @@ -744,6 +746,26 @@ export default function Share(props: { onMount(() => { lastScrollY = window.scrollY // Initialize scroll position + + // Create sentinel element + const sentinel = document.createElement("div") + sentinel.style.height = "1px" + sentinel.style.position = "absolute" + sentinel.style.bottom = "100px" + sentinel.style.width = "100%" + sentinel.style.pointerEvents = "none" + document.body.appendChild(sentinel) + + // Create intersection observer + const observer = new IntersectionObserver((entries) => { + setIsNearBottom(entries[0].isIntersecting) + }) + observer.observe(sentinel) + + // Store references for cleanup + scrollSentinel = sentinel + scrollObserver = observer + checkScrollNeed() window.addEventListener("scroll", checkScrollNeed) window.addEventListener("resize", checkScrollNeed) @@ -752,6 +774,15 @@ export default function Share(props: { onCleanup(() => { window.removeEventListener("scroll", checkScrollNeed) window.removeEventListener("resize", checkScrollNeed) + + // Clean up observer and sentinel + if (scrollObserver) { + scrollObserver.disconnect() + } + if (scrollSentinel) { + document.body.removeChild(scrollSentinel) + } + if (scrollTimeout) { clearTimeout(scrollTimeout) } @@ -855,7 +886,6 @@ export default function Share(props: { )} - @@ -880,8 +910,11 @@ export default function Share(props: { ) return null - const anchor = createMemo(() => `${msg.id}-${partIndex()}`) - const [showResults, setShowResults] = createSignal(false) + const anchor = createMemo( + () => `${msg.id}-${partIndex()}`, + ) + const [showResults, setShowResults] = + createSignal(false) const isLastPart = createMemo( () => data().messages.length === msgIndex() + 1 && @@ -903,7 +936,9 @@ export default function Share(props: { const duration = DateTime.fromMillis( metadata?.time.end || 0, ) - .diff(DateTime.fromMillis(metadata?.time.start || 0)) + .diff( + DateTime.fromMillis(metadata?.time.start || 0), + ) .toMillis() return { metadata, args, result, duration } @@ -921,7 +956,9 @@ export default function Share(props: { {/* User text */} {(part) => ( @@ -972,7 +1009,9 @@ export default function Share(props: { expand={isLastPart()} text={stripEnclosingTag(part().text)} /> - + {(_part) => { - const matches = () => toolData()?.metadata?.matches + const matches = () => + toolData()?.metadata?.matches const splitArgs = () => { const { pattern, ...rest } = toolData()?.args return { pattern, rest } @@ -1066,11 +1106,14 @@ export default function Share(props: {
Grep - “{splitArgs().pattern}” + + “{splitArgs().pattern}” +
0 + Object.keys(splitArgs().rest) + .length > 0 } >
@@ -1299,8 +1342,10 @@ export default function Share(props: { data().rootDir, ), ) - const hasError = () => toolData()?.metadata?.error - const preview = () => toolData()?.metadata?.preview + const hasError = () => + toolData()?.metadata?.error + const preview = () => + toolData()?.metadata?.preview return (
{/* Always try to show CodeBlock if preview is available (even if empty string) */} - +
@@ -1354,7 +1403,12 @@ export default function Share(props: {
{/* Fallback to TextPart if preview is not a string (e.g. undefined) AND result exists */} - +
toolData()?.metadata?.error + const hasError = () => + toolData()?.metadata?.error const content = () => toolData()?.args?.content const diagnostics = createMemo(() => getDiagnostics( @@ -1415,7 +1470,10 @@ export default function Share(props: { >
- +
@@ -1435,7 +1493,7 @@ export default function Share(props: {
{formatErrorString( - toolData()?.result + toolData()?.result, )}
@@ -1453,8 +1511,12 @@ export default function Share(props: {
@@ -1481,8 +1543,10 @@ export default function Share(props: { > {(_part) => { const diff = () => toolData()?.metadata?.diff - const message = () => toolData()?.metadata?.message - const hasError = () => toolData()?.metadata?.error + const message = () => + toolData()?.metadata?.message + const hasError = () => + toolData()?.metadata?.error const filePath = createMemo(() => stripWorkingDirectory( toolData()?.args.filePath, @@ -1504,7 +1568,10 @@ export default function Share(props: { >
- +
@@ -1527,7 +1594,9 @@ export default function Share(props: {
@@ -1556,9 +1625,12 @@ export default function Share(props: { } > {(_part) => { - const command = () => toolData()?.metadata?.title - const desc = () => toolData()?.metadata?.description - const result = () => toolData()?.metadata?.stdout + const command = () => + toolData()?.metadata?.title + const desc = () => + toolData()?.metadata?.description + const result = () => + toolData()?.metadata?.stdout const error = () => toolData()?.metadata?.stderr return ( @@ -1569,7 +1641,10 @@ export default function Share(props: { >
- +
@@ -1604,7 +1679,9 @@ export default function Share(props: { > {(_part) => { const todos = createMemo(() => - sortTodosByStatus(toolData()?.args?.todos ?? []), + sortTodosByStatus( + toolData()?.args?.todos ?? [], + ), ) const starting = () => todos().every((t) => t.status === "pending") @@ -1670,7 +1747,8 @@ export default function Share(props: { {(_part) => { const url = () => toolData()?.args.url const format = () => toolData()?.args.format - const hasError = () => toolData()?.metadata?.error + const hasError = () => + toolData()?.metadata?.error return (
- + @@ -1848,7 +1930,9 @@ export default function Share(props: {
- {part.type} + + {part.type} +