fix(agent-graph): center launch stepper hud
This commit is contained in:
parent
345fd3e41d
commit
19463edfc9
3 changed files with 18 additions and 63 deletions
|
|
@ -107,8 +107,8 @@ export function GraphControls({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="absolute inset-x-3 top-3 z-20 flex items-start gap-2 pointer-events-none">
|
<div className="pointer-events-none absolute inset-x-3 top-3 z-20 h-8">
|
||||||
<div className="flex shrink-0 items-center gap-0.5">
|
<div className="absolute left-0 top-0 flex shrink-0 items-center gap-0.5">
|
||||||
{onToggleSidebar ? (
|
{onToggleSidebar ? (
|
||||||
<div
|
<div
|
||||||
className="pointer-events-auto flex items-center rounded-md p-0 backdrop-blur-sm"
|
className="pointer-events-auto flex items-center rounded-md p-0 backdrop-blur-sm"
|
||||||
|
|
@ -165,15 +165,15 @@ export function GraphControls({
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex min-w-0 flex-1 justify-end px-2">
|
<div className="absolute left-1/2 top-0 w-[min(360px,38vw)] -translate-x-1/2 px-2">
|
||||||
{topToolbarContent ? (
|
{topToolbarContent ? (
|
||||||
<div className="pointer-events-auto min-w-0 max-w-[min(360px,42vw)]">
|
<div className="pointer-events-auto min-w-0">
|
||||||
{topToolbarContent}
|
{topToolbarContent}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex shrink-0 items-center gap-0.5">
|
<div className="absolute right-0 top-0 flex shrink-0 items-center gap-0.5">
|
||||||
<div
|
<div
|
||||||
className="pointer-events-auto flex items-center rounded-md p-0 backdrop-blur-sm"
|
className="pointer-events-auto flex items-center rounded-md p-0 backdrop-blur-sm"
|
||||||
style={{
|
style={{
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { DISPLAY_STEPS } from '@renderer/components/team/provisioningSteps';
|
||||||
import { StepProgressBar } from '@renderer/components/team/StepProgressBar';
|
import { StepProgressBar } from '@renderer/components/team/StepProgressBar';
|
||||||
import { TeamProvisioningPanel } from '@renderer/components/team/TeamProvisioningPanel';
|
import { TeamProvisioningPanel } from '@renderer/components/team/TeamProvisioningPanel';
|
||||||
import { useTeamProvisioningPresentation } from '@renderer/components/team/useTeamProvisioningPresentation';
|
import { useTeamProvisioningPresentation } from '@renderer/components/team/useTeamProvisioningPresentation';
|
||||||
import { Badge } from '@renderer/components/ui/badge';
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
|
|
@ -13,7 +12,6 @@ import {
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@renderer/components/ui/dialog';
|
} from '@renderer/components/ui/dialog';
|
||||||
import { cn } from '@renderer/lib/utils';
|
import { cn } from '@renderer/lib/utils';
|
||||||
import { AlertTriangle, CheckCircle2, Loader2 } from 'lucide-react';
|
|
||||||
|
|
||||||
import type { TeamProvisioningPresentation } from '@renderer/utils/teamProvisioningPresentation';
|
import type { TeamProvisioningPresentation } from '@renderer/utils/teamProvisioningPresentation';
|
||||||
import type { CSSProperties } from 'react';
|
import type { CSSProperties } from 'react';
|
||||||
|
|
@ -42,38 +40,23 @@ function shouldRenderLaunchHud(presentation: TeamProvisioningPresentation | null
|
||||||
|
|
||||||
function getToneClasses(tone: TeamProvisioningPresentation['compactTone']): {
|
function getToneClasses(tone: TeamProvisioningPresentation['compactTone']): {
|
||||||
border: string;
|
border: string;
|
||||||
badge: string;
|
|
||||||
icon: React.ReactNode;
|
|
||||||
iconClassName: string;
|
|
||||||
} {
|
} {
|
||||||
switch (tone) {
|
switch (tone) {
|
||||||
case 'error':
|
case 'error':
|
||||||
return {
|
return {
|
||||||
border: 'border-red-400/35 bg-[rgba(26,10,16,0.92)]',
|
border: 'border-red-400/35 bg-[rgba(26,10,16,0.9)]',
|
||||||
badge: 'border-red-500/30 text-red-300',
|
|
||||||
icon: <AlertTriangle size={12} />,
|
|
||||||
iconClassName: 'text-red-400',
|
|
||||||
};
|
};
|
||||||
case 'warning':
|
case 'warning':
|
||||||
return {
|
return {
|
||||||
border: 'border-amber-400/35 bg-[rgba(31,18,8,0.92)]',
|
border: 'border-amber-400/35 bg-[rgba(31,18,8,0.9)]',
|
||||||
badge: 'border-amber-500/30 text-amber-200',
|
|
||||||
icon: <AlertTriangle size={12} />,
|
|
||||||
iconClassName: 'text-amber-400',
|
|
||||||
};
|
};
|
||||||
case 'success':
|
case 'success':
|
||||||
return {
|
return {
|
||||||
border: 'border-emerald-400/35 bg-[rgba(8,24,18,0.92)]',
|
border: 'border-emerald-400/35 bg-[rgba(8,24,18,0.9)]',
|
||||||
badge: 'border-emerald-500/30 text-emerald-200',
|
|
||||||
icon: <CheckCircle2 size={12} />,
|
|
||||||
iconClassName: 'text-emerald-400',
|
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
border: 'border-cyan-400/25 bg-[rgba(8,14,26,0.92)]',
|
border: 'border-cyan-400/25 bg-[rgba(8,14,26,0.9)]',
|
||||||
badge: 'border-cyan-500/20 text-cyan-200',
|
|
||||||
icon: <Loader2 size={12} className="animate-spin" />,
|
|
||||||
iconClassName: 'text-cyan-300',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -109,14 +92,10 @@ export const GraphProvisioningHud = ({
|
||||||
}
|
}
|
||||||
}, [presentation]);
|
}, [presentation]);
|
||||||
|
|
||||||
const compactLabel = useMemo(() => {
|
const ariaLabel = useMemo(() => {
|
||||||
if (!presentation?.compactDetail) {
|
const parts = [presentation?.compactTitle, presentation?.compactDetail].filter(Boolean);
|
||||||
return null;
|
return parts.join(' - ') || 'Open launch details';
|
||||||
}
|
}, [presentation?.compactDetail, presentation?.compactTitle]);
|
||||||
return presentation.compactDetail.length > 54
|
|
||||||
? `${presentation.compactDetail.slice(0, 54)}...`
|
|
||||||
: presentation.compactDetail;
|
|
||||||
}, [presentation?.compactDetail]);
|
|
||||||
|
|
||||||
if (!shouldRender || !presentation || !tone) {
|
if (!shouldRender || !presentation || !tone) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -131,44 +110,20 @@ export const GraphProvisioningHud = ({
|
||||||
tone.border
|
tone.border
|
||||||
)}
|
)}
|
||||||
onClick={() => setDetailsOpen(true)}
|
onClick={() => setDetailsOpen(true)}
|
||||||
aria-label="Open launch details"
|
aria-label={ariaLabel}
|
||||||
>
|
>
|
||||||
<div className="flex min-w-0 items-center gap-2">
|
|
||||||
<span className={cn('shrink-0', tone.iconClassName)}>{tone.icon}</span>
|
|
||||||
<div className="min-w-0 flex-1">
|
|
||||||
<div className="flex min-w-0 items-center gap-2">
|
|
||||||
<div className="truncate text-[11px] font-semibold text-slate-50">
|
|
||||||
{presentation.compactTitle}
|
|
||||||
</div>
|
|
||||||
<Badge variant="outline" className={cn('px-1.5 py-0 text-[10px]', tone.badge)}>
|
|
||||||
{presentation.isFailed
|
|
||||||
? 'Issue'
|
|
||||||
: presentation.hasMembersStillJoining
|
|
||||||
? 'Joining'
|
|
||||||
: presentation.isActive
|
|
||||||
? 'Live'
|
|
||||||
: 'Ready'}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
{compactLabel ? (
|
|
||||||
<div className="mt-0.5 truncate text-[10px] leading-4 text-slate-300">
|
|
||||||
{compactLabel}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="border-cyan-300/12 mt-2 overflow-hidden rounded-lg border bg-[rgba(4,10,20,0.58)] px-2 py-1.5"
|
className="overflow-hidden rounded-lg border border-white/10 bg-[rgba(4,10,20,0.54)] px-2.5 py-1.5"
|
||||||
style={HUD_STEPPER_STYLE}
|
style={HUD_STEPPER_STYLE}
|
||||||
>
|
>
|
||||||
<StepProgressBar
|
<StepProgressBar
|
||||||
steps={MINI_STEPS}
|
steps={MINI_STEPS}
|
||||||
currentIndex={presentation.currentStepIndex}
|
currentIndex={presentation.currentStepIndex}
|
||||||
errorIndex={errorStepIndex}
|
errorIndex={errorStepIndex}
|
||||||
className="w-full"
|
className="w-full origin-top scale-[0.9]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<span className="sr-only">{ariaLabel}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Dialog open={detailsOpen} onOpenChange={setDetailsOpen}>
|
<Dialog open={detailsOpen} onOpenChange={setDetailsOpen}>
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ describe('GraphProvisioningHud', () => {
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
});
|
});
|
||||||
|
|
||||||
const openButton = host.querySelector('button[aria-label="Open launch details"]');
|
const openButton = host.querySelector('button[aria-label]');
|
||||||
expect(openButton).not.toBeNull();
|
expect(openButton).not.toBeNull();
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue