fix(bb-ui2): separate ThreadDrawer from RightPanel
- Created ThreadDrawer component (24rem) that slides from right edge of middle - RightPanel now reserved for Activity Feed + Agent roster (bb-ui2.29) - Updated URL state: added drawer and agentId params - Thread shows in drawer when card selected Architecture now matches PRD: - Right Panel (17rem): Activity Feed + Agent roster - Thread Drawer (24rem): Opens from middle when card clicked Beads: bb-ui2.31 thread drawer created, bb-ui2.13 closed
This commit is contained in:
parent
f6c5398f0c
commit
a7787733b9
6 changed files with 208 additions and 70 deletions
|
|
@ -1 +1 @@
|
||||||
62948
|
55588
|
||||||
|
|
|
||||||
133
src/components/shared/thread-drawer.tsx
Normal file
133
src/components/shared/thread-drawer.tsx
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { X, Send } from 'lucide-react';
|
||||||
|
import { ThreadView, type ThreadItem } from './thread-view';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
interface ThreadDrawerProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
title: string;
|
||||||
|
id: string;
|
||||||
|
items?: ThreadItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample data for demo
|
||||||
|
const SAMPLE_ITEMS: ThreadItem[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
type: 'status_change',
|
||||||
|
from: 'backlog',
|
||||||
|
to: 'in_progress',
|
||||||
|
timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
type: 'comment',
|
||||||
|
author: 'zenchantlive',
|
||||||
|
content: 'Started working on this task.',
|
||||||
|
timestamp: new Date(Date.now() - 1 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
type: 'protocol_event',
|
||||||
|
event: 'HANDOFF',
|
||||||
|
content: 'Handed off to agent',
|
||||||
|
timestamp: new Date(Date.now() - 30 * 60 * 1000),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function ThreadDrawer({ isOpen, onClose, title, id, items = SAMPLE_ITEMS }: ThreadDrawerProps) {
|
||||||
|
const [comment, setComment] = useState('');
|
||||||
|
|
||||||
|
if (!isOpen) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Backdrop */}
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 z-40 bg-black/30"
|
||||||
|
onClick={onClose}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Drawer */}
|
||||||
|
<div
|
||||||
|
className="fixed top-0 right-0 h-full z-50 w-[24rem] overflow-hidden"
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--color-bg-card)',
|
||||||
|
borderLeft: '1px solid rgba(255, 255, 255, 0.1)',
|
||||||
|
boxShadow: '-4px 0 20px rgba(0, 0, 0, 0.3)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Header */}
|
||||||
|
<div
|
||||||
|
className="flex items-center justify-between p-4 border-b"
|
||||||
|
style={{ borderColor: 'rgba(255, 255, 255, 0.1)' }}
|
||||||
|
>
|
||||||
|
<div className="flex-1 min-w-0 mr-4">
|
||||||
|
<span className="text-teal-400 font-mono text-sm">
|
||||||
|
{id}
|
||||||
|
</span>
|
||||||
|
<h2
|
||||||
|
className="text-sm font-semibold truncate"
|
||||||
|
style={{ color: 'var(--color-text-primary)' }}
|
||||||
|
title={title}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="p-1.5 rounded-md hover:bg-white/10 transition-colors flex-shrink-0"
|
||||||
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
<X size={18} style={{ color: 'var(--color-text-muted)' }} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Thread Content */}
|
||||||
|
<div className="flex-1 overflow-y-auto p-4" style={{ height: 'calc(100% - 8rem)' }}>
|
||||||
|
<ThreadView items={items} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Compose */}
|
||||||
|
<div
|
||||||
|
className="p-4 border-t"
|
||||||
|
style={{ borderColor: 'rgba(255, 255, 255, 0.1)' }}
|
||||||
|
>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={comment}
|
||||||
|
onChange={(e) => setComment(e.target.value)}
|
||||||
|
placeholder="Add a comment..."
|
||||||
|
className="flex-1 px-3 py-2 rounded-md text-sm"
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--color-bg-input)',
|
||||||
|
color: 'var(--color-text-primary)',
|
||||||
|
border: '1px solid rgba(255, 255, 255, 0.1)',
|
||||||
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter' && comment.trim()) {
|
||||||
|
// TODO: Post comment
|
||||||
|
setComment('');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className="p-2 rounded-md"
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--color-accent-green)',
|
||||||
|
color: '#fff',
|
||||||
|
}}
|
||||||
|
aria-label="Send comment"
|
||||||
|
>
|
||||||
|
<Send size={16} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -7,12 +7,11 @@ import { TopBar } from './top-bar';
|
||||||
import { LeftPanel } from './left-panel';
|
import { LeftPanel } from './left-panel';
|
||||||
import { RightPanel } from './right-panel';
|
import { RightPanel } from './right-panel';
|
||||||
import { MobileNav } from './mobile-nav';
|
import { MobileNav } from './mobile-nav';
|
||||||
|
import { ThreadDrawer } from './thread-drawer';
|
||||||
import { useUrlState } from '../../hooks/use-url-state';
|
import { useUrlState } from '../../hooks/use-url-state';
|
||||||
import { GraphView } from '../graph/graph-view';
|
import { GraphView } from '../graph/graph-view';
|
||||||
import { SocialPage } from '../social/social-page';
|
import { SocialPage } from '../social/social-page';
|
||||||
import { SocialDetail } from '../social/social-detail';
|
|
||||||
import { SwarmPage } from '../swarm/swarm-page';
|
import { SwarmPage } from '../swarm/swarm-page';
|
||||||
import { SwarmDetail } from '../swarm/swarm-detail';
|
|
||||||
import { buildSocialCards } from '../../lib/social-cards';
|
import { buildSocialCards } from '../../lib/social-cards';
|
||||||
import { buildSwarmCards } from '../../lib/swarm-cards';
|
import { buildSwarmCards } from '../../lib/swarm-cards';
|
||||||
|
|
||||||
|
|
@ -27,7 +26,7 @@ export interface UnifiedShellProps {
|
||||||
export function UnifiedShell({
|
export function UnifiedShell({
|
||||||
issues,
|
issues,
|
||||||
}: UnifiedShellProps) {
|
}: UnifiedShellProps) {
|
||||||
const { view, taskId, setTaskId, swarmId, setSwarmId, graphTab, setGraphTab, panel } = useUrlState();
|
const { view, taskId, setTaskId, swarmId, setSwarmId, graphTab, setGraphTab, panel, drawer, setDrawer } = useUrlState();
|
||||||
|
|
||||||
const socialCards = useMemo(() => buildSocialCards(issues), [issues]);
|
const socialCards = useMemo(() => buildSocialCards(issues), [issues]);
|
||||||
const swarmCards = useMemo(() => buildSwarmCards(issues), [issues]);
|
const swarmCards = useMemo(() => buildSwarmCards(issues), [issues]);
|
||||||
|
|
@ -39,14 +38,33 @@ export function UnifiedShell({
|
||||||
setTaskId(id);
|
setTaskId(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCardSelect = (id: string) => {
|
||||||
|
if (view === 'social') {
|
||||||
|
setTaskId(id);
|
||||||
|
} else if (view === 'swarm') {
|
||||||
|
setSwarmId(id);
|
||||||
|
}
|
||||||
|
setDrawer('open');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseDrawer = () => {
|
||||||
|
setDrawer('closed');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Thread drawer - shows when card selected
|
||||||
|
const isDrawerOpen = drawer === 'open' && (!!taskId || !!swarmId);
|
||||||
|
const drawerTitle = selectedSocialCard?.title || selectedSwarmCard?.title || '';
|
||||||
|
const drawerId = taskId || swarmId || '';
|
||||||
|
|
||||||
const renderRightPanel = () => {
|
const renderRightPanel = () => {
|
||||||
if (view === 'social' && taskId && selectedSocialCard) {
|
// TODO: Wire up ActivityPanel (bb-ui2.29) - for now show placeholder
|
||||||
return <SocialDetail data={selectedSocialCard} />;
|
return (
|
||||||
}
|
<div className="p-4 text-center text-text-muted text-sm">
|
||||||
if (view === 'swarm' && swarmId && selectedSwarmCard) {
|
Activity Panel coming
|
||||||
return <SwarmDetail card={selectedSwarmCard} />;
|
<br />
|
||||||
}
|
<span className="text-xs">(bb-ui2.29)</span>
|
||||||
return null;
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderMiddleContent = () => {
|
const renderMiddleContent = () => {
|
||||||
|
|
@ -105,12 +123,20 @@ export function UnifiedShell({
|
||||||
{renderMiddleContent()}
|
{renderMiddleContent()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* RIGHT PANEL: 17rem detail strip */}
|
{/* RIGHT PANEL: 17rem - Always shows Activity (bb-ui2.29) */}
|
||||||
<RightPanel isOpen={panel === 'open'}>
|
<RightPanel isOpen={panel === 'open'}>
|
||||||
{renderRightPanel()}
|
{renderRightPanel()}
|
||||||
</RightPanel>
|
</RightPanel>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* THREAD DRAWER: 24rem - Slides from right edge of middle when card selected */}
|
||||||
|
<ThreadDrawer
|
||||||
|
isOpen={isDrawerOpen}
|
||||||
|
onClose={handleCloseDrawer}
|
||||||
|
title={drawerTitle}
|
||||||
|
id={drawerId}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* MOBILE NAV: Bottom tab bar */}
|
{/* MOBILE NAV: Bottom tab bar */}
|
||||||
<MobileNav />
|
<MobileNav />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,34 +3,8 @@
|
||||||
import type { SocialCard as SocialCardData, AgentStatus } from '../../lib/social-cards';
|
import type { SocialCard as SocialCardData, AgentStatus } from '../../lib/social-cards';
|
||||||
import { StatusBadge } from '../shared/status-badge';
|
import { StatusBadge } from '../shared/status-badge';
|
||||||
import { AgentAvatar } from '../shared/agent-avatar';
|
import { AgentAvatar } from '../shared/agent-avatar';
|
||||||
import { ThreadView, type ThreadItem } from '../shared/thread-view';
|
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
|
|
||||||
// Sample data for demo - remove when real data connected
|
|
||||||
const SAMPLE_THREAD_ITEMS: ThreadItem[] = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
type: 'status_change',
|
|
||||||
from: 'backlog',
|
|
||||||
to: 'in_progress',
|
|
||||||
timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
type: 'comment',
|
|
||||||
author: 'zenchantlive',
|
|
||||||
content: 'Started working on this task. Will need input from the API team.',
|
|
||||||
timestamp: new Date(Date.now() - 1 * 60 * 60 * 1000),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
type: 'protocol_event',
|
|
||||||
event: 'HANDOFF',
|
|
||||||
content: 'Handed off to bb-agent-1 for implementation',
|
|
||||||
timestamp: new Date(Date.now() - 30 * 60 * 1000),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
interface SocialDetailProps {
|
interface SocialDetailProps {
|
||||||
data: SocialCardData;
|
data: SocialCardData;
|
||||||
}
|
}
|
||||||
|
|
@ -67,7 +41,9 @@ export function SocialDetail({ data }: SocialDetailProps) {
|
||||||
<h3 className="text-text-muted text-xs font-semibold uppercase tracking-wider">
|
<h3 className="text-text-muted text-xs font-semibold uppercase tracking-wider">
|
||||||
Thread
|
Thread
|
||||||
</h3>
|
</h3>
|
||||||
<ThreadView items={SAMPLE_THREAD_ITEMS} />
|
<p className="text-text-muted text-sm italic">
|
||||||
|
Thread drawer coming (bb-ui2.31)
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{data.blocks.length > 0 && (
|
{data.blocks.length > 0 && (
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
import type { SwarmCard as SwarmCardType } from '../../lib/swarm-cards';
|
import type { SwarmCard as SwarmCardType } from '../../lib/swarm-cards';
|
||||||
import { Badge } from '../../../components/ui/badge';
|
import { Badge } from '../../../components/ui/badge';
|
||||||
import { AgentAvatar } from '../shared/agent-avatar';
|
import { AgentAvatar } from '../shared/agent-avatar';
|
||||||
import { ThreadView, type ThreadItem } from '../shared/thread-view';
|
|
||||||
import { cn } from '../../lib/utils';
|
import { cn } from '../../lib/utils';
|
||||||
import { AlertTriangle, Clock, Users } from 'lucide-react';
|
import { AlertTriangle, Clock, Users } from 'lucide-react';
|
||||||
|
|
||||||
|
|
@ -149,38 +148,15 @@ function LastActivitySection({ date }: { date: Date }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sample data for demo - remove when real data connected
|
|
||||||
const SAMPLE_SWARM_THREAD: ThreadItem[] = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
type: 'status_change',
|
|
||||||
from: 'planning',
|
|
||||||
to: 'in_progress',
|
|
||||||
timestamp: new Date(Date.now() - 4 * 60 * 60 * 1000),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
type: 'comment',
|
|
||||||
author: 'bb-agent-1',
|
|
||||||
content: 'Starting work on the first batch of tasks.',
|
|
||||||
timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
type: 'protocol_event',
|
|
||||||
event: 'CLOSED',
|
|
||||||
content: 'Task bb-buff.1 completed',
|
|
||||||
timestamp: new Date(Date.now() - 30 * 60 * 1000),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
function ThreadSection() {
|
function ThreadSection() {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<span className="text-xs font-semibold uppercase tracking-wider" style={{ color: 'var(--color-text-muted)' }}>
|
<span className="text-xs font-semibold uppercase tracking-wider" style={{ color: 'var(--color-text-muted)' }}>
|
||||||
Thread
|
Thread
|
||||||
</span>
|
</span>
|
||||||
<ThreadView items={SAMPLE_SWARM_THREAD} />
|
<p className="text-text-muted text-sm italic">
|
||||||
|
Thread drawer coming (bb-ui2.31)
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,9 @@
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { useSearchParams, useRouter } from 'next/navigation';
|
import { useSearchParams, useRouter } from 'next/navigation';
|
||||||
|
|
||||||
export type ViewType = 'social' | 'graph' | 'swarm';
|
export type ViewType = 'social' | 'graph' | 'swarm' | 'activity';
|
||||||
export type PanelState = 'open' | 'closed';
|
export type PanelState = 'open' | 'closed';
|
||||||
|
export type DrawerState = 'open' | 'closed';
|
||||||
export type GraphTabType = 'flow' | 'overview';
|
export type GraphTabType = 'flow' | 'overview';
|
||||||
|
|
||||||
export interface UrlState {
|
export interface UrlState {
|
||||||
|
|
@ -14,8 +15,12 @@ export interface UrlState {
|
||||||
setTaskId: (id: string | null) => void;
|
setTaskId: (id: string | null) => void;
|
||||||
swarmId: string | null;
|
swarmId: string | null;
|
||||||
setSwarmId: (id: string | null) => void;
|
setSwarmId: (id: string | null) => void;
|
||||||
|
agentId: string | null;
|
||||||
|
setAgentId: (id: string | null) => void;
|
||||||
panel: PanelState;
|
panel: PanelState;
|
||||||
togglePanel: () => void;
|
togglePanel: () => void;
|
||||||
|
drawer: DrawerState;
|
||||||
|
setDrawer: (state: DrawerState) => void;
|
||||||
graphTab: GraphTabType;
|
graphTab: GraphTabType;
|
||||||
setGraphTab: (tab: GraphTabType) => void;
|
setGraphTab: (tab: GraphTabType) => void;
|
||||||
clearSelection: () => void;
|
clearSelection: () => void;
|
||||||
|
|
@ -23,17 +28,21 @@ export interface UrlState {
|
||||||
|
|
||||||
const DEFAULT_VIEW: ViewType = 'social';
|
const DEFAULT_VIEW: ViewType = 'social';
|
||||||
const DEFAULT_PANEL: PanelState = 'closed';
|
const DEFAULT_PANEL: PanelState = 'closed';
|
||||||
|
const DEFAULT_DRAWER: DrawerState = 'closed';
|
||||||
const DEFAULT_GRAPH_TAB: GraphTabType = 'flow';
|
const DEFAULT_GRAPH_TAB: GraphTabType = 'flow';
|
||||||
|
|
||||||
const VALID_VIEWS: ViewType[] = ['social', 'graph', 'swarm'];
|
const VALID_VIEWS: ViewType[] = ['social', 'graph', 'swarm', 'activity'];
|
||||||
const VALID_PANELS: PanelState[] = ['open', 'closed'];
|
const VALID_PANELS: PanelState[] = ['open', 'closed'];
|
||||||
|
const VALID_DRAWERS: DrawerState[] = ['open', 'closed'];
|
||||||
const VALID_GRAPH_TABS: GraphTabType[] = ['flow', 'overview'];
|
const VALID_GRAPH_TABS: GraphTabType[] = ['flow', 'overview'];
|
||||||
|
|
||||||
export function parseUrlState(searchParams: URLSearchParams): {
|
export function parseUrlState(searchParams: URLSearchParams): {
|
||||||
view: ViewType;
|
view: ViewType;
|
||||||
taskId: string | null;
|
taskId: string | null;
|
||||||
swarmId: string | null;
|
swarmId: string | null;
|
||||||
|
agentId: string | null;
|
||||||
panel: PanelState;
|
panel: PanelState;
|
||||||
|
drawer: DrawerState;
|
||||||
graphTab: GraphTabType;
|
graphTab: GraphTabType;
|
||||||
} {
|
} {
|
||||||
const viewParam = searchParams.get('view');
|
const viewParam = searchParams.get('view');
|
||||||
|
|
@ -43,18 +52,24 @@ export function parseUrlState(searchParams: URLSearchParams): {
|
||||||
|
|
||||||
const taskId = searchParams.get('task');
|
const taskId = searchParams.get('task');
|
||||||
const swarmId = searchParams.get('swarm');
|
const swarmId = searchParams.get('swarm');
|
||||||
|
const agentId = searchParams.get('agent');
|
||||||
|
|
||||||
const panelParam = searchParams.get('panel');
|
const panelParam = searchParams.get('panel');
|
||||||
const panel: PanelState = panelParam && VALID_PANELS.includes(panelParam as PanelState)
|
const panel: PanelState = panelParam && VALID_PANELS.includes(panelParam as PanelState)
|
||||||
? (panelParam as PanelState)
|
? (panelParam as PanelState)
|
||||||
: DEFAULT_PANEL;
|
: DEFAULT_PANEL;
|
||||||
|
|
||||||
|
const drawerParam = searchParams.get('drawer');
|
||||||
|
const drawer: DrawerState = drawerParam && VALID_DRAWERS.includes(drawerParam as DrawerState)
|
||||||
|
? (drawerParam as DrawerState)
|
||||||
|
: DEFAULT_DRAWER;
|
||||||
|
|
||||||
const graphTabParam = searchParams.get('graphTab');
|
const graphTabParam = searchParams.get('graphTab');
|
||||||
const graphTab: GraphTabType = graphTabParam && VALID_GRAPH_TABS.includes(graphTabParam as GraphTabType)
|
const graphTab: GraphTabType = graphTabParam && VALID_GRAPH_TABS.includes(graphTabParam as GraphTabType)
|
||||||
? (graphTabParam as GraphTabType)
|
? (graphTabParam as GraphTabType)
|
||||||
: DEFAULT_GRAPH_TAB;
|
: DEFAULT_GRAPH_TAB;
|
||||||
|
|
||||||
return { view, taskId, swarmId, panel, graphTab };
|
return { view, taskId, swarmId, agentId, panel, drawer, graphTab };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildUrlParams(
|
export function buildUrlParams(
|
||||||
|
|
@ -98,17 +113,25 @@ export function useUrlState(): UrlState {
|
||||||
updateUrl({ swarm: id, panel: id ? 'open' : null });
|
updateUrl({ swarm: id, panel: id ? 'open' : null });
|
||||||
}, [updateUrl]);
|
}, [updateUrl]);
|
||||||
|
|
||||||
|
const setAgentId = useCallback((id: string | null) => {
|
||||||
|
updateUrl({ agent: id, panel: id ? 'open' : null });
|
||||||
|
}, [updateUrl]);
|
||||||
|
|
||||||
const togglePanel = useCallback(() => {
|
const togglePanel = useCallback(() => {
|
||||||
const newPanel = state.panel === 'open' ? 'closed' : 'open';
|
const newPanel = state.panel === 'open' ? 'closed' : 'open';
|
||||||
updateUrl({ panel: newPanel });
|
updateUrl({ panel: newPanel });
|
||||||
}, [state.panel, updateUrl]);
|
}, [state.panel, updateUrl]);
|
||||||
|
|
||||||
|
const setDrawer = useCallback((state: DrawerState) => {
|
||||||
|
updateUrl({ drawer: state });
|
||||||
|
}, [updateUrl]);
|
||||||
|
|
||||||
const setGraphTab = useCallback((tab: GraphTabType) => {
|
const setGraphTab = useCallback((tab: GraphTabType) => {
|
||||||
updateUrl({ graphTab: tab });
|
updateUrl({ graphTab: tab });
|
||||||
}, [updateUrl]);
|
}, [updateUrl]);
|
||||||
|
|
||||||
const clearSelection = useCallback(() => {
|
const clearSelection = useCallback(() => {
|
||||||
updateUrl({ task: null, swarm: null, panel: 'closed' });
|
updateUrl({ task: null, swarm: null, panel: 'closed', drawer: 'closed' });
|
||||||
}, [updateUrl]);
|
}, [updateUrl]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -118,8 +141,12 @@ export function useUrlState(): UrlState {
|
||||||
setTaskId,
|
setTaskId,
|
||||||
swarmId: state.swarmId,
|
swarmId: state.swarmId,
|
||||||
setSwarmId,
|
setSwarmId,
|
||||||
|
agentId: state.agentId,
|
||||||
|
setAgentId,
|
||||||
panel: state.panel,
|
panel: state.panel,
|
||||||
togglePanel,
|
togglePanel,
|
||||||
|
drawer: state.drawer,
|
||||||
|
setDrawer,
|
||||||
graphTab: state.graphTab,
|
graphTab: state.graphTab,
|
||||||
setGraphTab,
|
setGraphTab,
|
||||||
clearSelection,
|
clearSelection,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue