fix(sources): keep the page header in view; mark the active source's row

Local Greptile (two P2s) on the PR round, both regressions against master:

- The redesigned Sources page scrolled as one, so the title, Clean up and
  Sort left the view in a long library (master kept a sticky header, and
  the UI guidelines say headers stay and lists scroll). The header now
  stays and only the list below it scrolls, which also keeps a row reached
  by keyboard from scrolling under the header.
- Master marked the active playlist on the Sources page; the new row only
  tinted the current row in the compact (switcher) size. The active
  source's row is now tinted with the same --app-list-selected-* token on
  the page too; the switcher keeps its check.

The design handoff called the header non-sticky and the current tint
switcher-only; both followed master and the guidelines instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5.5 committed 2026-10-13 21:31:51 +02:00
1 parent 13de38ebe5
commit e3a4ea7259
5 files changed
+33 -26

No files matched your search

@@ -1,7 +1,4 @@
<section
class="sources-page app-scrollbar"
[class.sources-page--empty]="!hasPlaylists()"
>
<section class="sources-page" [class.sources-page--empty]="!hasPlaylists()">
@if (hasPlaylists()) {
<header class="sources-header">
<div class="sources-header__title">
@@ -92,7 +89,7 @@
</div>
</header>
}
<div class="sources-content">
<div class="sources-content app-scrollbar">
<app-recent-playlists
[searchQueryInput]="searchQuery()"
(addPlaylistClicked)="onAddPlaylist($event)"
@@ -6,16 +6,19 @@
min-height: 100%;
}
// The page scrolls as one: the header is not sticky.
// The header stays and the list scrolls, as on every list page: Sort and
// Clean up stay in reach in a long library, and a row reached by keyboard
// never scrolls under the header.
.sources-page {
display: flex;
flex-direction: column;
height: 100%;
min-width: 0;
overflow-y: auto;
overflow: hidden;
}
.sources-header {
flex: none;
display: flex;
flex-wrap: wrap;
align-items: center;
@@ -80,6 +83,8 @@
display: flex;
flex: 1;
flex-direction: column;
min-height: 0;
overflow-y: auto;
padding: 0 20px 32px;
}
@@ -241,10 +241,12 @@ describe('WorkspaceSourcesComponent', () => {
expect(
element.querySelector('.sources-count')?.textContent?.trim()
).toBe('2');
// The page scrolls as one; the header is not a separate bar.
expect(element.querySelector('.sources-page')?.classList).toContain(
'app-scrollbar'
);
// The header stays; the list below it scrolls.
const scroller = element.querySelector('.sources-content');
expect(scroller?.classList).toContain('app-scrollbar');
expect(
scroller?.contains(element.querySelector('.sources-header'))
).toBe(false);
});
it('titles the page by the status filter and counts what it shows', async () => {