Feat: Add TTS to analysis tabs and improve Research Queries UX
- Add TextToSpeechButton to Outline, Takeaways, and Guest tabs in analysis phase - Add help icon with tooltip to Research Queries explaining the workflow - Change Run Research button to show 'Next: Select Query' when disabled - Add hint text 'Select a query to continue' when no queries selected
This commit is contained in:
@@ -3,12 +3,15 @@ import { Stack, Box, Typography, Chip, Paper } from "@mui/material";
|
||||
import { Quiz as TalkIcon } from "@mui/icons-material";
|
||||
import { PodcastAnalysis } from "../../types";
|
||||
import { AnalysisTabContent } from "../AnalysisTabNav";
|
||||
import { TextToSpeechButton } from "../../../shared/TextToSpeechButton";
|
||||
|
||||
interface GuestTabProps {
|
||||
analysis: PodcastAnalysis;
|
||||
}
|
||||
|
||||
export const GuestTab: React.FC<GuestTabProps> = ({ analysis }) => {
|
||||
const talkingPointsText = analysis.guest_talking_points?.map((p, idx) => `Question ${idx + 1}: ${p}`).join(" ") || "";
|
||||
|
||||
if (!analysis.guest_talking_points || analysis.guest_talking_points.length === 0) {
|
||||
return (
|
||||
<AnalysisTabContent title="Guest Talking Points" icon={<TalkIcon />}>
|
||||
@@ -22,6 +25,9 @@ export const GuestTab: React.FC<GuestTabProps> = ({ analysis }) => {
|
||||
return (
|
||||
<AnalysisTabContent title="Guest Talking Points" icon={<TalkIcon />}>
|
||||
<Stack spacing={2}>
|
||||
<Box sx={{ display: "flex", justifyContent: "flex-end", mb: 1 }}>
|
||||
<TextToSpeechButton text={talkingPointsText} size="small" showSettings />
|
||||
</Box>
|
||||
{analysis.guest_talking_points.map((point: string, idx: number) => (
|
||||
<Paper key={idx} elevation={0} sx={{ p: 2, bgcolor: "#faf5ff", border: "1px solid rgba(168,85,247,0.2)", borderRadius: 2, display: "flex", alignItems: "flex-start", gap: 1.5 }}>
|
||||
<Chip label="Q" size="small" sx={{ minWidth: 24, bgcolor: "#a855f7", color: "#fff" }} />
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React from "react";
|
||||
import { Stack, Box, Typography, Chip, TextField, IconButton } from "@mui/material";
|
||||
import { ListAlt as ListAltIcon, Add as AddIcon } from "@mui/icons-material";
|
||||
import { Stack, Box, Typography, Chip } from "@mui/material";
|
||||
import { ListAlt as ListAltIcon } from "@mui/icons-material";
|
||||
import { PodcastAnalysis } from "../../types";
|
||||
import { AnalysisTabContent } from "../AnalysisTabNav";
|
||||
import { TextToSpeechButton } from "../../../shared/TextToSpeechButton";
|
||||
|
||||
interface OutlineTabProps {
|
||||
analysis: PodcastAnalysis;
|
||||
@@ -11,6 +12,11 @@ interface OutlineTabProps {
|
||||
}
|
||||
|
||||
export const OutlineTab: React.FC<OutlineTabProps> = ({ analysis, isEditing, onUpdateOutline }) => {
|
||||
const outlineText = analysis.suggestedOutlines?.map((outline, idx) => {
|
||||
const segments = outline.segments?.map((s, sIdx) => `${sIdx + 1}. ${s}`).join(" ");
|
||||
return `Option ${idx + 1}: ${outline.title}. ${segments}`;
|
||||
}).join(" ") || "";
|
||||
|
||||
return (
|
||||
<AnalysisTabContent title="Episode Outline" icon={<ListAltIcon />}>
|
||||
<Stack spacing={3}>
|
||||
@@ -20,6 +26,10 @@ export const OutlineTab: React.FC<OutlineTabProps> = ({ analysis, isEditing, onU
|
||||
<Typography variant="subtitle2" sx={{ color: "#0f172a", fontWeight: 700 }}>
|
||||
Option {idx + 1}: {outline.title}
|
||||
</Typography>
|
||||
<TextToSpeechButton
|
||||
text={`Option ${idx + 1}: ${outline.title}. ${outline.segments?.map((s, sIdx) => `Step ${sIdx + 1}: ${s}`).join(" ")}`}
|
||||
size="small"
|
||||
/>
|
||||
</Stack>
|
||||
<Stack spacing={1}>
|
||||
{outline.segments?.map((segment: string, sIdx: number) => (
|
||||
|
||||
@@ -3,12 +3,15 @@ import { Stack, Box, Typography, Chip, Paper } from "@mui/material";
|
||||
import { Lightbulb as TipsIcon } from "@mui/icons-material";
|
||||
import { PodcastAnalysis } from "../../types";
|
||||
import { AnalysisTabContent } from "../AnalysisTabNav";
|
||||
import { TextToSpeechButton } from "../../../shared/TextToSpeechButton";
|
||||
|
||||
interface TakeawaysTabProps {
|
||||
analysis: PodcastAnalysis;
|
||||
}
|
||||
|
||||
export const TakeawaysTab: React.FC<TakeawaysTabProps> = ({ analysis }) => {
|
||||
const takeawaysText = analysis.key_takeaways?.map((t, idx) => `Takeaway ${idx + 1}: ${t}`).join(" ") || "";
|
||||
|
||||
if (!analysis.key_takeaways || analysis.key_takeaways.length === 0) {
|
||||
return (
|
||||
<AnalysisTabContent title="Key Takeaways" icon={<TipsIcon />}>
|
||||
@@ -22,6 +25,9 @@ export const TakeawaysTab: React.FC<TakeawaysTabProps> = ({ analysis }) => {
|
||||
return (
|
||||
<AnalysisTabContent title="Key Takeaways" icon={<TipsIcon />}>
|
||||
<Stack spacing={2}>
|
||||
<Box sx={{ display: "flex", justifyContent: "flex-end", mb: 1 }}>
|
||||
<TextToSpeechButton text={takeawaysText} size="small" showSettings />
|
||||
</Box>
|
||||
{analysis.key_takeaways.map((takeaway: string, idx: number) => (
|
||||
<Paper key={idx} elevation={0} sx={{ p: 2, bgcolor: "#f0fdf4", border: "1px solid rgba(34,197,94,0.2)", borderRadius: 2, display: "flex", alignItems: "flex-start", gap: 1.5 }}>
|
||||
<Chip label={idx + 1} size="small" sx={{ minWidth: 24, bgcolor: "#22c55e", color: "#fff" }} />
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
TextField,
|
||||
IconButton,
|
||||
} from "@mui/material";
|
||||
import { Search as SearchIcon, AutoAwesome as AutoAwesomeIcon, Refresh as RefreshIcon, Edit as EditIcon, Delete as DeleteIcon, CheckCircle as CheckCircleIcon } from "@mui/icons-material";
|
||||
import { Search as SearchIcon, AutoAwesome as AutoAwesomeIcon, Refresh as RefreshIcon, Edit as EditIcon, Delete as DeleteIcon, CheckCircle as CheckCircleIcon, Help as HelpIcon } from "@mui/icons-material";
|
||||
import { ResearchProvider } from "../../../services/blogWriterApi";
|
||||
import { Query } from "../types";
|
||||
import { GlassyCard, glassyCardSx, PrimaryButton, SecondaryButton } from "../ui";
|
||||
@@ -113,6 +113,24 @@ export const QuerySelection: React.FC<QuerySelectionProps> = ({
|
||||
<SearchIcon />
|
||||
Research Queries
|
||||
</Typography>
|
||||
<Tooltip
|
||||
title={
|
||||
<Box sx={{ maxWidth: 320 }}>
|
||||
<Typography variant="body2" sx={{ fontWeight: 600, mb: 1 }}>
|
||||
How it works:
|
||||
</Typography>
|
||||
<Typography variant="body2" component="div" sx={{ fontSize: "0.8125rem", lineHeight: 1.5 }}>
|
||||
1. Select one or more research queries to focus your research.<br/><br/>
|
||||
2. Click "Run Research" to gather web and semantic insights.<br/><br/>
|
||||
3. The research results will be used to generate a factual, relevant podcast script.
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
arrow
|
||||
placement="top"
|
||||
>
|
||||
<HelpIcon fontSize="small" sx={{ color: "#94a3b8", cursor: "help", ml: 0.5 }} />
|
||||
</Tooltip>
|
||||
<Tooltip title="Regenerate research queries with custom feedback">
|
||||
<PrimaryButton
|
||||
size="small"
|
||||
@@ -256,7 +274,12 @@ export const QuerySelection: React.FC<QuerySelectionProps> = ({
|
||||
))}
|
||||
</List>
|
||||
|
||||
<Box sx={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<Box sx={{ display: "flex", justifyContent: "flex-end", alignItems: "center", gap: 2, flexWrap: "wrap" }}>
|
||||
{selectedCount === 0 && (
|
||||
<Typography variant="caption" sx={{ color: "#64748b", fontStyle: "italic" }}>
|
||||
Select a query to continue
|
||||
</Typography>
|
||||
)}
|
||||
<PrimaryButton
|
||||
onClick={onRunResearch}
|
||||
disabled={selectedCount === 0 || isResearching}
|
||||
@@ -268,7 +291,7 @@ export const QuerySelection: React.FC<QuerySelectionProps> = ({
|
||||
: `Run research with ${selectedCount} selected ${selectedCount === 1 ? "query" : "queries"}`
|
||||
}
|
||||
>
|
||||
{isResearching ? "Running Research..." : "Run Research"}
|
||||
{isResearching ? "Running Research..." : selectedCount === 0 ? "Next: Select Query" : "Run Research"}
|
||||
</PrimaryButton>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
Reference in New Issue
Block a user