From 18d77efed55ec93f7b80ea3de3cecc9c24844b5a Mon Sep 17 00:00:00 2001 From: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Date: Sat, 27 Jun 2026 16:46:20 +0530 Subject: [PATCH 1/6] sticky session list header --- packages/app/src/index.css | 39 ++++++++++++++ packages/app/src/pages/home.tsx | 92 +++++++++++++++++++++++++++++---- 2 files changed, 122 insertions(+), 9 deletions(-) diff --git a/packages/app/src/index.css b/packages/app/src/index.css index a99034f5da9c..bec8b6c93f6b 100644 --- a/packages/app/src/index.css +++ b/packages/app/src/index.css @@ -108,6 +108,45 @@ } } + .home-session-header-new::before { + content: ""; + position: absolute; + top: -12px; + left: 0; + width: 100%; + height: 12px; + background: var(--v2-background-bg-base); + } + + .home-session-header-new::after { + content: ""; + position: absolute; + top: 100%; + left: 0; + width: 100%; + height: 12px; + pointer-events: none; + background: linear-gradient( + 180deg, + var(--v2-background-bg-base) 0%, + color-mix(in srgb, var(--v2-background-bg-base) 96.4148%, transparent) 16.11%, + color-mix(in srgb, var(--v2-background-bg-base) 91.509%, transparent) 30.26%, + color-mix(in srgb, var(--v2-background-bg-base) 85.4857%, transparent) 42.58%, + color-mix(in srgb, var(--v2-background-bg-base) 78.5481%, transparent) 53.21%, + color-mix(in srgb, var(--v2-background-bg-base) 70.8995%, transparent) 62.31%, + color-mix(in srgb, var(--v2-background-bg-base) 62.7429%, transparent) 70.01%, + color-mix(in srgb, var(--v2-background-bg-base) 54.2815%, transparent) 76.46%, + color-mix(in srgb, var(--v2-background-bg-base) 45.7185%, transparent) 81.8%, + color-mix(in srgb, var(--v2-background-bg-base) 37.2571%, transparent) 86.16%, + color-mix(in srgb, var(--v2-background-bg-base) 29.1005%, transparent) 89.7%, + color-mix(in srgb, var(--v2-background-bg-base) 21.4519%, transparent) 92.56%, + color-mix(in srgb, var(--v2-background-bg-base) 14.5143%, transparent) 94.87%, + color-mix(in srgb, var(--v2-background-bg-base) 8.49101%, transparent) 96.79%, + color-mix(in srgb, var(--v2-background-bg-base) 3.58519%, transparent) 98.45%, + transparent 100% + ); + } + [data-slot="titlebar-update-loader"] { display: block; flex-shrink: 0; diff --git a/packages/app/src/pages/home.tsx b/packages/app/src/pages/home.tsx index 7a8a33db8460..b6f1e00b549d 100644 --- a/packages/app/src/pages/home.tsx +++ b/packages/app/src/pages/home.tsx @@ -68,6 +68,8 @@ import { archiveHomeSession } from "./home-session-archive" import { showToast } from "@/utils/toast" const HOME_SESSION_LIMIT = 64 +const HOME_SESSION_HEADER_STICKY_TOP = 12 +const HOME_SESSION_HEADER_FADE_DISTANCE = 48 const SHOW_HOME_SESSION_ARCHIVE = false const HOME_ROW_LAYOUT = "flex min-w-0 w-full shrink-0 cursor-default items-center rounded-[6px] bg-transparent text-left transition-[background-color,color,box-shadow] duration-[120ms] ease-in-out focus-visible:outline-none" @@ -149,9 +151,13 @@ export function NewHome() { const marked = useMarked() const openSettings = useSettingsCommand() let focusSessionSearch: (() => void) | undefined + let sessionListViewport: HTMLDivElement | undefined + let headerOpacityFrame: number | undefined + const sessionHeaderRefs = new Map() const [state, setState] = createStore({ search: "", searchFocused: false, + headerTitleOpacity: {} as Partial>, }) const selection = layout.home.selection @@ -225,6 +231,18 @@ export function NewHome() { const groups = createMemo(() => groupSessions(records(), language)) const prefetched = new Set() + createEffect(() => { + const ids = new Set(groups().map((group) => group.id)) + sessionHeaderRefs.forEach((_, id) => { + if (!ids.has(id)) sessionHeaderRefs.delete(id) + }) + queueHeaderTitleOpacityUpdate() + }) + + onCleanup(() => { + if (headerOpacityFrame !== undefined) cancelAnimationFrame(headerOpacityFrame) + }) + createEffect(() => { const ctx = focusedServerCtx() if (!ctx) return @@ -267,6 +285,41 @@ export function NewHome() { setState("searchFocused", false) } + function setSessionHeaderRef(id: HomeSessionGroup["id"], el: HTMLDivElement) { + sessionHeaderRefs.set(id, el) + queueHeaderTitleOpacityUpdate() + } + + function queueHeaderTitleOpacityUpdate() { + if (typeof requestAnimationFrame === "undefined" || headerOpacityFrame !== undefined) return + headerOpacityFrame = requestAnimationFrame(() => { + headerOpacityFrame = undefined + updateHeaderTitleOpacity() + }) + } + + function updateHeaderTitleOpacity() { + if (!sessionListViewport) return + const viewportTop = sessionListViewport.getBoundingClientRect().top + const headerTops = groups().map((group) => { + const el = sessionHeaderRefs.get(group.id) + if (!el) return + return el.getBoundingClientRect().top - viewportTop + }) + + groups().forEach((group, index) => { + const nextTop = headerTops.slice(index + 1).find((top) => top !== undefined) + const opacity = + nextTop === undefined + ? 1 + : Math.max( + 0, + Math.min(1, (nextTop - HOME_SESSION_HEADER_STICKY_TOP) / HOME_SESSION_HEADER_FADE_DISTANCE), + ) + setState("headerTitleOpacity", group.id, Math.round(opacity * 1000) / 1000) + }) + } + function selectSearchSession(session: Session) { openSession(session) closeSearch() @@ -455,7 +508,14 @@ export function NewHome() { onClose={closeSearch} onSelect={selectSearchSession} /> - + { + sessionListViewport = el + queueHeaderTitleOpacityUpdate() + }} + onScroll={queueHeaderTitleOpacityUpdate} + > 0} fallback={} > -
+
{(group, index) => ( -
+ <> setSessionHeaderRef(group.id, el)} onNewSession={index() === 0 && newSessionProject() ? openNewSession : undefined} /> -
+
{(record) => (
-
+ )}
@@ -1130,11 +1194,21 @@ function HomeSessionSearchResultRow(props: { ) } -function HomeSessionGroupHeader(props: { title: string; onNewSession?: () => void }) { +function HomeSessionGroupHeader(props: { + title: string + titleOpacity: number + headerRef: (el: HTMLDivElement) => void + onNewSession?: () => void +}) { const language = useLanguage() return ( -
- +
+ {(onNewSession) => ( voi variant="ghost-muted" size="normal" icon="edit" - class="h-7 px-2 [font-weight:530]" + class="relative z-20 h-7 px-2 [font-weight:530]" onClick={onNewSession()} > {language.t("command.session.new")} From d0ca85b63c56f7ea6e48435699ea6420db3a709d Mon Sep 17 00:00:00 2001 From: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Date: Sat, 27 Jun 2026 17:01:07 +0530 Subject: [PATCH 2/6] adjusted fade animation --- packages/app/src/pages/home.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/app/src/pages/home.tsx b/packages/app/src/pages/home.tsx index b6f1e00b549d..2d8d81bf44fd 100644 --- a/packages/app/src/pages/home.tsx +++ b/packages/app/src/pages/home.tsx @@ -69,7 +69,8 @@ import { showToast } from "@/utils/toast" const HOME_SESSION_LIMIT = 64 const HOME_SESSION_HEADER_STICKY_TOP = 12 -const HOME_SESSION_HEADER_FADE_DISTANCE = 48 +const HOME_SESSION_HEADER_TEXT_HEIGHT = 16 +const HOME_SESSION_HEADER_FADE_DISTANCE = 16 const SHOW_HOME_SESSION_ARCHIVE = false const HOME_ROW_LAYOUT = "flex min-w-0 w-full shrink-0 cursor-default items-center rounded-[6px] bg-transparent text-left transition-[background-color,color,box-shadow] duration-[120ms] ease-in-out focus-visible:outline-none" @@ -309,12 +310,13 @@ export function NewHome() { groups().forEach((group, index) => { const nextTop = headerTops.slice(index + 1).find((top) => top !== undefined) + const fadeEnd = HOME_SESSION_HEADER_STICKY_TOP + HOME_SESSION_HEADER_TEXT_HEIGHT const opacity = nextTop === undefined ? 1 : Math.max( 0, - Math.min(1, (nextTop - HOME_SESSION_HEADER_STICKY_TOP) / HOME_SESSION_HEADER_FADE_DISTANCE), + Math.min(1, (nextTop - fadeEnd) / HOME_SESSION_HEADER_FADE_DISTANCE), ) setState("headerTitleOpacity", group.id, Math.round(opacity * 1000) / 1000) }) From ef830571837e7668f4d7c223e70afc631ceafd82 Mon Sep 17 00:00:00 2001 From: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Date: Sat, 27 Jun 2026 17:07:41 +0530 Subject: [PATCH 3/6] cleanup --- packages/app/src/pages/home.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/pages/home.tsx b/packages/app/src/pages/home.tsx index 2d8d81bf44fd..1432562b1017 100644 --- a/packages/app/src/pages/home.tsx +++ b/packages/app/src/pages/home.tsx @@ -1206,7 +1206,7 @@ function HomeSessionGroupHeader(props: { return (