Commit Graph

12 Commits

Author SHA1 Message Date
Krishna Kumar
09430e8e66 experiment(PodcastPlayerView): handle AVAudioSession interruptions — pause on call, resume after 2026-05-23 00:54:12 -05:00
Krishna Kumar
140c761850 experiment(PodcastPlayerView): add Retry button to failedView, clear pollingTask on failure 2026-05-23 00:52:55 -05:00
Krishna Kumar
f7d5c129fc experiment(PodcastPlayerView): add playbackSpeed control (0.75×/1×/1.25×/1.5×/2×) with menu 2026-05-23 00:51:23 -05:00
Krishna Kumar
be4a4c3a5f experiment(PodcastPlayerView): add lock screen skipBackwardCommand/skipForwardCommand (15s) 2026-05-23 00:50:07 -05:00
Krishna Kumar
fc0f98f45a experiment(PodcastPlayerView): add skip ±15s buttons to full player controls 2026-05-23 00:48:46 -05:00
Krishna Kumar
ab72611d7b Podcast UX baseline: mini player during generation, end-of-track reset, library view
- Mini player shows as soon as generation starts (not just when audio ready)
- Idempotency: tapping headphones again during generation is a no-op
- Playback resets to position 0 on completion (standard player behavior)
- Stop button in full player visible during generation
- PodcastLibraryView lists all generated podcasts
- Sparkles menu gains Podcasts entry
- Fix AnalyticsService Sendable error (serialize before Task.detached boundary)
2026-05-23 00:46:46 -05:00
Krishna Kumar
18423a7633 Add iOS Langfuse analytics via backend events endpoint
Some checks failed
CI / build-and-deploy (push) Failing after 8s
AnalyticsService.track() is fire-and-forget — posts to POST /v1/marks/events
which records a Langfuse trace. No external SDK, no Sentry on iOS side.

Events tracked:
- podcast.started (url, cached)
- podcast.completed (url, durationSec) — fires at 97% playback
- podcast.failed (url, error)
- search.semantic (query, resultCount)
- enrich.completed (count)
- collections.generated (collectionCount)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 21:30:03 -05:00
Krishna Kumar
38cd72416c Xcode project sync and seek async fix
All checks were successful
CI / build-and-deploy (push) Successful in 23s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 16:59:22 -05:00
Krishna Kumar
abe35f47d6 Add mini-player, per-row podcast button, and resume-from-position
Some checks failed
CI / build-and-deploy (push) Failing after 9s
- MiniPlayerView: persistent bar above tab bar, shows title/progress,
  play/pause and close buttons, slides in when audio starts
- PodcastPlayerViewModel lifted to BookmarksViewModel so it persists
  across sheet dismissals — audio keeps playing in background
- Headphone button on every bookmark row (filled = cached, outline = not)
- start() is idempotent: re-tapping same article resumes without restart
- stop() saves playback position before teardown (PodcastProgress)
- setupPlayer restores saved position on next open
- BrowserView keeps its own local VM (stopOnDismiss: true)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 16:19:21 -05:00
Krishna Kumar
544fcd442c Add podcast index: JSON mapping of article URLs to cached MP3 files
All checks were successful
CI / build-and-deploy (push) Successful in 25s
PodcastIndex.swift stores {articleUrl, filename, title, createdAt} in
App Support/podcasts/index.json. Updated on every successful download.
Enables building a podcast library view later.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 15:45:38 -05:00
Krishna Kumar
476ed9b2a1 Podcast: local MP3 cache, AirPods, lock screen controls, background audio
All checks were successful
CI / build-and-deploy (push) Successful in 23s
- Cache generated MP3 to App Support/podcasts/<hash>.mp3 — re-opening
  the same article plays instantly with zero backend calls
- AVAudioSession .playback/.spokenAudio + .allowBluetoothA2DP — routes
  audio to AirPods and Bluetooth headsets
- MPRemoteCommandCenter play/pause/seek — AirPods controls + lock screen
- MPNowPlayingInfoCenter — title and scrubber on lock screen
- UIBackgroundModes: audio in Info.plist — playback continues when backgrounded
- Auto-play when player is ready

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 15:39:14 -05:00
Krishna Kumar
bc16ee986d Migrate AI features to backend, add podcast generation
- ClaudeService: replace direct OpenRouter calls with backend endpoints
  (/v1/marks/enrich, /v1/marks/search, /v1/marks/collections)
- ClaudeService: add podcast methods (generate, status, downloadAudio)
- SettingsView: replace OpenRouter key field with backend URL + API key
- PodcastPlayerView: new sheet — generate → poll → AVPlayer playback
- BrowserView: headphones toolbar button triggers podcast for current URL
- BookmarksView: "Convert to Podcast" context menu item + sheet

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 18:39:15 -05:00