feat: show tool approval in Awaiting Replies + Allow All button
- PendingRepliesBlock: show "user — awaiting approval" entry when pendingApprovals exist, with amber pulse dot and ShieldQuestion icon - ToolApprovalSheet: add grey "Allow all" button (separated by divider) right of Allow/Deny — enables autoAllowAll setting instantly
This commit is contained in:
parent
73661c80a4
commit
d92ab08416
2 changed files with 102 additions and 18 deletions
|
|
@ -104,6 +104,7 @@ const RESPOND_TIMEOUT_MS = 10_000;
|
|||
export const ToolApprovalSheet: React.FC = () => {
|
||||
const pendingApprovals = useStore((s) => s.pendingApprovals);
|
||||
const respondToToolApproval = useStore((s) => s.respondToToolApproval);
|
||||
const updateToolApprovalSettings = useStore((s) => s.updateToolApprovalSettings);
|
||||
const teams = useStore((s) => s.teams);
|
||||
const selectedTeamName = useStore((s) => s.selectedTeamName);
|
||||
const { isLight } = useTheme();
|
||||
|
|
@ -281,6 +282,32 @@ export const ToolApprovalSheet: React.FC = () => {
|
|||
>
|
||||
Deny
|
||||
</button>
|
||||
|
||||
<div className="mx-1 h-4 w-px" style={{ backgroundColor: 'var(--color-border)' }} />
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void updateToolApprovalSettings({ autoAllowAll: true })}
|
||||
className="rounded-md px-2.5 py-1.5 text-[10px] transition-colors"
|
||||
style={{
|
||||
color: 'var(--color-text-muted)',
|
||||
backgroundColor: 'var(--color-surface-raised)',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
Object.assign(e.currentTarget.style, {
|
||||
color: 'var(--color-text-secondary)',
|
||||
backgroundColor: 'var(--color-border-emphasis)',
|
||||
});
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
Object.assign(e.currentTarget.style, {
|
||||
color: 'var(--color-text-muted)',
|
||||
backgroundColor: 'var(--color-surface-raised)',
|
||||
});
|
||||
}}
|
||||
>
|
||||
Allow all
|
||||
</button>
|
||||
</div>
|
||||
{pendingApprovals.length > 1 && (
|
||||
<span className="text-[11px] text-[var(--color-text-muted)]">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { CARD_BG, CARD_BORDER_STYLE, CARD_ICON_MUTED } from '@renderer/constants/cssVariables';
|
||||
import { getTeamColorSet, getThemedBadge } from '@renderer/constants/teamColors';
|
||||
import { useTheme } from '@renderer/hooks/useTheme';
|
||||
import { useStore } from '@renderer/store';
|
||||
import { formatAgentRole } from '@renderer/utils/formatAgentRole';
|
||||
import {
|
||||
agentAvatarUrl,
|
||||
|
|
@ -9,7 +10,7 @@ import {
|
|||
} from '@renderer/utils/memberHelpers';
|
||||
import { nameColorSet } from '@renderer/utils/projectColor';
|
||||
import { formatDistanceToNowStrict } from 'date-fns';
|
||||
import { Users } from 'lucide-react';
|
||||
import { ShieldQuestion, Users } from 'lucide-react';
|
||||
|
||||
import type { ResolvedTeamMember } from '@shared/types';
|
||||
|
||||
|
|
@ -32,6 +33,7 @@ export const PendingRepliesBlock = ({
|
|||
onMemberClick,
|
||||
}: PendingRepliesBlockProps): React.JSX.Element | null => {
|
||||
const { isLight } = useTheme();
|
||||
const pendingApprovals = useStore((s) => s.pendingApprovals);
|
||||
const colorMap = buildMemberColorMap(members);
|
||||
const memberPending = Object.entries(pendingRepliesByMember)
|
||||
.map(([name, sentAtMs]) => ({
|
||||
|
|
@ -49,7 +51,17 @@ export const PendingRepliesBlock = ({
|
|||
teamName: entry.teamName,
|
||||
sentAtMs: entry.sentAtMs,
|
||||
}));
|
||||
const pending = [...memberPending, ...teamPending].sort((a, b) => b.sentAtMs - a.sentAtMs);
|
||||
|
||||
// Tool approvals awaiting user response
|
||||
const userPending = pendingApprovals.map((a) => ({
|
||||
kind: 'user' as const,
|
||||
toolName: a.toolName,
|
||||
sentAtMs: new Date(a.receivedAt).getTime(),
|
||||
}));
|
||||
|
||||
const pending = [...memberPending, ...teamPending, ...userPending].sort(
|
||||
(a, b) => b.sentAtMs - a.sentAtMs
|
||||
);
|
||||
|
||||
if (pending.length === 0) return null;
|
||||
|
||||
|
|
@ -137,45 +149,90 @@ export const PendingRepliesBlock = ({
|
|||
);
|
||||
}
|
||||
|
||||
const colors = nameColorSet(entry.teamName, isLight);
|
||||
if (entry.kind === 'team') {
|
||||
const colors = nameColorSet(entry.teamName, isLight);
|
||||
return (
|
||||
<article
|
||||
key={`pending-reply:team:${entry.teamName}:${entry.sentAtMs}`}
|
||||
className="activity-card-enter-animate overflow-hidden rounded-md"
|
||||
style={{
|
||||
backgroundColor: CARD_BG,
|
||||
border: CARD_BORDER_STYLE,
|
||||
borderLeft: `3px solid ${colors.border}`,
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-2 px-3 py-2">
|
||||
<span className="relative inline-flex shrink-0 items-center justify-center rounded-full bg-[var(--color-surface-raised)] p-1">
|
||||
<Users size={12} style={{ color: colors.border }} />
|
||||
<span className="absolute -bottom-0.5 -right-0.5 flex size-2.5">
|
||||
<span className="absolute inline-flex size-full animate-ping rounded-full bg-emerald-400 opacity-70" />
|
||||
<span className="relative inline-flex size-full rounded-full bg-emerald-500" />
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className="rounded px-1.5 py-0.5 text-[10px] font-medium tracking-wide"
|
||||
style={{
|
||||
backgroundColor: getThemedBadge(colors, isLight),
|
||||
color: colors.text,
|
||||
border: `1px solid ${colors.border}40`,
|
||||
}}
|
||||
title={entry.teamName}
|
||||
>
|
||||
{entry.teamName}
|
||||
</span>
|
||||
<span className="text-[10px]" style={{ color: CARD_ICON_MUTED }}>
|
||||
external team
|
||||
</span>
|
||||
<span
|
||||
className="min-w-0 flex-1 truncate text-[10px]"
|
||||
style={{ color: CARD_ICON_MUTED }}
|
||||
title="Cross-team message sent, awaiting reply"
|
||||
>
|
||||
awaiting reply
|
||||
</span>
|
||||
<span className="shrink-0 text-[10px]" style={{ color: CARD_ICON_MUTED }}>
|
||||
{since}
|
||||
</span>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
// User tool approval pending
|
||||
return (
|
||||
<article
|
||||
key={`pending-reply:team:${entry.teamName}:${entry.sentAtMs}`}
|
||||
key={`pending-reply:user:${entry.sentAtMs}`}
|
||||
className="activity-card-enter-animate overflow-hidden rounded-md"
|
||||
style={{
|
||||
backgroundColor: CARD_BG,
|
||||
border: CARD_BORDER_STYLE,
|
||||
borderLeft: `3px solid ${colors.border}`,
|
||||
borderLeft: '3px solid var(--color-text-muted)',
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-2 px-3 py-2">
|
||||
<span className="relative inline-flex shrink-0 items-center justify-center rounded-full bg-[var(--color-surface-raised)] p-1">
|
||||
<Users size={12} style={{ color: colors.border }} />
|
||||
<ShieldQuestion size={12} style={{ color: 'var(--color-text-muted)' }} />
|
||||
<span className="absolute -bottom-0.5 -right-0.5 flex size-2.5">
|
||||
<span className="absolute inline-flex size-full animate-ping rounded-full bg-emerald-400 opacity-70" />
|
||||
<span className="relative inline-flex size-full rounded-full bg-emerald-500" />
|
||||
<span className="absolute inline-flex size-full animate-ping rounded-full bg-amber-400 opacity-70" />
|
||||
<span className="relative inline-flex size-full rounded-full bg-amber-500" />
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className="rounded px-1.5 py-0.5 text-[10px] font-medium tracking-wide"
|
||||
style={{
|
||||
backgroundColor: getThemedBadge(colors, isLight),
|
||||
color: colors.text,
|
||||
border: `1px solid ${colors.border}40`,
|
||||
backgroundColor: 'var(--color-surface-raised)',
|
||||
color: 'var(--color-text-secondary)',
|
||||
border: '1px solid var(--color-border-emphasis)',
|
||||
}}
|
||||
title={entry.teamName}
|
||||
>
|
||||
{entry.teamName}
|
||||
</span>
|
||||
<span className="text-[10px]" style={{ color: CARD_ICON_MUTED }}>
|
||||
external team
|
||||
user
|
||||
</span>
|
||||
<span
|
||||
className="min-w-0 flex-1 truncate text-[10px]"
|
||||
style={{ color: CARD_ICON_MUTED }}
|
||||
title="Cross-team message sent, awaiting reply"
|
||||
title={`Tool approval: ${entry.toolName}`}
|
||||
>
|
||||
awaiting reply
|
||||
awaiting approval
|
||||
</span>
|
||||
<span className="shrink-0 text-[10px]" style={{ color: CARD_ICON_MUTED }}>
|
||||
{since}
|
||||
|
|
|
|||
Loading…
Reference in a new issue