feat: add article list below player
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
58c64ad23b
commit
9adbf23768
84
index.html
84
index.html
|
|
@ -274,6 +274,52 @@
|
||||||
margin-top: 4px;
|
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 {
|
.voice-badge {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|
@ -391,6 +437,9 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 記事リスト -->
|
||||||
|
<div class="brief-list" id="briefList"></div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="toast" role="status" aria-live="polite"></div>
|
<div id="toast" role="status" aria-live="polite"></div>
|
||||||
|
|
@ -623,6 +672,7 @@ function updateArticleDisplay(a, idx) {
|
||||||
document.getElementById('briefTitle').textContent = a.title || '---';
|
document.getElementById('briefTitle').textContent = a.title || '---';
|
||||||
document.getElementById('briefSource').textContent = a.source || '';
|
document.getElementById('briefSource').textContent = a.source || '';
|
||||||
document.getElementById('briefProgress').textContent = '';
|
document.getElementById('briefProgress').textContent = '';
|
||||||
|
updateListHighlight(idx);
|
||||||
// MediaSession
|
// MediaSession
|
||||||
if ('mediaSession' in navigator) {
|
if ('mediaSession' in navigator) {
|
||||||
navigator.mediaSession.metadata = new MediaMetadata({
|
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 = `
|
||||||
|
<span class="brief-list-num${i === currentIdx ? ' active' : ''}">${i + 1}</span>
|
||||||
|
<div class="brief-list-info">
|
||||||
|
<div class="brief-list-title${i === currentIdx ? ' active' : ''}">${a.title || ''}</div>
|
||||||
|
${a.source ? `<div class="brief-list-source">${a.source}</div>` : ''}
|
||||||
|
</div>`;
|
||||||
|
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) {
|
function updateVoiceBadge(isVoicevox) {
|
||||||
const badge = document.getElementById('voiceBadge');
|
const badge = document.getElementById('voiceBadge');
|
||||||
const label = document.getElementById('voiceBadgeLabel');
|
const label = document.getElementById('voiceBadgeLabel');
|
||||||
|
|
@ -738,6 +821,7 @@ async function loadFeed() {
|
||||||
|
|
||||||
document.getElementById('briefCount').textContent = `${list.length}件`;
|
document.getElementById('briefCount').textContent = `${list.length}件`;
|
||||||
updateArticleDisplay(list[0], 0);
|
updateArticleDisplay(list[0], 0);
|
||||||
|
renderList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
document.getElementById('briefTitle').textContent = '記事の取得に失敗しました';
|
document.getElementById('briefTitle').textContent = '記事の取得に失敗しました';
|
||||||
document.getElementById('briefSource').textContent = 'ネットワークを確認してください';
|
document.getElementById('briefSource').textContent = 'ネットワークを確認してください';
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue