fix(team-ui): polish launch diagnostics controls
This commit is contained in:
parent
7b99a3713b
commit
7e6ebce093
8 changed files with 411 additions and 432 deletions
|
|
@ -206,10 +206,8 @@ export const PluginsPanel = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-md border border-amber-500/30 bg-amber-500/5 px-4 py-3 text-sm text-amber-300">
|
<div className="rounded-md border border-amber-500/30 bg-amber-500/5 px-4 py-3 text-sm text-amber-300">
|
||||||
In the multimodel runtime, plugins are currently guaranteed only for Anthropic
|
Plugin support is currently guaranteed for Anthropic (Claude) sessions only.
|
||||||
sessions. We are actively building broader plugin support for all agents, including
|
We're working to support plugins across all agents.
|
||||||
both universal plugins and agent-specific plugins.
|
|
||||||
{capability.reason ? ` ${capability.reason}` : ''}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,8 @@ export const SkipPermissionsCheckbox: React.FC<SkipPermissionsCheckboxProps> = (
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<Info className="mt-0.5 size-3.5 shrink-0 text-blue-400" />
|
<Info className="mt-0.5 size-3.5 shrink-0 text-blue-400" />
|
||||||
<p>
|
<p>
|
||||||
Unleash Claude's full power — no interruptions asking for permission. Autonomous
|
Autonomous mode: team tools execute without confirmation. Be cautious with untrusted
|
||||||
mode — all tools execute without confirmation. Be cautious with untrusted code.
|
code.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -57,7 +57,7 @@ export const SkipPermissionsCheckbox: React.FC<SkipPermissionsCheckboxProps> = (
|
||||||
>
|
>
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<Info className="mt-0.5 size-3.5 shrink-0 text-blue-400" />
|
<Info className="mt-0.5 size-3.5 shrink-0 text-blue-400" />
|
||||||
<p>Manual mode — you'll approve or deny each tool call in real-time.</p>
|
<p>Manual mode: you'll approve or deny each tool call in real time.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -1043,7 +1043,7 @@ export const MemberCard = memo(function MemberCard({
|
||||||
<MemberRuntimeTelemetryStrip runtimeEntry={runtimeEntry} scale={runtimeTelemetryScale} />
|
<MemberRuntimeTelemetryStrip runtimeEntry={runtimeEntry} scale={runtimeTelemetryScale} />
|
||||||
) : null}
|
) : null}
|
||||||
<div className="pointer-events-none absolute inset-0 z-10 rounded transition-colors group-hover:bg-white/5" />
|
<div className="pointer-events-none absolute inset-0 z-10 rounded transition-colors group-hover:bg-white/5" />
|
||||||
<div className="relative z-20 flex items-center gap-2.5">
|
<div className="relative z-20 grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-x-2.5 gap-y-1">
|
||||||
<div className="relative shrink-0">
|
<div className="relative shrink-0">
|
||||||
<div
|
<div
|
||||||
className="rounded-full border-2 p-px"
|
className="rounded-full border-2 p-px"
|
||||||
|
|
@ -1166,6 +1166,7 @@ export const MemberCard = memo(function MemberCard({
|
||||||
<MemberLaunchDiagnosticsButton
|
<MemberLaunchDiagnosticsButton
|
||||||
payload={launchDiagnosticsPayload}
|
payload={launchDiagnosticsPayload}
|
||||||
className="size-auto rounded p-1 text-red-300 transition-colors hover:bg-red-500/10 hover:text-red-200"
|
className="size-auto rounded p-1 text-red-300 transition-colors hover:bg-red-500/10 hover:text-red-200"
|
||||||
|
attention
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
|
|
@ -1201,22 +1202,8 @@ export const MemberCard = memo(function MemberCard({
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{launchFailureReason ? (
|
|
||||||
<div
|
|
||||||
data-testid="member-launch-failure-reason"
|
|
||||||
className="mt-1 min-w-0 whitespace-pre-wrap break-words text-[10px] font-medium leading-snug text-red-300/90"
|
|
||||||
title={rawLaunchFailureReason}
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
{renderLinkifiedText(launchFailureReason, {
|
|
||||||
linkClassName: 'underline underline-offset-2 hover:text-red-200',
|
|
||||||
stopPropagation: true,
|
|
||||||
getLinkLabel: getLaunchFailureLinkLabel,
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex shrink-0 items-center gap-2.5 justify-self-end">
|
||||||
{showLaunchBadge ? (
|
{showLaunchBadge ? (
|
||||||
<span
|
<span
|
||||||
className="flex shrink-0 items-center gap-1"
|
className="flex shrink-0 items-center gap-1"
|
||||||
|
|
@ -1244,7 +1231,9 @@ export const MemberCard = memo(function MemberCard({
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label={retryingLaunch ? restartActionBusyLabel : restartActionIdleLabel}
|
aria-label={
|
||||||
|
retryingLaunch ? restartActionBusyLabel : restartActionIdleLabel
|
||||||
|
}
|
||||||
className="rounded p-1 text-amber-300 transition-colors hover:bg-amber-500/10 hover:text-amber-200 disabled:cursor-not-allowed disabled:opacity-60"
|
className="rounded p-1 text-amber-300 transition-colors hover:bg-amber-500/10 hover:text-amber-200 disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
disabled={retryingLaunch}
|
disabled={retryingLaunch}
|
||||||
onClick={handleRestartMember}
|
onClick={handleRestartMember}
|
||||||
|
|
@ -1283,6 +1272,7 @@ export const MemberCard = memo(function MemberCard({
|
||||||
<MemberLaunchDiagnosticsButton
|
<MemberLaunchDiagnosticsButton
|
||||||
payload={launchDiagnosticsPayload}
|
payload={launchDiagnosticsPayload}
|
||||||
className="size-auto rounded p-1 text-red-300 transition-colors hover:bg-red-500/10 hover:text-red-200"
|
className="size-auto rounded p-1 text-red-300 transition-colors hover:bg-red-500/10 hover:text-red-200"
|
||||||
|
attention
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{canSkipFailedLaunch ? (
|
{canSkipFailedLaunch ? (
|
||||||
|
|
@ -1313,7 +1303,9 @@ export const MemberCard = memo(function MemberCard({
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label={retryingLaunch ? restartActionBusyLabel : restartActionIdleLabel}
|
aria-label={
|
||||||
|
retryingLaunch ? restartActionBusyLabel : restartActionIdleLabel
|
||||||
|
}
|
||||||
className="rounded p-1 text-red-300 transition-colors hover:bg-red-500/10 hover:text-red-200 disabled:cursor-not-allowed disabled:opacity-60"
|
className="rounded p-1 text-red-300 transition-colors hover:bg-red-500/10 hover:text-red-200 disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
disabled={retryingLaunch || skippingLaunch}
|
disabled={retryingLaunch || skippingLaunch}
|
||||||
onClick={handleRestartMember}
|
onClick={handleRestartMember}
|
||||||
|
|
@ -1355,7 +1347,9 @@ export const MemberCard = memo(function MemberCard({
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label={retryingLaunch ? restartActionBusyLabel : restartActionIdleLabel}
|
aria-label={
|
||||||
|
retryingLaunch ? restartActionBusyLabel : restartActionIdleLabel
|
||||||
|
}
|
||||||
className="rounded p-1 text-zinc-300 transition-colors hover:bg-zinc-500/10 hover:text-zinc-100 disabled:cursor-not-allowed disabled:opacity-60"
|
className="rounded p-1 text-zinc-300 transition-colors hover:bg-zinc-500/10 hover:text-zinc-100 disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
disabled={retryingLaunch}
|
disabled={retryingLaunch}
|
||||||
onClick={handleRestartMember}
|
onClick={handleRestartMember}
|
||||||
|
|
@ -1406,7 +1400,9 @@ export const MemberCard = memo(function MemberCard({
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label={retryingLaunch ? restartActionBusyLabel : restartActionIdleLabel}
|
aria-label={
|
||||||
|
retryingLaunch ? restartActionBusyLabel : restartActionIdleLabel
|
||||||
|
}
|
||||||
className="rounded p-1 text-red-300 transition-colors hover:bg-red-500/10 hover:text-red-200 disabled:cursor-not-allowed disabled:opacity-60"
|
className="rounded p-1 text-red-300 transition-colors hover:bg-red-500/10 hover:text-red-200 disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
disabled={retryingLaunch}
|
disabled={retryingLaunch}
|
||||||
onClick={handleRestartMember}
|
onClick={handleRestartMember}
|
||||||
|
|
@ -1428,6 +1424,7 @@ export const MemberCard = memo(function MemberCard({
|
||||||
<MemberLaunchDiagnosticsButton
|
<MemberLaunchDiagnosticsButton
|
||||||
payload={launchDiagnosticsPayload}
|
payload={launchDiagnosticsPayload}
|
||||||
className="size-auto rounded p-1 text-red-300 transition-colors hover:bg-red-500/10 hover:text-red-200"
|
className="size-auto rounded p-1 text-red-300 transition-colors hover:bg-red-500/10 hover:text-red-200"
|
||||||
|
attention
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -1533,6 +1530,22 @@ export const MemberCard = memo(function MemberCard({
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
{launchFailureReason ? (
|
||||||
|
<div
|
||||||
|
data-testid="member-launch-failure-reason"
|
||||||
|
className="col-span-2 col-start-2 min-w-0 whitespace-pre-wrap break-words text-[10px] font-medium leading-snug text-red-300/90"
|
||||||
|
title={rawLaunchFailureReason}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{renderLinkifiedText(launchFailureReason, {
|
||||||
|
linkClassName: 'underline underline-offset-2 hover:text-red-200',
|
||||||
|
stopPropagation: true,
|
||||||
|
getLinkLabel: getLaunchFailureLinkLabel,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -1549,7 +1562,7 @@ export const MemberCard = memo(function MemberCard({
|
||||||
>
|
>
|
||||||
<TooltipTrigger asChild>{cardContent}</TooltipTrigger>
|
<TooltipTrigger asChild>{cardContent}</TooltipTrigger>
|
||||||
<TooltipContent
|
<TooltipContent
|
||||||
side="top"
|
side="left"
|
||||||
align="start"
|
align="start"
|
||||||
sideOffset={8}
|
sideOffset={8}
|
||||||
className="border-blue-400/20 bg-[var(--color-surface)] p-3 shadow-xl shadow-black/30"
|
className="border-blue-400/20 bg-[var(--color-surface)] p-3 shadow-xl shadow-black/30"
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { useState } from 'react';
|
||||||
|
|
||||||
import { Button } from '@renderer/components/ui/button';
|
import { Button } from '@renderer/components/ui/button';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||||
|
import { cn } from '@renderer/lib/utils';
|
||||||
import {
|
import {
|
||||||
formatMemberLaunchDiagnosticsPayload,
|
formatMemberLaunchDiagnosticsPayload,
|
||||||
type MemberLaunchDiagnosticsPayload,
|
type MemberLaunchDiagnosticsPayload,
|
||||||
|
|
@ -13,6 +14,7 @@ interface MemberLaunchDiagnosticsButtonProps {
|
||||||
label?: string;
|
label?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
size?: 'icon' | 'sm';
|
size?: 'icon' | 'sm';
|
||||||
|
attention?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MemberLaunchDiagnosticsButton = ({
|
export const MemberLaunchDiagnosticsButton = ({
|
||||||
|
|
@ -20,6 +22,7 @@ export const MemberLaunchDiagnosticsButton = ({
|
||||||
label,
|
label,
|
||||||
className,
|
className,
|
||||||
size = label ? 'sm' : 'icon',
|
size = label ? 'sm' : 'icon',
|
||||||
|
attention = false,
|
||||||
}: MemberLaunchDiagnosticsButtonProps): React.JSX.Element => {
|
}: MemberLaunchDiagnosticsButtonProps): React.JSX.Element => {
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
|
|
@ -45,7 +48,7 @@ export const MemberLaunchDiagnosticsButton = ({
|
||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size={size}
|
size={size}
|
||||||
className={className}
|
className={cn(className, attention && !copied && 'member-launch-diagnostics-pulse')}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
aria-label={tooltip}
|
aria-label={tooltip}
|
||||||
onClick={copyDiagnostics}
|
onClick={copyDiagnostics}
|
||||||
|
|
|
||||||
|
|
@ -1056,6 +1056,44 @@ a[href],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes member-diagnostics-attention-fill {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
background-color: rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background-color: rgba(239, 68, 68, 0.22);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes member-diagnostics-attention-ring {
|
||||||
|
0% {
|
||||||
|
opacity: 0.7;
|
||||||
|
transform: scale(0.92);
|
||||||
|
}
|
||||||
|
70%,
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(1.38);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-launch-diagnostics-pulse {
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
animation: member-diagnostics-attention-fill 1.7s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-launch-diagnostics-pulse::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: -2px;
|
||||||
|
border: 1px solid rgba(248, 113, 113, 0.55);
|
||||||
|
border-radius: inherit;
|
||||||
|
pointer-events: none;
|
||||||
|
animation: member-diagnostics-attention-ring 1.7s ease-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
/* Skeleton-style shimmer for waiting members: a translucent light sweep */
|
/* Skeleton-style shimmer for waiting members: a translucent light sweep */
|
||||||
.member-waiting-shimmer {
|
.member-waiting-shimmer {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
@ -1653,6 +1691,8 @@ a[href],
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.kanban-comment-badge-pulse,
|
.kanban-comment-badge-pulse,
|
||||||
|
.member-launch-diagnostics-pulse,
|
||||||
|
.member-launch-diagnostics-pulse::after,
|
||||||
.message-composer-orbit-path,
|
.message-composer-orbit-path,
|
||||||
.message-composer-orbit-glow {
|
.message-composer-orbit-glow {
|
||||||
animation: none;
|
animation: none;
|
||||||
|
|
|
||||||
|
|
@ -8,97 +8,6 @@
|
||||||
/>
|
/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="icon" type="image/png" href="./favicon.png" />
|
<link rel="icon" type="image/png" href="./favicon.png" />
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
as="image"
|
|
||||||
type="image/png"
|
|
||||||
href="./assets/participant-avatars/01.png"
|
|
||||||
fetchpriority="high"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
as="image"
|
|
||||||
type="image/png"
|
|
||||||
href="./assets/participant-avatars/02.png"
|
|
||||||
fetchpriority="high"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
as="image"
|
|
||||||
type="image/png"
|
|
||||||
href="./assets/participant-avatars/03.png"
|
|
||||||
fetchpriority="high"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
as="image"
|
|
||||||
type="image/png"
|
|
||||||
href="./assets/participant-avatars/04.png"
|
|
||||||
fetchpriority="high"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
as="image"
|
|
||||||
type="image/png"
|
|
||||||
href="./assets/participant-avatars/05.png"
|
|
||||||
fetchpriority="high"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
as="image"
|
|
||||||
type="image/png"
|
|
||||||
href="./assets/participant-avatars/06.png"
|
|
||||||
fetchpriority="high"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
as="image"
|
|
||||||
type="image/png"
|
|
||||||
href="./assets/participant-avatars/07.png"
|
|
||||||
fetchpriority="high"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
as="image"
|
|
||||||
type="image/png"
|
|
||||||
href="./assets/participant-avatars/08.png"
|
|
||||||
fetchpriority="high"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
as="image"
|
|
||||||
type="image/png"
|
|
||||||
href="./assets/participant-avatars/09.png"
|
|
||||||
fetchpriority="high"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
as="image"
|
|
||||||
type="image/png"
|
|
||||||
href="./assets/participant-avatars/10.png"
|
|
||||||
fetchpriority="high"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
as="image"
|
|
||||||
type="image/png"
|
|
||||||
href="./assets/participant-avatars/11.png"
|
|
||||||
fetchpriority="high"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
as="image"
|
|
||||||
type="image/png"
|
|
||||||
href="./assets/participant-avatars/12.png"
|
|
||||||
fetchpriority="high"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
as="image"
|
|
||||||
type="image/png"
|
|
||||||
href="./assets/participant-avatars/13.png"
|
|
||||||
fetchpriority="high"
|
|
||||||
/>
|
|
||||||
<title>Agent Teams AI</title>
|
<title>Agent Teams AI</title>
|
||||||
<style>
|
<style>
|
||||||
/* Splash: animated gradient background */
|
/* Splash: animated gradient background */
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { act } from 'react';
|
import React, { act } from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
import type { CliInstallationStatus } from '@shared/types';
|
import type { CliInstallationStatus } from '@shared/types';
|
||||||
|
|
@ -214,7 +215,7 @@ describe('PluginsPanel effective runtime status', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(host.textContent).not.toContain(
|
expect(host.textContent).not.toContain(
|
||||||
'In the multimodel runtime, plugins currently apply only to Anthropic sessions.'
|
'Plugin support is currently guaranteed for Anthropic (Claude) sessions only.'
|
||||||
);
|
);
|
||||||
expect(host.textContent).not.toContain('Codex bootstrap placeholder');
|
expect(host.textContent).not.toContain('Codex bootstrap placeholder');
|
||||||
|
|
||||||
|
|
@ -224,7 +225,7 @@ describe('PluginsPanel effective runtime status', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('explains that broader plugin support for all agents is actively being built when Codex plugins are not supported yet', async () => {
|
it('explains that plugin support is guaranteed only for Anthropic sessions when Codex plugins are not supported yet', async () => {
|
||||||
const host = document.createElement('div');
|
const host = document.createElement('div');
|
||||||
document.body.appendChild(host);
|
document.body.appendChild(host);
|
||||||
const root = createRoot(host);
|
const root = createRoot(host);
|
||||||
|
|
@ -257,12 +258,10 @@ describe('PluginsPanel effective runtime status', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(host.textContent).toContain(
|
expect(host.textContent).toContain(
|
||||||
'plugins are currently guaranteed only for Anthropic sessions'
|
"Plugin support is currently guaranteed for Anthropic (Claude) sessions only. We're working to support plugins across all agents."
|
||||||
);
|
);
|
||||||
expect(host.textContent).toContain(
|
expect(host.textContent).not.toContain('multimodel runtime');
|
||||||
'We are actively building broader plugin support for all agents'
|
expect(host.textContent).not.toContain('Codex bootstrap placeholder');
|
||||||
);
|
|
||||||
expect(host.textContent).toContain('universal plugins and agent-specific plugins');
|
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
root.unmount();
|
root.unmount();
|
||||||
|
|
|
||||||
|
|
@ -73,11 +73,20 @@ vi.mock('@renderer/components/ui/tooltip', () => ({
|
||||||
React.createElement(React.Fragment, null, children),
|
React.createElement(React.Fragment, null, children),
|
||||||
TooltipContent: ({
|
TooltipContent: ({
|
||||||
children,
|
children,
|
||||||
|
side,
|
||||||
|
align,
|
||||||
className,
|
className,
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
side?: string;
|
||||||
|
align?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
}) => React.createElement('div', { className, 'data-testid': 'tooltip-content' }, children),
|
}) =>
|
||||||
|
React.createElement(
|
||||||
|
'div',
|
||||||
|
{ className, 'data-align': align, 'data-side': side, 'data-testid': 'tooltip-content' },
|
||||||
|
children
|
||||||
|
),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('@renderer/hooks/useTheme', () => ({
|
vi.mock('@renderer/hooks/useTheme', () => ({
|
||||||
|
|
@ -866,6 +875,10 @@ describe('MemberCard starting-state visuals', () => {
|
||||||
expect(
|
expect(
|
||||||
host.querySelector('[data-testid="tooltip-root"][data-delay-duration="0"]')
|
host.querySelector('[data-testid="tooltip-root"][data-delay-duration="0"]')
|
||||||
).not.toBeNull();
|
).not.toBeNull();
|
||||||
|
const runtimeTooltipContent = Array.from(
|
||||||
|
host.querySelectorAll('[data-testid="tooltip-content"]')
|
||||||
|
).find((content) => content.className.includes('border-blue-400/20'));
|
||||||
|
expect(runtimeTooltipContent?.getAttribute('data-side')).toBe('left');
|
||||||
expect(host.querySelector('[data-testid="tooltip-root"]')?.getAttribute('data-open')).toBe(
|
expect(host.querySelector('[data-testid="tooltip-root"]')?.getAttribute('data-open')).toBe(
|
||||||
'false'
|
'false'
|
||||||
);
|
);
|
||||||
|
|
@ -1148,6 +1161,7 @@ describe('MemberCard starting-state visuals', () => {
|
||||||
|
|
||||||
const button = host.querySelector('[aria-label="Copy diagnostics"]') as HTMLButtonElement;
|
const button = host.querySelector('[aria-label="Copy diagnostics"]') as HTMLButtonElement;
|
||||||
expect(button).not.toBeNull();
|
expect(button).not.toBeNull();
|
||||||
|
expect(button.className).toContain('member-launch-diagnostics-pulse');
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
button.click();
|
button.click();
|
||||||
|
|
@ -1163,6 +1177,7 @@ describe('MemberCard starting-state visuals', () => {
|
||||||
expect(payload.runId).toBe('run-42');
|
expect(payload.runId).toBe('run-42');
|
||||||
expect(payload.livenessKind).toBe('not_found');
|
expect(payload.livenessKind).toBe('not_found');
|
||||||
expect(payload.processCommand).toContain('--token [redacted]');
|
expect(payload.processCommand).toContain('--token [redacted]');
|
||||||
|
expect(button.className).not.toContain('member-launch-diagnostics-pulse');
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
root.unmount();
|
root.unmount();
|
||||||
|
|
@ -1233,6 +1248,8 @@ describe('MemberCard starting-state visuals', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const failureReason = host.querySelector('[data-testid="member-launch-failure-reason"]');
|
const failureReason = host.querySelector('[data-testid="member-launch-failure-reason"]');
|
||||||
|
expect(failureReason?.className).toContain('col-start-2');
|
||||||
|
expect(failureReason?.className).toContain('col-span-2');
|
||||||
expect(failureReason?.textContent).toContain('Insufficient credits');
|
expect(failureReason?.textContent).toContain('Insufficient credits');
|
||||||
expect(failureReason?.textContent).toContain('OpenRouter credits');
|
expect(failureReason?.textContent).toContain('OpenRouter credits');
|
||||||
expect(failureReason?.textContent).not.toContain('Latest assistant message');
|
expect(failureReason?.textContent).not.toContain('Latest assistant message');
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue