fix(chat): 优化权限请求折叠状态
Build Push and Deploy / docker-image (push) Successful in 2m28s
Build Push and Deploy / deploy-fallback-log (push) Has been skipped

This commit is contained in:
2026-06-08 13:44:23 +08:00
parent e32823e4b5
commit d31565d52c
+68 -53
View File
@@ -20,6 +20,7 @@ import {
alpha,
useTheme,
} from "@mui/material";
import type { Theme } from "@mui/material/styles";
import ContentCopyRounded from "@mui/icons-material/ContentCopyRounded";
import RefreshRounded from "@mui/icons-material/RefreshRounded";
import EditRounded from "@mui/icons-material/EditRounded";
@@ -149,6 +150,27 @@ const getPermissionStatusLabel = (status: NonNullable<Message["permissions"]>[nu
};
const pendingPermissionColor = "#f9a825";
const approvedOncePermissionColor = "#00838f";
const getPermissionStatusColor = (
status: NonNullable<Message["permissions"]>[number]["status"],
theme: Theme,
) => {
if (status === "approved_once") return approvedOncePermissionColor;
if (status === "approved_always") return theme.palette.success.main;
if (status === "rejected" || status === "error") return theme.palette.error.main;
return pendingPermissionColor;
};
const getPermissionStatusTextColor = (
status: NonNullable<Message["permissions"]>[number]["status"],
theme: Theme,
) => {
if (status === "approved_once") return "#006c78";
if (status === "approved_always") return theme.palette.success.dark;
if (status === "rejected" || status === "error") return theme.palette.error.main;
return "#8a5a00";
};
const PermissionRequestCard = ({
permission,
@@ -162,19 +184,9 @@ const PermissionRequestCard = ({
const isSubmitting = permission.status === "submitting";
const primaryValue = getPermissionPrimaryValue(permission);
const metadataText = formatMetadata(permission.metadata);
const accentColor =
permission.status === "rejected" || permission.status === "error"
? theme.palette.error.main
: permission.status === "pending" || permission.status === "submitting"
? pendingPermissionColor
: theme.palette.success.main;
const accentColor = getPermissionStatusColor(permission.status, theme);
const statusTextColor = getPermissionStatusTextColor(permission.status, theme);
const statusLabel = getPermissionStatusLabel(permission.status);
const statusColor =
permission.status === "rejected" || permission.status === "error"
? "error"
: permission.status === "pending" || permission.status === "submitting"
? "warning"
: "success";
return (
<Box
@@ -229,25 +241,14 @@ const PermissionRequestCard = ({
</Box>
<Chip
size="small"
color={statusColor}
label={statusLabel}
sx={{
height: 24,
fontSize: "0.7rem",
fontWeight: 800,
borderRadius: "12px",
bgcolor:
statusColor === "success"
? alpha(theme.palette.success.main, 0.12)
: statusColor === "error"
? alpha(theme.palette.error.main, 0.1)
: alpha(pendingPermissionColor, 0.14),
color:
statusColor === "success"
? theme.palette.success.dark
: statusColor === "error"
? theme.palette.error.main
: "#8a5a00",
bgcolor: alpha(accentColor, 0.12),
color: statusTextColor,
"& .MuiChip-label": { px: 1 },
}}
/>
@@ -401,9 +402,11 @@ const PermissionRequestCard = ({
const PermissionRequestGroup = ({
permissions,
isRunning,
onReply,
}: {
permissions: NonNullable<Message["permissions"]>;
isRunning: boolean;
onReply: (requestId: string, reply: PermissionReply) => void;
}) => {
const theme = useTheme();
@@ -422,11 +425,12 @@ const PermissionRequestGroup = ({
);
const summaryItems = [
{ label: "共", value: permissions.length, color: theme.palette.text.secondary },
{ label: "允许一次", value: onceCount, color: "#00838f" },
{ label: "始终允许", value: alwaysCount, color: theme.palette.success.main },
{ label: "拒绝", value: rejectedCount, color: theme.palette.error.main },
{ label: "允许一次", value: onceCount, color: getPermissionStatusColor("approved_once", theme), textColor: getPermissionStatusTextColor("approved_once", theme) },
{ label: "始终允许", value: alwaysCount, color: getPermissionStatusColor("approved_always", theme), textColor: getPermissionStatusTextColor("approved_always", theme) },
{ label: "拒绝", value: rejectedCount, color: getPermissionStatusColor("rejected", theme), textColor: getPermissionStatusTextColor("rejected", theme) },
];
const chipColor = pendingCount > 0 ? pendingPermissionColor : rejectedCount > 0 ? theme.palette.error.main : theme.palette.success.main;
const chipColor = pendingCount > 0 ? getPermissionStatusColor("pending", theme) : rejectedCount > 0 ? getPermissionStatusColor("rejected", theme) : getPermissionStatusColor("approved_always", theme);
const chipTextColor = pendingCount > 0 ? getPermissionStatusTextColor("pending", theme) : rejectedCount > 0 ? getPermissionStatusTextColor("rejected", theme) : getPermissionStatusTextColor("approved_always", theme);
return (
<Box
@@ -498,14 +502,20 @@ const PermissionRequestGroup = ({
borderRadius: "11px",
bgcolor: alpha(item.color, 0.08),
border: `1px solid ${alpha(item.color, 0.12)}`,
color: item.color,
color: "textColor" in item ? item.textColor : item.color,
fontSize: "0.7rem",
fontWeight: 800,
lineHeight: 1,
whiteSpace: "nowrap",
}}
>
<Box component="span" sx={{ color: alpha(item.color, 0.82), fontWeight: 700 }}>
<Box
component="span"
sx={{
color: "textColor" in item ? item.textColor : item.color,
fontWeight: 700,
}}
>
{item.label}
</Box>
<Box component="span">{item.value} </Box>
@@ -513,19 +523,21 @@ const PermissionRequestGroup = ({
))}
</Stack>
</Box>
<Chip
size="small"
label={`待确认 ${pendingCount}`}
sx={{
height: 24,
borderRadius: "12px",
fontSize: "0.7rem",
fontWeight: 800,
color: chipColor,
bgcolor: alpha(chipColor, 0.1),
"& .MuiChip-label": { px: 1 },
}}
/>
{isRunning && pendingCount > 0 ? (
<Chip
size="small"
label={`待确认 ${pendingCount}`}
sx={{
height: 24,
borderRadius: "12px",
fontSize: "0.7rem",
fontWeight: 800,
color: chipTextColor,
bgcolor: alpha(chipColor, 0.1),
"& .MuiChip-label": { px: 1 },
}}
/>
) : null}
<IconButton
size="small"
aria-label={expanded ? "收起权限请求" : "展开权限请求"}
@@ -545,17 +557,13 @@ const PermissionRequestGroup = ({
</IconButton>
</Stack>
{!expanded && !hasPendingPermissions && latestPermissions.length > 0 ? (
{!expanded && isRunning && !hasPendingPermissions && latestPermissions.length > 0 ? (
<Stack spacing={0} sx={{ px: 1.5, pb: 1.25 }}>
{latestPermissions.map((permission, index) => {
const primaryValue = getPermissionPrimaryValue(permission);
const isLast = index === latestPermissions.length - 1;
const itemColor =
permission.status === "rejected" || permission.status === "error"
? theme.palette.error.main
: permission.status === "approved_once" || permission.status === "approved_always"
? theme.palette.success.main
: pendingPermissionColor;
const itemColor = getPermissionStatusColor(permission.status, theme);
const itemTextColor = getPermissionStatusTextColor(permission.status, theme);
return (
<Stack
@@ -607,7 +615,7 @@ const PermissionRequestGroup = ({
borderRadius: "11px",
fontSize: "0.68rem",
fontWeight: 800,
color: itemColor,
color: itemTextColor,
bgcolor: alpha(itemColor, 0.08),
"& .MuiChip-label": { px: 0.85 },
}}
@@ -619,7 +627,7 @@ const PermissionRequestGroup = ({
) : null}
<AnimatePresence initial={false}>
{!expanded && hasPendingPermissions ? (
{!expanded && isRunning && hasPendingPermissions ? (
<motion.div
key="pending-permissions"
initial={{ opacity: 0, y: -10, height: 0 }}
@@ -678,6 +686,12 @@ export const AgentTurn = React.memo(
const [isEditing, setIsEditing] = React.useState(false);
const [editDraft, setEditDraft] = React.useState(message.content);
const rootMessageId = message.branchRootId ?? message.id;
const isProgressComplete = message.progress?.some(
(item) => item.phase === "complete" && item.status === "completed",
) ?? false;
const isProgressRunning = !isErrorMessage && !isProgressComplete && (
message.progress?.some((item) => item.status === "running") ?? false
);
const parsedAssistantSections = useMemo(
() =>
@@ -956,6 +970,7 @@ export const AgentTurn = React.memo(
{message.permissions?.length ? (
<PermissionRequestGroup
permissions={message.permissions}
isRunning={isProgressRunning}
onReply={onReplyPermission}
/>
) : null}