personal memory agent
0
fork

Configure Feed

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

Fix segment fetch URLs and add multi-stream zoom column layout

- Use directory-level stream name in cluster_segments() instead of
fragile stream.json marker value
- Include stream path component in JS fetch URLs for segment
content load and delete (fixes 404s)
- Render zoom timeline pills in side-by-side columns when multiple
streams exist on a day
- Add import.apple fixture stream on 20240101 for multi-stream testing

+19 -6
+13 -2
apps/transcripts/workspace.html
··· 1432 1432 1433 1433 function buildZoomSegments() { 1434 1434 const filtered = filterSegmentsInRange(); 1435 + const streams = [...new Set(filtered.map(s => s.stream))].sort(); 1436 + const colCount = streams.length; 1435 1437 zoomSegments.innerHTML = ''; 1436 1438 1437 1439 if (filtered.length === 0) { ··· 1452 1454 1453 1455 const pill = document.createElement('div'); 1454 1456 pill.className = 'tr-zoom-pill'; 1457 + 1458 + if (colCount > 1) { 1459 + const colIdx = streams.indexOf(seg.stream); 1460 + const colWidth = 100 / colCount; 1461 + const gap = 0.5; 1462 + pill.style.left = (colIdx * colWidth + gap / 2) + '%'; 1463 + pill.style.right = 'auto'; 1464 + pill.style.width = (colWidth - gap) + '%'; 1465 + } 1455 1466 1456 1467 // Determine pill type based on content 1457 1468 const hasAudio = seg.types.includes('audio'); ··· 1523 1534 tabsContainer.innerHTML = ''; 1524 1535 panel.innerHTML = '<div class="tr-unified-empty"><p>Loading segment...</p></div>'; 1525 1536 1526 - fetch(`/app/transcripts/api/segment/${day}/${seg.key}`) 1537 + fetch(`/app/transcripts/api/segment/${day}/${seg.stream}/${seg.key}`) 1527 1538 .then(r => r.json()) 1528 1539 .then(data => { 1529 1540 if (!selectedSegment || selectedSegment.key !== segmentToken) { ··· 2352 2363 if (!confirm(confirmMsg)) return; 2353 2364 2354 2365 try { 2355 - const response = await fetch(`/app/transcripts/api/segment/${day}/${seg.key}`, { 2366 + const response = await fetch(`/app/transcripts/api/segment/${day}/${seg.stream}/${seg.key}`, { 2356 2367 method: 'DELETE' 2357 2368 }); 2358 2369
+2
tests/fixtures/journal/20240101/import.apple/140000_300/audio.jsonl
··· 1 + {"raw": "audio.flac"} 2 + {"start": "00:00:05", "end": "00:00:10", "text": "This is an imported Apple recording for testing."}
+1
tests/fixtures/journal/20240101/import.apple/140000_300/stream.json
··· 1 + {"stream": "import.apple", "prev_day": null, "prev_segment": null, "seq": 1}
+2 -2
tests/test_cluster_full.py
··· 31 31 md, counts = mod.cluster( 32 32 "20240101", sources={"audio": True, "screen": False, "agents": True} 33 33 ) 34 - # Count: audio.jsonl (1) + audio.md (1) + screen.md (1) = 3 entries 35 - assert counts["audio"] == 1 34 + # Audio entries come from 2 segments on 20240101 (default + import.apple) 35 + assert counts["audio"] == 2 36 36 assert counts["agents"] == 2 # audio.md + screen.md 37 37 assert "Audio Transcript" in md 38 38 # Now uses insight format: "### {stem} summary"
+1 -2
think/cluster.py
··· 439 439 start_str = start_time.strftime("%H:%M") 440 440 end_str = end_time.strftime("%H:%M") 441 441 442 - marker = read_segment_stream(seg_path) 443 442 segments.append( 444 443 { 445 444 "key": seg_path.name, 446 445 "start": start_str, 447 446 "end": end_str, 448 447 "types": types, 449 - "stream": marker.get("stream") if marker else None, 448 + "stream": stream_name, 450 449 } 451 450 ) 452 451