UNPKG

@agentman/chat-widget

Version:

Agentman Chat Widget for easy integration with web applications

692 lines (525 loc) • 17.5 kB
# ChatWidget.ts Refactoring Plan ## Current State Analysis **File:** `assistantWidget/ChatWidget.ts` **Lines:** 2,688 lines **Properties/Methods:** 108 total ### Problem Statement ChatWidget.ts has grown too large and violates the Single Responsibility Principle. Despite previous refactoring efforts, it still contains ~2,688 lines of mixed concerns including: - Widget orchestration (appropriate) - UI component creation (should be extracted) - Animation logic (should be extracted) - State management for various UI modes (should be extracted) - Agent initialization (should be extracted) - Layout/positioning logic (should be extracted) ### Biggest Offenders | Method | Lines | Purpose | Should Move To | |--------|-------|---------|----------------| | `showWelcomeCard()` | 246 | Create floating welcome card with animations | WelcomeCardComponent | | `showInputBar()` | 205 | Create floating input bar with typewriter effects | InputBarComponent | | `showFloatingPrompts()` | 158 | Create floating prompt bubbles | FloatingPromptsComponent | | `createWidget()` | 92 | Widget initialization | Keep (orchestration) | | `handleToggleListView()` | 89 | Conversation list toggle | ConversationManager | | `emitToggleEvent()` | 86 | Toggle button handler | Keep (orchestration) | | `dismissWelcomeCard()` | 83 | Welcome card dismissal + animation | WelcomeCardComponent | **Total in top 7 methods:** 959 lines (36% of file!) ## What We've Already Extracted āœ… Previous refactoring efforts have successfully extracted: 1. **InputBarFullController** (created today) - Sheet mode expand/collapse logic - Focus management - State machine for transitions - Accessibility features 2. **ViewManager** - Main widget UI orchestration - Toggle button creation - View transitions (welcome ↔ conversation) 3. **ConversationManager** - Conversation list management - Conversation switching 4. **Handlers** - MessageHandler - Message rendering and streaming - FileHandler - File upload and attachment management - ErrorHandler - Error handling and display - ConversationOrchestrator - Conversation flow coordination 5. **Services** - ApiService - API communication - MessageService - Message processing - AgentService - Agent-related operations ## What Still Needs Extraction āŒ ### 1. Closed View UI Components (~550 lines) **Problem:** Three massive methods create DOM for "widget closed" modes: - `showWelcomeCard()` - 246 lines - `showInputBar()` - 205 lines - `showFloatingPrompts()` - 158 lines Plus supporting methods: - `dismissWelcomeCard()` - 83 lines - `hideFloatingPrompts()` - ~30 lines - `hideInputBar()` - ~20 lines - `startTypewriterEffect()` - ~40 lines - `startTypewriterEffectCycling()` - ~50 lines **State being managed:** ```typescript private welcomeCardStateMap = new WeakMap<HTMLElement, WelcomeCardState>(); private isWelcomeCardAnimating: boolean = false; private activeWelcomeCard: HTMLElement | null = null; private inputBarCleanup: (() => void)[] = []; private inputBarTypewriterCleanup: (() => void) | null = null; ``` ### 2. Agent Initialization (~150 lines) **Problem:** Agent capability detection mixed with widget logic: - `initializeAgent()` - ~70 lines - `processAgentCapabilities()` - ~40 lines - Supporting logic - ~40 lines **State being managed:** ```typescript private supportedMimeTypes: string[] = []; private supportsAttachments: boolean = false; private agentCapabilities: any = null; private isInitializingAgent: boolean = false; ``` ### 3. Conversation Restoration (~100 lines) **Problem:** Persistence logic embedded in widget: - `restoreCurrentConversation()` - ~46 lines - `setupConversationManagement()` - ~55 lines ### 4. Layout & Positioning (~150 lines) **Problem:** Responsive layout logic: - `updateResponsiveLayout()` - ~51 lines - `applyPositionToWidget()` - ~73 lines - `applyWelcomeCardPosition()` - ~31 lines **State being managed:** ```typescript private resizeTimeout: number | null = null; ``` ### 5. Miscellaneous (~100 lines) - Conversation list view toggle logic - Theme update coordination - Various cleanup handlers --- ## Refactoring Plan ### Phase 1: Extract ClosedViewManager šŸŽÆ **HIGHEST PRIORITY** **Impact:** Remove ~550 lines from ChatWidget.ts #### 1.1 Create ClosedViewManager **File:** `assistantWidget/controllers/ClosedViewManager.ts` **Responsibilities:** - Determine which closed view mode to show - Orchestrate creation of welcome card, input bar, or floating prompts - Handle dismissal and cleanup - Manage show/hide animations **Public API:** ```typescript export class ClosedViewManager { constructor( config: ChatConfig, theme: ChatTheme, assets: ChatAssets, onPromptClick: (prompt: string) => void, onOpen: () => void ) // Show appropriate closed view based on config show(): void // Hide all closed views hide(): void // Clean up and remove from DOM destroy(): void // Check if any closed view is currently visible isVisible(): boolean } ``` #### 1.2 Create WelcomeCardComponent **File:** `assistantWidget/components/WelcomeCardComponent.ts` **Responsibilities:** - Create welcome card DOM structure - Handle card animations (show/dismiss) - Manage card positioning - Handle user interactions (prompts, minimize) **Public API:** ```typescript export class WelcomeCardComponent { constructor( config: ChatConfig, theme: ChatTheme, assets: ChatAssets, prompts: string[], onPromptClick: (prompt: string) => void, onMinimize: () => void ) // Create and show the card show(): void // Animate card dismissal dismiss(): Promise<void> // Remove from DOM destroy(): void // Get card position for widget opening getPosition(): { x: number; y: number } } ``` #### 1.3 Create InputBarComponent **File:** `assistantWidget/components/InputBarComponent.ts` **Responsibilities:** - Create input bar DOM structure - Handle typewriter effect animations - Manage prompt cycling - Handle user interactions **Public API:** ```typescript export class InputBarComponent { constructor( config: ChatConfig, theme: ChatTheme, assets: ChatAssets, prompts: string[], onPromptClick: (prompt: string) => void, onOpen: () => void ) show(): void hide(): void destroy(): void stopTypewriter(): void } ``` #### 1.4 Create FloatingPromptsComponent **File:** `assistantWidget/components/FloatingPromptsComponent.ts` **Responsibilities:** - Create floating prompts DOM structure - Handle dismissal behavior - Manage prompt click events **Public API:** ```typescript export class FloatingPromptsComponent { constructor( config: ChatConfig, theme: ChatTheme, prompts: string[], onPromptClick: (prompt: string) => void ) show(): void dismiss(): void destroy(): void } ``` #### 1.5 Update ChatWidget.ts **Remove:** - `showWelcomeCard()` (246 lines) - `showInputBar()` (205 lines) - `showFloatingPrompts()` (158 lines) - `dismissWelcomeCard()` (83 lines) - `hideInputBar()` (~20 lines) - `hideFloatingPrompts()` (~30 lines) - `startTypewriterEffect()` (~40 lines) - `startTypewriterEffectCycling()` (~50 lines) - All related state properties **Add:** ```typescript private closedViewManager: ClosedViewManager | null = null; // In createWidget(): if (this.config.variant === 'corner' && !this.config.initiallyOpen) { this.closedViewManager = new ClosedViewManager( this.config, this.theme, this.assets, (prompt) => this.handlePromptClick(prompt), () => this.handleOpen() ); const delay = this.config.agentClosedView === 'input-bar' || this.isInputBarFullMode ? 0 : 5000; setTimeout(() => { this.closedViewManager?.show(); }, delay); } // In destroy(): this.closedViewManager?.destroy(); ``` **Result:** ChatWidget.ts drops from 2,688 → **~2,100 lines** (22% reduction) --- ### Phase 2: Extract AgentInitializer **Impact:** Remove ~150 lines from ChatWidget.ts #### 2.1 Create AgentInitializer **File:** `assistantWidget/handlers/AgentInitializer.ts` **Responsibilities:** - Initialize agent and fetch capabilities - Process agent metadata - Extract supported MIME types - Determine attachment support **Public API:** ```typescript export class AgentInitializer { constructor( apiService: ApiService, agentService: AgentService, config: ChatConfig ) async initialize(): Promise<AgentCapabilities> getCapabilities(): AgentCapabilities getSupportedMimeTypes(): string[] supportsAttachments(): boolean } export interface AgentCapabilities { supportedMimeTypes: string[]; supportsAttachments: boolean; metadata: any; } ``` #### 2.2 Update ChatWidget.ts **Remove:** - `initializeAgent()` (~70 lines) - `processAgentCapabilities()` (~40 lines) - Related state properties **Add:** ```typescript private agentInitializer: AgentInitializer; // In constructor: this.agentInitializer = new AgentInitializer( this.apiService, this.agentService, this.config ); // In createWidget(): this.agentInitializer.initialize().then(capabilities => { this.updateFileHandlerCapabilities(capabilities); }); ``` **Result:** ChatWidget.ts drops from ~2,100 → **~1,950 lines** --- ### Phase 3: Extract ConversationRestorer **Impact:** Remove ~100 lines from ChatWidget.ts #### 3.1 Create ConversationRestorer **File:** `assistantWidget/handlers/ConversationRestorer.ts` **Responsibilities:** - Restore conversation from persistence - Setup conversation management - Handle conversation switching **Public API:** ```typescript export class ConversationRestorer { constructor( persistenceManager: PersistenceManager, conversationManager: ConversationManager, messageHandler: MessageHandler ) async restoreCurrentConversation(): Promise<void> setupConversationManagement(): void } ``` #### 3.2 Update ChatWidget.ts **Remove:** - `restoreCurrentConversation()` (~46 lines) - `setupConversationManagement()` (~55 lines) **Add:** ```typescript private conversationRestorer: ConversationRestorer | null = null; // In createWidget(): if (this.persistenceManager) { this.conversationRestorer = new ConversationRestorer( this.persistenceManager, this.conversationManager, this.messageHandler ); this.conversationRestorer.setupConversationManagement(); } ``` **Result:** ChatWidget.ts drops from ~1,950 → **~1,850 lines** --- ### Phase 4: Extract LayoutManager **Impact:** Remove ~150 lines from ChatWidget.ts #### 4.1 Create LayoutManager **File:** `assistantWidget/managers/LayoutManager.ts` **Responsibilities:** - Handle responsive layout updates - Manage widget positioning - Handle welcome card positioning - Debounce resize events **Public API:** ```typescript export class LayoutManager { constructor( element: HTMLElement, config: ChatConfig, variant: string ) updateResponsiveLayout(): void applyPositionToWidget(): void applyWelcomeCardPosition(card: HTMLElement): void handleResize(): void destroy(): void } ``` #### 4.2 Update ChatWidget.ts **Remove:** - `updateResponsiveLayout()` (~51 lines) - `applyPositionToWidget()` (~73 lines) - `applyWelcomeCardPosition()` (~31 lines) - `resizeTimeout` property **Add:** ```typescript private layoutManager: LayoutManager; // In constructor: this.layoutManager = new LayoutManager( this.element, this.config, this.config.variant ); // In setupEventListeners(): window.addEventListener('resize', () => this.layoutManager.handleResize()); ``` **Result:** ChatWidget.ts drops from ~1,850 → **~1,700 lines** --- ### Phase 5: Clean Up & Optimize **Impact:** Remove ~900 lines from ChatWidget.ts #### Move Remaining Logic to Existing Managers 1. **Conversation List Toggle → ConversationManager** - Move `handleToggleListView()` (89 lines) 2. **Theme Management → StyleManager** - Consolidate theme update logic 3. **UI Updates → ViewManager/UIManager** - Move remaining UI coordination 4. **Event Handling Cleanup** - Remove redundant event handlers - Consolidate event subscriptions **Result:** ChatWidget.ts drops from ~1,700 → **~800 lines** --- ## Final Architecture ### ChatWidget.ts (~800 lines) **Responsibilities (ONLY):** - āœ… Initialize configuration and managers - āœ… Orchestrate component lifecycle - āœ… Coordinate between managers/controllers - āœ… Provide public API methods - āœ… Handle EventBus subscriptions - āœ… Manage widget destroy/cleanup **What it does NOT do:** - āŒ Create DOM directly - āŒ Manage animations - āŒ Handle positioning/layout - āŒ Process agent capabilities - āŒ Restore conversations - āŒ Create floating UI components ### New Component Tree ``` ChatWidget (800 lines - orchestrator only) │ ā”œā”€ā”€ Configuration │ ā”œā”€ā”€ ConfigManager │ └── StateManager │ ā”œā”€ā”€ Managers │ ā”œā”€ā”€ StyleManager │ ā”œā”€ā”€ PersistenceManager │ └── LayoutManager ⭐ NEW │ ā”œā”€ā”€ Controllers │ ā”œā”€ā”€ ClosedViewManager ⭐ NEW │ │ ā”œā”€ā”€ WelcomeCardComponent ⭐ NEW │ │ ā”œā”€ā”€ InputBarComponent ⭐ NEW │ │ └── FloatingPromptsComponent ⭐ NEW │ └── InputBarFullController āœ… DONE │ └── SheetStateMachine āœ… DONE │ ā”œā”€ā”€ Components │ ā”œā”€ā”€ ViewManager │ ā”œā”€ā”€ ConversationManager │ ā”œā”€ā”€ WelcomeScreen │ └── ConversationView │ ā”œā”€ā”€ Handlers │ ā”œā”€ā”€ AgentInitializer ⭐ NEW │ ā”œā”€ā”€ ConversationRestorer ⭐ NEW │ ā”œā”€ā”€ MessageHandler │ ā”œā”€ā”€ FileHandler │ ā”œā”€ā”€ ErrorHandler │ └── ConversationOrchestrator │ └── Services ā”œā”€ā”€ ApiService ā”œā”€ā”€ MessageService └── AgentService ``` --- ## Implementation Timeline ### Phase 1: ClosedViewManager (Highest Priority) ā±ļø 2-3 hours - **Day 1 Morning:** Create ClosedViewManager + WelcomeCardComponent - **Day 1 Afternoon:** Create InputBarComponent + FloatingPromptsComponent - **Day 1 Evening:** Integrate with ChatWidget.ts, test all modes **Deliverable:** 550 lines removed, all closed view modes working ### Phase 2: AgentInitializer ā±ļø 1 hour - **Day 2 Morning:** Create AgentInitializer, integrate, test **Deliverable:** 150 lines removed, agent initialization isolated ### Phase 3: ConversationRestorer ā±ļø 1 hour - **Day 2 Afternoon:** Create ConversationRestorer, integrate, test **Deliverable:** 100 lines removed, conversation restoration isolated ### Phase 4: LayoutManager ā±ļø 1 hour - **Day 2 Afternoon:** Create LayoutManager, integrate, test **Deliverable:** 150 lines removed, layout logic isolated ### Phase 5: Clean Up & Optimize ā±ļø 1 hour - **Day 2 Evening:** Move remaining logic, final cleanup **Deliverable:** 900 lines removed, ChatWidget.ts at ~800 lines **Total Time:** 4-6 hours of focused refactoring --- ## Testing Strategy ### Per-Phase Testing After each phase, verify: 1. **Visual Testing:** - All UI modes render correctly - Animations work smoothly - Responsive behavior intact 2. **Functional Testing:** - User interactions work (clicks, typing, etc.) - State transitions correct - Event handlers firing properly 3. **Integration Testing:** - Components communicate correctly - EventBus messages flowing - No memory leaks ### Full Regression Testing After all phases: 1. Test all variants (corner, centered, inline, input-bar-sheet) 2. Test all closed view modes (welcome-card, input-bar, floating-prompts, toggle-only) 3. Test conversation flows 4. Test file uploads 5. Test mobile responsive behavior 6. Test accessibility features --- ## Success Metrics | Metric | Before | After | Improvement | |--------|--------|-------|-------------| | ChatWidget.ts lines | 2,688 | ~800 | -70% | | Largest method | 246 lines | <50 lines | -80% | | Total methods | 108 | ~40 | -63% | | Testability | Low | High | āœ… | | Maintainability | Low | High | āœ… | | Code reusability | Low | High | āœ… | --- ## Risk Assessment ### Low Risk āœ… - **Phase 2-4:** Small, well-contained extractions - Clear interfaces, minimal dependencies ### Medium Risk āš ļø - **Phase 1:** Large extraction affecting multiple modes - Requires careful testing of all closed view modes - Animation timing must be preserved ### Mitigation Strategy 1. **Start with Phase 1** - Biggest impact, sets pattern 2. **Test thoroughly** after each phase 3. **Keep git commits clean** - One phase per commit 4. **Maintain backward compatibility** - Don't break public API --- ## Next Steps 1. āœ… Review this plan with team 2. ā³ Get approval to proceed 3. ā³ Start Phase 1: ClosedViewManager extraction 4. ā³ Iterate through phases 2-5 5. ā³ Comprehensive testing 6. ā³ Deploy and monitor --- ## Questions to Answer Before Starting 1. **Should we maintain backward compatibility with existing public API?** - Answer: Yes, only internal refactoring 2. **Do we need to support both old and new architecture during transition?** - Answer: No, one clean cut per phase 3. **What's the testing requirements before each phase can be considered done?** - Answer: Visual + functional + integration tests pass 4. **Should we create this as a new major version or maintain current version?** - Answer: Maintain current version, internal refactoring only --- *Document created: 2025-01-09* *Last updated: 2025-01-09* *Status: 🟔 Awaiting Approval*