feat(app): fade composer text at scroll edges (#57248)

This commit is contained in:
Luke Parker
2026-10-20 11:44:19 +10:00
committed by GitHub
Unverified
parent 08cb592c1c
commit 004e21a744
2 changed files with 29 additions and 1 deletions
@@ -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;
+17 -1
View File
@@ -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) {
<ScrollView
data-component="composer-scroll"
class="min-h-[60px] max-h-[180px]"
style={{ "--composer-fade-bottom": `${fadeBottom()}px` }}
onScroll={updateScroll}
viewportRef={(element) => {
viewport = element
element.tabIndex = -1