UNPKG

@agentman/chat-widget

Version:

Agentman Chat Widget for easy integration with web applications

777 lines (620 loc) 17.7 kB
# Stage 2: Incremental Config Consolidation - v3.0 Clean Break ## Philosophy **Clean v3.0 with incremental development:** 1. NO backward compatibility - Clean code, no deprecated properties 2. Migrate one namespace at a time - Smaller changes, easier testing 3. Develop on separate branch - v2.x stays stable on main 4. Deploy to separate repo/CDN when complete - Users choose v2.x or v3.0 This approach: - **Not disruptive:** Incremental phases = smaller changesets, easier debugging - **No dead code:** Zero backward compatibility logic - **Parallel development:** v2.x stable on main, v3.0 on branch - **Safe deployment:** Test everything before exposing to users --- ## Strategy: Clean Break + Incremental Implementation ### Core Approach ```typescript // v3.0 - Clean structure, no old properties interface ChatConfig { api: ApiConfig; // Required, no fallback variant: ChatVariant; containerId: string; // ... new namespaced structure only } // ConfigManager - Clean accessors class ConfigManager { getApiUrl(): string { return this.config.api.url; // No fallback, no deprecation warnings } } ``` ### Development Workflow 1. **Create `v3-clean-config` branch** - All v3 work happens here 2. **Implement 10 phases incrementally** - Commit after each phase 3. **Test thoroughly** - Smaller changes = easier to verify 4. **Deploy when complete** - To new repo/CDN as v3.0.0 --- ## 10 Incremental Phases Each phase is small, focused, and independently testable. ### Phase 1: API Config ⏱️ 30 minutes **Goal:** Replace flat `apiUrl`/`agentToken` with `api` namespace #### Changes **Type Definitions:** ```typescript // assistantWidget/types/types.ts export interface ApiConfig { url: string; token: string; } export interface ChatConfig { api: ApiConfig; // NEW: Required namespace // REMOVED: apiUrl, agentToken variant: ChatVariant; containerId: string; // ... rest } ``` **ConfigManager:** ```typescript // assistantWidget/config/ConfigManager.ts getApiUrl(): string { return this.config.api.url; // Clean, no fallback } getApiToken(): string { return this.config.api.token; // Clean, no fallback } private validateApiConfig(): void { if (!this.config.api?.url || !this.config.api?.token) { throw new Error('api.url and api.token are required'); } } ``` **Test Files:** ```javascript // Before const widget = new ChatWidget({ apiUrl: 'http://localhost:8000', agentToken: 'abc123', variant: 'corner', containerId: 'test' }); // After const widget = new ChatWidget({ api: { url: 'http://localhost:8000', token: 'abc123' }, variant: 'corner', containerId: 'test' }); ``` **Deliverable:** Clean API namespace, all tests pass --- ### Phase 2: Assets Config ⏱️ 30 minutes **Goal:** Consolidate `logo`, `headerLogo`, `icons` into `assets` namespace #### Changes **Type Definitions:** ```typescript export interface AssetsConfig { logo: string; headerLogo: string; icons?: { close?: string; send?: string; minimize?: string; maximize?: string; expand?: string; collapse?: string; reduce?: string; }; } export interface ChatConfig { api: ApiConfig; assets: AssetsConfig; // NEW: Required namespace // REMOVED: logo, headerLogo, icons from root } ``` **ConfigManager:** ```typescript getLogo(): string { return this.config.assets.logo; // Clean, no fallback } getHeaderLogo(): string { return this.config.assets.headerLogo; // Clean, no fallback } getIcon(name: string): string | undefined { return this.config.assets.icons?.[name]; } ``` **Deliverable:** Clean assets namespace --- ### Phase 3: Layout Config ⏱️ 45 minutes **Goal:** Group positioning into `layout.desktop` and `layout.mobile` #### Changes **Type Definitions:** ```typescript export interface LayoutConfig { desktop?: { position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'; width?: string; height?: string; initiallyOpen?: boolean; }; mobile?: { height?: string; maxHeight?: string; }; } export interface ChatConfig { api: ApiConfig; assets: AssetsConfig; layout?: LayoutConfig; // NEW: Optional namespace // REMOVED: position, initialWidth, initialHeight, initiallyOpen, mobileHeight, mobileMaxHeight } ``` **ConfigManager:** ```typescript getDesktopPosition(): string | undefined { return this.config.layout?.desktop?.position; } getDesktopWidth(): string | undefined { return this.config.layout?.desktop?.width; } getMobileHeight(): string | undefined { return this.config.layout?.mobile?.height; } ``` **Update Consumers:** - `ChatWidget.ts` - Use `config.layout.desktop.*` - `UIManager.ts` - Use `config.layout.mobile.*` - `InputBarSheetController.ts` - Use `config.layout.mobile.height` **Deliverable:** Clean layout namespace, desktop/mobile separated --- ### Phase 4: Theme Colors ⏱️ 45 minutes **Goal:** Nest colors into `theme.colors` #### Changes **Type Definitions:** ```typescript export interface ThemeColors { text: string; background: string; button: string; buttonText: string; agentMessage: string; userMessage: string; } export interface ChatTheme { colors: ThemeColors; // NEW: Required sub-namespace // REMOVED: textColor, backgroundColor, buttonColor, etc. } export interface ChatConfig { api: ApiConfig; assets: AssetsConfig; layout?: LayoutConfig; theme?: Partial<ChatTheme>; // Optional with defaults } ``` **ConfigManager:** ```typescript getTextColor(): string { return this.config.theme?.colors?.text ?? '#0f172a'; } getBackgroundColor(): string { return this.config.theme?.colors?.background ?? '#ffffff'; } ``` **Update Consumers:** - All files in `assistantWidget/styles/` - Use `theme.colors.*` - `StyleManager.ts` - Update theme application **Deliverable:** Clean theme.colors namespace --- ### Phase 5: Theme Toggle ⏱️ 30 minutes **Goal:** Consolidate toggle theme, eliminate `toggleStyle` duplication #### Changes **Type Definitions:** ```typescript export interface ToggleTheme { background: string; text: string; icon: string; content?: string; // Toggle button text } export interface ChatTheme { colors: ThemeColors; toggle: ToggleTheme; // NEW: Required sub-namespace // REMOVED: toggleBackgroundColor, toggleTextColor, toggleIconColor } export interface ChatConfig { // ... theme?: Partial<ChatTheme>; // REMOVED: toggleStyle (duplicate eliminated!) // REMOVED: toggleText (moved to theme.toggle.content) } ``` **ConfigManager:** ```typescript getToggleBackground(): string { return this.config.theme?.toggle?.background ?? '#1e1b4b'; } getToggleText(): string { return this.config.theme?.toggle?.content ?? 'Chat'; } ``` **Deliverable:** Clean theme.toggle, NO duplication --- ### Phase 6: Theme InputBar ⏱️ 30 minutes **Goal:** Consolidate input bar theme properties #### Changes **Type Definitions:** ```typescript export interface InputBarTheme { brandText?: string; brandBackground?: string; brandTextColor?: string; logoBackground?: string; logoIcon?: string; buttonBackground?: string; buttonIcon?: string; glowColor?: string; } export interface ChatTheme { colors: ThemeColors; toggle: ToggleTheme; inputBar?: InputBarTheme; // NEW: Optional sub-namespace // REMOVED: inputBarBrandBackground, inputBarBrandText, etc. } export interface ChatConfig { // ... theme?: Partial<ChatTheme>; // REMOVED: inputBarBrandText from root } ``` **ConfigManager:** ```typescript getInputBarBrandText(): string | undefined { return this.config.theme?.inputBar?.brandText; } ``` **Update Consumers:** - `input-bar-sheet.ts` - Use `theme.inputBar.*` - `InputBarSheetController.ts` - Use `theme.inputBar.brandText` **Deliverable:** Complete clean theme consolidation --- ### Phase 7: Content Config ⏱️ 30 minutes **Goal:** Group user-facing strings into `content` namespace #### Changes **Type Definitions:** ```typescript export interface ContentConfig { title?: string; placeholder?: string; initialMessage?: string; welcome?: { message?: string; prompts?: [string?, string?, string?]; }; disclaimer?: { enabled: boolean; message: string; linkText?: string; linkUrl?: string; }; } export interface ChatConfig { api: ApiConfig; assets: AssetsConfig; layout?: LayoutConfig; theme?: Partial<ChatTheme>; content?: ContentConfig; // NEW: Optional namespace // REMOVED: title, placeholder, initialMessage, messagePrompts, disclaimer from root } ``` **ConfigManager:** ```typescript getTitle(): string | undefined { return this.config.content?.title; } getPlaceholder(): string | undefined { return this.config.content?.placeholder; } getWelcomeMessage(): string | undefined { return this.config.content?.welcome?.message; } ``` **Update Consumers:** - `ViewManager.ts` - Use `content.title`, `content.placeholder` - `WelcomeScreen.ts` - Use `content.welcome.*` **Deliverable:** Clean content namespace --- ### Phase 8: Features Config ⏱️ 45 minutes **Goal:** Consolidate feature flags into `features` namespace #### Changes **Type Definitions:** ```typescript export interface FeaturesConfig { attachments?: { enabled?: boolean; maxFileSize?: number; allowedTypes?: string[]; }; streaming?: { enabled?: boolean; preferStreaming?: boolean; fallbackToNonStreaming?: boolean; }; persistence?: { enabled: boolean; storageKey?: string; maxConversations?: number; maxMessagesPerConversation?: number; ttlDays?: number; }; metadata?: { collectClientMetadata?: boolean; collectIPAddress?: boolean; custom?: Record<string, any>; }; } export interface ChatConfig { api: ApiConfig; assets: AssetsConfig; layout?: LayoutConfig; theme?: Partial<ChatTheme>; content?: ContentConfig; features?: FeaturesConfig; // NEW: Optional namespace // REMOVED: enableAttachments, streaming, persistence, collectClientMetadata, etc. } ``` **ConfigManager:** ```typescript isAttachmentsEnabled(): boolean { return this.config.features?.attachments?.enabled ?? true; } isStreamingEnabled(): boolean { return this.config.features?.streaming?.enabled ?? true; } isPersistenceEnabled(): boolean { return this.config.features?.persistence?.enabled ?? false; } ``` **Update Consumers:** - `FileHandler.ts` - Use `features.attachments.*` - `MessageHandler.ts` - Use `features.streaming.*` - `PersistenceManager.ts` - Use `features.persistence.*` **Deliverable:** Clean features namespace --- ### Phase 9: Closed View Config ⏱️ 20 minutes **Goal:** Create `closedView` namespace #### Changes **Type Definitions:** ```typescript export interface ClosedViewConfig { mode: ClosedViewMode; delay?: number; // Delay before showing (ms) } export interface ChatConfig { api: ApiConfig; assets: AssetsConfig; layout?: LayoutConfig; theme?: Partial<ChatTheme>; content?: ContentConfig; features?: FeaturesConfig; closedView?: ClosedViewConfig; // NEW: Optional namespace // REMOVED: agentClosedView from root } ``` **ConfigManager:** ```typescript getClosedViewMode(): ClosedViewMode | undefined { return this.config.closedView?.mode; } getClosedViewDelay(): number { return this.config.closedView?.delay ?? 0; } ``` **Update Consumers:** - `ChatWidget.ts` - Use `config.closedView.mode` - `ViewManager.ts` - Use `config.closedView.delay` **Deliverable:** Clean closedView namespace --- ### Phase 10: Advanced Config ⏱️ 30 minutes **Goal:** Group advanced settings into `advanced` namespace #### Changes **Type Definitions:** ```typescript export interface AdvancedConfig { debug?: { enabled: boolean; logLevel?: 'error' | 'warn' | 'info' | 'debug' | 'verbose'; timestamps?: boolean; console?: boolean; logger?: (level: string, message: string, data?: any) => void; }; markdown?: { cdnUrls?: string[]; timeout?: number; options?: { gfm?: boolean; breaks?: boolean; headerIds?: boolean; mangle?: boolean; pedantic?: boolean; smartLists?: boolean; smartypants?: boolean; }; }; animations?: { forceAnimations?: boolean; customDurations?: { expand?: number; collapse?: number; transition?: number; }; }; } export interface ChatConfig { api: ApiConfig; assets: AssetsConfig; layout?: LayoutConfig; theme?: Partial<ChatTheme>; content?: ContentConfig; features?: FeaturesConfig; closedView?: ClosedViewConfig; advanced?: AdvancedConfig; // NEW: Optional namespace // REMOVED: debug, markdownConfig, forceAnimations from root } ``` **ConfigManager:** ```typescript isDebugEnabled(): boolean { return this.config.advanced?.debug?.enabled ?? false; } getMarkdownCdnUrls(): string[] | undefined { return this.config.advanced?.markdown?.cdnUrls; } shouldForceAnimations(): boolean { return this.config.advanced?.animations?.forceAnimations ?? false; } ``` **Update Consumers:** - `Logger.ts` - Use `advanced.debug.*` - `MarkdownLoader.ts` - Use `advanced.markdown.*` - `InputBarSheetController.ts` - Use `advanced.animations.forceAnimations` **Deliverable:** Config consolidation COMPLETE! --- ## Timeline | Phase | Namespace | Duration | Cumulative | |-------|-----------|----------|------------| | 1 | API | 30 min | 30 min | | 2 | Assets | 30 min | 1 hour | | 3 | Layout | 45 min | 1h 45m | | 4 | Theme Colors | 45 min | 2h 30m | | 5 | Theme Toggle | 30 min | 3 hours | | 6 | Theme InputBar | 30 min | 3h 30m | | 7 | Content | 30 min | 4 hours | | 8 | Features | 45 min | 4h 45m | | 9 | Closed View | 20 min | 5h 05m | | 10 | Advanced | 30 min | 5h 35m | **Total Active Time:** ~6 hours **Calendar Time:** 2-3 days (with testing and breaks) --- ## Final Structure (v3.0) ```typescript interface ChatConfig { // Required core api: ApiConfig; variant: ChatVariant; containerId: string; // Required namespaces assets: AssetsConfig; // Optional namespaces layout?: LayoutConfig; theme?: Partial<ChatTheme>; content?: ContentConfig; features?: FeaturesConfig; closedView?: ClosedViewConfig; advanced?: AdvancedConfig; } // Clean example const widget = new ChatWidget({ api: { url: '...', token: '...' }, variant: 'corner', containerId: 'chat', assets: { logo: '/logo.svg', headerLogo: '/header.svg' }, layout: { desktop: { position: 'bottom-right', width: '400px' }, mobile: { height: '80vh' } }, theme: { colors: { text: '#000', background: '#fff' }, toggle: { background: '#1e1b4b', content: 'Chat' }, inputBar: { brandText: 'Ask AI' } }, content: { title: 'Support', placeholder: 'Message...', welcome: { message: 'How can we help?', prompts: ['Pricing', 'Features', 'Support'] } }, features: { attachments: { enabled: true }, streaming: { enabled: true }, persistence: { enabled: true } }, closedView: { mode: 'welcome-card' }, advanced: { debug: { enabled: true }, animations: { forceAnimations: true } } }); ``` --- ## Benefits ### Code Quality - **Zero dead code** - No backward compatibility - **Clean types** - No deprecated properties - **Simple logic** - Direct property access - **Better IntelliSense** - Grouped structure ### Developer Experience - **Clear hierarchy** - 8 namespaces vs 35+ flat - **Faster to learn** - Logical grouping - **Type-safe** - Required vs optional clear - **No confusion** - Single source of truth ### Maintenance - **Easier to extend** - Add to namespace - **Simpler testing** - Test by namespace - **Better docs** - Natural structure - **Cleaner PRs** - Focused changes --- ## Deployment ### Recommended: Branch-Based CDN ```bash # v2.x on main branch https://cdn.agentman.ai/chat-widget/v2/index.js # v3.x on v3-clean-config branch https://cdn.agentman.ai/chat-widget/v4/index.js ``` Users choose version by URL. Both versions maintained independently. --- ## Migration Guide ### v2.x → v3.0 Quick Reference | v2.x Property | v3.0 Property | |---------------|---------------| | `apiUrl` | `api.url` | | `agentToken` | `api.token` | | `logo` | `assets.logo` | | `headerLogo` | `assets.headerLogo` | | `icons` | `assets.icons` | | `position` | `layout.desktop.position` | | `initialWidth` | `layout.desktop.width` | | `initialHeight` | `layout.desktop.height` | | `initiallyOpen` | `layout.desktop.initiallyOpen` | | `mobileHeight` | `layout.mobile.height` | | `mobileMaxHeight` | `layout.mobile.maxHeight` | | `theme.textColor` | `theme.colors.text` | | `theme.backgroundColor` | `theme.colors.background` | | `theme.toggleBackgroundColor` | `theme.toggle.background` | | `toggleText` | `theme.toggle.content` | | `toggleStyle` | `theme.toggle` (duplicate removed) | | `inputBarBrandText` | `theme.inputBar.brandText` | | `title` | `content.title` | | `placeholder` | `content.placeholder` | | `initialMessage` | `content.initialMessage` | | `messagePrompts` | `content.welcome` | | `enableAttachments` | `features.attachments.enabled` | | `streaming` | `features.streaming` | | `persistence` | `features.persistence` | | `collectClientMetadata` | `features.metadata.collectClientMetadata` | | `agentClosedView` | `closedView.mode` | | `debug` | `advanced.debug` | | `markdownConfig` | `advanced.markdown` | | `forceAnimations` | `advanced.animations.forceAnimations` | --- *Document updated: 2025-01-12* *Status: 🟢 Ready to Implement* *Approach: Clean v3.0 with incremental phases (NO backward compatibility)*