summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJay V <[email protected]>2025-06-27 19:16:46 -0400
committerJay V <[email protected]>2025-06-27 19:16:46 -0400
commit688f3fd12f56e1fde152435a7464ffaf18473c67 (patch)
treec7c78c87b0ea6f86a47b977461cbfffe25e2372d
parentc0773dc7c53cf15e9c8d63b4e49aa7527c9a1328 (diff)
parent145df084440470bb53655f62b5f5588e2615f1ba (diff)
downloadopencode-688f3fd12f56e1fde152435a7464ffaf18473c67.tar.gz
opencode-688f3fd12f56e1fde152435a7464ffaf18473c67.zip
Merge branch 'jeremyosih-feat/scroll-to-bottom-button' into dev
-rw-r--r--packages/web/src/components/Share.tsx85
-rw-r--r--packages/web/src/components/share.module.css28
2 files changed, 113 insertions, 0 deletions
diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx
index d11276153..008f863c9 100644
--- a/packages/web/src/components/Share.tsx
+++ b/packages/web/src/components/Share.tsx
@@ -39,6 +39,7 @@ import {
IconMagnifyingGlass,
IconWrenchScrewdriver,
IconDocumentMagnifyingGlass,
+ IconArrowDown,
} from "./icons"
import DiffView from "./DiffView"
import CodeBlock from "./CodeBlock"
@@ -594,12 +595,17 @@ export default function Share(props: {
info: Session.Info
messages: Record<string, Message.Info>
}) {
+ let lastScrollY = 0
let hasScrolled = false
+ let scrollTimeout: number | undefined
const id = props.id
const params = new URLSearchParams(window.location.search)
const debug = params.get("debug") === "true"
+ const [showScrollButton, setShowScrollButton] = createSignal(false)
+ const [isButtonHovered, setIsButtonHovered] = createSignal(false)
+
const anchorId = createMemo<string | null>(() => {
const raw = window.location.hash.slice(1)
const [id] = raw.split("-")
@@ -715,6 +721,54 @@ export default function Share(props: {
})
})
+ function checkScrollNeed() {
+ 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
+
+ // Update last scroll position
+ lastScrollY = currentScrollY
+
+ if (shouldShow) {
+ setShowScrollButton(true)
+ // Clear existing timeout
+ if (scrollTimeout) {
+ clearTimeout(scrollTimeout)
+ }
+ // Hide button after 3 seconds of no scrolling (unless hovered)
+ scrollTimeout = window.setTimeout(() => {
+ if (!isButtonHovered()) {
+ setShowScrollButton(false)
+ }
+ }, 3000)
+ } else if (!isButtonHovered()) {
+ // Only hide if not hovered (to prevent disappearing while user is about to click)
+ setShowScrollButton(false)
+ if (scrollTimeout) {
+ clearTimeout(scrollTimeout)
+ }
+ }
+ }
+
+ onMount(() => {
+ lastScrollY = window.scrollY // Initialize scroll position
+ checkScrollNeed()
+ window.addEventListener("scroll", checkScrollNeed)
+ window.addEventListener("resize", checkScrollNeed)
+ })
+
+ onCleanup(() => {
+ window.removeEventListener("scroll", checkScrollNeed)
+ window.removeEventListener("resize", checkScrollNeed)
+ if (scrollTimeout) {
+ clearTimeout(scrollTimeout)
+ }
+ })
+
const data = createMemo(() => {
const result = {
rootDir: undefined as string | undefined,
@@ -825,6 +879,7 @@ export default function Share(props: {
</span>
)}
</div>
+
</div>
</div>
@@ -1902,6 +1957,36 @@ export default function Share(props: {
</div>
</div>
</Show>
+
+ <Show when={showScrollButton()}>
+ <button
+ type="button"
+ class={styles["scroll-button"]}
+ onClick={() =>
+ document.body.scrollIntoView({ behavior: "smooth", block: "end" })
+ }
+ onMouseEnter={() => {
+ setIsButtonHovered(true)
+ if (scrollTimeout) {
+ clearTimeout(scrollTimeout)
+ }
+ }}
+ onMouseLeave={() => {
+ setIsButtonHovered(false)
+ if (showScrollButton()) {
+ scrollTimeout = window.setTimeout(() => {
+ if (!isButtonHovered()) {
+ setShowScrollButton(false)
+ }
+ }, 3000)
+ }
+ }}
+ title="Scroll to bottom"
+ aria-label="Scroll to bottom"
+ >
+ <IconArrowDown width={20} height={20} />
+ </button>
+ </Show>
</main>
)
}
diff --git a/packages/web/src/components/share.module.css b/packages/web/src/components/share.module.css
index ae45d3768..dafbdd8a2 100644
--- a/packages/web/src/components/share.module.css
+++ b/packages/web/src/components/share.module.css
@@ -784,3 +784,31 @@
}
}
}
+
+.scroll-button {
+ position: fixed;
+ bottom: 2rem;
+ right: 2rem;
+ width: 2.5rem;
+ height: 2.5rem;
+ border-radius: 0.25rem;
+ border: 1px solid var(--sl-color-divider);
+ background-color: var(--sl-color-bg-surface);
+ color: var(--sl-color-text-secondary);
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.15s ease, opacity 0.5s ease;
+ z-index: 100;
+ appearance: none;
+ opacity: 1;
+
+ &:active {
+ transform: translateY(1px);
+ }
+
+ svg {
+ display: block;
+ }
+}