Choose a channel by what it is, not by its name in a list
A <select> of ninety channels tells the reader almost nothing: a wall of bare names, nothing to say which site a name belongs to or what it carries, and no way to look for one. The server has been sending far more than the name all along -- a favicon, a description, tags, its own groupings, and whether a channel still says "work in progress" -- so the picker shows that. It opens as a dialog in the site's own style: a search field, then the server's groups as sections, each led by an "All <group>" row that browses the whole group. Search matches the name, the id, the description, the tags and the group, so "jav" finds Tokyo Motion (whose name never says it) and "leaks" finds the OnlyFans mirrors. Arrows and Enter walk the list, Escape closes it, and opening it with nothing typed scrolls to the channel already being read. On a phone it fills the screen and leaves the field unfocused -- the keyboard would cover the thing being chosen from. Favicons load through attachThumbnail, so they get the same route race, proxy fallback and retries as every other remote picture, with the channel's initial behind them for the ones that never arrive. The <select> was also where the command palette read its channel actions, so the list lives in App.ui.channels now and the palette asks for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBDkEXP4htyXTCZUwMLphd
This commit is contained in:
@@ -2704,6 +2704,257 @@ body.theme-light .video-card img:not(.is-loaded) {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* --- Channel picker ---------------------------------------------------- */
|
||||
/* The trigger in the menu drawer: what's being read now, favicon and all. */
|
||||
.channel-trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
color: var(--text-primary);
|
||||
font-family: var(--font-body);
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s ease, background 0.15s ease;
|
||||
}
|
||||
|
||||
.channel-trigger:hover,
|
||||
.channel-trigger:focus-visible {
|
||||
border-color: var(--border-hover);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.channel-trigger-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.channel-trigger-name,
|
||||
.channel-trigger-note {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.channel-trigger-note {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: none;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.channel-trigger-caret {
|
||||
flex-shrink: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-top: 6px solid var(--text-secondary);
|
||||
}
|
||||
|
||||
/* A favicon, with the channel's initial behind it for the ones that never
|
||||
arrive -- a row is never a name beside an empty square. */
|
||||
.channel-mark {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 8px;
|
||||
background: rgba(201, 165, 103, 0.14);
|
||||
border: 1px solid var(--border);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.channel-mark::before {
|
||||
content: attr(data-letter);
|
||||
font-family: var(--font-display);
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.channel-favicon {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
padding: 3px;
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.channel-picker {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 4200;
|
||||
display: none;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding: 8vh 12px 12px 12px;
|
||||
background: rgba(10, 8, 4, 0.55);
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
|
||||
.channel-picker.open { display: flex; }
|
||||
|
||||
.channel-picker-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: min(560px, 100%);
|
||||
max-height: min(74vh, 700px);
|
||||
/* Solid, not the usual translucent panel: the section headings stick over
|
||||
the rows as they scroll, and a see-through heading is unreadable. */
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: 0 30px 70px var(--shadow);
|
||||
overflow: hidden;
|
||||
animation: card-rise 0.2s ease both;
|
||||
}
|
||||
|
||||
.channel-picker-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 16px 18px 10px 18px;
|
||||
}
|
||||
|
||||
.channel-picker-head h3 {
|
||||
font-family: var(--font-display);
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.channel-search {
|
||||
margin: 0 18px 12px 18px;
|
||||
padding: 11px 14px;
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
color: var(--text-primary);
|
||||
font-family: var(--font-body);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.channel-search::placeholder { color: var(--text-secondary); }
|
||||
.channel-search:focus { outline: none; border-color: var(--border-hover); }
|
||||
|
||||
.channel-picker-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 0 10px 10px 10px;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.channel-section {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
padding: 10px 8px 6px 8px;
|
||||
background: var(--bg-primary);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.channel-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 10px;
|
||||
background: transparent;
|
||||
color: var(--text-primary);
|
||||
font-family: var(--font-body);
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.channel-row.is-active { background: var(--bg-tertiary); }
|
||||
|
||||
.channel-row.is-current {
|
||||
border-color: var(--border-hover);
|
||||
background: rgba(201, 165, 103, 0.10);
|
||||
}
|
||||
|
||||
.channel-row-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.channel-row-name {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.channel-row-note {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
margin-top: 2px;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.channel-row-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.channel-tag {
|
||||
padding: 2px 7px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.channel-row-flag {
|
||||
flex-shrink: 0;
|
||||
align-self: center;
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.channel-picker-empty {
|
||||
padding: 8px 20px 20px 20px;
|
||||
color: var(--text-secondary);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.channel-picker { padding: 0; }
|
||||
|
||||
.channel-picker-box {
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Reels HUD polish: serif title + brass scrubber + mute pulse ------- */
|
||||
.feed-title { font-family: var(--font-display); }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user