personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(transcripts): drop duplicate screen tab and nav hint

- Suppress md_files["screen"] in the segment-content route when screen
chunks are present, so the tab row no longer renders two screen tabs.
Speaker attribution still reads talents/screen.md from disk, unaffected.
- Lowercase the three structural JS tab labels (transcript / audio /
screen). Tab IDs and URL-hash routing are unchanged.
- Remove the [ ] to navigate hint span, its CSS, and its visibility
toggles. Keyboard navigation itself is unchanged.

Updates the segment-detail API baseline to match the intentional
contract change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+24 -18
+8
apps/transcripts/routes.py
··· 517 517 except Exception: 518 518 continue 519 519 520 + # UI dedup: when a segment has screen chunks, the structural "screen" tab 521 + # already covers it — drop talents/screen.md from md_files so the tab row 522 + # doesn't render two screen-labeled tabs. Speaker attribution reads 523 + # talents/screen.md directly from disk (apps/speakers/attribution.py), 524 + # unaffected by this UI-side suppression. 525 + if any(c["type"] == "screen" for c in chunks): 526 + md_files.pop("screen", None) 527 + 520 528 return jsonify( 521 529 { 522 530 "chunks": chunks,
+12
apps/transcripts/tests/test_segment_routes.py
··· 187 187 assert "media_sizes" in data 188 188 189 189 190 + def test_segment_content_drops_screen_md_when_screen_chunks_present(client): 191 + response = client.get( 192 + f"/app/transcripts/api/segment/{FIXTURE_DAY}/{FIXTURE_STREAM}/{FIXTURE_SEGMENT}" 193 + ) 194 + 195 + assert response.status_code == 200 196 + data = response.get_json() 197 + assert any(c["type"] == "screen" for c in data["chunks"]) 198 + assert "screen" not in data["md_files"] 199 + assert "audio" in data["md_files"] 200 + 201 + 190 202 def test_delete_segment_happy_path_removes_segment_directory( 191 203 client, journal_copy, monkeypatch 192 204 ):
+3 -16
apps/transcripts/workspace.html
··· 325 325 font-variant-numeric: tabular-nums; 326 326 } 327 327 328 - .tr-nav-hint { 329 - font-size: 12px; 330 - color: #9ca3af; 331 - display: none; 332 - } 333 - 334 - .tr-nav-hint.visible { 335 - display: inline; 336 - } 337 - 338 328 .tr-tabs { 339 329 gap: 8px; 340 330 padding: 8px 0; ··· 1416 1406 <div> 1417 1407 <h2 class="tr-title">transcripts</h2> 1418 1408 <div class="tr-range-text" id="trRangeText"></div> 1419 - <span class="tr-nav-hint" id="trNavHint">[ ] to navigate</span> 1420 1409 </div> 1421 1410 <button type="button" id="trDeleteBtn" class="tr-delete-btn" title="delete segment"> 1422 1411 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> ··· 2143 2132 zoomSegments.innerHTML = ''; 2144 2133 2145 2134 if (filtered.length === 0) { 2146 - document.getElementById('trNavHint').classList.remove('visible'); 2147 2135 zoomSegments.innerHTML = window.SurfaceState.empty({ 2148 2136 icon: emptyIcons.nothing, 2149 2137 heading: 'no segments in this range', ··· 2152 2140 return; 2153 2141 } 2154 2142 2155 - document.getElementById('trNavHint').classList.add('visible'); 2156 2143 filtered.forEach(seg => { 2157 2144 const segStart = parseTime(seg.start); 2158 2145 const segEnd = parseTime(seg.end); ··· 2448 2435 tabsContainer.appendChild(btn); 2449 2436 }; 2450 2437 2451 - addTab('transcript', 'Transcript'); 2438 + addTab('transcript', 'transcript'); 2452 2439 if (data.audio_file) { 2453 - addTab('audio', 'Audio'); 2440 + addTab('audio', 'audio'); 2454 2441 } 2455 2442 if ((data.chunks || []).some(chunk => chunk.type === 'screen')) { 2456 - addTab('screen', 'Screen'); 2443 + addTab('screen', 'screen'); 2457 2444 } 2458 2445 2459 2446 const mdStems = Object.keys(data.md_files || {}).sort((a, b) => a.localeCompare(b));
+1 -2
tests/baselines/api/transcripts/segment-detail.json
··· 129 129 ], 130 130 "cost": 0.0, 131 131 "md_files": { 132 - "audio": "# Audio Summary\n\nMorning keynote at Denver Tech Summit. Juliet Capulet presented on unified API gateway architecture. Romeo Montague and Mercutio Escalus attended. Mercutio noted the similarity to Montague Tech's approach.\n", 133 - "screen": "# Screen Summary\n\nConference live stream showing presentation slides on enterprise API architecture. Juliet Capulet presenting unified gateway concept.\n" 132 + "audio": "# Audio Summary\n\nMorning keynote at Denver Tech Summit. Juliet Capulet presented on unified API gateway architecture. Romeo Montague and Mercutio Escalus attended. Mercutio noted the similarity to Montague Tech's approach.\n" 134 133 }, 135 134 "media_purged": true, 136 135 "media_sizes": {