增加会话标题重命名功能,优化历史面板交互
This commit is contained in:
@@ -8,34 +8,69 @@ import {
|
||||
Box,
|
||||
IconButton,
|
||||
Stack,
|
||||
TextField,
|
||||
Tooltip,
|
||||
Typography,
|
||||
alpha,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import EditNoteRounded from "@mui/icons-material/EditNoteRounded";
|
||||
import CheckRounded from "@mui/icons-material/CheckRounded";
|
||||
import CloseRounded from "@mui/icons-material/CloseRounded";
|
||||
import EditRounded from "@mui/icons-material/EditRounded";
|
||||
import EditNoteRounded from "@mui/icons-material/EditNoteRounded";
|
||||
import HistoryRounded from "@mui/icons-material/HistoryRounded";
|
||||
|
||||
type AgentHeaderProps = {
|
||||
sessionTitle?: string;
|
||||
canRenameSessionTitle?: boolean;
|
||||
isHydrating?: boolean;
|
||||
isStreaming: boolean;
|
||||
isHistoryOpen: boolean;
|
||||
onHistoryToggle: () => void;
|
||||
onRenameSessionTitle?: (title: string) => void;
|
||||
onNewConversation: () => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export const AgentHeader = ({
|
||||
sessionTitle,
|
||||
canRenameSessionTitle = false,
|
||||
isHydrating = false,
|
||||
isStreaming,
|
||||
isHistoryOpen,
|
||||
onHistoryToggle,
|
||||
onRenameSessionTitle,
|
||||
onNewConversation,
|
||||
onClose,
|
||||
}: AgentHeaderProps) => {
|
||||
const theme = useTheme();
|
||||
const displayTitle = sessionTitle?.trim() || "TJWater Agent";
|
||||
const [isEditingTitle, setIsEditingTitle] = React.useState(false);
|
||||
const [draftTitle, setDraftTitle] = React.useState(sessionTitle?.trim() || "");
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isEditingTitle) {
|
||||
setDraftTitle(sessionTitle?.trim() || "");
|
||||
}
|
||||
}, [isEditingTitle, sessionTitle]);
|
||||
|
||||
const handleStartEditing = () => {
|
||||
if (!canRenameSessionTitle || isHydrating || isStreaming) return;
|
||||
setDraftTitle(sessionTitle?.trim() || "");
|
||||
setIsEditingTitle(true);
|
||||
};
|
||||
|
||||
const handleCancelEditing = () => {
|
||||
setDraftTitle(sessionTitle?.trim() || "");
|
||||
setIsEditingTitle(false);
|
||||
};
|
||||
|
||||
const handleConfirmEditing = () => {
|
||||
const normalizedTitle = draftTitle.trim();
|
||||
if (!normalizedTitle) return;
|
||||
onRenameSessionTitle?.(normalizedTitle);
|
||||
setIsEditingTitle(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
@@ -89,35 +124,142 @@ export const AgentHeader = ({
|
||||
"0%": { boxShadow: `0 0 0 0 ${alpha("#ff9800", 0.7)}` },
|
||||
"70%": { boxShadow: `0 0 0 6px ${alpha("#ff9800", 0)}` },
|
||||
"100%": { boxShadow: `0 0 0 0 ${alpha("#ff9800", 0)}` },
|
||||
}
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</motion.div>
|
||||
<Box sx={{ minWidth: 0 }}>
|
||||
<Typography
|
||||
variant="h6"
|
||||
fontWeight={800}
|
||||
sx={{
|
||||
background: `linear-gradient(90deg, #01579b, #00838f)`,
|
||||
backgroundClip: "text",
|
||||
color: "transparent",
|
||||
letterSpacing: -0.3,
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
maxWidth: { xs: "calc(100vw - 220px)", sm: 320 },
|
||||
}}
|
||||
>
|
||||
{displayTitle}
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary" fontWeight={500}>
|
||||
{isStreaming
|
||||
? "正在思考分析任务..."
|
||||
: displayTitle === "TJWater Agent"
|
||||
? "基于大模型的水力分析引擎"
|
||||
: "当前会话标题"}
|
||||
</Typography>
|
||||
<Box sx={{ minWidth: 0, minHeight: 52, display: "flex", flexDirection: "column", justifyContent: "center" }}>
|
||||
{isEditingTitle ? (
|
||||
<Box>
|
||||
<Stack direction="row" spacing={0.75} alignItems="center" sx={{ width: { xs: "calc(100vw - 256px)", sm: 280 }, transform: "translateY(2px)" }}>
|
||||
<TextField
|
||||
value={draftTitle}
|
||||
onChange={(event) => setDraftTitle(event.target.value)}
|
||||
size="small"
|
||||
autoFocus
|
||||
placeholder="请输入对话标题"
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
handleConfirmEditing();
|
||||
} else if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
handleCancelEditing();
|
||||
}
|
||||
}}
|
||||
sx={{
|
||||
flex: 1,
|
||||
minWidth: 0,
|
||||
"& .MuiOutlinedInput-root": {
|
||||
height: 34,
|
||||
bgcolor: alpha("#fff", 0.7),
|
||||
borderRadius: 1.5,
|
||||
transition: "all 0.2s ease-in-out",
|
||||
"& fieldset": {
|
||||
borderColor: alpha("#000", 0.08),
|
||||
},
|
||||
"&:hover fieldset": {
|
||||
borderColor: alpha(theme.palette.primary.main, 0.4),
|
||||
},
|
||||
"&.Mui-focused fieldset": {
|
||||
borderColor: theme.palette.primary.main,
|
||||
borderWidth: "1.5px",
|
||||
boxShadow: `0 0 0 3px ${alpha(theme.palette.primary.main, 0.1)}`,
|
||||
},
|
||||
},
|
||||
"& .MuiInputBase-input": {
|
||||
padding: "4px 12px",
|
||||
fontSize: "1.05rem",
|
||||
fontWeight: 700,
|
||||
color: theme.palette.text.primary,
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
size="small"
|
||||
aria-label="确认"
|
||||
onClick={handleConfirmEditing}
|
||||
disabled={!draftTitle.trim()}
|
||||
sx={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
color: "success.main",
|
||||
bgcolor: alpha(theme.palette.success.main, 0.1),
|
||||
"&:hover": { bgcolor: alpha(theme.palette.success.main, 0.2) },
|
||||
}}
|
||||
>
|
||||
<CheckRounded sx={{ fontSize: 18 }} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="small"
|
||||
aria-label="取消"
|
||||
onClick={handleCancelEditing}
|
||||
sx={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
color: "text.secondary",
|
||||
bgcolor: alpha("#000", 0.05),
|
||||
"&:hover": { bgcolor: alpha("#000", 0.1) },
|
||||
}}
|
||||
>
|
||||
<CloseRounded sx={{ fontSize: 18 }} />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</Box>
|
||||
) : (
|
||||
<>
|
||||
<Stack direction="row" spacing={0.75} alignItems="center" sx={{ minWidth: 0 }}>
|
||||
<Typography
|
||||
variant="h6"
|
||||
fontWeight={800}
|
||||
sx={{
|
||||
background: `linear-gradient(90deg, #01579b, #00838f)`,
|
||||
backgroundClip: "text",
|
||||
color: "transparent",
|
||||
letterSpacing: -0.3,
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
maxWidth: { xs: "calc(100vw - 256px)", sm: 284 },
|
||||
}}
|
||||
>
|
||||
{displayTitle}
|
||||
</Typography>
|
||||
{canRenameSessionTitle ? (
|
||||
<Tooltip title="修改对话标题">
|
||||
<span>
|
||||
<IconButton
|
||||
size="small"
|
||||
aria-label="修改对话标题"
|
||||
onClick={handleStartEditing}
|
||||
disabled={isHydrating || isStreaming}
|
||||
sx={{
|
||||
width: 24,
|
||||
height: 24,
|
||||
color: "text.secondary",
|
||||
bgcolor: alpha("#fff", 0.45),
|
||||
"&:hover": {
|
||||
color: "primary.main",
|
||||
bgcolor: alpha(theme.palette.primary.main, 0.08),
|
||||
},
|
||||
}}
|
||||
>
|
||||
<EditRounded sx={{ fontSize: 16 }} />
|
||||
</IconButton>
|
||||
</span>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
</Stack>
|
||||
<Typography variant="caption" color="text.secondary" fontWeight={500}>
|
||||
{isStreaming
|
||||
? "正在思考分析任务..."
|
||||
: displayTitle === "TJWater Agent"
|
||||
? "基于大模型的水力分析引擎"
|
||||
: "当前会话标题"}
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user