fix(ux): remove dup Signal btn, add minimize to all feeds, strip shows recent events
- Remove duplicate Signal (telemetry) button from DAG nodes - Add minimize (ChevronLeft) button to Epic Command Feed view, not just global feed - TelemetryStrip now shows 8 most recently updated tasks as status-colored dots instead of static status counts — reflects live activity like the full feed does - Each dot is colored by task status (blocked=red, active=amber, ready=green) with hover tooltip showing task id, title, and status Co-Authored-By: Oz <oz-agent@warp.dev>
This commit is contained in:
parent
c246ceaf21
commit
a0787f85de
4 changed files with 89 additions and 49 deletions
|
|
@ -3002,3 +3002,35 @@ time="2026-03-01T18:17:37-08:00" level=info msg=NewConnection DisableClientMulti
|
|||
time="2026-03-01T18:17:37-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1183
|
||||
time="2026-03-01T18:17:37-08:00" level=info msg=ConnectionClosed connectionID=1182
|
||||
time="2026-03-01T18:17:38-08:00" level=info msg=ConnectionClosed connectionID=1183
|
||||
time="2026-03-01T18:18:37-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1184
|
||||
time="2026-03-01T18:18:39-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1185
|
||||
time="2026-03-01T18:18:39-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1186
|
||||
time="2026-03-01T18:18:39-08:00" level=warning msg="Cannot read client handshake response from client 1186 (127.0.0.1:62203): read tcp 127.0.0.1:3307->127.0.0.1:62203: wsarecv: An existing connection was forcibly closed by the remote host.\nio.ReadFull(header size) failed, it may not be a valid MySQL client"
|
||||
time="2026-03-01T18:18:39-08:00" level=info msg=ConnectionClosed connectionID=1186
|
||||
time="2026-03-01T18:18:39-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1187
|
||||
time="2026-03-01T18:18:39-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1188
|
||||
time="2026-03-01T18:18:39-08:00" level=info msg=ConnectionClosed connectionID=1187
|
||||
time="2026-03-01T18:18:39-08:00" level=info msg=ConnectionClosed connectionID=1188
|
||||
time="2026-03-01T18:18:40-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1189
|
||||
time="2026-03-01T18:18:40-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1190
|
||||
time="2026-03-01T18:18:40-08:00" level=info msg=ConnectionClosed connectionID=1190
|
||||
time="2026-03-01T18:18:40-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1191
|
||||
time="2026-03-01T18:18:40-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1192
|
||||
time="2026-03-01T18:18:40-08:00" level=info msg=ConnectionClosed connectionID=1191
|
||||
time="2026-03-01T18:18:40-08:00" level=info msg=ConnectionClosed connectionID=1192
|
||||
time="2026-03-01T18:18:43-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1193
|
||||
time="2026-03-01T18:18:43-08:00" level=warning msg="Cannot read client handshake response from client 1193 (127.0.0.1:51203): read tcp 127.0.0.1:3307->127.0.0.1:51203: wsarecv: An existing connection was forcibly closed by the remote host.\nio.ReadFull(header size) failed, it may not be a valid MySQL client"
|
||||
time="2026-03-01T18:18:43-08:00" level=info msg=ConnectionClosed connectionID=1193
|
||||
time="2026-03-01T18:18:43-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1194
|
||||
time="2026-03-01T18:18:43-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1195
|
||||
time="2026-03-01T18:18:43-08:00" level=info msg=ConnectionClosed connectionID=1194
|
||||
time="2026-03-01T18:18:43-08:00" level=info msg=ConnectionClosed connectionID=1195
|
||||
time="2026-03-01T18:18:43-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1196
|
||||
time="2026-03-01T18:18:43-08:00" level=warning msg="Cannot read client handshake response from client 1196 (127.0.0.1:51207): read tcp 127.0.0.1:3307->127.0.0.1:51207: wsarecv: An existing connection was forcibly closed by the remote host.\nio.ReadFull(header size) failed, it may not be a valid MySQL client"
|
||||
time="2026-03-01T18:18:43-08:00" level=info msg=ConnectionClosed connectionID=1196
|
||||
time="2026-03-01T18:18:43-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1197
|
||||
time="2026-03-01T18:18:43-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1198
|
||||
time="2026-03-01T18:18:43-08:00" level=info msg=ConnectionClosed connectionID=1197
|
||||
time="2026-03-01T18:18:44-08:00" level=info msg=ConnectionClosed connectionID=1198
|
||||
time="2026-03-01T18:20:06-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1199
|
||||
time="2026-03-01T18:20:29-08:00" level=info msg=NewConnection DisableClientMultiStatements=false connectionID=1200
|
||||
|
|
|
|||
|
|
@ -43,11 +43,29 @@ export function ContextualRightPanel({ epicId, taskId, swarmId, issues, projectR
|
|||
|
||||
if (epicId) {
|
||||
return (
|
||||
<SwarmCommandFeed
|
||||
epicId={epicId}
|
||||
issues={issues}
|
||||
projectRoot={projectRoot}
|
||||
/>
|
||||
<div className="flex h-full flex-col overflow-hidden bg-[var(--surface-primary)]">
|
||||
{onMinimize && (
|
||||
<div className="flex shrink-0 items-center justify-between border-b border-[var(--border-subtle)] px-3 py-2">
|
||||
<span className="text-[10px] font-semibold uppercase tracking-[0.1em] text-[var(--text-tertiary)]">Epic Command Feed</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onMinimize}
|
||||
className="rounded p-1 text-[var(--text-tertiary)] transition-colors hover:bg-[var(--alpha-white-low)] hover:text-[var(--text-primary)]"
|
||||
aria-label="Minimize to telemetry"
|
||||
title="Minimize to telemetry"
|
||||
>
|
||||
<ChevronLeft className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<div className="min-h-0 flex-1 overflow-hidden">
|
||||
<SwarmCommandFeed
|
||||
epicId={epicId}
|
||||
issues={issues}
|
||||
projectRoot={projectRoot}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -294,16 +294,6 @@ export function GraphNodeCard({ id, data, selected }: NodeProps<Node<GraphNodeDa
|
|||
<Signal className="h-3 w-3" />
|
||||
</button>
|
||||
) : null}
|
||||
{onViewTelemetry ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => { e.stopPropagation(); onViewTelemetry(id); }}
|
||||
className="rounded p-0.5 text-[var(--accent-info)]/50 transition-colors hover:text-[var(--accent-info)] hover:bg-[var(--alpha-white-low)]"
|
||||
title="Live feed"
|
||||
>
|
||||
<Signal className="h-3 w-3" />
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 flex-wrap">
|
||||
{assignedArchetypes.map((archetype) => (
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
import { useMemo } from 'react';
|
||||
import { Signal } from 'lucide-react';
|
||||
import type { BeadIssue } from '../../lib/types';
|
||||
|
||||
interface TelemetryStripProps {
|
||||
|
|
@ -8,53 +9,52 @@ interface TelemetryStripProps {
|
|||
onMaximize: () => void;
|
||||
}
|
||||
|
||||
interface Dot {
|
||||
color: string;
|
||||
glow: string;
|
||||
count: number;
|
||||
label: string;
|
||||
function dotColor(status: BeadIssue['status']): { bg: string; glow: string } {
|
||||
switch (status) {
|
||||
case 'blocked': return { bg: 'var(--accent-danger)', glow: 'rgba(255,76,114,0.4)' };
|
||||
case 'in_progress': return { bg: 'var(--accent-warning)', glow: 'rgba(255,178,74,0.4)' };
|
||||
case 'open': return { bg: 'var(--accent-success)', glow: 'rgba(53,217,143,0.4)' };
|
||||
case 'closed': return { bg: 'var(--text-tertiary)', glow: 'transparent' };
|
||||
default: return { bg: 'var(--accent-info)', glow: 'rgba(125,211,252,0.3)' };
|
||||
}
|
||||
}
|
||||
|
||||
export function TelemetryStrip({ issues, onMaximize }: TelemetryStripProps) {
|
||||
const tasks = issues.filter((i) => i.issue_type !== 'epic');
|
||||
const blocked = tasks.filter((i) => i.status === 'blocked').length;
|
||||
const active = tasks.filter((i) => i.status === 'in_progress').length;
|
||||
const ready = tasks.filter((i) => i.status === 'open').length;
|
||||
const done = tasks.filter((i) => i.status === 'closed').length;
|
||||
|
||||
const dots: Dot[] = [
|
||||
{ color: 'var(--accent-danger)', glow: 'rgba(255,76,114,0.4)', count: blocked, label: 'blocked' },
|
||||
{ color: 'var(--accent-warning)', glow: 'rgba(255,178,74,0.4)', count: active, label: 'active' },
|
||||
{ color: 'var(--accent-success)', glow: 'rgba(53,217,143,0.4)', count: ready, label: 'ready' },
|
||||
{ color: 'var(--text-tertiary)', glow: 'transparent', count: done, label: 'done' },
|
||||
];
|
||||
// Show the 8 most recently updated tasks as live dots
|
||||
const recentTasks = useMemo(() => {
|
||||
return [...issues]
|
||||
.filter((i) => i.issue_type !== 'epic')
|
||||
.sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime())
|
||||
.slice(0, 8);
|
||||
}, [issues]);
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-9 flex-shrink-0 flex-col items-center border-l border-[var(--border-subtle)] bg-[var(--surface-primary)] py-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onMaximize}
|
||||
className="mb-3 rounded p-1 text-[var(--text-tertiary)] transition-colors hover:bg-[var(--alpha-white-low)] hover:text-[var(--text-primary)]"
|
||||
className="mb-2 rounded p-1 text-[var(--accent-info)] transition-colors hover:bg-[var(--alpha-white-low)] hover:text-[var(--text-primary)]"
|
||||
title="Restore live feed"
|
||||
aria-label="Restore live feed"
|
||||
>
|
||||
<ChevronRight className="h-3.5 w-3.5" />
|
||||
<Signal className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
{dots.map((dot) => (
|
||||
<div key={dot.label} className="flex flex-col items-center gap-0.5" title={`${dot.count} ${dot.label}`}>
|
||||
<span
|
||||
className="h-2.5 w-2.5 rounded-full transition-all"
|
||||
style={{
|
||||
backgroundColor: dot.color,
|
||||
boxShadow: dot.count > 0 ? `0 0 6px 1px ${dot.glow}` : 'none',
|
||||
opacity: dot.count > 0 ? 1 : 0.25,
|
||||
}}
|
||||
/>
|
||||
<span className="font-mono text-[8px] text-[var(--text-tertiary)]">{dot.count}</span>
|
||||
</div>
|
||||
))}
|
||||
<div className="flex flex-1 flex-col items-center gap-2 overflow-hidden">
|
||||
{recentTasks.map((task) => {
|
||||
const { bg, glow } = dotColor(task.status);
|
||||
return (
|
||||
<div key={task.id} className="flex flex-col items-center" title={`${task.id}: ${task.title} (${task.status})`}>
|
||||
<span
|
||||
className="h-2 w-2 rounded-full"
|
||||
style={{
|
||||
backgroundColor: bg,
|
||||
boxShadow: `0 0 5px 1px ${glow}`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue