import type { TranslationBundle } from '@jupyterlab/translation'; import { addIcon, LabIcon } from '@jupyterlab/ui-components'; import * as React from 'react'; import type { IAgent } from '../launcher/agents'; /** * A running agent terminal offered as a prompt target: the session data * from `IAgentTerminals` plus the matching agent's icon, resolved by the * plugin so these components stay presentation-only. */ export interface ISessionTarget { /** * The terminal session name. */ name: string; /** * The session's display label (usually the agent's name). */ label: string; /** * The agent's latest activity line, when one is available. */ activity: string | null; /** * The running agent's icon. */ icon: LabIcon; } /** * Keyboard support shared by the two radiogroups below, following the ARIA * radio-group pattern: the group is one tab stop (only the checked radio is * tabbable) and the arrow keys move the selection to the neighbouring * radio, wrapping around. Selection follows focus, so the handler clicks * the neighbour and the re-render moves the roving tabindex along. */ function onRadioGroupKeyDown(event: React.KeyboardEvent): void { let delta: number; switch (event.key) { case 'ArrowRight': case 'ArrowDown': delta = 1; break; case 'ArrowLeft': case 'ArrowUp': delta = -1; break; default: return; } const radios = Array.from( event.currentTarget.querySelectorAll('[role="radio"]') ); const index = radios.findIndex(radio => radio === event.target); if (index === -1) { return; } event.preventDefault(); const next = radios[(index + delta + radios.length) % radios.length]; next.focus(); next.click(); } /** * The chip row picking where a prompt goes: "New terminal" plus one chip * per running agent session. Renders nothing while no agent session is * running (a new terminal is then the only possibility and the row would * be noise). */ export function TargetChips(props: { /** * Agents that could start in a new terminal; gates the "New terminal" chip. */ agents: IAgent[]; /** * The running agent sessions. */ targets: ISessionTarget[]; /** * The selected session name, or `null` for a new terminal. */ targetName: string | null; trans: TranslationBundle; onSelect: (targetName: string | null) => void; }): JSX.Element | null { const { agents, targets, targetName, trans, onSelect } = props; if (targets.length === 0) { return null; } return (
{agents.length > 0 && ( )} {targets.map(target => ( ))}
); } /** * The icon radiogroup picking which agent a new terminal starts with. * Renders nothing when no agent accepts an initial prompt. */ export function AgentChoices(props: { agents: IAgent[]; agentId: string; trans: TranslationBundle; onSelect: (agentId: string) => void; }): JSX.Element | null { const { agents, agentId, trans, onSelect } = props; if (agents.length === 0) { return null; } return (
{agents.map(agent => ( ))}
); }