diff --git a/packages/app/src/composer/editor/editor.css b/packages/app/src/composer/editor/editor.css index 9740e90d839..88d74d45fe3 100644 --- a/packages/app/src/composer/editor/editor.css +++ b/packages/app/src/composer/editor/editor.css @@ -2,6 +2,18 @@ content: "\200B"; } +/* Matches the 16px session title fade. The top fade only covers the editor's 16px top padding until text scrolls + under it; the mask sits on the viewport so the thumb stays solid. */ +[data-component="composer-scroll"] > .scroll-view__viewport { + mask-image: linear-gradient( + to bottom, + transparent, + black 16px, + black calc(100% - var(--composer-fade-bottom, 0px)), + transparent + ); +} + /* Masks only cover hidden content; they do not reserve space in the toolbar. */ [data-slot="composer-controls"][data-overflow-start="true"] { --mask-start: 16px; diff --git a/packages/app/src/composer/editor/editor.tsx b/packages/app/src/composer/editor/editor.tsx index 2c9742016c1..979a308d6a1 100644 --- a/packages/app/src/composer/editor/editor.tsx +++ b/packages/app/src/composer/editor/editor.tsx @@ -110,12 +110,26 @@ export function ComposerEditor(props: ComposerEditorProps) { }) } + // The bottom fade shrinks with the remaining scroll so the last line ends clear of it. + const [fadeBottom, setFadeBottom] = createSignal(0) + + const updateScroll = () => { + if (!viewport) return + setFadeBottom(Math.min(16, Math.max(0, viewport.scrollHeight - viewport.clientHeight - viewport.scrollTop))) + } + onMount(() => { const observer = new ResizeObserver(updateOverflow) observer.observe(controlsViewport) observer.observe(controlsContent) updateOverflow() - onCleanup(() => observer.disconnect()) + // Typing and width changes both resize the editor, so it is the only element that needs watching. + const scrollObserver = new ResizeObserver(updateScroll) + if (editor) scrollObserver.observe(editor) + onCleanup(() => { + observer.disconnect() + scrollObserver.disconnect() + }) }) let localInput = false @@ -225,6 +239,8 @@ export function ComposerEditor(props: ComposerEditorProps) { { viewport = element element.tabIndex = -1