Major feature update: Enhanced RSS, voice fixes, dark mode, and UI improvements

## New Features
- Article Summary Mode: AI-generated 30-second summaries with complexity analysis
- Reading Stats Dashboard: Track articles read, listening time, and streaks
- Bookmark/Resume: Auto-save progress when pausing, resume from where you left off
- Audio Export: Export articles as downloadable WAV files
- RSS Feed Manager: Subscribe to feeds with real-time validation and 31+ recommendations
- Smart Speed: Auto-adjust playback based on article complexity
- Voice Moods: Quick presets for different listening scenarios

## RSS Enhancements
- Expanded recommendations from 8 to 31 sources across 5 categories:
  * General News (9 sources)
  * Technology (8 sources)
  * Business & Finance (5 sources)
  * Science & Research (5 sources)
  * International News (4 sources)
- Real-time URL validation with visual feedback
- Detailed error messages for different failure scenarios
- Always-visible categorized recommendations
- Auto-loading articles when feeds are added

## Bug Fixes
- Fixed voice selection: Selected voice now consistently applies to playback
- Implemented voice generation counter to prevent voice mixing between paragraphs
- Fixed speed control to snap to clean 0.5 increments (1.0, 1.5, 2.0, etc.)
- Fixed dark mode toggle by configuring Tailwind CDN for class-based dark mode
- Removed vibe visualizer animation

## UI/UX Improvements
- Redesigned voice selector with prominent voice panel and preview functionality
- Added voice cards with emojis and descriptions
- Enhanced feature toolbar with quick access to all new features
- Improved reader view with better typography and reading modes
- Added ambient reading modes (clean, sepia, night light)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Tony0410
2025-11-28 00:42:14 +00:00
parent 3169438f7e
commit 9025d1b8f1
16 changed files with 2198 additions and 128 deletions

View File

@@ -0,0 +1,94 @@
import React from 'react';
import {
BarChart3,
Rss,
Bookmark,
Download,
Zap,
FileText
} from 'lucide-react';
interface FeatureToolbarProps {
onOpenStats: () => void;
onOpenRSS: () => void;
onOpenBookmarks: () => void;
onExportAudio: () => void;
smartSpeedEnabled: boolean;
onToggleSmartSpeed: () => void;
hasBookmark?: boolean;
canExport?: boolean;
isExporting?: boolean;
}
export const FeatureToolbar: React.FC<FeatureToolbarProps> = ({
onOpenStats,
onOpenRSS,
onOpenBookmarks,
onExportAudio,
smartSpeedEnabled,
onToggleSmartSpeed,
hasBookmark,
canExport,
isExporting
}) => {
const buttons = [
{
icon: BarChart3,
label: 'Stats',
onClick: onOpenStats,
color: 'text-blue-500',
bgColor: 'bg-blue-50 dark:bg-blue-900/30'
},
{
icon: Rss,
label: 'RSS Feeds',
onClick: onOpenRSS,
color: 'text-orange-500',
bgColor: 'bg-orange-50 dark:bg-orange-900/30'
},
{
icon: Bookmark,
label: 'Bookmarks',
onClick: onOpenBookmarks,
color: hasBookmark ? 'text-yellow-500' : 'text-slate-500',
bgColor: hasBookmark ? 'bg-yellow-50 dark:bg-yellow-900/30' : 'bg-slate-50 dark:bg-slate-800'
},
{
icon: Download,
label: isExporting ? 'Exporting...' : 'Export',
onClick: onExportAudio,
color: 'text-green-500',
bgColor: 'bg-green-50 dark:bg-green-900/30',
disabled: !canExport || isExporting
},
{
icon: Zap,
label: 'Smart Speed',
onClick: onToggleSmartSpeed,
color: smartSpeedEnabled ? 'text-purple-500' : 'text-slate-400',
bgColor: smartSpeedEnabled ? 'bg-purple-50 dark:bg-purple-900/30' : 'bg-slate-50 dark:bg-slate-800',
active: smartSpeedEnabled
}
];
return (
<div className="flex items-center gap-2 overflow-x-auto pb-2">
{buttons.map((btn, i) => (
<button
key={i}
onClick={btn.onClick}
disabled={btn.disabled}
className={`
flex items-center gap-2 px-3 py-2 rounded-xl text-sm font-medium transition-all whitespace-nowrap
${btn.bgColor} ${btn.color}
${btn.disabled ? 'opacity-50 cursor-not-allowed' : 'hover:shadow-md active:scale-95'}
${btn.active ? 'ring-2 ring-purple-500 ring-offset-1' : ''}
`}
>
<btn.icon className="w-4 h-4" />
<span className="hidden sm:inline">{btn.label}</span>
</button>
))}
</div>
);
};