From a398819e260c3e78d3cd68c48bd20fa0a46edbe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBeeRad=E2=80=9D?= Date: Thu, 29 Jan 2026 15:27:53 +1100 Subject: [PATCH] feat(rah-light): remove chat pane, simplify to two-panel layout - Deleted src/components/panes/ChatPane.tsx - Removed 'chat' from PaneType in types.ts - Updated ThreePanelLayout.tsx to remove chat pane case and references - Updated LeftToolbar.tsx to remove chat icon and type - Default slot B is now null (closed) instead of chat - Cmd+\ now opens node pane instead of chat Co-Authored-By: Claude Opus 4.5 --- src/components/layout/LeftToolbar.tsx | 9 +- src/components/layout/ThreePanelLayout.tsx | 79 +++------ src/components/panes/ChatPane.tsx | 183 --------------------- src/components/panes/index.ts | 1 - src/components/panes/types.ts | 23 +-- 5 files changed, 30 insertions(+), 265 deletions(-) delete mode 100644 src/components/panes/ChatPane.tsx diff --git a/src/components/layout/LeftToolbar.tsx b/src/components/layout/LeftToolbar.tsx index 76824a6..ec852e5 100644 --- a/src/components/layout/LeftToolbar.tsx +++ b/src/components/layout/LeftToolbar.tsx @@ -5,7 +5,6 @@ import { Search, Plus, LayoutList, - MessageSquare, Map, Folder, Workflow, @@ -23,10 +22,9 @@ interface LeftToolbarProps { slotBType: PaneType | null; } -// Map pane types to their icons +// Map pane types to their icons (chat removed in rah-light) const PANE_TYPE_ICONS: Record = { views: LayoutList, - chat: MessageSquare, map: Map, dimensions: Folder, workflows: Workflow, @@ -34,14 +32,13 @@ const PANE_TYPE_ICONS: Record = { const PANE_TYPE_LABELS: Record = { views: 'Feed', - chat: 'Chat', map: 'Map', dimensions: 'Dimensions', workflows: 'Workflows', }; -// Pane types shown in the toolbar (excludes 'node' which is opened via Feed) -const TOOLBAR_PANE_TYPES: PaneType[] = ['views', 'chat', 'map', 'dimensions', 'workflows']; +// Pane types shown in the toolbar (excludes 'node' which is opened via Feed, chat removed in rah-light) +const TOOLBAR_PANE_TYPES: PaneType[] = ['views', 'map', 'dimensions', 'workflows']; interface ToolbarButtonProps { icon: typeof Search; diff --git a/src/components/layout/ThreePanelLayout.tsx b/src/components/layout/ThreePanelLayout.tsx index 9cc765c..d5a9ff5 100644 --- a/src/components/layout/ThreePanelLayout.tsx +++ b/src/components/layout/ThreePanelLayout.tsx @@ -6,7 +6,7 @@ import SearchModal from '../nodes/SearchModal'; import { Node } from '@/types/database'; import { DatabaseEvent } from '@/services/events'; import { usePersistentState } from '@/hooks/usePersistentState'; -import type { ChatMessage } from '@/components/agents/hooks/useSSEChat'; +// ChatMessage import removed - chat disabled in rah-light // Stub type for delegation (delegation system removed in rah-light) type AgentDelegation = { @@ -25,8 +25,8 @@ type AgentDelegation = { import LeftToolbar from './LeftToolbar'; import SplitHandle from './SplitHandle'; -// Pane components -import { NodePane, ChatPane, WorkflowsPane, DimensionsPane, MapPane, ViewsPane } from '../panes'; +// Pane components (ChatPane removed in rah-light) +import { NodePane, WorkflowsPane, DimensionsPane, MapPane, ViewsPane } from '../panes'; import QuickAddInput from '../agents/QuickAddInput'; import type { PaneType, SlotState, PaneAction } from '../panes/types'; @@ -35,14 +35,14 @@ export default function ThreePanelLayout() { const containerRef = useRef(null); // Slot states - the core of the flexible pane system - // Default: Feed on left, Chat on right - const [slotA, setSlotA] = usePersistentState('ui.slotA.v3', { + // Default: Feed on left, closed on right (chat removed in rah-light) + const [slotA, setSlotA] = usePersistentState('ui.slotA.v4', { type: 'views', }); // SlotB can be null (closed) or a SlotState - // Default: Chat on the right - const [slotB, setSlotB] = usePersistentState('ui.slotB.v3', { type: 'chat' }); + // Default: closed (chat removed in rah-light) + const [slotB, setSlotB] = usePersistentState('ui.slotB.v4', null); // SlotB width as percentage (when open) const [slotBWidth, setSlotBWidth] = usePersistentState('ui.slotBWidth', 50); @@ -75,17 +75,14 @@ export default function ThreePanelLayout() { const [focusPanelRefresh, setFocusPanelRefresh] = useState(0); const [folderViewRefresh, setFolderViewRefresh] = useState(0); - // Active dimension tracking (for chat context) + // Active dimension tracking const [activeDimension, setActiveDimension] = usePersistentState('ui.focus.activeDimension', null); // Delegations state (deprecated - kept for component compatibility) const [delegationsMap] = useState>({}); const delegations = useMemo(() => Object.values(delegationsMap), [delegationsMap]); - // Chat state (lifted to persist across pane type changes) - const [chatMessages, setChatMessages] = useState([]); - - // Source awareness - highlighted passage context for agent + // Source awareness - highlighted passage context const [highlightedPassage, setHighlightedPassage] = useState<{ nodeId: number; nodeTitle: string; @@ -95,29 +92,13 @@ export default function ThreePanelLayout() { // Ref to get current openTabs value in SSE handler const openTabsRef = useRef([]); - // Get open tabs from the slot that has nodes (for chat context) + // Get open tabs from the slot that has nodes // Memoize to prevent infinite re-renders const { openTabs, activeTab } = useMemo(() => { const slotAHasNodes = slotA?.type === 'node'; const slotBHasNodes = slotB?.type === 'node'; - // If chat is in Slot A, use Slot B's nodes - if (slotA?.type === 'chat' && slotBHasNodes) { - return { - openTabs: slotB.nodeTabs ?? [], - activeTab: slotB.activeNodeTab ?? null, - }; - } - - // If chat is in Slot B (default), use Slot A's nodes - if (slotB?.type === 'chat' && slotAHasNodes && slotA) { - return { - openTabs: slotA.nodeTabs ?? [], - activeTab: slotA.activeNodeTab ?? null, - }; - } - - // Fallback: use Slot A if it has nodes + // Use Slot A if it has nodes if (slotAHasNodes && slotA) { return { openTabs: slotA.nodeTabs ?? [], @@ -125,6 +106,14 @@ export default function ThreePanelLayout() { }; } + // Fallback: use Slot B if it has nodes + if (slotBHasNodes && slotB) { + return { + openTabs: slotB.nodeTabs ?? [], + activeTab: slotB.activeNodeTab ?? null, + }; + } + return { openTabs: [], activeTab: null }; }, [slotA, slotB]); @@ -189,8 +178,8 @@ export default function ThreePanelLayout() { if (slotB) { setSlotB(null); } else { - // Open with chat by default - setSlotB({ type: 'chat' }); + // Open with node pane by default (chat removed in rah-light) + setSlotB({ type: 'node', nodeTabs: [], activeNodeTab: null }); } } // Cmd+N - open Add Stuff modal @@ -749,7 +738,7 @@ export default function ThreePanelLayout() { // Split handle callbacks const handleOpenSecondPane = useCallback(() => { - setSlotB({ type: 'chat' }); // Default to chat when opening + setSlotB({ type: 'node', nodeTabs: [], activeNodeTab: null }); // Default to node pane (chat removed in rah-light) setActivePane('B'); }, [setSlotB]); @@ -816,29 +805,7 @@ export default function ThreePanelLayout() { /> ); - case 'chat': - return ( - setActiveDimension(null)} - onNodeClick={(nodeId) => { - handleNodeSelect(nodeId, false); - setActivePane(slot); - }} - delegations={delegations} - chatMessages={chatMessages as unknown[]} - setChatMessages={setChatMessages as React.Dispatch>} - highlightedPassage={highlightedPassage} - onClearPassage={() => setHighlightedPassage(null)} - /> - ); + // case 'chat' removed in rah-light case 'workflows': return ( diff --git a/src/components/panes/ChatPane.tsx b/src/components/panes/ChatPane.tsx deleted file mode 100644 index b07105b..0000000 --- a/src/components/panes/ChatPane.tsx +++ /dev/null @@ -1,183 +0,0 @@ -"use client"; - -import { useState, useEffect } from 'react'; -import RAHChat from '@/components/agents/RAHChat'; -import PaneHeader from './PaneHeader'; -import { ChatPaneProps } from './types'; -import type { ChatMessage } from '@/components/agents/hooks/useSSEChat'; - -export default function ChatPane({ - slot, - isActive, - onPaneAction, - onCollapse, - onSwapPanes, - openTabsData, - activeTabId, - activeDimension, - onClearDimension, - onNodeClick, - delegations, - chatMessages: externalMessages, - setChatMessages: externalSetMessages, - highlightedPassage, - onClearPassage, -}: ChatPaneProps) { - const [rahMode, setRahMode] = useState<'easy' | 'hard'>('easy'); - const [hasStarted, setHasStarted] = useState(false); - - // Use lifted state if provided, otherwise fall back to local state - const [internalMessages, setInternalMessages] = useState([]); - - const rahMessages = (externalMessages as ChatMessage[]) ?? internalMessages; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const setRahMessages = (externalSetMessages as any) ?? setInternalMessages; - - // Load mode from localStorage - useEffect(() => { - if (typeof window === 'undefined') return; - const stored = window.localStorage.getItem('rah-mode'); - if (stored === 'easy' || stored === 'hard') { - setRahMode(stored); - } - }, []); - - // Persist mode to localStorage - useEffect(() => { - if (typeof window === 'undefined') return; - window.localStorage.setItem('rah-mode', rahMode); - }, [rahMode]); - - // Listen for mode toggle events - useEffect(() => { - const handler = (event: Event) => { - const detail = (event as CustomEvent<{ mode: 'easy' | 'hard' }>).detail; - if (detail?.mode) { - setRahMode(detail.mode); - } - }; - window.addEventListener('rah:mode-toggle', handler as EventListener); - return () => { - window.removeEventListener('rah:mode-toggle', handler as EventListener); - }; - }, []); - - // Auto-start if there are existing messages - useEffect(() => { - if (rahMessages.length > 0 && !hasStarted) { - setHasStarted(true); - } - }, [rahMessages.length, hasStarted]); - - return ( -
- - -
- {!hasStarted ? ( -
- {/* Start Chat button */} -
- -
-
- ) : ( - - )} -
-
- ); -} diff --git a/src/components/panes/index.ts b/src/components/panes/index.ts index 6f45346..1c24365 100644 --- a/src/components/panes/index.ts +++ b/src/components/panes/index.ts @@ -1,5 +1,4 @@ export { default as NodePane } from './NodePane'; -export { default as ChatPane } from './ChatPane'; export { default as WorkflowsPane } from './WorkflowsPane'; export { default as DimensionsPane } from './DimensionsPane'; export { default as MapPane } from './MapPane'; diff --git a/src/components/panes/types.ts b/src/components/panes/types.ts index 1336b02..8461676 100644 --- a/src/components/panes/types.ts +++ b/src/components/panes/types.ts @@ -14,8 +14,8 @@ export type AgentDelegation = { updatedAt: string; }; -// The six pane types -export type PaneType = 'node' | 'chat' | 'workflows' | 'dimensions' | 'map' | 'views'; +// The five pane types (chat removed in rah-light) +export type PaneType = 'node' | 'workflows' | 'dimensions' | 'map' | 'views'; // State for each slot export interface SlotState { @@ -66,21 +66,7 @@ export interface HighlightedPassage { selectedText: string; } -// ChatPane specific props -export interface ChatPaneProps extends BasePaneProps { - openTabsData: Node[]; - activeTabId: number | null; - activeDimension?: string | null; - onClearDimension?: () => void; - onNodeClick?: (nodeId: number) => void; - delegations: AgentDelegation[]; - // Lifted state for persistence - chatMessages?: unknown[]; - setChatMessages?: React.Dispatch>; - // Source awareness - highlightedPassage?: HighlightedPassage | null; - onClearPassage?: () => void; -} +// ChatPaneProps removed in rah-light // WorkflowsPane specific props export interface WorkflowsPaneProps extends BasePaneProps { @@ -124,7 +110,6 @@ export interface PaneHeaderProps { // Labels for pane types export const PANE_LABELS: Record = { node: 'Nodes', - chat: 'Chat', workflows: 'Workflows', dimensions: 'Dimensions', map: 'Map', @@ -139,5 +124,5 @@ export const DEFAULT_SLOT_A: SlotState = { }; export const DEFAULT_SLOT_B: SlotState = { - type: 'chat', + type: 'workflows', };