diff --git a/apps/desktop/src/components/main/body/sessions/note-input/index.tsx b/apps/desktop/src/components/main/body/sessions/note-input/index.tsx index f3085cbbd9..b031c4e4ea 100644 --- a/apps/desktop/src/components/main/body/sessions/note-input/index.tsx +++ b/apps/desktop/src/components/main/body/sessions/note-input/index.tsx @@ -1,5 +1,12 @@ -import { useCallback, useEffect, useRef, useState } from "react"; +import { + type RefObject, + useCallback, + useEffect, + useRef, + useState, +} from "react"; import { useHotkeys } from "react-hotkeys-hook"; +import { useResizeObserver } from "usehooks-ts"; import type { TiptapEditor } from "@hypr/tiptap/editor"; import { cn } from "@hypr/utils"; @@ -39,6 +46,10 @@ export function NoteInput({ : currentTab.type, ); + const { fadeRef, atStart, atEnd } = useScrollFade([ + currentTab, + ]); + const handleTabChange = useCallback( (view: EditorView) => { onBeforeTabChange(); @@ -78,39 +89,42 @@ export function NoteInput({ /> -
{ - scrollRef.current = node; - } - : undefined - } - onClick={handleContainerClick} - className={cn([ - "flex-1 mt-2 px-3", - currentTab.type === "transcript" - ? "overflow-hidden" - : ["overflow-auto", "pb-6"], - ])} - > - {currentTab.type === "enhanced" && ( - - )} - {currentTab.type === "raw" && ( - - )} - {currentTab.type === "transcript" && ( - - )} +
+
{ + fadeRef.current = node; + if (currentTab.type !== "transcript") { + scrollRef.current = node; + } + }} + onClick={handleContainerClick} + className={cn([ + "h-full px-3", + currentTab.type === "transcript" + ? "overflow-hidden" + : ["overflow-auto", "pb-6"], + ])} + > + {currentTab.type === "enhanced" && ( + + )} + {currentTab.type === "raw" && ( + + )} + {currentTab.type === "transcript" && ( + + )} +
+ {!atStart && } + {!atEnd && }
); @@ -225,3 +239,46 @@ function useTabShortcuts({ [currentTab, editorTabs, handleTabChange], ); } + +function useScrollFade(deps: unknown[] = []) { + const fadeRef = useRef(null); + const [state, setState] = useState({ atStart: true, atEnd: true }); + + const update = useCallback(() => { + const el = fadeRef.current; + if (!el) return; + + const { scrollTop, scrollHeight, clientHeight } = el; + setState({ + atStart: scrollTop <= 1, + atEnd: scrollTop + clientHeight >= scrollHeight - 1, + }); + }, []); + + useResizeObserver({ ref: fadeRef as RefObject, onResize: update }); + + useEffect(() => { + const el = fadeRef.current; + if (!el) return; + + update(); + el.addEventListener("scroll", update); + return () => el.removeEventListener("scroll", update); + }, [update, ...deps]); + + return { fadeRef, ...state }; +} + +function ScrollFadeOverlay({ position }: { position: "top" | "bottom" }) { + return ( +
+ ); +}