From 9adbf2376888af1fcdb19a6c535b8bd44f09b2fb Mon Sep 17 00:00:00 2001 From: posimai Date: Sun, 22 Mar 2026 14:10:48 +0900 Subject: [PATCH] feat: add article list below player Co-Authored-By: Claude Sonnet 4.6 --- index.html | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/index.html b/index.html index df20cb0..ffca73b 100644 --- a/index.html +++ b/index.html @@ -274,6 +274,52 @@ margin-top: 4px; } + /* ── 記事リスト ─────────────────────────────────────────────── */ + .brief-list { + flex-shrink: 0; + max-height: 38dvh; + overflow-y: auto; + border-top: 1px solid var(--border); + scrollbar-width: none; + } + .brief-list::-webkit-scrollbar { display: none; } + .brief-list-item { + display: flex; + align-items: flex-start; + gap: 12px; + padding: 10px 16px; + border-bottom: 1px solid var(--border); + cursor: pointer; + transition: background 0.12s; + } + .brief-list-item:active { background: var(--surface); } + .brief-list-item.active { background: var(--surface); } + .brief-list-num { + font-size: 11px; + color: var(--text3); + width: 16px; + flex-shrink: 0; + padding-top: 2px; + font-variant-numeric: tabular-nums; + } + .brief-list-num.active { color: var(--accent); } + .brief-list-info { flex: 1; min-width: 0; } + .brief-list-title { + font-size: 13px; + color: var(--text2); + line-height: 1.4; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + } + .brief-list-title.active { color: var(--text); font-weight: 500; } + .brief-list-source { + font-size: 11px; + color: var(--text3); + margin-top: 2px; + } + /* ── 音声エンジン表示 ───────────────────────────────────────── */ .voice-badge { display: inline-flex; @@ -391,6 +437,9 @@ + +
+
@@ -623,6 +672,7 @@ function updateArticleDisplay(a, idx) { document.getElementById('briefTitle').textContent = a.title || '---'; document.getElementById('briefSource').textContent = a.source || ''; document.getElementById('briefProgress').textContent = ''; + updateListHighlight(idx); // MediaSession if ('mediaSession' in navigator) { navigator.mediaSession.metadata = new MediaMetadata({ @@ -633,6 +683,39 @@ function updateArticleDisplay(a, idx) { } } +function renderList() { + const list = document.getElementById('briefList'); + list.innerHTML = ''; + articles.forEach((a, i) => { + const item = document.createElement('div'); + item.className = 'brief-list-item' + (i === currentIdx ? ' active' : ''); + item.innerHTML = ` + ${i + 1} +
+
${a.title || ''}
+ ${a.source ? `
${a.source}
` : ''} +
`; + item.addEventListener('click', () => { + const wasPlaying = isPlaying; + stopAudio(); + currentIdx = i; + updateArticleDisplay(articles[i], i); + if (wasPlaying) { isPlaying = true; updatePlayBtn(); playNext(); } + }); + list.appendChild(item); + }); +} + +function updateListHighlight(idx) { + document.querySelectorAll('.brief-list-item').forEach((el, i) => { + const active = i === idx; + el.classList.toggle('active', active); + el.querySelector('.brief-list-num').classList.toggle('active', active); + el.querySelector('.brief-list-title').classList.toggle('active', active); + if (active) el.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); + }); +} + function updateVoiceBadge(isVoicevox) { const badge = document.getElementById('voiceBadge'); const label = document.getElementById('voiceBadgeLabel'); @@ -738,6 +821,7 @@ async function loadFeed() { document.getElementById('briefCount').textContent = `${list.length}件`; updateArticleDisplay(list[0], 0); + renderList(); } catch (e) { document.getElementById('briefTitle').textContent = '記事の取得に失敗しました'; document.getElementById('briefSource').textContent = 'ネットワークを確認してください';