[Haik]: refactor: restructure frontend component hierarchy — move OnboardingModal into OnboardingModal/ with ProviderStep, ToolsStep, and useOnboarding submodules; move OpenSwarmThread from thread/ into OpenSwarmThread/ with AssistantMessage, BranchPicker, MessageActions, UserMessage components and utils; split AgentCardCollapsed into AgentCardCollapsed/ extracting GoogleServiceIcon, getToolDisplayName, and summarizeToolInput into components/; move approval-card to ApprovalCard (PascalCase) with ApprovalCard.tsx, schema.ts, _adapter.tsx, and README.md; update import paths in AgentChat.tsx, CompactActionablePill, ExpandedCard, BrowserCard, AgentCard, and approval-utils; remove re-exports from approval-tools.tsx in favor of direct imports; replace aliased component names (ApprovalBar → ApprovalRouter, BatchApprovalBar → BatchApprovalWrapper)

This commit is contained in:
haikdc
2026-04-18 14:42:41 -07:00
parent 2f0ff10d3f
commit 1c0b886052
25 changed files with 84 additions and 36 deletions
@@ -7,7 +7,8 @@ import CloseIcon from '@mui/icons-material/Close';
import CheckIcon from '@mui/icons-material/Check';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { motion } from 'framer-motion';
import { parseMcpToolName, useMcpToolMeta, getToolIcon } from '@/app/pages/AgentChat/toolkit/approval-tools';
import { parseMcpToolName, getToolIcon } from '@/app/pages/AgentChat/toolkit/approval-utils';
import { useMcpToolMeta } from '@/app/pages/AgentChat/toolkit/approval-tools';
import { SPRING_BOUNCE } from './islandTypes';
import type { ClaudeTokens } from './islandTypes';
import type { ApprovalRequest } from '@/shared/state/agentsSlice';
@@ -4,7 +4,7 @@ import Typography from '@mui/material/Typography';
import IconButton from '@mui/material/IconButton';
import CloseIcon from '@mui/icons-material/Close';
import { motion } from 'framer-motion';
import { ApprovalRouter as ApprovalBar, BatchApprovalWrapper as BatchApprovalBar } from '@/app/pages/AgentChat/toolkit/approval-tools';
import { ApprovalRouter, BatchApprovalWrapper } from '@/app/pages/AgentChat/toolkit/approval-tools';
import { AgentStatusRow } from './AgentStatusRow';
import { CompletedAgentsList } from './CompletedAgentsList';
import type { ClaudeTokens, SessionApprovalGroup, TrackedAgent } from './islandTypes';
@@ -144,14 +144,14 @@ export const ExpandedCard: React.FC<{
</Typography>
)}
{group.approvals.length > 1 ? (
<BatchApprovalBar
<BatchApprovalWrapper
requests={group.approvals}
onApprove={onApprove}
onDeny={onDeny}
/>
) : (
group.approvals.map((req) => (
<ApprovalBar
<ApprovalRouter
key={req.id}
request={req}
onApprove={onApprove}
@@ -1,9 +1,9 @@
import React from 'react';
import { Box, Modal } from '@mui/material';
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
import { useOnboarding } from './useOnboarding';
import ProviderStep from './ProviderStep';
import ToolsStep from './ToolsStep';
import { useOnboarding } from './components/useOnboarding';
import ProviderStep from './components/ProviderStep';
import ToolsStep from './components/ToolsStep';
const OnboardingModal: React.FC = () => {
const c = useClaudeTokens();
@@ -5,7 +5,7 @@ import { AssistantRuntimeProvider, useAui, Tools } from '@assistant-ui/react';
import { ApprovalRouter, BatchApprovalWrapper } from './toolkit/approval-tools';
import ChatHeader from './ChatHeader';
import MessageQueue from './MessageQueue';
import OpenSwarmThread from './thread/OpenSwarmThread';
import OpenSwarmThread from './OpenSwarmThread/OpenSwarmThread';
import ChatInput from './ChatInput/ChatInput';
import { useAgentChat } from './hooks/useAgentChat';
import { useOpenSwarmRuntime, type ComposerExtras, type DispatchableMessage } from './runtime/useOpenSwarmRuntime';
@@ -1,4 +1,4 @@
import { createContext, useContext, type FC, type ReactNode } from 'react';
import { type FC, type ReactNode } from 'react';
import {
ThreadPrimitive,
MessagePrimitive,
@@ -9,16 +9,9 @@ import { ArrowDownIcon } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { TooltipProvider } from '@/components/ui/tooltip';
import { TooltipIconButton } from '@/components/assistant-ui/tooltip-icon-button';
import { UserMessage } from './UserMessage';
import { AssistantMessage } from './AssistantMessage';
const SessionIdContext = createContext<string | undefined>(undefined);
export const useSessionId = () => useContext(SessionIdContext);
const BranchChatContext = createContext<
((newSessionId: string) => void) | undefined
>(undefined);
export const useBranchChatCallback = () => useContext(BranchChatContext);
import { UserMessage } from './components/UserMessage';
import { AssistantMessage } from './components/AssistantMessage';
import { SessionIdContext, BranchChatContext } from './utils';
interface OpenSwarmThreadProps {
sessionId?: string;
@@ -17,7 +17,7 @@ import {
setActiveSession,
} from '@/shared/state/agentsSlice';
import { DUPLICATE_SESSION } from '@/shared/backend-bridge/apps/agents';
import { useSessionId, useBranchChatCallback } from './OpenSwarmThread';
import { useSessionId, useBranchChatCallback } from '../utils';
export const UserActionBar: FC = () => (
<ActionBarPrimitive.Root
@@ -7,7 +7,7 @@ import {
import { UserMessageAttachments } from '@/components/assistant-ui/attachment';
import { useAppSelector } from '@/shared/hooks';
import type { AgentMessage } from '@/shared/state/agentsSlice';
import { useSessionId } from './OpenSwarmThread';
import { useSessionId } from '../utils';
import { UserActionBar } from './MessageActions';
import { BranchPicker } from './BranchPicker';
@@ -0,0 +1,9 @@
import { createContext, useContext } from 'react';
export const SessionIdContext = createContext<string | undefined>(undefined);
export const useSessionId = () => useContext(SessionIdContext);
export const BranchChatContext = createContext<
((newSessionId: string) => void) | undefined
>(undefined);
export const useBranchChatCallback = () => useContext(BranchChatContext);
@@ -1,6 +1,6 @@
import React, { useMemo, useCallback } from 'react';
import type { Toolkit } from '@assistant-ui/react';
import { ApprovalCard } from '@/components/tool-ui/approval-card/approval-card';
import { ApprovalCard } from '@/components/tool-ui/ApprovalCard/ApprovalCard';
import type { ApprovalRequest } from '@/shared/state/agentsSlice';
import { useAppSelector } from '@/shared/hooks';
import type { ToolDefinition } from '@/shared/state/toolsSlice';
@@ -12,13 +12,6 @@ import {
} from './approval-utils';
import { ToolQuestion } from './approval-question';
// Re-exports so external consumers can import everything from this file
export {
parseMcpToolName,
getToolIcon,
} from './approval-utils';
export { ToolQuestion } from './approval-question';
// ---------------------------------------------------------------------------
// useMcpToolMeta (React hook — lives here alongside other component code)
// ---------------------------------------------------------------------------
@@ -1,6 +1,7 @@
import React from 'react';
import type { ReactNode } from 'react';
import type { MetadataItem } from '@/components/tool-ui/approval-card';
// TODO: what is this even supposed to try and import/use ???
import type { MetadataItem } from '@/components/tool-ui/ApprovalCard/schema';
import {
Terminal, FileText, FilePen, Search,
MessageCircleQuestion, Wrench,
@@ -16,7 +16,7 @@ import { useClaudeTokens } from '@/shared/styles/ThemeContext';
import { useOverlayScrollPassthrough } from '../shared/useOverlayScrollPassthrough';
import { type ResizeDir, DRAG_THRESHOLD, CURSOR_MAP, HANDLE_DEFS } from '../shared/cardLayoutConstants';
import CardGlowOverlay from './components/CardGlowOverlay';
import AgentCardCollapsed from './components/AgentCardCollapsed';
import AgentCardCollapsed from './components/AgentCardCollapsed/AgentCardCollapsed';
import { formatDuration, getStatusColors, getPreviewContent } from './components/agentCardUtils';
interface Props {
@@ -12,10 +12,11 @@ import TerminalIcon from '@mui/icons-material/Terminal';
import { AgentSession } from '@/shared/state/agentsSlice';
import { HANDLE_APPROVAL } from '@/shared/backend-bridge/apps/agents';
import { useAppDispatch } from '@/shared/hooks';
import { ToolQuestion as QuestionForm } from '@/app/pages/AgentChat/toolkit/approval-tools';
import { ToolQuestion } from '@/app/pages/AgentChat/toolkit/approval-question';
import { parseMcpToolName } from '@/app/pages/AgentChat/toolkit/approval-utils';
import GoogleServiceIcon from '@/app/components/GoogleServiceIcon';
import { summarizeToolInput, getToolDisplayName } from './agentCardUtils';
import GoogleServiceIcon from './components/GoogleServiceIcon';
import { summarizeToolInput } from './components/summarizeToolInput';
import { getToolDisplayName } from './components/getToolDisplayName';
interface AgentCardCollapsedProps {
session: AgentSession;
@@ -75,7 +76,7 @@ const AgentCardCollapsed: React.FC<AgentCardCollapsedProps> = ({
{hasPending && pendingReq && pendingReq.tool_name === 'AskUserQuestion' ? (
<Box onClick={(e) => e.stopPropagation()}>
<QuestionForm
<ToolQuestion
compact
request={pendingReq}
onApprove={(requestId, updatedInput) =>
@@ -0,0 +1,7 @@
import { parseMcpToolName } from '@/app/pages/AgentChat/toolkit/approval-utils';
export function getToolDisplayName(toolName: string): string {
const mcp = parseMcpToolName(toolName);
if (mcp.isMcp) return mcp.displayName;
return toolName;
}
@@ -0,0 +1,43 @@
import { parseMcpToolName } from '@/app/pages/AgentChat/toolkit/approval-utils';
export function summarizeToolInput(toolName: string, toolInput: Record<string, any>): string {
const mcp = parseMcpToolName(toolName);
if (mcp.isMcp) {
const keys = Object.keys(toolInput || {});
if (keys.length === 0) return '';
if (keys.length === 1) {
const v = toolInput[keys[0]];
const s = typeof v === 'string' ? v : JSON.stringify(v);
return s.length > 60 ? s.slice(0, 60) + '…' : s;
}
return keys.slice(0, 3).map((k) => {
const v = toolInput[k];
const s = typeof v === 'string' ? v : JSON.stringify(v);
return `${k}: ${s.length > 30 ? s.slice(0, 30) + '…' : s}`;
}).join(' ');
}
switch (toolName) {
case 'Bash':
return toolInput.command || '(command)';
case 'Read':
return toolInput.file_path || toolInput.path || '(file)';
case 'Write':
case 'Edit':
return toolInput.file_path || toolInput.path || '(file)';
case 'Grep':
return `/${toolInput.pattern || ''}/${toolInput.path ? ` in ${toolInput.path}` : ''}`;
case 'Glob':
return toolInput.glob_pattern || toolInput.pattern || '(pattern)';
case 'AskUserQuestion': {
const questions = toolInput.questions;
if (Array.isArray(questions) && questions.length > 0) {
return questions[0].question || questions[0].prompt || questions[0].text || 'Question pending';
}
return 'Question pending';
}
default: {
return toolInput.command || toolInput.file_path || toolInput.path || toolInput.query
|| JSON.stringify(toolInput).slice(0, 60);
}
}
}
@@ -8,7 +8,7 @@ import { useBrowserActivity } from '@/shared/useBrowserActivity';
import { resolveInput, isGoogleSearch } from '@/shared/resolveUrl';
import BrowserAgentOverlay from './components/BrowserAgentOverlay/BrowserAgentOverlay';
import { useOverlayScrollPassthrough } from '../shared/useOverlayScrollPassthrough';
import { useElementSelection } from '@/app/components/ElementSelectionContext';
import { useElementSelection } from '@/app/pages/Dashboard/_shared/element_selection/useElementSelection';
import { type ResizeDir, CURSOR_MAP, HANDLE_DEFS, DRAG_THRESHOLD } from '../shared/cardLayoutConstants';
import { useWebviewLifecycle, isElectron, chromeUserAgent, webviewPreloadPath, type WebviewElement } from './hooks/useWebviewLifecycle';
import type { TabLocalState } from './TabLocalState';