@nimbusai/webchat-sdk 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/types/config.ts","../src/utils/validator.ts","../src/utils/device.ts","../src/utils/logger.ts","../src/utils/PerformanceTracker.ts","../src/core/EventBus.ts","../src/core/ChatSession.ts","../src/core/WebSocketManager.ts","../src/core/ApiClient.ts","../src/styles/compiled.ts","../src/utils/DOMBuilder.ts","../src/ui/ShadowContainer.ts","../src/ui/ThemeManager.ts","../src/icons/lucide.ts","../src/utils/IconRenderer.ts","../src/utils/StyleUtils.ts","../src/utils/ButtonBuilder.ts","../src/utils/ComponentFactory.ts","../src/ui/Header.ts","../src/utils/FileUtils.ts","../src/ui/MessageBubble.ts","../src/ui/MessageList.ts","../src/ui/FileUploadButton.ts","../src/ui/FilePreview.ts","../src/ui/InputArea.ts","../src/ui/ChatBubble.ts","../src/ui/BaseWindow.ts","../src/ui/ChatWindow.ts","../src/ui/SidepanelWindow.ts","../src/NimbusChat.ts"],"sourcesContent":["import { NimbusChat } from \"./NimbusChat\";\nimport type { NimbusChatConfig } from \"./types/config\";\nimport { createLogger } from \"./utils/logger\";\n\nconst logger = createLogger('SDK');\n\n// ── Singleton for CDN / script-tag usage ────────────────────────\n\nlet globalInstance: NimbusChat | null = null;\n\n/**\n * Initialize the NimbusChat widget (singleton pattern).\n * Use this when loading via CDN <script> tag.\n *\n * NimbusChat.init({ agent_id: \"550e8400-e29b-41d4-a716-446655440000\" });\n */\nfunction init(config: NimbusChatConfig): NimbusChat {\n if (globalInstance) {\n logger.warn(\"Already initialized. Returning existing instance.\");\n return globalInstance;\n }\n globalInstance = new NimbusChat(config);\n return globalInstance;\n}\n\n/**\n * Get the existing singleton instance (or null if not initialized).\n */\nfunction getInstance(): NimbusChat | null {\n return globalInstance;\n}\n\n// ── Exports ─────────────────────────────────────────────────────\n\n// Named exports for npm users\nexport { NimbusChat, init, getInstance };\nexport type { NimbusChatConfig } from \"./types/config\";\nexport type { ChatPosition, BubblePosition, BubbleConfig, ResolvedConfig, MessageConfig, ThemeConfig, HeaderConfig, TextConfig, TextElement, ColorPair, IconConfig, WelcomeConfig, InputConfig, SendButtonConfig, ErrorConfig, ErrorStyleConfig, ErrorButtonConfig } from \"./types/config\";\nexport { ICON_NAMES, ICON_SIZES } from \"./types/config\";\nexport type {\n ServerMessage,\n UserMessage,\n ChatMessage,\n MessageType,\n ConnectionState,\n} from \"./types/message\";\n\n// Default export for CDN UMD — tsup's globalName:\"NimbusChat\" exposes this on window\nexport default { NimbusChat, init, getInstance };\n","export type ChatPosition =\n | \"bottom-right\"\n | \"bottom-left\"\n | \"sidepanel-left\"\n | \"sidepanel-right\";\n\nexport type BubblePosition = \"bottom-right\" | \"bottom-left\";\n\n\n/** Reusable text configuration used throughout the SDK. */\nexport interface TextConfig {\n /** Whether to display this text element. Not used everywhere. */\n display?: boolean;\n /** The text value / content. */\n value?: string;\n /** Text color (CSS color string). */\n color?: string;\n /** Font family override. */\n font?: string;\n /** Font size in px. */\n size?: number;\n}\n\n/** Unified text element with value and nested styling */\nexport interface TextElement {\n /** The text value / content. */\n value?: string;\n /** Text styling configuration */\n text?: TextConfig;\n}\n\n/** Reusable color pair configuration for primary/secondary colors */\nexport interface ColorPair {\n /** Primary color (usually background or main color) */\n primary?: string;\n /** Secondary color (usually foreground or accent color) */\n secondary?: string;\n}\n\n/**\n * Icon configuration used throughout the SDK.\n * Pass `null` to explicitly hide an icon.\n */\nexport interface IconConfig {\n /** Image URL for the icon. */\n img?: string;\n /** Size of the icon with width and height in pixels. */\n size?: { width: number; height: number };\n /** Icon stroke/fill color (CSS color string). Only applies to Lucide icons, ignored for image URLs. */\n color?: string;\n}\n\n\nexport interface ThemeConfig {\n /** Primary theme color — send button bg, header bg, bubble bg. */\n primary: string;\n /** Secondary theme color — send button icon, header text, bubble icon. */\n secondary: string;\n}\n\n\nexport interface HeaderConfig {\n /** Icon configuration for the header. Not shown if omitted/null. */\n icon?: IconConfig | null;\n /** Header text configuration. */\n text?: TextConfig;\n /** Header color configuration (primary = background, secondary = close icon). */\n color?: ColorPair;\n}\n\n\nexport interface WelcomeConfig {\n /** Whether to display the welcome message. Default: true */\n display?: boolean;\n /** Pre-title text (e.g. \"Welcome to \"). */\n preTitle?: TextElement | TextConfig;\n /** Title text (e.g. \"Nimbus\"). */\n title?: TextElement | TextConfig;\n /** Subtitle text (e.g. \"Send a message to start a conversation\"). */\n subtitle?: TextElement | TextConfig;\n}\n\n\nexport interface MessageConfig {\n /** Bubble background color. */\n background?: string;\n /** Width of the message bubble (e.g. \"90%\", \"300px\"). Default: \"70%\" */\n width?: string;\n /** Text configuration for the message. */\n text?: TextConfig;\n /** Avatar icon configuration. Pass null to hide, omit to not show. */\n icon?: IconConfig | null;\n}\n\n\nexport interface TypingIndicatorConfig {\n /** Where to display the indicator: \"top\" = header status area, \"bottom\" = above input. Default: \"top\" */\n position?: \"top\" | \"bottom\";\n /** Title configuration for typing indicator */\n title?: {\n value?: string;\n text?: TextConfig;\n };\n}\n\nexport interface WaitForReplyConfig {\n /** Timeout in milliseconds. Default: 10000 (10 seconds) */\n timeout?: number;\n /** Wait for bot's first message before allowing user to type in new conversation. Default: false */\n firstReply?: boolean;\n}\n\n\nexport interface MobileConfig {\n /** Widget position on mobile devices. If not set, uses the regular position. */\n position?: ChatPosition;\n /** Viewport width breakpoint to consider as mobile (e.g. \"480px\"). If set, overrides user-agent detection. */\n breakpoint?: string;\n}\n\nexport interface StyleConfig {\n /** Width of the chat window/sidepanel (e.g. \"380px\", \"100%\"). Default: \"380px\" */\n width?: string;\n /** Height of the chat window (e.g. \"560px\", \"100%\"). Only applies to window mode. Default: \"560px\" */\n height?: string;\n /** Widget position on the page. Default: \"bottom-right\" */\n position?: ChatPosition;\n /** Mobile-specific overrides */\n mobile?: MobileConfig;\n /** Global font family for the entire chat widget. No default (uses system font). */\n font?: string;\n /** Chat body background — can be a CSS color or an image URL. Default: \"#ffffff\" */\n background?: string;\n}\n\nexport interface ReconnectConfig {\n /** Maximum number of reconnection attempts. Default: 5 */\n attempts?: number;\n /** Delay between reconnection attempts in ms. Default: 5000 (5 seconds) */\n timeout?: number;\n}\n\n\n\n\nexport interface ShowMoreConfig {\n /** Button text. Default: \"Show More\" */\n value?: string;\n /** Whether button should be sticky at top. Default: true */\n sticky?: boolean;\n /** Button background color. Default: theme.secondary */\n background?: string;\n /** Text styling for the button */\n text?: TextConfig;\n /** Icon configuration for the button */\n icon?: IconConfig;\n}\n\n\nexport interface UploadConfig {\n /** Maximum file size in bytes. Default: 5MB */\n maxFileSize?: number;\n /** Array of allowed MIME types. */\n allowedFileTypes?: string[];\n /** Icon configuration for the upload button. */\n icon?: IconConfig | null;\n /** Duration in milliseconds for error message display. Default: 2000 */\n errorDisplayDuration?: number;\n}\n\nexport interface MaxCharactersConfig {\n /** Maximum number of characters allowed. If null/undefined, unlimited */\n limit: number;\n /** Text styling configuration */\n text?: TextConfig;\n}\n\nexport interface InputConfig {\n /** Placeholder text for the input. Default: \"Ask Nimbus\" */\n placeholder?: string;\n /** If true, the input expands to a textarea. Default: false */\n expandable?: boolean;\n /** Text styling for the input field. */\n text?: TextConfig;\n /** Background colors for the input (primary = input area, secondary = container). */\n background?: ColorPair;\n /** File upload configuration */\n upload?: UploadConfig;\n /** Character limit configuration with counter display */\n maxCharacters?: MaxCharactersConfig;\n}\n\n\nexport interface SendButtonConfig {\n /** Custom icon configuration. Pass null to hide, omit for default. */\n icon?: IconConfig | null;\n /** If true, align input and send button horizontally on the same row. Default: false */\n align?: boolean;\n}\n\n\nexport interface ErrorButtonConfig {\n /** Background color of the button */\n background?: string;\n /** Text configuration for the button */\n text?: TextConfig;\n /** Icon configuration for the button. Pass null to hide. */\n icon?: IconConfig | null;\n}\n\nexport interface ErrorStyleConfig {\n /** Background color for the error message */\n background?: string;\n /** Title configuration for the error message. Pass null to use default. */\n title?: TextElement | null;\n /** Subtitle configuration for the error message. Pass null to use default. */\n subtitle?: TextElement | null;\n /** Button configuration for the error */\n button?: ErrorButtonConfig;\n}\n\nexport interface ErrorConfig {\n /** Styling for inactivity timeout errors */\n inactivity?: ErrorStyleConfig;\n /** Styling for session conflict errors */\n conflict?: ErrorStyleConfig;\n /** Styling for session TTL expiration errors */\n session_ttl?: ErrorStyleConfig;\n /** Styling for unauthorized access errors */\n unauthorized?: ErrorStyleConfig;\n}\n\n\nexport interface BubbleConfig {\n /** Position of the floating bubble. Default: \"bottom-right\" */\n position?: BubblePosition;\n /** If true, hide the bubble when the chat panel is open. Default: false */\n autoHide?: boolean;\n /** Custom icon configuration. Pass null to hide custom, omit for default. */\n icon?: IconConfig | null;\n}\n\nexport interface NimbusChatConfig {\n /** Required — the agent ID (UUID) for this chat instance */\n agent_id: string;\n\n /** DNS endpoint for chat services. Default: \"api.nimbus.ai/api/v1/webchat\" */\n dns?: string;\n\n /** Whether to resume previous conversation on reconnect. Default: false */\n resumeConversation?: boolean;\n\n /** Style configuration (position, dimensions, font, background, mobile) */\n style?: StyleConfig;\n\n /** Number of messages to load per page. Default: 10 */\n messagesPerPage?: number;\n \n /** Bubble configuration (position, autoHide, icon) */\n bubble?: BubbleConfig;\n\n /** Theme colors (primary, secondary) */\n theme?: Partial<ThemeConfig>;\n\n /** User message styling */\n userMessage?: MessageConfig;\n\n /** Bot message styling */\n botMessage?: MessageConfig;\n\n /** Header configuration (icon, text, color) */\n header?: HeaderConfig;\n\n /** Input field configuration */\n input?: InputConfig;\n\n /** Send button configuration */\n sendButton?: SendButtonConfig;\n\n /** Welcome message configuration shown when chat is empty. */\n welcome?: WelcomeConfig;\n\n /** Error styling configuration for conflict and inactivity errors */\n error?: ErrorConfig;\n\n /** Enable debug logging to console. Default: false */\n debug?: boolean;\n\n /** Reconnection configuration for unexpected disconnections */\n reconnect?: ReconnectConfig;\n \n /** Show new chat button in header. Default: false */\n allowNewChat?: boolean;\n \n /** Wait for bot reply before allowing next user message */\n waitForReply?: WaitForReplyConfig;\n \n /** Typing indicator configuration. Independent from waitForReply. */\n isTypingIndicator?: TypingIndicatorConfig;\n \n /** Show More button styling */\n showMore?: ShowMoreConfig;\n}\n\n\nexport interface ResolvedConfig {\n agent_id: string;\n dns: string;\n resumeConversation: boolean;\n style: {\n position: ChatPosition;\n mobile?: {\n position?: ChatPosition;\n breakpoint?: string;\n };\n width: string;\n height: string;\n font?: string;\n background: string;\n };\n messagesPerPage: number;\n bubble: Required<Pick<BubbleConfig, \"position\" | \"autoHide\">> &\n Pick<BubbleConfig, \"icon\">;\n theme: ThemeConfig;\n userMessage: {\n background: string;\n width: string;\n text: TextConfig;\n icon?: IconConfig | null;\n };\n botMessage: {\n background: string;\n width: string;\n text: TextConfig;\n icon?: IconConfig | null;\n };\n header: HeaderConfig;\n input: {\n placeholder: string;\n expandable: boolean;\n text?: TextConfig;\n background?: ColorPair;\n upload?: UploadConfig;\n maxCharacters?: MaxCharactersConfig;\n };\n sendButton: Required<Pick<SendButtonConfig, \"align\">> &\n Pick<SendButtonConfig, \"icon\">;\n welcome: WelcomeConfig;\n error: Required<ErrorConfig>;\n debug: boolean;\n reconnect: ReconnectConfig;\n allowNewChat: boolean;\n waitForReply?: WaitForReplyConfig;\n isTypingIndicator?: TypingIndicatorConfig;\n showMore: ShowMoreConfig;\n}\n\n\n// ── Icon Constants ─────────────────────────────────────────────\n\n/** Standard icon names used throughout the SDK */\nexport const ICON_NAMES = {\n MESSAGE_CIRCLE: \"message-circle\",\n SEND: \"send\",\n CLOSE: \"x\",\n NEW_CHAT: \"rotate-cw\",\n CHEVRON_DOWN: \"chevron-down\",\n PAPERCLIP: \"paperclip\",\n TYPE: \"type\",\n} as const;\n\n/** Standard icon sizes used throughout the SDK */\nexport const ICON_SIZES = {\n BUTTON_SMALL: 16,\n BUTTON_MEDIUM: 18,\n AVATAR: 28,\n BUBBLE_OPEN: 26,\n} as const;\n\n/** Standard UI text labels used throughout the SDK */\nexport const UI_TEXT = {\n OPEN_CHAT: \"Open chat\",\n CLOSE_CHAT: \"Close chat\",\n NEW_CHAT: \"New Chat\",\n SEND: \"Send\",\n CONNECTED: \"Connected\",\n SHOW_MORE: \"Show More\",\n DOWNLOAD_FILE: \"Download file\",\n START_NEW_CHAT: \"Start new chat\",\n USE_HERE: \"Use here\",\n UPLOAD_FILE: \"Upload file\",\n} as const;\n\n/** Input area configuration constants */\nexport const INPUT_CONSTANTS = {\n MAX_LINES: 8,\n LINE_HEIGHT: 21,\n VERTICAL_PADDING: 20,\n get MAX_HEIGHT() {\n return this.MAX_LINES * this.LINE_HEIGHT + this.VERTICAL_PADDING;\n }\n} as const;\n\n/** Z-index values for proper layering */\nexport const Z_INDEX = {\n WIDGET_ROOT: 2147483647,\n} as const;\n\n/** WebSocket close codes */\nexport const WEBSOCKET_CLOSE_CODES = {\n NORMAL: 1000,\n SESSION_UNAVAILABLE: 4403,\n SESSION_CONFLICT: 4409,\n} as const;\n\n/** Default timeout values in milliseconds */\nexport const TIMEOUTS = {\n REPLY_WAIT: 10000,\n RECONNECT: 5000,\n} as const;\n\n/** Default widget dimensions and limits */\nexport const WIDGET_DEFAULTS = {\n WIDTH: \"380px\",\n HEIGHT: \"560px\",\n MESSAGE_LIMIT: 10,\n MESSAGES_PER_PAGE: 10,\n MAX_RECONNECT_ATTEMPTS: 5,\n MAX_FILE_SIZE: 5 * 1024 * 1024, // 5MB\n ERROR_DISPLAY_DURATION: 2000, // 2 seconds\n};\n\n/** UI sizing and scaling factors */\nexport const UI_FACTORS = {\n DEFAULT_BUBBLE_MAX_WIDTH: 280,\n DEFAULT_ICON_SIZE_FACTOR: 0.75,\n MESSAGE_AVATAR_SIZE_FACTOR: 1.0,\n} as const;\n\n/** Time conversion constants */\nexport const TIME_CONVERSION = {\n MILLISECONDS_TO_SECONDS: 1000,\n} as const;\n\n// ── Default Configurations ─────────────────────────────────────\n\nconst DEFAULT_THEME: ThemeConfig = {\n primary: \"#ffce1c\",\n secondary: \"#f3f1ef\",\n};\n\nconst DEFAULT_MESSAGE_WIDTH = \"80%\";\n\nconst DEFAULT_USER_MESSAGE = {\n background: \"#DCF8C6\",\n width: DEFAULT_MESSAGE_WIDTH,\n text: { color: \"#111B21\", size: 13 } as TextConfig,\n icon: { img: \"user\", size: { width: 20, height: 20 } } as IconConfig,\n};\n\nconst DEFAULT_BOT_MESSAGE = {\n background: \"#FFFFFF\",\n width: DEFAULT_MESSAGE_WIDTH,\n text: { color: \"#111B21\", size: 13 } as TextConfig,\n icon: { img: \"bot\", size: { width: 20, height: 20 } } as IconConfig,\n};\n\nconst DEFAULT_BUBBLE: BubbleConfig = {\n position: \"bottom-right\" as BubblePosition,\n autoHide: false,\n icon: {\n img: \"message-circle\",\n size: { width: 35, height: 35 },\n },\n};\n\nconst DEFAULT_WELCOME: WelcomeConfig = {\n display: true,\n preTitle: { value: \"Welcome to :\", text: { color: \"#1e293b\" } },\n title: { value: \" Nimbus Chat!\", text: { color: \"#1e293b\" } },\n subtitle: { value: \"Send a message to start a conversation\", text: { color: \"gray\" } },\n};\n\nconst DEFAULT_SEND_BUTTON: SendButtonConfig = {\n align: false,\n};\n\nconst DEFAULT_TYPING_INDICATOR: TypingIndicatorConfig = {\n position: \"top\",\n title: {\n value: \"AI Assistant is typing...\",\n text: {\n color: \"#1e293b\",\n font: \"\",\n }\n },\n};\n\nconst DEFAULT_WAIT_FOR_REPLY: WaitForReplyConfig = {\n timeout: TIMEOUTS.REPLY_WAIT,\n};\n\nconst DEFAULT_SHOW_MORE: ShowMoreConfig = {\n value: \"Show More\",\n sticky: true,\n background: \"#FFF4CC\",\n text: {\n color: \"black\",\n size: 13,\n },\n icon: {\n img: \"chevron-down\",\n size: { width: 16, height: 16 },\n },\n};\n\nconst DEFAULT_UPLOAD: UploadConfig = {\n maxFileSize: WIDGET_DEFAULTS.MAX_FILE_SIZE,\n errorDisplayDuration: WIDGET_DEFAULTS.ERROR_DISPLAY_DURATION,\n allowedFileTypes: [\n \"image/jpeg\",\n \"image/png\",\n \"image/gif\",\n \"image/webp\",\n \"application/pdf\",\n \"application/msword\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.ms-excel\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ],\n};\n\nconst DEFAULT_RECONNECT: ReconnectConfig = {\n attempts: WIDGET_DEFAULTS.MAX_RECONNECT_ATTEMPTS,\n timeout: TIMEOUTS.RECONNECT,\n};\n\nconst DEFAULT_ERROR: ErrorConfig = {\n inactivity: {\n background: \"#f3f1ef\",\n title: {\n value: \"This chat session is ended due to inactivity.\",\n text: { color: \"#1e293b\", size: 15 }\n },\n subtitle: {\n value: \"Click 'Connect' to begin a new conversation.\",\n text: { color: \"gray\", size: 13 }\n },\n button: {\n background: \"#ffce1c\",\n text: { color: \"white\", size: 15 },\n icon: null\n }\n },\n conflict: {\n background: \"#f3f1ef\",\n title: {\n value: \"Chat is already open in another tab.\",\n text: { color: \"#1e293b\", size: 15 }\n },\n subtitle: {\n value: \"Click 'Use Here' to open the chat in this tab.\",\n text: { color: \"gray\", size: 13 }\n },\n button: {\n background: \"#ffce1c\",\n text: { color: \"white\", size: 15 },\n icon: null\n }\n },\n session_ttl: {\n background: \"#f3f1ef\",\n title: {\n value: \"Your chat session has expired.\",\n text: { color: \"#1e293b\", size: 15 }\n },\n subtitle: {\n value: \"Click 'Connect' to start a new conversation.\",\n text: { color: \"gray\", size: 13 }\n },\n button: {\n background: \"#ffce1c\",\n text: { color: \"white\", size: 15 },\n icon: null\n }\n },\n unauthorized: {\n background: \"#f3f1ef\",\n title: {\n value: \"This SDK agent link is not authorized.\",\n text: { color: \"#1e293b\", size: 15 }\n },\n subtitle: {\n value: \"Contact us for assistance at sdk@nmbs.ai.\",\n text: { color: \"gray\", size: 13 }\n },\n }\n};\n\nconst DEFAULT_STYLE = {\n position: \"bottom-right\" as ChatPosition,\n width: WIDGET_DEFAULTS.WIDTH as string,\n height: WIDGET_DEFAULTS.HEIGHT as string,\n background: \"#f3f1ef\",\n};\n\nexport const DEFAULT_CONFIG = {\n dns: \"api.nimbus.ai/api/v1/webchat\",\n style: DEFAULT_STYLE,\n messagesPerPage: WIDGET_DEFAULTS.MESSAGES_PER_PAGE,\n bubble: DEFAULT_BUBBLE,\n theme: DEFAULT_THEME,\n userMessage: DEFAULT_USER_MESSAGE,\n botMessage: DEFAULT_BOT_MESSAGE,\n header: {\n icon: null,\n text: { value: \"Nimbus AI\", color: \"#1e293b\" },\n color: { primary: \"#f3f1ef\", secondary: \"#ffce1c\" },\n },\n input: {\n placeholder: \"Ask Nimbus\",\n expandable: true,\n text: { color: \"#1e293b\" },\n background: { primary: \"white\", secondary: \"#f3f1ef\" },\n },\n sendButton: {\n ...DEFAULT_SEND_BUTTON,\n icon: { img: \"send\", size: { width: 20, height: 20 } },\n },\n welcome: DEFAULT_WELCOME,\n error: DEFAULT_ERROR,\n debug: false,\n reconnect: DEFAULT_RECONNECT,\n allowNewChat: false,\n waitForReply: DEFAULT_WAIT_FOR_REPLY,\n showMore: DEFAULT_SHOW_MORE,\n icon: {\n defaultSize: 24,\n defaultColor: \"currentColor\",\n },\n};\n\nexport function resolveConfig(config: NimbusChatConfig): ResolvedConfig {\n const s = config.style;\n const resolved = {\n agent_id: config.agent_id,\n dns: config.dns ?? DEFAULT_CONFIG.dns,\n resumeConversation: config.resumeConversation ?? false,\n style: {\n position: s?.position ?? DEFAULT_STYLE.position,\n mobile: s?.mobile ? {\n position: s.mobile.position,\n breakpoint: s.mobile.breakpoint,\n } : undefined,\n width: s?.width ?? DEFAULT_STYLE.width,\n height: s?.height ?? DEFAULT_STYLE.height,\n font: s?.font,\n background: s?.background ?? DEFAULT_STYLE.background,\n },\n messagesPerPage: config.messagesPerPage ?? DEFAULT_CONFIG.messagesPerPage,\n bubble: {\n position: config.bubble?.position ?? DEFAULT_BUBBLE.position!,\n autoHide: config.bubble?.autoHide ?? DEFAULT_BUBBLE.autoHide!,\n icon: config.bubble?.icon ?? DEFAULT_BUBBLE.icon,\n },\n theme: { ...DEFAULT_THEME, ...config.theme },\n userMessage: {\n background: config.userMessage?.background ?? DEFAULT_USER_MESSAGE.background,\n width: config.userMessage?.width ?? DEFAULT_USER_MESSAGE.width,\n text: { ...DEFAULT_USER_MESSAGE.text, ...config.userMessage?.text },\n icon: config.userMessage?.icon !== undefined ? config.userMessage.icon : DEFAULT_USER_MESSAGE.icon,\n },\n botMessage: {\n background: config.botMessage?.background ?? DEFAULT_BOT_MESSAGE.background,\n width: config.botMessage?.width ?? DEFAULT_BOT_MESSAGE.width,\n text: { ...DEFAULT_BOT_MESSAGE.text, ...config.botMessage?.text },\n icon: config.botMessage?.icon ?? DEFAULT_BOT_MESSAGE.icon,\n },\n header: {\n icon: config.header?.icon !== undefined ? config.header.icon : DEFAULT_CONFIG.header.icon,\n text: config.header?.text ?? DEFAULT_CONFIG.header.text,\n color: config.header?.color ?? DEFAULT_CONFIG.header.color,\n },\n input: {\n placeholder: config.input?.placeholder ?? DEFAULT_CONFIG.input.placeholder,\n expandable: config.input?.expandable ?? DEFAULT_CONFIG.input.expandable,\n text: config.input?.text ?? DEFAULT_CONFIG.input.text,\n background: config.input?.background ?? DEFAULT_CONFIG.input.background,\n upload: config.input?.upload ? {\n maxFileSize: config.input.upload.maxFileSize ?? DEFAULT_UPLOAD.maxFileSize,\n errorDisplayDuration: config.input.upload.errorDisplayDuration ?? DEFAULT_UPLOAD.errorDisplayDuration,\n allowedFileTypes: config.input.upload.allowedFileTypes ?? DEFAULT_UPLOAD.allowedFileTypes,\n icon: config.input.upload.icon,\n } : undefined,\n maxCharacters: config.input?.maxCharacters ? {\n limit: config.input.maxCharacters.limit,\n text: config.input.maxCharacters.text,\n } : undefined,\n },\n sendButton: {\n align: config.sendButton?.align ?? DEFAULT_SEND_BUTTON.align!,\n icon: config.sendButton?.icon ?? DEFAULT_CONFIG.sendButton.icon,\n },\n welcome: {\n display: config.welcome?.display ?? DEFAULT_WELCOME.display,\n preTitle: { ...DEFAULT_WELCOME.preTitle, ...config.welcome?.preTitle },\n title: { ...DEFAULT_WELCOME.title, ...config.welcome?.title },\n subtitle: { ...DEFAULT_WELCOME.subtitle, ...config.welcome?.subtitle },\n },\n error: {\n inactivity: { ...DEFAULT_ERROR.inactivity, ...config.error?.inactivity },\n conflict: { ...DEFAULT_ERROR.conflict, ...config.error?.conflict },\n session_ttl: { ...DEFAULT_ERROR.session_ttl, ...config.error?.session_ttl },\n unauthorized: { ...DEFAULT_ERROR.unauthorized, ...config.error?.unauthorized },\n },\n debug: config.debug ?? DEFAULT_CONFIG.debug,\n reconnect: {\n attempts: config.reconnect?.attempts ?? DEFAULT_RECONNECT.attempts!,\n timeout: config.reconnect?.timeout ?? DEFAULT_RECONNECT.timeout!,\n },\n allowNewChat: config.allowNewChat ?? DEFAULT_CONFIG.allowNewChat,\n waitForReply: config.waitForReply ? {\n timeout: config.waitForReply.timeout ?? DEFAULT_WAIT_FOR_REPLY.timeout,\n firstReply: config.waitForReply.firstReply ?? false,\n } : undefined,\n isTypingIndicator: config.isTypingIndicator ? {\n position: config.isTypingIndicator.position ?? DEFAULT_TYPING_INDICATOR.position,\n title: {\n value: config.isTypingIndicator.title?.value ?? DEFAULT_TYPING_INDICATOR.title?.value,\n text: config.isTypingIndicator.title?.text ?? DEFAULT_TYPING_INDICATOR.title?.text,\n },\n } : undefined,\n showMore: {\n value: config.showMore?.value ?? DEFAULT_CONFIG.showMore.value,\n sticky: config.showMore?.sticky ?? DEFAULT_CONFIG.showMore.sticky,\n background: config.showMore?.background ?? ({ ...DEFAULT_THEME, ...config.theme }).secondary,\n text: {\n color: config.showMore?.text?.color ?? DEFAULT_CONFIG.showMore.text?.color,\n font: config.showMore?.text?.font ?? s?.font,\n size: config.showMore?.text?.size ?? DEFAULT_CONFIG.showMore.text?.size,\n },\n icon: config.showMore?.icon ?? DEFAULT_CONFIG.showMore.icon,\n },\n };\n \n return resolved;\n}\n\n/** UI positioning and spacing constants */\nexport const SPACING = {\n BUBBLE_OFFSET: 84, // Distance bubble is offset from bottom\n EDGE_DISTANCE: 20, // Distance from viewport edges \n BUTTON_OFFSET: 7, // Precise button positioning\n AVATAR_MARGIN: 2, // Avatar bottom margin\n} as const;\n\n/** Border radius values used throughout */\nexport const BORDER_RADIUS = {\n AVATAR: \"50%\", // Circular avatars\n PANEL: \"12px\", // Panel corners\n PANEL_MOBILE: \"12px 12px 0 0\", // Mobile panel corners\n} as const;\n\n/** Icon scaling factors */ \nexport const ICON_SCALE = {\n BUBBLE_ICON: 0.65, // Chat bubble icon scaling\n} as const;\n","import type { NimbusChatConfig, ChatPosition, BubblePosition } from \"../types/config\";\n\nconst VALID_POSITIONS: ChatPosition[] = [\n \"bottom-right\",\n \"bottom-left\",\n \"sidepanel-left\",\n \"sidepanel-right\",\n];\n\nconst VALID_BUBBLE_POSITIONS: BubblePosition[] = [\n \"bottom-right\",\n \"bottom-left\",\n];\n\n\n/** Validate a TextConfig value. */\nfunction validateTextConfig(value: unknown, path: string): void {\n if (typeof value !== \"object\" || value === null) {\n throw new Error(`[NimbusChat] ${path} must be an object`);\n }\n const t = value as Record<string, unknown>;\n if (t.display !== undefined && typeof t.display !== \"boolean\") {\n throw new Error(`[NimbusChat] ${path}.display must be a boolean`);\n }\n if (t.value !== undefined && typeof t.value !== \"string\") {\n throw new Error(`[NimbusChat] ${path}.value must be a string`);\n }\n if (t.color !== undefined && typeof t.color !== \"string\") {\n throw new Error(`[NimbusChat] ${path}.color must be a string`);\n }\n if (t.font !== undefined && typeof t.font !== \"string\") {\n throw new Error(`[NimbusChat] ${path}.font must be a string`);\n }\n if (t.size !== undefined && typeof t.size !== \"number\") {\n throw new Error(`[NimbusChat] ${path}.size must be a number`);\n }\n}\n\n/** Validate an IconConfig value (or null). */\nfunction validateIconConfig(value: unknown, path: string): void {\n if (value === null) return;\n if (typeof value !== \"object\") {\n throw new Error(`[NimbusChat] ${path} must be an object or null`);\n }\n const icon = value as Record<string, unknown>;\n if (icon.img !== undefined && typeof icon.img !== \"string\") {\n throw new Error(`[NimbusChat] ${path}.img must be a string`);\n }\n if (icon.size !== undefined) {\n if (typeof icon.size !== \"object\" || icon.size === null) {\n throw new Error(`[NimbusChat] ${path}.size must be an object`);\n }\n const size = icon.size as Record<string, unknown>;\n if (size.width !== undefined && typeof size.width !== \"number\") {\n throw new Error(`[NimbusChat] ${path}.size.width must be a number`);\n }\n if (size.height !== undefined && typeof size.height !== \"number\") {\n throw new Error(`[NimbusChat] ${path}.size.height must be a number`);\n }\n }\n}\n\nexport function validateConfig(config: unknown): asserts config is NimbusChatConfig {\n if (!config || typeof config !== \"object\") {\n throw new Error(\"[NimbusChat] Config must be an object\");\n }\n\n const c = config as Record<string, unknown>;\n\n if (!c.agent_id || typeof c.agent_id !== \"string\" || c.agent_id.trim() === \"\") {\n throw new Error(\"[NimbusChat] agent_id is required and must be a non-empty UUID string\");\n }\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (!uuidRegex.test(c.agent_id.trim())) {\n throw new Error(\"[NimbusChat] agent_id must be a valid UUID (e.g. 550e8400-e29b-41d4-a716-446655440000)\");\n }\n if (c.wsUrl !== undefined && typeof c.wsUrl !== \"string\") {\n throw new Error(\"[NimbusChat] wsUrl must be a string\");\n }\n if (c.style !== undefined) {\n if (typeof c.style !== \"object\" || c.style === null) {\n throw new Error(\"[NimbusChat] style must be an object\");\n }\n const st = c.style as Record<string, unknown>;\n if (st.position !== undefined && !VALID_POSITIONS.includes(st.position as ChatPosition)) {\n throw new Error(`[NimbusChat] style.position must be one of: ${VALID_POSITIONS.join(\", \")}`);\n }\n if (st.background !== undefined && typeof st.background !== \"string\") {\n throw new Error(\"[NimbusChat] style.background must be a string\");\n }\n if (st.font !== undefined && typeof st.font !== \"string\") {\n throw new Error(\"[NimbusChat] style.font must be a string\");\n }\n }\n\n if (c.theme !== undefined) {\n if (typeof c.theme !== \"object\" || c.theme === null) {\n throw new Error(\"[NimbusChat] theme must be an object\");\n }\n const t = c.theme as Record<string, unknown>;\n if (t.primary !== undefined && typeof t.primary !== \"string\") {\n throw new Error(\"[NimbusChat] theme.primary must be a string\");\n }\n if (t.secondary !== undefined && typeof t.secondary !== \"string\") {\n throw new Error(\"[NimbusChat] theme.secondary must be a string\");\n }\n }\n\n if (c.bubble !== undefined) {\n if (typeof c.bubble !== \"object\" || c.bubble === null) {\n throw new Error(\"[NimbusChat] bubble must be an object\");\n }\n const bub = c.bubble as Record<string, unknown>;\n if (bub.position !== undefined && !VALID_BUBBLE_POSITIONS.includes(bub.position as BubblePosition)) {\n throw new Error(`[NimbusChat] bubble.position must be one of: ${VALID_BUBBLE_POSITIONS.join(\", \")}`);\n }\n if (bub.autoHide !== undefined && typeof bub.autoHide !== \"boolean\") {\n throw new Error(\"[NimbusChat] bubble.autoHide must be a boolean\");\n }\n if (bub.icon !== undefined) validateIconConfig(bub.icon, \"bubble.icon\");\n }\n\n if (c.userMessage !== undefined) {\n if (typeof c.userMessage !== \"object\" || c.userMessage === null) {\n throw new Error(\"[NimbusChat] userMessage must be an object\");\n }\n const msg = c.userMessage as Record<string, unknown>;\n if (msg.background !== undefined && typeof msg.background !== \"string\") {\n throw new Error(\"[NimbusChat] userMessage.background must be a string\");\n }\n if (msg.text !== undefined) validateTextConfig(msg.text, \"userMessage.text\");\n if (msg.icon !== undefined) validateIconConfig(msg.icon, \"userMessage.icon\");\n }\n\n if (c.botMessage !== undefined) {\n if (typeof c.botMessage !== \"object\" || c.botMessage === null) {\n throw new Error(\"[NimbusChat] botMessage must be an object\");\n }\n const msg = c.botMessage as Record<string, unknown>;\n if (msg.background !== undefined && typeof msg.background !== \"string\") {\n throw new Error(\"[NimbusChat] botMessage.background must be a string\");\n }\n if (msg.text !== undefined) validateTextConfig(msg.text, \"botMessage.text\");\n if (msg.icon !== undefined) validateIconConfig(msg.icon, \"botMessage.icon\");\n }\n\n if (c.header !== undefined) {\n if (typeof c.header !== \"object\" || c.header === null) {\n throw new Error(\"[NimbusChat] header must be an object\");\n }\n const hdr = c.header as Record<string, unknown>;\n if (hdr.icon !== undefined) validateIconConfig(hdr.icon, \"header.icon\");\n if (hdr.text !== undefined) validateTextConfig(hdr.text, \"header.text\");\n if (hdr.color !== undefined) {\n if (typeof hdr.color !== \"object\" || hdr.color === null) {\n throw new Error(\"[NimbusChat] header.color must be an object\");\n }\n const hc = hdr.color as Record<string, unknown>;\n if (hc.primary !== undefined && typeof hc.primary !== \"string\") {\n throw new Error(\"[NimbusChat] header.color.primary must be a string\");\n }\n if (hc.secondary !== undefined && typeof hc.secondary !== \"string\") {\n throw new Error(\"[NimbusChat] header.color.secondary must be a string\");\n }\n }\n }\n\n if (c.input !== undefined) {\n if (typeof c.input !== \"object\" || c.input === null) {\n throw new Error(\"[NimbusChat] input must be an object\");\n }\n const inp = c.input as Record<string, unknown>;\n if (inp.placeholder !== undefined && typeof inp.placeholder !== \"string\") {\n throw new Error(\"[NimbusChat] input.placeholder must be a string\");\n }\n if (inp.expandable !== undefined && typeof inp.expandable !== \"boolean\") {\n throw new Error(\"[NimbusChat] input.expandable must be a boolean\");\n }\n if (inp.text !== undefined) validateTextConfig(inp.text, \"input.text\");\n if (inp.background !== undefined) {\n if (typeof inp.background !== \"object\" || inp.background === null) {\n throw new Error(\"[NimbusChat] input.background must be an object\");\n }\n const bg = inp.background as Record<string, unknown>;\n if (bg.primary !== undefined && typeof bg.primary !== \"string\") {\n throw new Error(\"[NimbusChat] input.background.primary must be a string\");\n }\n if (bg.secondary !== undefined && typeof bg.secondary !== \"string\") {\n throw new Error(\"[NimbusChat] input.background.secondary must be a string\");\n }\n }\n }\n\n if (c.sendButton !== undefined) {\n if (typeof c.sendButton !== \"object\" || c.sendButton === null) {\n throw new Error(\"[NimbusChat] sendButton must be an object\");\n }\n const btn = c.sendButton as Record<string, unknown>;\n if (btn.icon !== undefined) validateIconConfig(btn.icon, \"sendButton.icon\");\n if (btn.align !== undefined && typeof btn.align !== \"boolean\") {\n throw new Error(\"[NimbusChat] sendButton.align must be a boolean\");\n }\n }\n\n if (c.welcome !== undefined) {\n if (typeof c.welcome !== \"object\" || c.welcome === null) {\n throw new Error(\"[NimbusChat] welcome must be an object\");\n }\n const w = c.welcome as Record<string, unknown>;\n if (w.display !== undefined && typeof w.display !== \"boolean\") {\n throw new Error(\"[NimbusChat] welcome.display must be a boolean\");\n }\n if (w.preTitle !== undefined) validateTextConfig(w.preTitle, \"welcome.preTitle\");\n if (w.title !== undefined) validateTextConfig(w.title, \"welcome.title\");\n if (w.subtitle !== undefined) validateTextConfig(w.subtitle, \"welcome.subtitle\");\n }\n}\n","let cachedBreakpoint: string | undefined;\nlet cachedResult: boolean | null = null;\n\nexport function setMobileBreakpoint(breakpoint?: string): void {\n if (breakpoint) {\n cachedBreakpoint = breakpoint;\n cachedResult = null;\n }\n}\n\nexport function isMobileDevice(): boolean {\n if (cachedResult === null) {\n const userAgent = navigator.userAgent.toLowerCase();\n cachedResult = /mobile|android|iphone|ipad|ipod|blackberry|windows phone/.test(userAgent);\n }\n\n if (cachedBreakpoint) {\n const px = parseInt(cachedBreakpoint, 10);\n if (!isNaN(px) && window.innerWidth <= px) return true;\n }\n\n return cachedResult;\n}\n","type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst LEVEL_CONFIG: Record<LogLevel, { badge: string; bg: string; fg: string; consoleFn: \"log\" | \"info\" | \"warn\" | \"error\" }> = {\n debug: { badge: \"DBG\", bg: \"#6B7280\", fg: \"#fff\", consoleFn: \"log\" },\n info: { badge: \"INF\", bg: \"#3B82F6\", fg: \"#fff\", consoleFn: \"info\" },\n warn: { badge: \"WRN\", bg: \"#F59E0B\", fg: \"#000\", consoleFn: \"warn\" },\n error: { badge: \"ERR\", bg: \"#EF4444\", fg: \"#fff\", consoleFn: \"error\" },\n};\n\nconst COMPONENT_COLORS: Record<string, string> = {\n websocket: \"#10B981\",\n ws: \"#10B981\",\n api: \"#F97316\",\n ui: \"#EC4899\",\n window: \"#EC4899\",\n session: \"#06B6D4\",\n perf: \"#22C55E\",\n};\n\nconst DEFAULT_COMPONENT_COLOR = \"#8B5CF6\";\n\nconst BADGE_STYLE = (bg: string, fg: string) =>\n `background:${bg};color:${fg};padding:1px 6px;border-radius:3px;font-weight:600;font-size:11px`;\n\nconst COMPONENT_STYLE = (color: string) =>\n `background:${color}22;color:${color};padding:1px 6px;border-radius:3px;font-weight:600;font-size:11px`;\n\nconst TIMESTAMP_STYLE = \"color:#9CA3AF;font-size:10px;font-weight:400\";\nconst MESSAGE_STYLE = \"color:#E5E7EB;font-weight:400\";\n\nfunction getComponentColor(component: string): string {\n const lower = component.toLowerCase();\n for (const [key, color] of Object.entries(COMPONENT_COLORS)) {\n if (lower.includes(key)) return color;\n }\n return DEFAULT_COMPONENT_COLOR;\n}\n\nfunction sanitizeArgs(args: unknown[]): unknown[] {\n return args.map(arg => {\n if (arg && typeof arg === 'object') {\n const obj = arg as Record<string, unknown>;\n if (obj.type === 'file' && obj.content) {\n return { ...obj, content: '[base64 omitted]' };\n }\n }\n return arg;\n });\n}\n\nfunction tryParseJSON(str: string): unknown | null {\n try {\n return JSON.parse(str);\n } catch {\n return null;\n }\n}\n\nfunction sanitizeFilePayload(message: string): { sanitized: string; json: unknown | null } {\n const wsMatch = message.match(/^(Sending:|Received:)\\s*(.+)$/s);\n\n if (wsMatch) {\n const [, prefix, payload] = wsMatch;\n const parsed = tryParseJSON(payload);\n if (parsed && typeof parsed === 'object') {\n const obj = parsed as Record<string, unknown>;\n if (obj.type === 'file' && obj.content) {\n const clean = { ...obj, content: '[base64 omitted]' };\n return { sanitized: `${prefix}`, json: clean };\n }\n return { sanitized: `${prefix}`, json: parsed };\n }\n }\n\n const parsed = tryParseJSON(message);\n if (parsed && typeof parsed === 'object') {\n const obj = parsed as Record<string, unknown>;\n if (obj.type === 'file' && obj.content) {\n return { sanitized: '', json: { ...obj, content: '[base64 omitted]' } };\n }\n return { sanitized: '', json: parsed };\n }\n\n return { sanitized: message, json: null };\n}\n\nexport type { Logger };\n\nclass Logger {\n private component: string;\n private enabled: boolean;\n private color: string;\n\n constructor(component: string, enabled = false) {\n this.component = component;\n this.enabled = enabled;\n this.color = getComponentColor(component);\n }\n\n private log(level: LogLevel, message: string, ...args: unknown[]): void {\n if (!this.enabled) return;\n\n const { badge, bg, fg, consoleFn } = LEVEL_CONFIG[level];\n const time = new Date().toISOString().substring(11, 23);\n\n const { sanitized, json } = sanitizeFilePayload(message);\n const cleanArgs = sanitizeArgs(args);\n\n const parts = [\n `%c${badge}%c ${this.component} %c${time}`,\n ];\n const styles = [\n BADGE_STYLE(bg, fg),\n COMPONENT_STYLE(this.color),\n TIMESTAMP_STYLE,\n ];\n\n if (sanitized) {\n parts[0] += ` %c${sanitized}`;\n styles.push(MESSAGE_STYLE);\n }\n\n const allArgs: unknown[] = [parts[0], ...styles];\n\n if (json) {\n allArgs.push(json);\n }\n\n allArgs.push(...cleanArgs);\n\n console[consoleFn](...allArgs);\n }\n\n debug(message: string, ...args: unknown[]): void {\n this.log(\"debug\", message, ...args);\n }\n\n info(message: string, ...args: unknown[]): void {\n this.log(\"info\", message, ...args);\n }\n\n warn(message: string, ...args: unknown[]): void {\n this.log(\"warn\", message, ...args);\n }\n\n error(message: string, ...args: unknown[]): void {\n this.log(\"error\", message, ...args);\n }\n\n object(label: string, obj: unknown): void {\n if (!this.enabled) return;\n this.debug(label);\n console.dir(obj, { depth: 3, colors: true });\n }\n\n group(label: string): void {\n if (!this.enabled) return;\n console.groupCollapsed(\n `%c${this.component}%c ${label}`,\n COMPONENT_STYLE(this.color),\n MESSAGE_STYLE,\n );\n }\n\n groupEnd(): void {\n if (!this.enabled) return;\n console.groupEnd();\n }\n\n child(subComponent: string): Logger {\n return new Logger(`${this.component}:${subComponent}`, this.enabled);\n }\n\n setEnabled(enabled: boolean): void {\n this.enabled = enabled;\n }\n\n table(data: unknown): void {\n if (!this.enabled) return;\n console.table(data);\n }\n}\n\nexport function createLogger(component: string, debug = false): Logger {\n return new Logger(component, debug);\n}\n\nlet bannerPrinted = false;\n\nexport function printBanner(version?: string): void {\n if (bannerPrinted) return;\n bannerPrinted = true;\n\n const art = [\n \"\",\n \" %c\",\n \" ███╗ ██╗██╗███╗ ███╗██████╗ ██╗ ██╗███████╗\",\n \" ████╗ ██║██║████╗ ████║██╔══██╗██║ ██║██╔════╝\",\n \" ██╔██╗ ██║██║██╔████╔██║██████╔╝██║ ██║███████╗\",\n \" ██║╚██╗██║██║██║╚██╔╝██║██╔══██╗██║ ██║╚════██║\",\n \" ██║ ╚████║██║██║ ╚═╝ ██║██████╔╝╚██████╔╝███████║\",\n \" ╚═╝ ╚═══╝╚═╝╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚══════╝\",\n \"\",\n ].join(\"\\n\");\n\n const subtitle = version\n ? `%c WebChat SDK v${version}`\n : \"%c WebChat SDK\";\n const line = \"%c ──────────────────────────────────────────────\";\n\n console.log(\n art + \"\\n\" + subtitle + \"\\n\" + line + \"\\n\",\n \"color:#ffce1c;font-weight:bold\",\n \"color:#9CA3AF;font-size:11px\",\n \"color:#374151\",\n );\n}\n","import { createLogger, type Logger } from './logger';\n\n/**\n * Tracks timing metrics for chat interactions.\n * Only logs when debug mode is enabled.\n */\nexport class PerformanceTracker {\n private logger: Logger;\n private sessionStartTime: number | null = null;\n private messageSendTime: number | null = null;\n private waitingForFirstMessage = false;\n private waitingForReply = false;\n\n constructor(debug: boolean) {\n this.logger = createLogger('Perf', debug);\n }\n\n /** Call when a new session starts (session:started event) */\n markSessionStarted(): void {\n this.sessionStartTime = performance.now();\n this.waitingForFirstMessage = true;\n this.logger.debug('Session started — waiting for first AI message');\n }\n\n /** Call when the user sends a message */\n markMessageSent(): void {\n this.messageSendTime = performance.now();\n this.waitingForReply = true;\n this.logger.debug('User message sent — waiting for AI response');\n }\n\n /** Call when a bot (OUTBOUND) message is received */\n markBotMessageReceived(): void {\n const now = performance.now();\n\n if (this.waitingForFirstMessage && this.sessionStartTime !== null) {\n const elapsed = now - this.sessionStartTime;\n this.logger.info(`First AI message: ${elapsed.toFixed(0)}ms (from session start)`);\n this.waitingForFirstMessage = false;\n this.sessionStartTime = null;\n }\n\n if (this.waitingForReply && this.messageSendTime !== null) {\n const elapsed = now - this.messageSendTime;\n this.logger.info(`AI response time: ${elapsed.toFixed(0)}ms (from user message)`);\n this.waitingForReply = false;\n this.messageSendTime = null;\n }\n }\n\n /** Reset all tracking state */\n reset(): void {\n this.sessionStartTime = null;\n this.messageSendTime = null;\n this.waitingForFirstMessage = false;\n this.waitingForReply = false;\n }\n}\n","import type { ChatMessage, ConnectionState, ServerEvent } from \"../types/message\";\nimport { createLogger, type Logger } from \"../utils/logger\";\n\nexport interface EventMap {\n \"ws:open\": [];\n \"ws:close\": [event: globalThis.CloseEvent];\n \"ws:error\": [event: Event];\n \"ws:message\": [event: ServerEvent];\n \"ws:request-connect\": [];\n \"session:id\": [flowId: string];\n \"session:connected\": [];\n \"session:cleared\": [];\n \"message:added\": [message: ChatMessage];\n \"history:loaded\": [messages: ChatMessage[]];\n \"history:merged\": [messages: ChatMessage[]];\n \"history:has_more\": [hasMore: boolean];\n \"connection:state\": [state: ConnectionState];\n \"ui:toggle\": [];\n \"ui:show-error\": [message: string];\n \"ui:show-more\": [];\n \"ui:start-new-chat\": [];\n}\n\nexport type EventName = keyof EventMap;\nexport type EventHandler<K extends EventName = EventName> = (...args: EventMap[K]) => void;\n\nexport class EventBus {\n private listeners = new Map<EventName, Set<EventHandler<any>>>();\n private logger: Logger;\n\n constructor() {\n this.logger = createLogger(\"EventBus\", false);\n }\n\n on<K extends EventName>(event: K, handler: EventHandler<K>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(handler);\n }\n\n off<K extends EventName>(event: K, handler: EventHandler<K>): void {\n this.listeners.get(event)?.delete(handler);\n }\n\n once<K extends EventName>(event: K, handler: EventHandler<K>): void {\n const wrapper = ((...args: EventMap[K]) => {\n handler(...args);\n this.off(event, wrapper);\n }) as EventHandler<K>;\n this.on(event, wrapper);\n }\n\n emit<K extends EventName>(event: K, ...args: EventMap[K]): void {\n this.listeners.get(event)?.forEach((handler) => {\n try {\n handler(...args);\n } catch (err) {\n this.logger.error(`EventBus error in \"${event}\" handler:`, err);\n }\n });\n }\n\n removeAll(): void {\n this.listeners.clear();\n }\n}\n","import type { ChatMessage, MessageType } from \"../types/message\";\nimport type { EventBus } from \"./EventBus\";\nimport { TIME_CONVERSION } from \"../types/config\";\n\n/**\n * Chat session manager with message history support.\n * Manages current flow and message list.\n */\nexport class ChatSession {\n private flowId: string | null = null;\n private messages: ChatMessage[] = [];\n private eventBus: EventBus;\n\n constructor(eventBus: EventBus) {\n this.eventBus = eventBus;\n }\n\n // ── Flow ID ──────────────────────────────────────────────────\n\n setFlowId(flowId: string): void {\n this.flowId = flowId;\n this.eventBus.emit(\"session:id\", flowId);\n }\n\n getFlowId(): string | null {\n return this.flowId;\n }\n\n // ── Messages ────────────────────────────────────────────────────\n\n addMessage(message: ChatMessage): void {\n this.messages.push(message);\n this.eventBus.emit(\"message:added\", message);\n }\n\n getMessages(): ChatMessage[] {\n return [...this.messages];\n }\n\n clearMessages(): void {\n this.messages = [];\n }\n\n // ── Factory helpers ─────────────────────────────────────────────\n\n createUserMessage(content: string | object, messageType: MessageType = \"text\"): ChatMessage {\n const msg: ChatMessage = {\n id: crypto.randomUUID(),\n direction: \"INBOUND\",\n message_type: messageType,\n content,\n created_at: Date.now() / TIME_CONVERSION.MILLISECONDS_TO_SECONDS,\n };\n this.addMessage(msg);\n return msg;\n }\n\n createBotMessage(\n content: string,\n id?: string,\n created_at?: number,\n ): ChatMessage {\n const msg: ChatMessage = {\n id: id ?? crypto.randomUUID(),\n direction: \"OUTBOUND\",\n message_type: \"text\",\n content,\n created_at: created_at ?? Date.now() / TIME_CONVERSION.MILLISECONDS_TO_SECONDS,\n };\n this.addMessage(msg);\n return msg;\n }\n\n loadHistoryMessages(messages: ChatMessage[]): void {\n this.messages = messages;\n this.eventBus.emit(\"history:loaded\", this.messages);\n }\n\n loadHistoryMessagesWithMerge(messages: ChatMessage[]): void {\n const localMessages = [...this.messages];\n\n const fetchedMessages = messages;\n\n const allMessages = [...fetchedMessages, ...localMessages];\n\n const uniqueMessages = new Map<string, ChatMessage>();\n\n for (const msg of allMessages) {\n const key = msg.id;\n\n if (!uniqueMessages.has(key)) {\n uniqueMessages.set(key, msg);\n }\n }\n\n this.messages = Array.from(uniqueMessages.values())\n .sort((a, b) => a.created_at - b.created_at);\n\n this.eventBus.emit(\"history:merged\", this.messages);\n }\n\n // ── Lifecycle ───────────────────────────────────────────────────\n\n clear(): void {\n this.flowId = null;\n this.messages = [];\n this.eventBus.emit(\"session:cleared\");\n }\n}\n","import type { EventBus } from \"./EventBus\";\nimport type { ConnectionState, ServerEvent, UserMessage } from \"../types/message\";\nimport type { ReconnectConfig } from \"../types/config\";\nimport { WEBSOCKET_CLOSE_CODES } from \"../types/config\";\nimport { createLogger, type Logger } from \"../utils/logger\";\n\nconst STORAGE_KEYS = {\n FLOW_ID: 'nimbus_webchat_flow_id',\n} as const;\n\n/**\n * Manages the WebSocket lifecycle: connect, send, receive, reconnect.\n * Supports flow-based connections with optional flow restoration.\n */\nexport class WebSocketManager {\n private ws: WebSocket | null = null;\n private dns: string;\n private agentId: string;\n private flowId: string | null = null;\n private eventBus: EventBus;\n private reconnectAttempts = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private _state: ConnectionState = \"idle\";\n private intentionalClose = false;\n private logger: Logger;\n private reconnectConfig: ReconnectConfig;\n\n constructor(\n dns: string,\n agentId: string,\n eventBus: EventBus,\n reconnectConfig: ReconnectConfig,\n debug: boolean,\n ) {\n this.dns = dns;\n this.agentId = agentId;\n this.eventBus = eventBus;\n this.reconnectConfig = reconnectConfig;\n this.logger = createLogger(\"WebSocket\", debug);\n\n // Always load saved flow_id to reuse existing conversation\n this.flowId = this.loadFlowId();\n }\n\n // ── Public API ──────────────────────────────────────────────────\n\n get state(): ConnectionState {\n return this._state;\n }\n\n getFlowId(): string | null {\n return this.flowId;\n }\n\n connect(forceNew = false): void {\n this.logger.debug(`connect() called with forceNew=${forceNew}, ws state=${this.ws?.readyState}, flowId=${this.flowId}`);\n\n this.clearReconnectTimer();\n\n if (!forceNew && this.isConnectedOrConnecting()) {\n this.logger.debug(\"Already connected or connecting, skipping new connection\");\n return;\n }\n\n const closingExisting = this.closeExistingConnection(forceNew);\n\n this.resetConnectionState(closingExisting);\n\n this.createWebSocketConnection();\n }\n\n private isConnectedOrConnecting(): boolean {\n return this.ws !== null &&\n (this.ws.readyState === WebSocket.OPEN ||\n this.ws.readyState === WebSocket.CONNECTING);\n }\n\n private closeExistingConnection(forceNew: boolean): boolean {\n if (!forceNew || !this.ws) return false;\n\n if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {\n this.intentionalClose = true;\n this.ws.onopen = null;\n this.ws.onmessage = null;\n this.ws.onerror = null;\n this.ws.onclose = null;\n this.ws.close(WEBSOCKET_CLOSE_CODES.NORMAL, \"Creating new connection\");\n this.ws = null;\n return true;\n }\n\n this.ws = null;\n return false;\n }\n\n private resetConnectionState(keepIntentionalClose: boolean): void {\n if (!keepIntentionalClose) {\n this.intentionalClose = false;\n }\n\n this.setState(\"connecting\");\n }\n\n private createWebSocketConnection(): void {\n const url = this.buildWebSocketUrl();\n this.logger.info(`Connecting to ${url}`);\n\n this.ws = new WebSocket(url);\n this.ws.onopen = this.onOpen.bind(this);\n this.ws.onmessage = this.onMessage.bind(this);\n this.ws.onerror = this.onError.bind(this);\n this.ws.onclose = this.onClose.bind(this);\n }\n\n private buildWebSocketUrl(): string {\n const wsUrl = `wss://${this.dns}`;\n const params = new URLSearchParams();\n\n params.set('agent_id', this.agentId);\n if (this.flowId) {\n params.set('flow_id', this.flowId);\n }\n\n return `${wsUrl}?${params.toString()}`;\n }\n\n disconnect(): void {\n this.intentionalClose = true;\n this.clearReconnectTimer();\n\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n this.ws.close(WEBSOCKET_CLOSE_CODES.NORMAL, \"Client disconnect\");\n }\n\n this.ws = null;\n this.setState(\"disconnected\");\n }\n\n send(message: UserMessage): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n this.logger.warn(\"Cannot send message — WebSocket not connected\");\n return;\n }\n const payload = JSON.stringify(message);\n this.logger.debug(`Sending: ${payload}`);\n this.ws.send(payload);\n }\n\n // ── WebSocket event handlers ────────────────────────────────────\n\n private onOpen(): void {\n this.logger.info(\"Connected\");\n this.reconnectAttempts = 0;\n this.intentionalClose = false;\n this.setState(\"connected\");\n this.eventBus.emit(\"ws:open\");\n }\n\n private onMessage(event: MessageEvent): void {\n this.logger.debug(`Received: ${event.data}`);\n try {\n const parsed: ServerEvent = JSON.parse(event.data);\n\n if (parsed.type === \"connected\") {\n this.flowId = parsed.flow_id;\n this.saveFlowId(parsed.flow_id);\n this.logger.info(`Connected with flow_id: ${parsed.flow_id}`);\n } else if (parsed.type === \"message\") {\n // Validate message: must have direction \"outbound\" and non-empty content\n if (parsed.direction !== \"outbound\" || !parsed.content) {\n this.logger.debug(\"Ignoring invalid message: missing/empty content or wrong direction\");\n return;\n }\n } else {\n // Silently ignore unknown event types\n this.logger.debug(`Ignoring unknown event type: ${(parsed as any).type}`);\n return;\n }\n\n this.eventBus.emit(\"ws:message\", parsed);\n } catch (err) {\n this.logger.error(\"Failed to parse WebSocket message:\", err);\n }\n }\n\n private onError(event: Event): void {\n this.logger.error(\"WebSocket error:\", event);\n this.eventBus.emit(\"ws:error\", event);\n }\n\n private onClose(event: CloseEvent): void {\n this.logger.info(`Connection closed: code=${event.code} reason=\"${event.reason}\"`);\n if (this.ws === event.target) {\n this.ws = null;\n }\n this.eventBus.emit(\"ws:close\", event);\n\n if (!this.intentionalClose) {\n this.attemptReconnect();\n } else {\n this.setState(\"disconnected\");\n }\n }\n\n // ── Reconnection ───────────────────────────────────────────────\n\n private attemptReconnect(): void {\n if (this.hasExceededMaxReconnectAttempts()) {\n this.logger.error(\"Max reconnect attempts reached\");\n this.setState(\"disconnected\");\n return;\n }\n\n const delay = this.calculateReconnectDelay();\n this.scheduleReconnect(delay);\n }\n\n private hasExceededMaxReconnectAttempts(): boolean {\n return this.reconnectAttempts >= this.reconnectConfig.attempts!;\n }\n\n private calculateReconnectDelay(): number {\n return this.reconnectConfig.timeout!;\n }\n\n private scheduleReconnect(delay: number): void {\n this.setState(\"connecting\");\n this.reconnectAttempts++;\n\n this.logger.info(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.reconnectConfig.attempts})`);\n\n this.reconnectTimer = setTimeout(() => {\n this.connect();\n }, delay);\n }\n\n private clearReconnectTimer(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n // ── Helpers ────────────────────────────────────────────────────\n\n private setState(state: ConnectionState): void {\n this._state = state;\n this.eventBus.emit(\"connection:state\", state);\n }\n\n // ── Flow ID Management ──────────────────────────────────────────\n\n clearSession(): void {\n this.flowId = null;\n this.clearFlowId();\n this.eventBus.emit(\"session:cleared\");\n }\n\n private saveFlowId(flowId: string): void {\n try {\n localStorage.setItem(STORAGE_KEYS.FLOW_ID, flowId);\n } catch {\n }\n }\n\n private loadFlowId(): string | null {\n try {\n return localStorage.getItem(STORAGE_KEYS.FLOW_ID);\n } catch {\n return null;\n }\n }\n\n private clearFlowId(): void {\n try {\n localStorage.removeItem(STORAGE_KEYS.FLOW_ID);\n } catch {\n }\n }\n}\n","import type { ChatMessagesResponse } from \"../types/message\";\nimport { WIDGET_DEFAULTS } from \"../types/config\";\nimport { createLogger, type Logger } from \"../utils/logger\";\n\nexport class ApiClient {\n private dns: string;\n private logger: Logger;\n private abortController: AbortController | null = null;\n\n constructor(dns: string, debug: boolean) {\n this.dns = dns;\n this.logger = createLogger(\"API\", debug);\n }\n\n async fetchMessageHistory(\n sessionId: string,\n limit = WIDGET_DEFAULTS.MESSAGE_LIMIT,\n before?: number\n ): Promise<ChatMessagesResponse> {\n this.abortController?.abort();\n this.abortController = new AbortController();\n\n const url = new URL(`https://${this.dns}/sessions/${sessionId}/messages`);\n url.searchParams.set(\"limit\", limit.toString());\n if (before) {\n url.searchParams.set(\"before\", before.toString());\n }\n\n this.logger.debug(`Fetching history: ${url.toString()}`);\n\n try {\n const response = await fetch(url.toString(), {\n method: 'GET',\n headers: { 'Accept': 'application/json' },\n signal: this.abortController.signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const data: ChatMessagesResponse = await response.json();\n this.logger.debug(`Retrieved ${data.messages.length} messages, has_more: ${data.has_more}`);\n\n return data;\n } catch (error) {\n if (error instanceof DOMException && error.name === 'AbortError') {\n this.logger.debug(\"Request aborted\");\n throw error;\n }\n this.logger.error(\"Failed to fetch message history:\", error);\n throw error;\n }\n }\n\n abort(): void {\n this.abortController?.abort();\n this.abortController = null;\n }\n}\n","// AUTO-GENERATED — do not edit. Run \"npm run build:css\" to regenerate.\nexport const COMPILED_CSS = \"*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:\\\"\\\"}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.-right-1{right:-.25rem}.-top-1{top:-.25rem}.bottom-1{bottom:.25rem}.bottom-12{bottom:3rem}.bottom-2{bottom:.5rem}.bottom-5{bottom:1.25rem}.bottom-\\\\[10px\\\\]{bottom:10px}.left-0{left:0}.left-1{left:.25rem}.left-2{left:.5rem}.left-5{left:1.25rem}.right-0{right:0}.right-1{right:.25rem}.right-2{right:.5rem}.right-5{right:1.25rem}.top-0{top:0}.top-1{top:.25rem}.top-2{top:.5rem}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.my-3{margin-top:.75rem;margin-bottom:.75rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.ml-2{margin-left:.5rem}.mr-2{margin-right:.5rem}.mt-0{margin-top:0}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.contents{display:contents}.hidden{display:none}.h-14{height:3.5rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-screen{height:100vh}.max-h-48{max-height:12rem}.max-h-64{max-height:16rem}.w-14{width:3.5rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-full{width:100%}.max-w-\\\\[200px\\\\]{max-width:200px}.max-w-full{max-width:100%}.flex-1{flex:1 1 0%}.flex-shrink{flex-shrink:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-grow{flex-grow:1}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.resize{resize:both}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-2\\\\.5{gap:.625rem}.gap-3{gap:.75rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.rounded-t-xl{border-top-left-radius:.75rem;border-top-right-radius:.75rem}.rounded-bl-md{border-bottom-left-radius:.375rem}.rounded-br-md{border-bottom-right-radius:.375rem}.border{border-width:1px}.border-0{border-width:0}.border-t{border-top-width:1px}.border-slate-200{--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity,1))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.bg-black\\\\/10{background-color:rgba(0,0,0,.1)}.bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.bg-gray-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity,1))}.bg-green-400{--tw-bg-opacity:1;background-color:rgb(74 222 128/var(--tw-bg-opacity,1))}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity,1))}.bg-orange-400{--tw-bg-opacity:1;background-color:rgb(251 146 60/var(--tw-bg-opacity,1))}.bg-red-400{--tw-bg-opacity:1;background-color:rgb(248 113 113/var(--tw-bg-opacity,1))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-slate-100{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity,1))}.bg-slate-300{--tw-bg-opacity:1;background-color:rgb(203 213 225/var(--tw-bg-opacity,1))}.bg-slate-50{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity,1))}.bg-slate-600{--tw-bg-opacity:1;background-color:rgb(71 85 105/var(--tw-bg-opacity,1))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-yellow-400{--tw-bg-opacity:1;background-color:rgb(250 204 21/var(--tw-bg-opacity,1))}.bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(254 252 232/var(--tw-bg-opacity,1))}.object-cover{-o-object-fit:cover;object-fit:cover}.p-1{padding:.25rem}.p-1\\\\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\\\\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\\\\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\\\\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2{padding-bottom:.5rem}.pl-12{padding-left:3rem}.pl-4{padding-left:1rem}.pl-5{padding-left:1.25rem}.pl-6{padding-left:1.5rem}.pl-8{padding-left:2rem}.pr-12{padding-right:3rem}.pr-4{padding-right:1rem}.pr-6{padding-right:1.5rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.text-\\\\[10px\\\\]{font-size:10px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-red-50{--tw-text-opacity:1;color:rgb(254 242 242/var(--tw-text-opacity,1))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity,1))}.text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}.text-slate-500{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity,1))}.text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}.text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-yellow-700{--tw-text-opacity:1;color:rgb(161 98 7/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.opacity-50{opacity:.5}.opacity-75{opacity:.75}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-2xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring-blue-500{--tw-ring-opacity:1;--tw-ring-color:rgb(59 130 246/var(--tw-ring-opacity,1))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-shadow{transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.hover\\\\:scale-110:hover{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\\\\:bg-blue-600:hover{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.hover\\\\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.hover\\\\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.hover\\\\:bg-red-500:hover{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.hover\\\\:bg-slate-100:hover{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity,1))}.hover\\\\:bg-white\\\\/20:hover{background-color:hsla(0,0%,100%,.2)}.hover\\\\:text-blue-600:hover{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.hover\\\\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.hover\\\\:opacity-80:hover{opacity:.8}.focus\\\\:border-blue-300:focus{--tw-border-opacity:1;border-color:rgb(147 197 253/var(--tw-border-opacity,1))}.focus\\\\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\\\\:ring-blue-500\\\\/20:focus{--tw-ring-color:rgba(59,130,246,.2)}.disabled\\\\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\\\\:opacity-40:disabled{opacity:.4}\";\n","type ElementProps<T extends HTMLElement = HTMLElement> = Partial<Omit<T, 'style'>> & {\n className?: string;\n style?: Partial<CSSStyleDeclaration>;\n dataset?: Record<string, string>;\n attrs?: Record<string, string>;\n on?: Record<string, EventListenerOrEventListenerObject | ((e: never) => void)>;\n};\n\ntype Child = Node | DOMBuilder | string | null | undefined | false | Child[];\n\n/**\n * Fluent DOM builder for creating elements with a chainable API\n */\nexport class DOMBuilder<T extends HTMLElement = HTMLElement> {\n private element: T;\n\n constructor(element: T) {\n this.element = element;\n }\n\n /**\n * Create a new DOM element with optional properties and children\n */\n static create<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n props?: ElementProps<HTMLElementTagNameMap[K]>,\n ...children: Child[]\n ): DOMBuilder<HTMLElementTagNameMap[K]> {\n const element = document.createElement(tag);\n const builder = new DOMBuilder(element);\n \n if (props) {\n builder.props(props);\n }\n \n if (children.length > 0) {\n builder.append(...children);\n }\n \n return builder;\n }\n\n /**\n * Shorthand static methods for common elements\n */\n static div(props?: ElementProps<HTMLDivElement>, ...children: Child[]) {\n return this.create('div', props, ...children);\n }\n\n static span(props?: ElementProps<HTMLSpanElement>, ...children: Child[]) {\n return this.create('span', props, ...children);\n }\n\n static button(props?: ElementProps<HTMLButtonElement>, ...children: Child[]) {\n return this.create('button', props, ...children);\n }\n\n static img(props?: ElementProps<HTMLImageElement>) {\n return this.create('img', props);\n }\n\n static input(props?: ElementProps<HTMLInputElement>) {\n return this.create('input', props);\n }\n\n static textarea(props?: ElementProps<HTMLTextAreaElement>) {\n return this.create('textarea', props);\n }\n\n static form(props?: ElementProps<HTMLFormElement>, ...children: Child[]) {\n return this.create('form', props, ...children);\n }\n\n static label(props?: ElementProps<HTMLLabelElement>, ...children: Child[]) {\n return this.create('label', props, ...children);\n }\n\n /**\n * Apply properties to the element\n */\n props(props: ElementProps<T>): this {\n const { className, style, dataset, attrs, on, ...rest } = props;\n \n if (className) {\n this.element.className = className;\n }\n \n if (style) {\n Object.assign(this.element.style, style);\n }\n \n if (dataset) {\n Object.entries(dataset).forEach(([key, value]) => {\n this.element.dataset[key] = value;\n });\n }\n \n if (attrs) {\n Object.entries(attrs).forEach(([key, value]) => {\n this.element.setAttribute(key, value);\n });\n }\n \n if (on) {\n Object.entries(on).forEach(([event, handler]) => {\n this.element.addEventListener(event, handler as EventListenerOrEventListenerObject);\n });\n }\n \n // Apply remaining properties directly\n Object.entries(rest).forEach(([key, value]) => {\n (this.element as Record<string, unknown>)[key] = value;\n });\n \n return this;\n }\n\n /**\n * Add CSS classes\n */\n addClass(...classes: string[]): this {\n this.element.classList.add(...classes.filter(Boolean));\n return this;\n }\n\n /**\n * Set styles\n */\n setStyle(styles: Partial<CSSStyleDeclaration>): this {\n Object.assign(this.element.style, styles);\n return this;\n }\n\n /**\n * Add event listeners\n */\n on(event: string, handler: EventListener): this {\n this.element.addEventListener(event, handler);\n return this;\n }\n\n /**\n * Append children to the element\n */\n append(...children: Child[]): this {\n const processChild = (child: Child) => {\n if (child === null || child === undefined || child === false) return;\n \n if (Array.isArray(child)) {\n child.forEach(processChild);\n } else if (child instanceof DOMBuilder) {\n this.element.appendChild(child.build());\n } else if (child instanceof Node) {\n this.element.appendChild(child);\n } else if (typeof child === 'string') {\n this.element.appendChild(document.createTextNode(child));\n } else if (typeof child === 'number') {\n this.element.appendChild(document.createTextNode(String(child)));\n }\n };\n \n children.forEach(processChild);\n return this;\n }\n\n /**\n * Set innerHTML (use with caution)\n */\n html(html: string): this {\n this.element.innerHTML = html;\n return this;\n }\n\n /**\n * Set text content\n */\n text(text: string): this {\n this.element.textContent = text;\n return this;\n }\n\n /**\n * Set an attribute\n */\n attr(name: string, value: string): this {\n this.element.setAttribute(name, value);\n return this;\n }\n\n /**\n * Set data attribute\n */\n data(key: string, value: string): this {\n this.element.dataset[key] = value;\n return this;\n }\n\n /**\n * Conditionally apply modifications\n */\n if(condition: boolean, callback: (builder: DOMBuilder<T>) => void): this {\n if (condition) {\n callback(this);\n }\n return this;\n }\n\n /**\n * Get the built element\n */\n build(): T {\n return this.element;\n }\n\n /**\n * Mount the element to a parent\n */\n mountTo(parent: Element | DOMBuilder): T {\n const parentEl = parent instanceof DOMBuilder ? parent.build() : parent;\n parentEl.appendChild(this.element);\n return this.element;\n }\n}\n\n/**\n * Functional helper for creating elements (alternative to class-based approach)\n */\nexport function createElement<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n props?: ElementProps<HTMLElementTagNameMap[K]>,\n ...children: Child[]\n): HTMLElementTagNameMap[K] {\n return DOMBuilder.create(tag, props, ...children).build();\n}\n\n/**\n * Create a document fragment with multiple elements\n */\nexport function createFragment(...children: Child[]): DocumentFragment {\n const fragment = document.createDocumentFragment();\n const temp = DOMBuilder.create('div').append(...children).build();\n \n while (temp.firstChild) {\n fragment.appendChild(temp.firstChild);\n }\n \n return fragment;\n}","import { ThemeManager } from \"./ThemeManager\";\nimport { COMPILED_CSS } from \"../styles/compiled\";\nimport { Z_INDEX } from \"../types/config\";\nimport { DOMBuilder } from \"../utils/DOMBuilder\";\n\n/**\n * Creates the host element and Shadow DOM root.\n * Injects compiled Tailwind CSS + theme CSS variables into the shadow root.\n */\nexport class ShadowContainer {\n readonly host: HTMLElement;\n readonly root: ShadowRoot;\n\n constructor(theme: ThemeManager) {\n // Create host element using DOMBuilder\n this.host = DOMBuilder.div({\n id: \"nimbus-chat-root\",\n style: {\n all: \"initial\",\n position: \"fixed\",\n zIndex: String(Z_INDEX.WIDGET_ROOT)\n }\n }).mountTo(document.body);\n\n // Attach shadow root\n this.root = this.host.attachShadow({ mode: \"open\" });\n\n // Inject compiled Tailwind CSS (imported as string from build step)\n this.injectCSS(\n COMPILED_CSS ||\n \"*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\"\n );\n\n // Inject theme CSS custom properties\n this.injectCSS(theme.getCSSVariables());\n\n // Inject base component styles that use CSS variables\n this.injectCSS(this.getComponentCSS());\n }\n\n private injectCSS(css: string): void {\n const style = DOMBuilder.create('style')\n .text(css)\n .build();\n this.root.appendChild(style);\n }\n\n /**\n * Component-level styles that reference CSS custom properties for theming.\n */\n private getComponentCSS(): string {\n return `\n :host {\n font-family: var(--nimbus-font, inherit);\n font-size: 14px;\n line-height: 1.5;\n color: #1e293b;\n }\n\n .nimbus-header {\n background-color: var(--nimbus-header-bg, var(--nimbus-theme-primary, #2563eb));\n color: var(--nimbus-theme-secondary, #ffffff);\n }\n\n .nimbus-header button svg {\n color: var(--nimbus-header-close, var(--nimbus-theme-secondary, #ffffff));\n stroke: var(--nimbus-header-close, var(--nimbus-theme-secondary, #ffffff));\n }\n\n .nimbus-sidepanel {\n background-color: var(--nimbus-bg-color, #ffffff);\n }\n\n .nimbus-bubble {\n background-color: var(--nimbus-theme-primary, #2563eb);\n }\n\n .nimbus-bubble svg {\n color: var(--nimbus-theme-secondary, #ffffff);\n stroke: var(--nimbus-theme-secondary, #ffffff);\n }\n\n .nimbus-send-btn {\n background-color: var(--nimbus-theme-primary, #2563eb);\n }\n\n .nimbus-send-btn:hover {\n filter: brightness(0.9);\n }\n\n .nimbus-send-btn svg {\n color: var(--nimbus-theme-secondary, #ffffff);\n stroke: var(--nimbus-theme-secondary, #ffffff);\n }\n\n .nimbus-msg-user {\n background-color: var(--nimbus-user-msg-bg, #2563eb);\n color: var(--nimbus-user-msg-text, #fff);\n font-family: var(--nimbus-user-msg-font, inherit);\n font-size: var(--nimbus-user-msg-size, inherit);\n }\n\n .nimbus-msg-bot {\n background-color: var(--nimbus-bot-msg-bg, #f1f5f9);\n color: var(--nimbus-bot-msg-text, #1e293b);\n font-family: var(--nimbus-bot-msg-font, inherit);\n font-size: var(--nimbus-bot-msg-size, inherit);\n }\n\n .nimbus-input-field {\n font-family: var(--nimbus-input-font, inherit);\n background-color: var(--nimbus-input-bg, #f8fafc);\n }\n\n .nimbus-input-container {\n background-color: var(--nimbus-input-container-bg, #ffffff);\n }\n\n .nimbus-send-btn-square {\n border-radius: 0.375rem;\n }\n\n .nimbus-send-btn-text {\n display: flex;\n align-items: center;\n gap: 6px;\n color: var(--nimbus-theme-secondary, #fff);\n font-size: 13px;\n font-weight: 600;\n padding: 8px 16px;\n }\n\n /* Auto-expanding textarea (no manual resize) */\n .nimbus-textarea {\n resize: none;\n overflow-y: hidden;\n min-height: 45px;\n padding-right: 52px; /* room for send button inside */\n line-height: 1.5;\n transition: height 0.1s ease;\n }\n\n /* Scrollable once it hits max height */\n .nimbus-textarea.nimbus-textarea-scroll {\n overflow-y: auto !important;\n }\n\n .nimbus-textarea::-webkit-scrollbar {\n width: 4px;\n }\n .nimbus-textarea::-webkit-scrollbar-track {\n background: transparent;\n margin-right: 4px;\n }\n .nimbus-textarea::-webkit-scrollbar-thumb {\n background: #cbd5e1;\n border-radius: 2px;\n }\n\n /* Textarea wrapper for send btn */\n .nimbus-textarea-wrap {\n position: relative;\n }\n\n /* Send button positioned inside textarea (bottom-right) */\n .nimbus-send-inside {\n position: absolute;\n bottom: 9px;\n right: 5px;\n height: 35px;\n width: 35px;\n }\n\n .nimbus-chat-body {\n background-color: var(--nimbus-bg-color, #ffffff);\n background-image: var(--nimbus-bg-image, none);\n background-size: cover;\n background-position: center;\n }\n\n /* Scrollbar */\n .nimbus-message-list::-webkit-scrollbar {\n width: 6px;\n }\n .nimbus-message-list::-webkit-scrollbar-track {\n background: transparent;\n }\n .nimbus-message-list::-webkit-scrollbar-thumb {\n background: #cbd5e1;\n border-radius: 3px;\n }\n\n /* Typing indicator animation */\n @keyframes nimbus-bounce {\n 0%, 80%, 100% { transform: scale(0); }\n 40% { transform: scale(1); }\n }\n .nimbus-typing-dot {\n animation: nimbus-bounce 1.4s infinite ease-in-out both;\n }\n .nimbus-typing-dot:nth-child(1) { animation-delay: -0.32s; }\n .nimbus-typing-dot:nth-child(2) { animation-delay: -0.16s; }\n\n /* Loader spin */\n @keyframes nimbus-spin {\n to { transform: rotate(360deg); }\n }\n .nimbus-spin {\n animation: nimbus-spin 1s linear infinite;\n }\n\n /* Bubble pulse */\n @keyframes nimbus-pulse {\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(1.05); }\n }\n\n /* Transition helpers */\n .nimbus-fade-in {\n animation: nimbus-fade 0.2s ease-out;\n }\n @keyframes nimbus-fade {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n `;\n }\n\n /**\n * Remove the widget from the DOM entirely.\n */\n destroy(): void {\n this.host.remove();\n }\n}\n","import type { ResolvedConfig } from \"../types/config\";\n\n/**\n * Generates and manages CSS custom properties for runtime theming.\n */\nexport class ThemeManager {\n private config: ResolvedConfig;\n\n constructor(config: ResolvedConfig) {\n this.config = config;\n }\n\n /**\n * Returns a <style> block that sets CSS custom properties on :host.\n */\n getCSSVariables(): string {\n const c = this.config;\n\n // Detect if background is a URL (image) or a plain color\n const bg = c.style.background;\n const isImage = bg.startsWith(\"url(\") || bg.startsWith(\"http\") || bg.startsWith(\"/\");\n\n const vars = [\n ` --nimbus-theme-primary: ${c.theme.primary};`,\n ` --nimbus-theme-secondary: ${c.theme.secondary};`,\n ` --nimbus-bg-color: ${isImage ? \"transparent\" : bg};`,\n ` --nimbus-bg-image: ${isImage ? (bg.startsWith(\"url(\") ? bg : `url(${bg})`) : \"none\"};`,\n ` --nimbus-user-msg-bg: ${c.userMessage.background};`,\n ` --nimbus-user-msg-text: ${c.userMessage.text.color};`,\n ` --nimbus-bot-msg-bg: ${c.botMessage.background};`,\n ` --nimbus-bot-msg-text: ${c.botMessage.text.color};`,\n c.style.font ? ` --nimbus-font: ${c.style.font};` : null,\n c.userMessage.text.font ? ` --nimbus-user-msg-font: ${c.userMessage.text.font};` : null,\n c.userMessage.text.size ? ` --nimbus-user-msg-size: ${c.userMessage.text.size}px;` : null,\n c.botMessage.text.font ? ` --nimbus-bot-msg-font: ${c.botMessage.text.font};` : null,\n c.botMessage.text.size ? ` --nimbus-bot-msg-size: ${c.botMessage.text.size}px;` : null,\n c.input.text?.font ? ` --nimbus-input-font: ${c.input.text.font};` : null,\n c.input.background?.primary ? ` --nimbus-input-bg: ${c.input.background.primary};` : null,\n c.input.background?.secondary ? ` --nimbus-input-container-bg: ${c.input.background.secondary};` : null,\n ].filter(Boolean).join(\"\\n\");\n\n return `:host {\\n${vars}\\n}`;\n }\n}\n","import { icons } from 'lucide';\nimport { createLogger } from '../utils/logger';\nimport { DOMBuilder } from '../utils/DOMBuilder';\n\nconst logger = createLogger('Icons');\n\ntype SVGAttributes = Record<string, string | number>;\ntype SVGElementData = [string, SVGAttributes, ...SVGElementData[]];\ntype LucideIconData = SVGElementData[];\n\nconst iconCache = new Map<string, LucideIconData>();\n\nfunction toPascalCase(str: string): string {\n return str\n .split('-')\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n\nfunction lookupIcon(name: string): LucideIconData | undefined {\n if (iconCache.has(name)) {\n return iconCache.get(name);\n }\n\n const pascalName = toPascalCase(name);\n const data = (icons as Record<string, LucideIconData>)[pascalName];\n\n if (data) {\n iconCache.set(name, data);\n }\n\n return data;\n}\n\nfunction isUrl(str: string): boolean {\n return str.startsWith('http://') || str.startsWith('https://');\n}\n\nexport function renderIcon(\n iconSource: string,\n size: number,\n color: string = \"currentColor\",\n className?: string\n): SVGSVGElement | HTMLImageElement {\n if (isUrl(iconSource)) {\n return renderImageIcon(iconSource, size, className);\n } else {\n return renderLucideIcon(iconSource, size, color, className);\n }\n}\n\nfunction renderImageIcon(\n src: string,\n size: number,\n className?: string\n): HTMLImageElement {\n return DOMBuilder.img({\n src,\n width: size,\n height: size,\n className: className || '',\n style: { display: 'block' }\n }).build() as HTMLImageElement;\n}\n\nfunction renderLucideIcon(\n name: string,\n size: number,\n color: string,\n className?: string\n): SVGSVGElement {\n const iconData = lookupIcon(name);\n\n if (!iconData) {\n logger.warn(`Unknown icon: ${name}`);\n const empty = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n return empty;\n }\n\n const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 24 24\");\n svg.setAttribute(\"width\", String(size));\n svg.setAttribute(\"height\", String(size));\n svg.setAttribute(\"fill\", \"none\");\n svg.setAttribute(\"stroke\", color);\n svg.setAttribute(\"stroke-width\", \"2\");\n svg.setAttribute(\"stroke-linecap\", \"round\");\n svg.setAttribute(\"stroke-linejoin\", \"round\");\n if (className) svg.setAttribute(\"class\", className);\n\n function createElement(data: SVGElementData): Element {\n const [tagName, attributes, ...children] = data;\n const element = document.createElementNS(\"http://www.w3.org/2000/svg\", tagName);\n\n if (attributes && typeof attributes === 'object') {\n Object.entries(attributes).forEach(([key, value]) => {\n element.setAttribute(key, String(value));\n });\n }\n\n children.forEach(child => {\n if (Array.isArray(child)) {\n element.appendChild(createElement(child));\n }\n });\n\n return element;\n }\n\n iconData.forEach(elementData => {\n svg.appendChild(createElement(elementData));\n });\n\n return svg;\n}\n","import type { IconConfig } from \"../types/config\";\nimport { ICON_NAMES, UI_FACTORS } from \"../types/config\";\nimport { renderIcon } from \"../icons/lucide\";\nimport { DOMBuilder } from \"./DOMBuilder\";\n\nexport class IconRenderer {\n /**\n * Creates a wrapped icon element with proper sizing and styling.\n * \n * @param icon - Icon configuration (can be null/undefined)\n * @param defaultIcon - Default icon name to use if no icon.img provided\n * @param defaultSize - Default size in pixels\n * @param options - Additional styling options\n * @returns HTMLElement wrapper containing the icon\n */\n static create(\n icon: IconConfig | null | undefined,\n defaultIcon: string = ICON_NAMES.MESSAGE_CIRCLE,\n defaultSize: number = 24,\n options: {\n borderRadius?: string;\n marginBottom?: string;\n objectFit?: \"contain\" | \"cover\";\n sizeFactor?: number; // Multiplier for icon size within wrapper (0.65-0.75)\n } = {}\n ): HTMLElement {\n const w = icon?.size?.width ?? defaultSize;\n const h = icon?.size?.height ?? defaultSize;\n \n // Build wrapper styles\n const wrapperStyles: Partial<CSSStyleDeclaration> = {\n alignItems: \"center\",\n justifySelf: \"anchor-center\",\n width: `${w}px`,\n height: `${h}px`,\n overflow: \"hidden\",\n flexShrink: \"0\",\n };\n \n // Add optional styles\n if (options.borderRadius) {\n wrapperStyles.borderRadius = options.borderRadius;\n }\n if (options.marginBottom) {\n wrapperStyles.marginBottom = options.marginBottom;\n }\n \n // Only render icon if we have an image or default\n const iconSource = icon?.img ?? defaultIcon;\n let iconElement: HTMLElement | null = null;\n \n if (iconSource) {\n const sizeFactor = options.sizeFactor ?? UI_FACTORS.DEFAULT_ICON_SIZE_FACTOR;\n const iconSize = Math.min(w, h) * sizeFactor;\n const iconColor = icon?.color ?? \"currentColor\";\n const rawIcon = renderIcon(iconSource, iconSize, iconColor);\n \n // Apply consistent icon styling\n const objectFit = options.objectFit ?? \"contain\";\n rawIcon.style.cssText = `width: 100%; height: 100%; object-fit: ${objectFit}; border: none;`;\n \n iconElement = rawIcon as HTMLElement;\n }\n \n // Create wrapper using DOMBuilder\n return DOMBuilder.div({ style: wrapperStyles }, iconElement).build();\n }\n\n /**\n * Creates a simple icon without wrapper (used for buttons like close, new chat).\n * \n * @param iconName - Lucide icon name\n * @param size - Size in pixels\n * @param color - Icon color\n * @returns SVG or IMG element\n */\n static createSimple(\n iconName: string,\n size: number = 18,\n color: string = \"currentColor\"\n ): SVGSVGElement | HTMLImageElement {\n return renderIcon(iconName, size, color);\n }\n \n /**\n * Creates an exception icon with tooltip for error messages.\n * \n * @param exceptionConfig - Exception icon configuration\n * @param reason - Error reason to show in tooltip\n * @param isUserMessage - Whether this is a user message (affects positioning)\n * @param className - Optional CSS class name\n * @returns HTMLElement wrapper containing the exception icon with tooltip\n */\n static createExceptionIcon(\n exceptionConfig: IconConfig | undefined,\n reason: string,\n isUserMessage: boolean,\n className?: string\n ): HTMLElement | null {\n if (!exceptionConfig) return null;\n \n const iconName = exceptionConfig.img || \"alert-triangle\";\n const iconSize = exceptionConfig.size?.width || 20;\n const iconColor = exceptionConfig.color;\n \n const exceptionEl = IconRenderer.createSimple(\n iconName,\n iconSize,\n iconColor\n );\n \n if (!exceptionEl) return null;\n \n // Wrap icon in a span for better tooltip display\n const wrapper = DOMBuilder.span({\n title: reason,\n className: className,\n style: {\n marginLeft: isUserMessage ? \"0\" : \"4px\",\n marginRight: isUserMessage ? \"4px\" : \"0\",\n display: \"inline-flex\",\n alignItems: \"center\"\n }\n }, exceptionEl).build();\n \n return wrapper;\n }\n}","import type { TextConfig } from \"../types/config\";\n\nexport class StyleUtils {\n /**\n * Applies text styling from TextConfig to an HTML element.\n * \n * @param element - The HTML element to style\n * @param textConfig - Text configuration with color, font, size options\n * @param defaults - Optional default values to apply if not specified in config\n */\n static applyTextStyle(\n element: HTMLElement, \n textConfig: TextConfig | undefined,\n defaults?: Partial<TextConfig>\n ): void {\n if (!textConfig && !defaults) return;\n\n const color = textConfig?.color ?? defaults?.color;\n if (color) element.style.color = color;\n\n const font = textConfig?.font ?? defaults?.font;\n if (font) element.style.fontFamily = font;\n\n const size = textConfig?.size ?? defaults?.size;\n if (size) element.style.fontSize = `${size}px`;\n }\n\n /**\n * Builds a CSS class string from an array of class names, filtering out falsy values.\n * \n * @param classes - Array of class names or conditional expressions\n * @returns Space-separated class string\n */\n static buildClassNames(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(\" \");\n }\n\n /**\n * Applies multiple CSS properties to an element via style object.\n * \n * @param element - The HTML element to style\n * @param styles - Object with CSS property names as keys\n */\n static applyStyles(element: HTMLElement, styles: Record<string, string>): void {\n Object.entries(styles).forEach(([property, value]) => {\n if (value) {\n element.style.setProperty(property, value);\n }\n });\n }\n\n /**\n * Creates a CSS variable name with the nimbus prefix.\n * \n * @param name - Variable name without prefix\n * @returns CSS variable string with --nimbus- prefix\n */\n static cssVar(name: string): string {\n return `--nimbus-${name}`;\n }\n\n /**\n * Sets CSS variables on an element.\n * \n * @param element - The element to set variables on\n * @param variables - Object with variable names (without --nimbus- prefix) as keys\n */\n static setCssVariables(element: HTMLElement, variables: Record<string, string>): void {\n Object.entries(variables).forEach(([name, value]) => {\n if (value) {\n element.style.setProperty(StyleUtils.cssVar(name), value);\n }\n });\n }\n}","import { DOMBuilder } from \"./DOMBuilder\";\nimport { IconRenderer } from \"./IconRenderer\";\nimport { ICON_SIZES } from \"../types/config\";\n\nexport type ButtonVariant = 'header' | 'action' | 'remove' | 'primary' | 'secondary';\n\nexport interface ButtonConfig {\n icon?: string;\n iconSize?: number;\n title?: string;\n className?: string;\n onClick: () => void;\n}\n\n/**\n * Utility for creating consistent buttons throughout the application\n */\nexport class ButtonBuilder {\n private static readonly VARIANTS: Record<ButtonVariant, string> = {\n header: \"p-1 rounded-lg hover:bg-white/20 transition-colors cursor-pointer\",\n action: \"px-4 py-2 rounded transition-opacity hover:opacity-80\",\n remove: \"w-5 h-5 rounded-full bg-slate-300 hover:bg-red-500 text-slate-600 hover:text-white transition-colors\",\n primary: \"px-3 py-1 bg-blue-500 text-white rounded text-sm hover:bg-blue-600\",\n secondary: \"block w-full text-left p-2 border rounded hover:bg-gray-50\"\n };\n\n /**\n * Create an icon button with specified variant styling\n */\n static createIconButton(\n icon: string,\n config: ButtonConfig,\n variant: ButtonVariant = 'action'\n ): HTMLButtonElement {\n const iconElement = IconRenderer.createSimple(\n icon, \n config.iconSize || ICON_SIZES.BUTTON_MEDIUM\n );\n\n const className = [\n this.VARIANTS[variant],\n config.className || \"\"\n ].filter(Boolean).join(\" \");\n\n return DOMBuilder.button({\n className,\n title: config.title,\n type: \"button\",\n on: { click: config.onClick }\n }, iconElement).build() as HTMLButtonElement;\n }\n\n /**\n * Create a text button with specified variant styling\n */\n static createTextButton(\n text: string,\n config: ButtonConfig,\n variant: ButtonVariant = 'action'\n ): HTMLButtonElement {\n const className = [\n this.VARIANTS[variant],\n config.className || \"\"\n ].filter(Boolean).join(\" \");\n\n return DOMBuilder.button({\n className,\n title: config.title,\n type: \"button\",\n on: { click: config.onClick }\n }).text(text).build() as HTMLButtonElement;\n }\n\n /**\n * Create a button with both icon and text\n */\n static createIconTextButton(\n icon: string,\n text: string,\n config: ButtonConfig,\n variant: ButtonVariant = 'action'\n ): HTMLButtonElement {\n const iconElement = IconRenderer.createSimple(\n icon, \n config.iconSize || ICON_SIZES.BUTTON_SMALL\n );\n\n const className = [\n this.VARIANTS[variant],\n \"flex items-center gap-2\",\n config.className || \"\"\n ].filter(Boolean).join(\" \");\n\n return DOMBuilder.button({\n className,\n title: config.title,\n type: \"button\",\n on: { click: config.onClick }\n }, iconElement, DOMBuilder.span().text(text)).build() as HTMLButtonElement;\n }\n\n /**\n * Create a close/remove button (common pattern)\n */\n static createCloseButton(onClick: () => void, title: string = \"Close\"): HTMLButtonElement {\n return this.createTextButton(\"×\", {\n onClick,\n title,\n className: \"absolute top-2 right-2 z-10 flex items-center justify-center\"\n }, 'remove');\n }\n}","import { createLogger } from './logger';\n\n/**\n * Component lifecycle hooks\n */\nexport interface ComponentHooks {\n onMount?: () => void;\n onUnmount?: () => void;\n onDestroy?: () => void;\n onUpdate?: (data: unknown) => void;\n}\n\n/**\n * Base component interface\n */\nexport interface Component {\n element: HTMLElement;\n mount(parent: HTMLElement): void;\n unmount(): void;\n destroy(): void;\n update?(data: unknown): void;\n}\n\n/**\n * Abstract base class for all components\n */\nexport abstract class BaseComponent implements Component {\n protected _element: HTMLElement | null = null;\n protected hooks: ComponentHooks;\n protected logger: ReturnType<typeof createLogger>;\n protected destroyed = false;\n protected mounted = false;\n protected eventListeners: Array<{\n element: EventTarget;\n event: string;\n handler: EventListener;\n }> = [];\n\n constructor(hooks: ComponentHooks = {}) {\n this.hooks = hooks;\n this.logger = createLogger(this.constructor.name);\n }\n\n get element(): HTMLElement {\n if (!this._element) {\n this._element = this.render();\n this.afterRender();\n }\n return this._element;\n }\n\n /**\n * Abstract method to render the component\n */\n protected abstract render(): HTMLElement;\n\n /**\n * Called after render, useful for setting up event listeners\n */\n protected afterRender(): void {\n // Override in subclasses\n }\n\n /**\n * Add event listener with automatic cleanup\n */\n protected addEventListener(\n element: EventTarget,\n event: string,\n handler: EventListener\n ): void {\n element.addEventListener(event, handler);\n this.eventListeners.push({ element, event, handler });\n }\n\n /**\n * Mount component to parent\n */\n mount(parent: HTMLElement): void {\n if (this.mounted || this.destroyed) return;\n \n parent.appendChild(this.element);\n this.mounted = true;\n this.hooks.onMount?.();\n }\n\n /**\n * Unmount component from DOM\n */\n unmount(): void {\n if (!this.mounted || this.destroyed) return;\n \n this.element.remove();\n this.mounted = false;\n this.hooks.onUnmount?.();\n }\n\n /**\n * Update component with new data\n */\n update(data: unknown): void {\n if (this.destroyed) return;\n this.hooks.onUpdate?.(data);\n }\n\n /**\n * Destroy component and cleanup\n */\n destroy(): void {\n if (this.destroyed) return;\n \n this.unmount();\n \n // Remove all event listeners\n this.eventListeners.forEach(({ element, event, handler }) => {\n element.removeEventListener(event, handler);\n });\n this.eventListeners = [];\n \n this.hooks.onDestroy?.();\n this.destroyed = true;\n this._element = null;\n }\n}\n\n","import type { ConnectionState } from \"../types/message\";\nimport type { HeaderConfig, TypingIndicatorConfig } from \"../types/config\";\nimport { IconRenderer } from \"../utils/IconRenderer\";\nimport { StyleUtils } from \"../utils/StyleUtils\";\nimport { ButtonBuilder } from \"../utils/ButtonBuilder\";\nimport { ICON_NAMES, ICON_SIZES, UI_TEXT } from \"../types/config\";\nimport { DOMBuilder } from \"../utils/DOMBuilder\";\nimport { BaseComponent } from \"../utils/ComponentFactory\";\n\nexport class Header extends BaseComponent {\n readonly el: HTMLElement;\n private statusDot!: HTMLElement;\n private statusText?: HTMLElement;\n private currentConnectionState: ConnectionState = \"idle\";\n private isTyping: boolean = false;\n private headerConfig: HeaderConfig;\n private onClose: () => void;\n private allowNewChat: boolean;\n private onNewChat?: () => void;\n\n constructor(headerConfig: HeaderConfig, onClose: () => void, allowNewChat: boolean, onNewChat?: () => void) {\n super();\n this.headerConfig = headerConfig;\n this.onClose = onClose;\n this.allowNewChat = allowNewChat;\n this.onNewChat = onNewChat;\n this.el = this.element;\n }\n\n protected render(): HTMLElement {\n const headerColor = this.headerConfig.color;\n \n // Build left section components\n const leftChildren = [];\n \n // Add icon if configured\n const iconCfg = this.headerConfig.icon;\n if (iconCfg) {\n leftChildren.push(\n IconRenderer.create(iconCfg, ICON_NAMES.MESSAGE_CIRCLE, ICON_SIZES.AVATAR)\n );\n }\n\n // Add title text if configured\n const textCfg = this.headerConfig.text;\n const showText = textCfg?.display !== false;\n if (showText) {\n const titleEl = DOMBuilder.span({\n className: \"font-semibold truncate\"\n }).text(textCfg?.value ?? \"Nimbus\").build();\n \n StyleUtils.applyTextStyle(titleEl, textCfg, { size: 14 });\n leftChildren.push(titleEl);\n }\n\n // Create status dot\n this.statusDot = DOMBuilder.span({\n className: \"inline-block w-2 h-2 rounded-full bg-green-400 shrink-0\",\n title: UI_TEXT.CONNECTED\n }).build();\n leftChildren.push(this.statusDot);\n \n // Create status text (Online/Typing)\n this.statusText = DOMBuilder.span({\n className: \"text-xs\"\n }).text(\"Online\").build();\n \n // Apply text styling if header text config exists\n if (textCfg) {\n StyleUtils.applyTextStyle(this.statusText, textCfg, { size: 12 });\n }\n leftChildren.push(this.statusText);\n\n // Build left section\n const leftSection = DOMBuilder.div({\n className: \"flex items-center gap-2.5\"\n }, ...leftChildren);\n\n // Build right section components\n const rightChildren = [];\n \n // Add new chat button if allowed\n if (this.allowNewChat && this.onNewChat) {\n rightChildren.push(\n ButtonBuilder.createIconButton(ICON_NAMES.NEW_CHAT, {\n onClick: this.onNewChat,\n title: UI_TEXT.NEW_CHAT,\n iconSize: ICON_SIZES.BUTTON_MEDIUM\n }, 'header')\n );\n }\n\n // Add close button\n rightChildren.push(\n ButtonBuilder.createIconButton(ICON_NAMES.CLOSE, {\n onClick: this.onClose,\n title: \"Close\",\n iconSize: ICON_SIZES.BUTTON_MEDIUM\n }, 'header')\n );\n\n // Build right section\n const rightSection = DOMBuilder.div({\n className: \"flex items-center gap-1\"\n }, ...rightChildren);\n\n // Build main header container\n const header = DOMBuilder.div({\n className: \"nimbus-header flex items-center justify-between px-4 py-3 rounded-t-xl select-none\",\n style: headerColor?.primary ? { backgroundColor: headerColor.primary } : undefined\n }, leftSection, rightSection).build();\n\n // Set secondary color property if configured\n if (headerColor?.secondary) {\n header.style.setProperty(\"--nimbus-header-close\", headerColor.secondary);\n }\n\n return header;\n }\n\n\n setConnectionState(state: ConnectionState): void {\n this.currentConnectionState = state;\n \n // Update status dot\n this.statusDot.className = `inline-block w-2 h-2 rounded-full shrink-0 ${this.getStatusDotColor()}`;\n this.statusDot.title = state.charAt(0).toUpperCase() + state.slice(1);\n \n // Update status text if not typing\n if (!this.isTyping && this.statusText) {\n this.statusText.textContent = this.getStatusText();\n }\n }\n\n\n showTypingIndicator(config: TypingIndicatorConfig): void {\n this.isTyping = true;\n \n if (this.statusText && config.title?.value) {\n this.statusText.textContent = config.title.value;\n if (config.title.text) {\n StyleUtils.applyTextStyle(this.statusText, config.title.text, { size: 12 });\n }\n }\n }\n\n hideTypingIndicator(): void {\n this.isTyping = false;\n \n if (this.statusText) {\n this.statusText.textContent = this.getStatusText();\n StyleUtils.applyTextStyle(this.statusText, this.headerConfig.text, { size: 12 });\n }\n }\n\n\n private getStatusText(): string {\n switch (this.currentConnectionState) {\n case \"connected\": return \"Online\";\n case \"connecting\": return \"Connecting...\";\n case \"disconnected\": return \"Offline\";\n default: return \"Offline\";\n }\n }\n\n private getStatusDotColor(): string {\n const colors: Record<ConnectionState, string> = {\n idle: \"bg-gray-400\",\n connected: \"bg-green-400\",\n connecting: \"bg-yellow-400\",\n disconnected: \"bg-red-400\",\n };\n return colors[this.currentConnectionState];\n }\n}\n","/**\n * Shared utilities for file operations\n */\nexport class FileUtils {\n /**\n * Format file size in bytes to human-readable format\n * @param bytes - Size in bytes\n * @param shortFormat - Use short format (B, KB) vs long format (Bytes, KB)\n */\n static formatFileSize(bytes: number, shortFormat = false): string {\n if (bytes === 0) return shortFormat ? '0 B' : '0 Bytes';\n \n const k = 1024;\n const sizes = shortFormat ? ['B', 'KB', 'MB', 'GB'] : ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n \n return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];\n }\n\n static fileToBase64(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n if (typeof reader.result === \"string\") {\n const base64 = reader.result.split(\",\")[1];\n resolve(base64);\n } else {\n reject(new Error(\"Failed to read file as base64\"));\n }\n };\n reader.onerror = reject;\n reader.readAsDataURL(file);\n });\n }\n}","import type { ChatMessage, FileContent } from \"../types/message\";\nimport type { IconConfig } from \"../types/config\";\nimport { IconRenderer } from \"../utils/IconRenderer\";\nimport { FileUtils } from \"../utils/FileUtils\";\nimport { ICON_NAMES, ICON_SIZES, UI_TEXT, UI_FACTORS, BORDER_RADIUS, SPACING } from \"../types/config\";\nimport { DOMBuilder } from \"../utils/DOMBuilder\";\nimport { BaseComponent } from \"../utils/ComponentFactory\";\n\nexport interface MessageBubbleOptions {\n /** Avatar icon configuration. Pass null to hide. Omit to not show. */\n icon?: IconConfig | null;\n /** Width of the message bubble (e.g. \"90%\", \"300px\"). Default: \"70%\" */\n width?: string;\n}\n\n/**\n * Renders a single chat message bubble with optional avatar icon.\n */\nexport class MessageBubble extends BaseComponent {\n readonly el: HTMLElement;\n private message: ChatMessage;\n private options?: MessageBubbleOptions;\n\n constructor(message: ChatMessage, options?: MessageBubbleOptions) {\n super();\n this.message = message;\n this.options = options;\n this.el = this.element;\n }\n\n protected render(): HTMLElement {\n const isUser = this.message.direction === \"INBOUND\";\n const bubbleWidth = this.options?.width ?? `${UI_FACTORS.DEFAULT_BUBBLE_MAX_WIDTH}px`;\n\n // Build the row with avatar and bubble\n const iconCfg = this.options?.icon;\n const iconEl = iconCfg ? IconRenderer.create(\n iconCfg, \n ICON_NAMES.MESSAGE_CIRCLE, \n ICON_SIZES.AVATAR,\n { \n borderRadius: BORDER_RADIUS.AVATAR, \n marginBottom: `${SPACING.AVATAR_MARGIN}px`, \n objectFit: \"cover\",\n sizeFactor: UI_FACTORS.MESSAGE_AVATAR_SIZE_FACTOR\n }\n ) : null;\n\n // Create bubble with time\n const bubble = DOMBuilder.div({\n className: [\n isUser ? \"nimbus-msg-user\" : \"nimbus-msg-bot\",\n \"px-3.5 py-2.5 text-sm leading-relaxed\",\n isUser ? \"rounded-2xl rounded-br-md\" : \"rounded-2xl rounded-bl-md\",\n \"break-words whitespace-pre-wrap overflow-wrap-anywhere\",\n ].join(\" \"),\n style: {\n maxWidth: \"100%\",\n wordBreak: \"break-word\"\n }\n },\n this.renderContent(this.message),\n DOMBuilder.div({\n className: \"text-[10px] mt-1 opacity-50 select-none\"\n }).text(this.formatTime(new Date(this.message.created_at * 1000)))\n );\n\n // Create row with conditional avatar placement\n const row = DOMBuilder.div({\n className: `flex items-end gap-2 ${isUser ? \"flex-row-reverse\" : \"flex-row\"}`,\n style: { maxWidth: bubbleWidth }\n });\n\n if (iconEl) row.append(iconEl);\n row.append(bubble);\n\n // Create main container\n return DOMBuilder.div({\n className: `flex ${isUser ? \"justify-end\" : \"justify-start\"} nimbus-fade-in`\n }, row).build();\n }\n\n private renderContent(message: ChatMessage): Node {\n switch (message.message_type) {\n case \"json\":\n return this.renderJson(message.content);\n case \"file\":\n // Handle SHARED_FILE content - use message metadata directly\n if (message.content === \"SHARED_FILE\") {\n return this.renderSharedFile(message);\n }\n \n // For AI messages with file metadata, construct FileContent from message\n if (message.direction === \"OUTBOUND\" && message.filename) {\n const fileData: FileContent = {\n content: typeof message.content === 'string' ? message.content : JSON.stringify(message.content),\n filename: message.filename ?? undefined,\n filetype: message.filetype ?? undefined,\n filesize: message.filesize ?? undefined,\n description: message.description ?? undefined\n };\n return this.renderFile(fileData);\n }\n \n // For file messages, try to parse if it's a string\n let fileContent = message.content;\n if (typeof fileContent === 'string') {\n const parsed = this.tryParseJson(fileContent);\n if (parsed) {\n fileContent = parsed;\n }\n }\n return this.renderFile(fileContent);\n case \"text\":\n default:\n return this.renderText(typeof message.content === 'string' ? message.content : JSON.stringify(message.content));\n }\n }\n\n private renderText(content: string): Node {\n return DOMBuilder.span().text(content).build();\n }\n\n private renderJson(content: string | object): Node {\n let text: string;\n if (typeof content === 'object') {\n text = JSON.stringify(content, null, 2);\n } else {\n try {\n text = JSON.stringify(JSON.parse(content), null, 2);\n } catch {\n text = content;\n }\n }\n return DOMBuilder.create('pre', {\n className: \"text-xs font-mono bg-black/10 rounded p-2 overflow-x-auto\"\n }).text(text).build();\n }\n\n private renderSharedFile(message: ChatMessage): Node {\n const container = DOMBuilder.div({ className: \"space-y-2\" });\n\n // Display description FIRST if provided (like a caption)\n if (message.description && message.description.trim()) {\n container.append(\n DOMBuilder.div({ className: \"text-sm mb-2\" }).text(message.description)\n );\n }\n \n // Display file info\n container.append(this.createFileInfo(message.filename, message.filesize));\n \n // Show \"Shared a file\" message since no content is available\n container.append(this.createSharedFileIndicator());\n\n return container.build();\n }\n\n private renderFile(content: string | object): Node {\n if (typeof content === 'object') {\n return this.renderFileObject(content as FileContent);\n } else if (typeof content === 'string') {\n // Try to parse as JSON first\n const parsed = this.tryParseJson(content);\n if (parsed) {\n return this.renderFileObject(parsed);\n }\n \n // Handle raw base64 content (images or data URLs)\n return this.renderRawContent(content);\n }\n\n // If we can't parse the content, show error\n const container = DOMBuilder.div({ className: \"space-y-2\" });\n container.append(\n DOMBuilder.div({ className: \"text-red-500 text-sm\" }).text(\"Invalid file format\")\n );\n return container.build();\n }\n\n private renderFileObject(fileData: FileContent): Node {\n const container = DOMBuilder.div({ className: \"space-y-2\" });\n \n // Display description FIRST if provided (like a caption)\n if (fileData.description && fileData.description.trim()) {\n container.append(\n DOMBuilder.div({ className: \"text-sm mb-2\" }).text(fileData.description)\n );\n }\n \n // Display file info\n container.append(this.createFileInfo(fileData.filename, fileData.filesize));\n \n // Check if we have actual file content (base64)\n const hasFileContent = fileData.content && fileData.content.trim();\n\n if (hasFileContent) {\n container.append(this.renderFileContent(fileData));\n } else {\n container.append(this.createSharedFileIndicator());\n }\n\n return container.build();\n }\n\n private renderFileContent(fileData: FileContent): HTMLElement {\n const isUser = this.message.direction === \"INBOUND\";\n \n // Display image if it's an image type\n if (fileData.filetype && fileData.filetype.startsWith('image/')) {\n return DOMBuilder.img({\n src: `data:${fileData.filetype};base64,${fileData.content}`,\n className: \"max-w-full rounded-lg max-h-64\",\n alt: fileData.filename || \"Uploaded image\"\n }).build();\n } else {\n // For user files (INBOUND), show \"Shared a file\" instead of download link\n if (isUser) {\n return this.createSharedFileIndicator();\n }\n \n // For AI assistant files (OUTBOUND), show download link\n const downloadLink = DOMBuilder.create('a', {\n href: `data:${fileData.filetype || 'application/octet-stream'};base64,${fileData.content}`,\n download: fileData.filename || 'download',\n className: \"inline-flex items-center gap-2 px-3 py-2 bg-gray-100 rounded hover:bg-gray-200\"\n });\n \n const icon = IconRenderer.create(null, \"file\", ICON_SIZES.BUTTON_SMALL);\n downloadLink.append(icon);\n downloadLink.append(\n DOMBuilder.span().text(UI_TEXT.DOWNLOAD_FILE)\n );\n \n return downloadLink.build();\n }\n }\n\n\n\n private createSharedFileIndicator(): HTMLElement {\n const sharedFileMsg = DOMBuilder.div({\n className: \"inline-flex items-center gap-2 px-3 py-2 bg-gray-100 rounded text-gray-600\"\n });\n \n const icon = IconRenderer.create(null, \"file\", ICON_SIZES.BUTTON_SMALL);\n sharedFileMsg.append(icon);\n sharedFileMsg.append(\n DOMBuilder.span().text(\"Shared a file\")\n );\n \n return sharedFileMsg.build();\n }\n\n private createFileInfo(filename?: string | null, filesize?: number | null): HTMLElement {\n const fileInfo = DOMBuilder.div({ className: \"text-xs opacity-75 mb-1\" });\n fileInfo.append(\n DOMBuilder.span().text(`${filename || 'File'} `),\n DOMBuilder.span({ className: \"text-gray-500\" }).text(\n filesize ? `(${FileUtils.formatFileSize(filesize)})` : ''\n )\n );\n return fileInfo.build();\n }\n\n private tryParseJson(content: string): FileContent | null {\n try {\n return JSON.parse(content) as FileContent;\n } catch {\n return null;\n }\n }\n\n private renderRawContent(content: string): Node {\n const isUser = this.message.direction === \"INBOUND\";\n const container = DOMBuilder.div({ className: \"space-y-2\" });\n\n // Handle data URLs (data:image/png;base64,...)\n if (content.startsWith(\"data:\")) {\n if (content.startsWith(\"data:image\")) {\n container.append(\n DOMBuilder.img({\n src: content,\n className: \"max-w-full rounded-lg max-h-64\",\n alt: \"Shared image\"\n })\n );\n } else {\n // Non-image data URL - show \"Shared a file\" for user files, download link for AI files\n if (isUser) {\n container.append(this.createSharedFileIndicator());\n } else {\n container.append(\n DOMBuilder.create('a', {\n href: content,\n download: \"file\",\n className: \"inline-flex items-center gap-2 px-3 py-2 bg-gray-100 rounded hover:bg-gray-200\"\n }, \n IconRenderer.create(null, \"file\", ICON_SIZES.BUTTON_SMALL),\n DOMBuilder.span().text(UI_TEXT.DOWNLOAD_FILE)\n )\n );\n }\n }\n }\n // Handle raw base64 strings (assume image if it looks like base64)\n else if (content.match(/^[A-Za-z0-9+/=]+$/)) {\n container.append(\n DOMBuilder.img({\n src: `data:image/png;base64,${content}`,\n className: \"max-w-full rounded-lg max-h-64\",\n alt: \"Shared image\"\n })\n );\n }\n // Handle regular URLs - only show download for AI files\n else if (content.startsWith(\"http\")) {\n if (isUser) {\n container.append(this.createSharedFileIndicator());\n } else {\n container.append(\n DOMBuilder.create('a', {\n href: content,\n className: \"underline text-blue-500 hover:text-blue-600\",\n target: \"_blank\",\n rel: \"noopener noreferrer\"\n }).text(UI_TEXT.DOWNLOAD_FILE)\n );\n }\n }\n // Fallback - show as text\n else {\n container.append(\n DOMBuilder.div({ className: \"text-gray-600 text-sm\" }).text(`File: ${content.substring(0, 50)}${content.length > 50 ? '...' : ''}`)\n );\n }\n\n return container.build();\n }\n\n private formatTime(date: Date): string {\n return date.toLocaleTimeString(undefined, {\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n }\n}\n","import type { ChatMessage } from \"../types/message\";\nimport type { IconConfig, WelcomeConfig, TextConfig, TextElement, ShowMoreConfig } from \"../types/config\";\nimport { MessageBubble } from \"./MessageBubble\";\nimport type { MessageBubbleOptions } from \"./MessageBubble\";\nimport { IconRenderer } from \"../utils/IconRenderer\";\nimport { StyleUtils } from \"../utils/StyleUtils\";\nimport { ICON_NAMES, ICON_SIZES, UI_TEXT } from \"../types/config\";\nimport { DOMBuilder } from \"../utils/DOMBuilder\";\nimport { BaseComponent } from \"../utils/ComponentFactory\";\n\n/**\n * Scrollable container for chat messages.\n */\nexport class MessageList extends BaseComponent {\n readonly el: HTMLElement;\n private userIcon?: IconConfig | null;\n private botIcon?: IconConfig | null;\n private showMoreConfig: ShowMoreConfig;\n private primaryColor: string;\n private showMoreButton?: HTMLButtonElement;\n private hasMoreMessages = false;\n private userMessageWidth: string;\n private botMessageWidth: string;\n\n constructor(\n showMoreConfig: ShowMoreConfig,\n primaryColor: string,\n userMessageWidth: string,\n botMessageWidth: string,\n userIcon?: IconConfig | null,\n botIcon?: IconConfig | null\n ) {\n super();\n this.userIcon = userIcon;\n this.botIcon = botIcon;\n this.userMessageWidth = userMessageWidth;\n this.botMessageWidth = botMessageWidth;\n this.showMoreConfig = showMoreConfig;\n this.primaryColor = primaryColor;\n this.el = this.element;\n }\n\n protected render(): HTMLElement {\n return DOMBuilder.div({\n className: \"nimbus-message-list nimbus-chat-body flex-1 overflow-y-auto p-4 space-y-3\"\n }).build();\n }\n\n addMessage(message: ChatMessage): void {\n this.hideWelcome();\n const isUser = message.direction === \"INBOUND\";\n const opts: MessageBubbleOptions = {\n icon: isUser ? this.userIcon : this.botIcon,\n width: isUser ? this.userMessageWidth : this.botMessageWidth,\n };\n const bubble = new MessageBubble(message, opts);\n bubble.el.setAttribute('data-message-id', message.id);\n this.el.appendChild(bubble.el);\n this.scrollToBottom();\n }\n\n clear(): void {\n this.el.replaceChildren();\n this.showMoreButton = undefined;\n }\n\n showWelcome(welcome: WelcomeConfig): void {\n if (welcome.display === false) return;\n\n const titleRow = DOMBuilder.div();\n\n const preTitleValue = this.getWelcomeTextValue(welcome.preTitle);\n if (preTitleValue) {\n const pre = DOMBuilder.span().text(preTitleValue).build();\n this.applyWelcomeTextStyle(pre, welcome.preTitle);\n titleRow.append(pre);\n }\n\n const titleValue = this.getWelcomeTextValue(welcome.title);\n if (titleValue) {\n const title = DOMBuilder.create('strong').text(titleValue).build();\n this.applyWelcomeTextStyle(title, welcome.title);\n titleRow.append(title);\n }\n\n const subtitleValue = this.getWelcomeTextValue(welcome.subtitle);\n const subtitle = subtitleValue ? (() => {\n const sub = DOMBuilder.div({ className: \"mt-1 text-xs\" }).text(subtitleValue).build();\n this.applyWelcomeTextStyle(sub, welcome.subtitle);\n return sub;\n })() : null;\n\n const container = DOMBuilder.div({\n className: \"nimbus-welcome text-center text-sm py-8 select-none\"\n }, titleRow, subtitle).build();\n\n this.el.appendChild(container);\n }\n\n private hideWelcome(): void {\n const welcome = this.el.querySelector(\".nimbus-welcome\");\n if (welcome) welcome.remove();\n }\n\n private getWelcomeTextValue(element: TextElement | TextConfig | undefined): string | undefined {\n return (element as TextElement)?.value ?? (element as TextConfig)?.value;\n }\n\n private applyWelcomeTextStyle(el: HTMLElement, element: TextElement | TextConfig | undefined): void {\n if (!element) return;\n\n if ('text' in element && element.text) {\n StyleUtils.applyTextStyle(el, element.text);\n } else {\n StyleUtils.applyTextStyle(el, element as TextConfig);\n }\n }\n\n showError(message: string): void {\n const className = \"nimbus-system-message text-center p-3 rounded bg-red-50 text-red-700\";\n const container = DOMBuilder.div({ className },\n DOMBuilder.div().text(message)\n ).build();\n this.el.appendChild(container);\n }\n\n showShowMoreButton(onClick: () => void): void {\n this.hideShowMoreButton();\n\n const baseClasses = \"nimbus-show-more mx-auto mb-4 px-4 py-2 rounded-full text-sm transition-colors flex items-center gap-2\";\n const stickyClasses = this.showMoreConfig.sticky ? \"sticky top-0 z-10\" : \"\";\n\n const buttonChildren = [];\n\n if (this.showMoreConfig.icon) {\n const iconEl = IconRenderer.create(\n this.showMoreConfig.icon,\n ICON_NAMES.CHEVRON_DOWN,\n ICON_SIZES.BUTTON_SMALL,\n { borderRadius: \"50%\" }\n );\n if (iconEl) buttonChildren.push(iconEl);\n }\n\n const textSpan = DOMBuilder.span().text(\n this.showMoreConfig.value || UI_TEXT.SHOW_MORE\n ).build();\n\n if (this.showMoreConfig.text) {\n StyleUtils.applyTextStyle(textSpan, this.showMoreConfig.text);\n }\n buttonChildren.push(textSpan);\n\n this.showMoreButton = DOMBuilder.button({\n className: `${baseClasses} ${stickyClasses}`.trim(),\n style: {\n backgroundColor: this.showMoreConfig.background || \"#ffffff\",\n border: `1px solid ${this.primaryColor}`\n },\n type: \"button\",\n on: { click: onClick }\n }, ...buttonChildren).build() as HTMLButtonElement;\n\n this.el.insertBefore(this.showMoreButton, this.el.firstChild);\n }\n\n hideShowMoreButton(): void {\n if (this.showMoreButton) {\n this.showMoreButton.remove();\n this.showMoreButton = undefined;\n }\n }\n\n updateHasMore(hasMore: boolean): void {\n this.hasMoreMessages = hasMore;\n if (!hasMore) {\n this.hideShowMoreButton();\n }\n }\n\n private scrollToBottom(): void {\n requestAnimationFrame(() => {\n this.el.scrollTop = this.el.scrollHeight;\n });\n }\n\n scrollToTop(): void {\n requestAnimationFrame(() => {\n this.el.scrollTop = 0;\n });\n }\n}\n","import type { IconConfig, UploadConfig } from \"../types/config\";\nimport { IconRenderer } from \"../utils/IconRenderer\";\nimport { ICON_NAMES, ICON_SIZES, UI_TEXT, WIDGET_DEFAULTS } from \"../types/config\";\nimport { DOMBuilder } from \"../utils/DOMBuilder\";\nimport { FileUtils } from \"../utils/FileUtils\";\nimport { createLogger, type Logger } from \"../utils/logger\";\n\nexport interface FileUploadCallbacks {\n onFileSelected: (file: File, base64Content: string) => void;\n onError: (error: string) => void;\n}\n\nexport class FileUploadButton {\n private buttonEl: HTMLButtonElement;\n private inputEl: HTMLInputElement;\n private config: UploadConfig;\n private callbacks: FileUploadCallbacks;\n private logger: Logger;\n\n constructor(config: UploadConfig, callbacks: FileUploadCallbacks, debug: boolean) {\n this.config = config;\n this.callbacks = callbacks;\n this.logger = createLogger(\"FileUploadButton\", debug);\n \n // Create hidden file input\n this.inputEl = DOMBuilder.input({\n type: \"file\",\n className: \"hidden\",\n accept: this.config.allowedFileTypes?.join(\",\") || \"*\",\n on: {\n change: () => this.handleFileChange()\n }\n }).build() as HTMLInputElement;\n \n // Create upload button with icon\n const iconEl = IconRenderer.create(\n this.config.icon,\n ICON_NAMES.PAPERCLIP,\n ICON_SIZES.BUTTON_SMALL\n );\n \n this.buttonEl = DOMBuilder.button({\n className: [\n \"nimbus-upload-btn\",\n \"p-2\",\n \"rounded-full\",\n \"transition-all cursor-pointer\",\n \"hover:bg-slate-100\",\n \"disabled:opacity-40 disabled:cursor-not-allowed\",\n \"flex items-center justify-center\",\n ].join(\" \"),\n title: UI_TEXT.UPLOAD_FILE,\n type: \"button\",\n on: {\n click: () => this.openFileDialog()\n }\n }, iconEl, this.inputEl).build() as HTMLButtonElement;\n }\n\n get element(): HTMLElement {\n // Return just the button, input is already a child from constructor\n return this.buttonEl;\n }\n\n private openFileDialog(): void {\n this.inputEl.click();\n }\n\n private async handleFileChange(): Promise<void> {\n const file = this.inputEl.files?.[0];\n if (!file) return;\n\n // Validate file size\n const maxSize = this.config.maxFileSize ?? WIDGET_DEFAULTS.MAX_FILE_SIZE;\n if (file.size > maxSize) {\n const maxSizeFormatted = FileUtils.formatFileSize(maxSize);\n this.callbacks.onError(`Max File Size: ${maxSizeFormatted}`);\n this.inputEl.value = \"\";\n return;\n }\n\n // Validate file type\n if (this.config.allowedFileTypes && this.config.allowedFileTypes.length > 0) {\n if (!this.config.allowedFileTypes.includes(file.type)) {\n this.callbacks.onError(`File type ${file.type} is not allowed`);\n this.inputEl.value = \"\";\n return;\n }\n }\n\n try {\n // Convert file to base64\n const base64Content = await FileUtils.fileToBase64(file);\n this.logger.debug(`File selected: ${file.name} (${file.size} bytes)`);\n \n // Trigger callback with file and base64 content\n this.callbacks.onFileSelected(file, base64Content);\n \n // Reset input for next selection\n this.inputEl.value = \"\";\n } catch (error) {\n this.logger.error(\"Error processing file:\", error);\n this.callbacks.onError(\"Failed to process file\");\n this.inputEl.value = \"\";\n }\n }\n\n enable(): void {\n this.buttonEl.disabled = false;\n }\n\n disable(): void {\n this.buttonEl.disabled = true;\n }\n\n setEnabled(enabled: boolean): void {\n this.buttonEl.disabled = !enabled;\n }\n}","import { DOMBuilder } from \"../utils/DOMBuilder\";\nimport { IconRenderer } from \"../utils/IconRenderer\";\nimport { FileUtils } from \"../utils/FileUtils\";\nimport { ICON_NAMES, ICON_SIZES } from \"../types/config\";\n\nexport interface FilePreviewData {\n file: File;\n base64Content: string;\n}\n\nexport class FilePreview {\n private container: HTMLElement;\n private fileData: FilePreviewData | null = null;\n private onRemove: () => void;\n\n constructor(onRemove: () => void) {\n this.onRemove = onRemove;\n this.container = this.createContainer();\n }\n\n private createContainer(): HTMLElement {\n return DOMBuilder.div({\n className: \"nimbus-file-preview hidden\"\n }).build();\n }\n\n setFile(fileData: FilePreviewData | null): void {\n this.fileData = fileData;\n \n if (!fileData) {\n this.container.classList.add(\"hidden\");\n this.container.innerHTML = \"\";\n return;\n }\n\n // Clear existing content\n this.container.innerHTML = \"\";\n this.container.classList.remove(\"hidden\");\n\n // Create full-width file preview bar\n const previewWrapper = DOMBuilder.div({\n className: \"relative w-full px-3 py-2 bg-slate-100 border border-slate-200 rounded-lg\"\n }).build();\n\n // Close button in top right corner\n const removeBtn = DOMBuilder.button({\n className: [\n \"absolute top-1 right-1\",\n \"flex items-center justify-center\",\n \"w-5 h-5 rounded-full\",\n \"bg-slate-300 hover:bg-red-500 text-slate-600 hover:text-white\",\n \"transition-colors duration-200\",\n \"text-xs leading-none\"\n ].join(\" \"),\n type: \"button\",\n title: \"Remove file\",\n on: {\n click: (e: Event) => {\n e.preventDefault();\n e.stopPropagation();\n this.removeFile();\n }\n }\n }).text(\"×\").build();\n previewWrapper.appendChild(removeBtn);\n\n // File icon\n const fileIcon = IconRenderer.create(\n null,\n \"file-text\", // Generic file icon\n ICON_SIZES.BUTTON_MEDIUM\n );\n\n // File name\n const fileName = DOMBuilder.span({\n className: \"text-sm font-medium text-slate-700 truncate max-w-[200px]\"\n }).text(fileData.file.name);\n\n // File size\n const fileSize = DOMBuilder.span({\n className: \"text-xs text-slate-500 flex-shrink-0\"\n }).text(FileUtils.formatFileSize(fileData.file.size, true));\n\n // Build content area using DOMBuilder\n const contentArea = DOMBuilder.div({\n className: \"flex items-center justify-between pr-6\" // Right padding to avoid close button\n },\n // Left side: Icon and file name\n DOMBuilder.div({\n className: \"flex items-center gap-3\"\n }, fileIcon, fileName),\n // Right side: File size\n fileSize\n );\n\n previewWrapper.appendChild(contentArea.build());\n\n this.container.appendChild(previewWrapper);\n }\n\n\n private removeFile(): void {\n this.setFile(null);\n this.onRemove();\n }\n\n getFileData(): FilePreviewData | null {\n return this.fileData;\n }\n\n get element(): HTMLElement {\n return this.container;\n }\n}","import type { IconConfig, TextConfig, ColorPair, UploadConfig, MaxCharactersConfig, TypingIndicatorConfig } from \"../types/config\";\nimport { IconRenderer } from \"../utils/IconRenderer\";\nimport { ICON_NAMES, ICON_SIZES, UI_TEXT, INPUT_CONSTANTS } from \"../types/config\";\nimport { DOMBuilder } from \"../utils/DOMBuilder\";\nimport { BaseComponent } from \"../utils/ComponentFactory\";\nimport { FileUploadButton } from \"./FileUploadButton\";\nimport { FilePreview, type FilePreviewData } from \"./FilePreview\";\nimport { createLogger, type Logger } from \"../utils/logger\";\n\nexport interface InputAreaConfig {\n placeholder: string;\n expandable: boolean;\n text?: TextConfig;\n background?: ColorPair;\n sendButton: {\n icon?: IconConfig | null;\n align: boolean;\n };\n upload?: UploadConfig;\n maxCharacters?: MaxCharactersConfig;\n}\n\nexport interface InputAreaCallbacks {\n onSend: (content: string) => void;\n onFileUpload?: (file: File, base64Content: string, description?: string) => void;\n}\n\nexport class InputArea extends BaseComponent {\n readonly el: HTMLElement;\n private inputEl!: HTMLInputElement | HTMLTextAreaElement;\n private sendBtn!: HTMLButtonElement;\n private uploadButton?: FileUploadButton;\n private filePreview?: FilePreview;\n private pendingFile: FilePreviewData | null = null;\n private config: InputAreaConfig;\n private callbacks: InputAreaCallbacks;\n private expandable: boolean;\n protected logger: Logger;\n private debug: boolean;\n private errorContainer: HTMLElement | null = null;\n private errorTimeout: number | null = null;\n private bottomArea!: HTMLElement;\n private characterCounter?: HTMLElement;\n private typingBar?: HTMLElement;\n\n constructor(\n config: InputAreaConfig,\n callbacks: InputAreaCallbacks,\n debug: boolean\n ) {\n super();\n this.config = config;\n this.debug = debug;\n this.logger = createLogger(\"InputArea\", debug);\n this.callbacks = callbacks;\n this.expandable = config.expandable;\n this.el = this.element;\n }\n\n protected render(): HTMLElement {\n // Create file preview component\n if (this.config.upload) {\n this.filePreview = new FilePreview(() => {\n this.pendingFile = null;\n this.toggleUploadButton(true);\n });\n }\n\n // Create upload button if enabled\n if (this.config.upload && this.callbacks.onFileUpload) {\n this.uploadButton = new FileUploadButton(\n this.config.upload,\n {\n onFileSelected: (file, base64) => {\n // Store the file and show preview instead of sending immediately\n this.pendingFile = { file, base64Content: base64 };\n this.filePreview?.setFile(this.pendingFile);\n this.toggleUploadButton(false);\n },\n onError: (error) => {\n this.logger.error(\"File upload error:\", error);\n this.showError(error);\n }\n },\n this.debug\n );\n }\n\n // Create send button\n const iconEl = IconRenderer.create(this.config.sendButton.icon, ICON_NAMES.SEND, ICON_SIZES.BUTTON_SMALL);\n this.sendBtn = DOMBuilder.button({\n className: [\n \"nimbus-send-btn\",\n \"rounded-full\",\n \"p-1.5\",\n \"transition-all cursor-pointer shrink-0\",\n \"disabled:opacity-40 disabled:cursor-not-allowed\",\n ].join(\" \"),\n title: UI_TEXT.SEND,\n type: \"button\",\n on: { click: () => this.handleSend() }\n }, iconEl).build() as HTMLButtonElement;\n\n // Calculate shared values once\n const hasUpload = this.config.upload && this.uploadButton;\n const paddingClasses = hasUpload ? \"pl-12 pr-12\" : \"pl-4 pr-12\";\n const sharedInputClasses = [\n \"nimbus-input-field\",\n \"w-full py-2.5 text-sm outline-none\",\n \"focus:ring-2 focus:ring-blue-500/20 focus:border-blue-300 transition-all\",\n paddingClasses\n ];\n \n const sharedKeydownHandler = (e: KeyboardEvent) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n this.handleSend();\n }\n };\n\n this.typingBar = DOMBuilder.div({\n className: \"nimbus-typing-bar hidden px-4 py-1.5 text-xs border-t border-slate-200\"\n }).build();\n\n // Create container\n const container = DOMBuilder.div({\n className: \"nimbus-input-container border-t border-slate-200\"\n });\n\n const inputStyle: Partial<CSSStyleDeclaration> = {};\n if (this.config.text?.color) inputStyle.color = this.config.text.color;\n if (this.config.text?.font) inputStyle.fontFamily = this.config.text.font;\n if (this.config.text?.size) inputStyle.fontSize = `${this.config.text.size}px`;\n\n if (this.config.expandable) {\n this.inputEl = DOMBuilder.textarea({\n placeholder: this.config.placeholder,\n rows: 1,\n className: [\n ...sharedInputClasses,\n \"nimbus-textarea border border-slate-200 rounded-xl\",\n ].join(\" \"),\n style: inputStyle,\n on: {\n keydown: sharedKeydownHandler,\n input: () => {\n this.autoResize(this.inputEl as HTMLTextAreaElement);\n this.updateCharacterCounter();\n }\n }\n }).build() as HTMLTextAreaElement;\n } else {\n this.inputEl = DOMBuilder.input({\n type: \"text\",\n placeholder: this.config.placeholder,\n className: [\n ...sharedInputClasses,\n \"border border-slate-200 rounded-full\",\n ].join(\" \"),\n style: inputStyle,\n on: {\n keydown: sharedKeydownHandler,\n input: () => this.updateCharacterCounter()\n }\n }).build() as HTMLInputElement;\n }\n\n // Create wrapper to position buttons (unified for both input types)\n const inputWrapper = DOMBuilder.div({\n className: this.config.expandable ? \"relative w-full\" : \"relative w-full flex items-center\"\n }).build();\n \n // Add input element\n inputWrapper.appendChild(this.inputEl);\n \n // Add upload button positioned absolutely on the left inside the input\n if (this.uploadButton) {\n const uploadClasses = this.config.expandable \n ? [\"absolute\", \"left-1\", \"bottom-2\", \"z-10\"]\n : [\"absolute\", \"left-1\", \"z-10\"];\n this.uploadButton.element.classList.add(...uploadClasses);\n inputWrapper.appendChild(this.uploadButton.element);\n }\n \n // Add send button positioned absolutely on the right inside the input\n const sendClasses = [\"absolute\", \"right-1\", \"bottom-[10px]\", \"z-10\"];\n this.sendBtn.classList.add(...sendClasses);\n inputWrapper.appendChild(this.sendBtn);\n \n container.append(this.typingBar);\n \n const innerWrap = DOMBuilder.div({ className: \"p-3\" });\n innerWrap.append(inputWrapper);\n\n // Add file preview / error area below the input\n this.errorContainer = null;\n this.bottomArea = DOMBuilder.div({ className: \"nimbus-bottom-area\" }).build();\n if (this.filePreview) {\n this.bottomArea.appendChild(this.filePreview.element);\n }\n innerWrap.append(this.bottomArea);\n\n // Add character counter if enabled\n if (this.config.maxCharacters) {\n this.characterCounter = this.createCharacterCounter();\n innerWrap.append(this.characterCounter);\n }\n\n container.append(innerWrap);\n\n return container.build();\n }\n\n\n private createCharacterCounter(): HTMLElement {\n const config = this.config.maxCharacters!;\n const style: Partial<CSSStyleDeclaration> = {};\n if (config.text?.color) style.color = config.text.color;\n if (config.text?.size) style.fontSize = `${config.text.size}px`;\n if (config.text?.font) style.fontFamily = config.text.font;\n return DOMBuilder.div({\n className: \"nimbus-character-counter text-right px-3 py-1\",\n style\n })\n .text(`0/${config.limit}`)\n .build();\n }\n\n private updateCharacterCounter(): void {\n if (!this.characterCounter || !this.config.maxCharacters) return;\n \n const currentLength = this.inputEl.value.length;\n const maxLength = this.config.maxCharacters.limit;\n \n this.characterCounter.textContent = `${currentLength}/${maxLength}`;\n \n // Apply color based on limit proximity\n if (currentLength > maxLength) {\n this.characterCounter.style.color = 'red';\n } else if (currentLength > maxLength * 0.9) {\n this.characterCounter.style.color = 'orange';\n } else {\n this.characterCounter.style.color = this.config.maxCharacters.text?.color || '#6b7280';\n }\n }\n\n private autoResize(textarea: HTMLTextAreaElement): void {\n textarea.style.height = \"auto\";\n const scrollH = textarea.scrollHeight;\n if (scrollH > INPUT_CONSTANTS.MAX_HEIGHT) {\n textarea.style.height = INPUT_CONSTANTS.MAX_HEIGHT + \"px\";\n textarea.classList.add(\"nimbus-textarea-scroll\");\n } else {\n textarea.style.height = scrollH + \"px\";\n textarea.classList.remove(\"nimbus-textarea-scroll\");\n }\n }\n\n private handleSend(): void {\n const text = this.inputEl.value.trim();\n \n // Check character limit validation\n if (this.config.maxCharacters && text.length > this.config.maxCharacters.limit) {\n this.logger.warn(`Message exceeds character limit: ${text.length}/${this.config.maxCharacters.limit}`);\n return; // Prevent sending messages that exceed the limit\n }\n \n // If there's a pending file, send it with the description\n if (this.pendingFile) {\n if (this.callbacks.onFileUpload) {\n // Send file with description (text as description)\n this.callbacks.onFileUpload(this.pendingFile.file, this.pendingFile.base64Content, text);\n }\n // Clear file preview\n this.pendingFile = null;\n this.filePreview?.setFile(null);\n this.toggleUploadButton(true);\n } else if (text) {\n // Send regular text message\n this.callbacks.onSend(text);\n } else {\n // No text and no file, don't send anything\n return;\n }\n \n // Clear input and update counter\n this.inputEl.value = \"\";\n this.updateCharacterCounter();\n if (this.expandable && this.inputEl instanceof HTMLTextAreaElement) {\n this.inputEl.style.height = \"auto\";\n this.inputEl.classList.remove(\"nimbus-textarea-scroll\");\n }\n this.inputEl.focus();\n }\n\n private toggleUploadButton(show: boolean): void {\n if (this.uploadButton) {\n this.uploadButton.element.classList.toggle(\"hidden\", !show);\n }\n // Update input padding based on whether upload button is shown\n this.updateInputPadding(!show);\n }\n\n private updateInputPadding(hasFilePreview: boolean): void {\n if (hasFilePreview) {\n // When file preview is active, remove left padding\n this.inputEl.classList.remove(\"pl-12\");\n this.inputEl.classList.add(\"pl-4\");\n } else {\n // When upload button is visible, add left padding\n if (this.config.upload) {\n this.inputEl.classList.remove(\"pl-4\");\n this.inputEl.classList.add(\"pl-12\");\n }\n }\n }\n\n disable(): void {\n this.inputEl.disabled = true;\n this.sendBtn.disabled = true;\n this.uploadButton?.disable();\n }\n\n enable(): void {\n this.inputEl.disabled = false;\n this.sendBtn.disabled = false;\n this.uploadButton?.enable();\n }\n\n setEnabled(enabled: boolean): void {\n if (enabled) {\n this.enable();\n } else {\n this.disable();\n }\n }\n\n focus(): void {\n this.inputEl.focus();\n }\n\n showTypingIndicator(config: TypingIndicatorConfig): void {\n if (!this.typingBar) return;\n this.typingBar.textContent = config.title?.value ?? \"\";\n if (config.title?.text?.color) {\n this.typingBar.style.color = config.title.text.color;\n }\n if (config.title?.text?.font) {\n this.typingBar.style.fontFamily = config.title.text.font;\n }\n if (config.title?.text?.size) {\n this.typingBar.style.fontSize = `${config.title.text.size}px`;\n }\n this.typingBar.classList.remove(\"hidden\");\n }\n\n hideTypingIndicator(): void {\n if (!this.typingBar) return;\n this.typingBar.classList.add(\"hidden\");\n }\n\n private showError(message: string): void {\n this.hideError();\n\n this.errorContainer = DOMBuilder.div({\n className: \"nimbus-upload-error bg-red-500 text-white text-sm px-3 py-2 mt-2 rounded-lg\"\n }).text(message).build();\n\n this.bottomArea.appendChild(this.errorContainer);\n\n const duration = this.config.upload?.errorDisplayDuration || 2000;\n this.errorTimeout = setTimeout(() => {\n this.hideError();\n }, duration);\n }\n\n private hideError(): void {\n if (this.errorTimeout) {\n clearTimeout(this.errorTimeout);\n this.errorTimeout = null;\n }\n\n if (this.errorContainer) {\n this.errorContainer.remove();\n this.errorContainer = null;\n }\n }\n}\n","import type { BubblePosition, IconConfig } from \"../types/config\";\nimport { IconRenderer } from \"../utils/IconRenderer\";\nimport { ICON_NAMES, ICON_SIZES, UI_TEXT, ICON_SCALE } from \"../types/config\";\nimport { DOMBuilder } from \"../utils/DOMBuilder\";\nimport { BaseComponent } from \"../utils/ComponentFactory\";\n\nexport interface ChatBubbleConfig {\n position: BubblePosition;\n autoHide: boolean;\n icon?: IconConfig | null;\n}\n\n/**\n * Floating action button that opens/closes the chat window.\n * Supports custom icon (IconConfig), null (no custom icon), or default chat icon.\n */\nexport class ChatBubble extends BaseComponent {\n readonly el: HTMLElement;\n private isOpen = false;\n private config: ChatBubbleConfig;\n private onToggle: () => void;\n\n constructor(config: ChatBubbleConfig, onToggle: () => void) {\n super();\n this.config = config;\n this.onToggle = onToggle;\n this.el = this.element;\n }\n\n protected render(): HTMLElement {\n const button = DOMBuilder.button({\n className: [\n \"nimbus-bubble\",\n \"fixed z-50 w-14 h-14 shadow-lg\",\n \"rounded-full\",\n \"flex items-center justify-center cursor-pointer\",\n \"transition-all duration-200 hover:scale-110\",\n this.config.position === \"bottom-right\" ? \"right-5 bottom-5\" : \"left-5 bottom-5\",\n ].join(\" \"),\n title: UI_TEXT.OPEN_CHAT,\n type: \"button\",\n on: {\n click: () => {\n this.isOpen = !this.isOpen;\n this.updateIcon();\n this.onToggle();\n }\n }\n }, this.createBubbleIcon()).build();\n\n return button;\n }\n\n setOpen(open: boolean): void {\n this.isOpen = open;\n this.updateIcon();\n }\n\n private createBubbleIcon(): HTMLElement | null {\n return IconRenderer.create(\n this.config.icon, \n ICON_NAMES.MESSAGE_CIRCLE, \n ICON_SIZES.AVATAR,\n { sizeFactor: ICON_SCALE.BUBBLE_ICON }\n );\n }\n\n private updateIcon(): void {\n // Clear current content\n this.el.replaceChildren();\n \n if (this.isOpen) {\n const closeIcon = IconRenderer.createSimple(ICON_NAMES.CHEVRON_DOWN, ICON_SIZES.BUBBLE_OPEN);\n this.el.appendChild(closeIcon);\n this.el.title = UI_TEXT.CLOSE_CHAT;\n } else {\n const openIcon = this.createBubbleIcon();\n if (openIcon) {\n this.el.appendChild(openIcon);\n }\n this.el.title = UI_TEXT.OPEN_CHAT;\n }\n }\n}\n","import type { EventBus } from \"../core/EventBus\";\nimport type { ChatSession } from \"../core/ChatSession\";\nimport type { WebSocketManager } from \"../core/WebSocketManager\";\nimport type { ResolvedConfig } from \"../types/config\";\nimport type { ChatMessage, ConnectionState, UserMessage } from \"../types/message\";\nimport { isMobileDevice } from \"../utils/device\";\nimport { ShadowContainer } from \"./ShadowContainer\";\nimport { ThemeManager } from \"./ThemeManager\";\nimport { Header } from \"./Header\";\nimport { MessageList } from \"./MessageList\";\nimport { InputArea } from \"./InputArea\";\nimport { ChatBubble } from \"./ChatBubble\";\nimport { DOMBuilder } from \"../utils/DOMBuilder\";\n\n/**\n * Base class for chat window implementations.\n * Contains all shared functionality between ChatWindow and SidepanelWindow.\n */\nexport abstract class BaseWindow {\n protected config: ResolvedConfig;\n protected eventBus: EventBus;\n protected session: ChatSession;\n protected wsManager: WebSocketManager;\n\n protected shadow!: ShadowContainer;\n protected bubble!: ChatBubble;\n protected panel!: HTMLElement;\n protected header!: Header;\n protected messageList!: MessageList;\n protected inputArea!: InputArea;\n protected isOpen = false;\n protected hasRequestedConnect = false;\n protected waitForReplyTimeout: ReturnType<typeof setTimeout> | null = null;\n private shouldScrollToTop = false;\n private waitingForFirstReply = false;\n private firstReplyTimeout: ReturnType<typeof setTimeout> | null = null;\n private eventUnsubscribers: Array<() => void> = [];\n\n constructor(\n config: ResolvedConfig,\n eventBus: EventBus,\n session: ChatSession,\n wsManager: WebSocketManager\n ) {\n this.config = config;\n this.eventBus = eventBus;\n this.session = session;\n this.wsManager = wsManager;\n\n this.initializeComponents();\n\n this.panel = this.buildPanel();\n\n this.setupDOM();\n\n this.messageList.showWelcome(config.welcome);\n\n this.bindEvents();\n }\n\n protected initializeComponents(): void {\n const theme = new ThemeManager(this.config);\n this.shadow = new ShadowContainer(theme);\n\n this.header = new Header(\n this.config.header,\n () => this.close(),\n this.config.allowNewChat,\n () => this.eventBus.emit(\"ui:start-new-chat\")\n );\n\n this.messageList = new MessageList(\n this.config.showMore,\n this.config.theme.primary,\n this.config.userMessage.width,\n this.config.botMessage.width,\n this.config.userMessage.icon,\n this.config.botMessage.icon\n );\n this.inputArea = new InputArea(\n {\n placeholder: this.config.input.placeholder,\n expandable: this.config.input.expandable,\n text: this.config.input.text,\n background: this.config.input.background,\n sendButton: this.config.sendButton,\n upload: this.config.input.upload,\n maxCharacters: this.config.input.maxCharacters,\n },\n {\n onSend: (content) => this.handleSend(content),\n onFileUpload: this.config.input.upload\n ? (file, base64, description) => this.handleFileUpload(file, base64, description)\n : undefined\n },\n this.config.debug\n );\n\n this.bubble = new ChatBubble(\n {\n position: this.config.bubble.position,\n autoHide: this.config.bubble.autoHide,\n icon: this.config.bubble.icon,\n },\n () => this.toggle()\n );\n }\n\n protected setupDOM(): void {\n this.shadow.root.appendChild(this.bubble.el);\n this.shadow.root.appendChild(this.panel);\n }\n\n open(): void {\n if (!this.hasRequestedConnect) {\n this.hasRequestedConnect = true;\n this.eventBus.emit(\"ws:request-connect\");\n }\n\n this.isOpen = true;\n this.showPanel();\n this.inputArea.focus();\n }\n\n close(): void {\n this.isOpen = false;\n this.hidePanel();\n }\n\n toggle(): void {\n this.isOpen ? this.close() : this.open();\n }\n\n destroy(): void {\n if (this.waitForReplyTimeout) {\n clearTimeout(this.waitForReplyTimeout);\n this.waitForReplyTimeout = null;\n }\n if (this.firstReplyTimeout) {\n clearTimeout(this.firstReplyTimeout);\n this.firstReplyTimeout = null;\n }\n this.eventUnsubscribers.forEach(unsub => unsub());\n this.eventUnsubscribers = [];\n this.shadow.destroy();\n }\n\n private onMessageSent(): void {\n if (this.config.waitForReply) {\n this.setupWaitForReplyTimeout();\n }\n this.showTypingIndicator();\n }\n\n protected handleFileUpload(file: File, base64Content: string, description?: string): void {\n const fileContent = {\n filename: file.name,\n filetype: file.type,\n filesize: file.size,\n content: base64Content,\n description: description || \"\"\n };\n\n this.session.createUserMessage(fileContent, \"file\");\n this.onMessageSent();\n this.wsManager.send({\n type: \"message\",\n direction: \"inbound\",\n content: JSON.stringify(fileContent),\n });\n }\n\n protected handleSend(content: string): void {\n // Create local message\n this.session.createUserMessage(content, \"text\");\n\n // Build payload with simplified schema\n const messagePayload: UserMessage = {\n type: \"message\",\n direction: \"inbound\",\n content,\n };\n\n this.onMessageSent();\n this.wsManager.send(messagePayload);\n }\n\n private subscribe<K extends import(\"../core/EventBus\").EventName>(\n event: K,\n handler: import(\"../core/EventBus\").EventHandler<K>\n ): void {\n this.eventBus.on(event, handler);\n this.eventUnsubscribers.push(() => this.eventBus.off(event, handler));\n }\n\n protected bindEvents(): void {\n this.subscribe(\"message:added\", (msg: ChatMessage) => {\n this.messageList.addMessage(msg);\n\n if (msg.direction === \"OUTBOUND\") {\n if (this.waitingForFirstReply) {\n this.waitingForFirstReply = false;\n this.inputArea.setEnabled(true);\n this.hideTypingIndicator();\n if (this.firstReplyTimeout) {\n clearTimeout(this.firstReplyTimeout);\n this.firstReplyTimeout = null;\n }\n } else if (this.waitForReplyTimeout) {\n clearTimeout(this.waitForReplyTimeout);\n this.waitForReplyTimeout = null;\n this.inputArea.setEnabled(true);\n this.hideTypingIndicator();\n } else {\n this.hideTypingIndicator();\n }\n }\n });\n\n const handleHistoryUpdate = (messages: ChatMessage[]) => {\n this.messageList.clear();\n if (messages.length === 0) {\n this.messageList.showWelcome(this.config.welcome);\n } else {\n messages.forEach(msg => this.messageList.addMessage(msg));\n if (this.shouldScrollToTop) {\n this.messageList.scrollToTop();\n this.shouldScrollToTop = false;\n }\n }\n };\n\n this.subscribe(\"history:loaded\", handleHistoryUpdate);\n this.subscribe(\"history:merged\", handleHistoryUpdate);\n\n // Only show \"show more\" button if resumeConversation is enabled\n if (this.config.resumeConversation) {\n this.subscribe(\"history:has_more\", (hasMore: boolean) => {\n if (hasMore) {\n this.messageList.showShowMoreButton(() => {\n this.shouldScrollToTop = true;\n this.eventBus.emit(\"ui:show-more\");\n });\n } else {\n this.messageList.hideShowMoreButton();\n }\n this.messageList.updateHasMore(hasMore);\n });\n }\n\n this.subscribe(\"connection:state\", (state: ConnectionState) => {\n this.header.setConnectionState(state);\n if (state === \"connected\") {\n if (!this.waitingForFirstReply) {\n this.inputArea.setEnabled(true);\n }\n }\n });\n\n this.subscribe(\"session:connected\", () => {\n if (this.config.waitForReply?.firstReply) {\n this.waitingForFirstReply = true;\n this.inputArea.setEnabled(false);\n this.showTypingIndicator();\n\n this.firstReplyTimeout = setTimeout(() => {\n this.waitingForFirstReply = false;\n this.inputArea.setEnabled(true);\n this.hideTypingIndicator();\n this.firstReplyTimeout = null;\n }, this.config.waitForReply.timeout);\n }\n });\n\n this.subscribe(\"session:cleared\", () => {\n this.messageList.clear();\n this.messageList.showWelcome(this.config.welcome);\n\n if (!this.config.waitForReply?.firstReply) {\n this.inputArea.setEnabled(true);\n }\n });\n\n this.subscribe(\"ui:show-error\", (message: string) => {\n this.messageList.showError(message);\n });\n }\n\n protected createPanelElement(): HTMLElement {\n return DOMBuilder.div({},\n this.header.el,\n this.messageList.el,\n this.inputArea.el\n ).build();\n }\n\n protected isMobileDevice(): boolean {\n return isMobileDevice();\n }\n\n protected getEffectivePosition(): string {\n if (isMobileDevice() && this.config.style.mobile?.position) {\n return this.config.style.mobile.position;\n }\n return this.config.style.position;\n }\n\n protected isSidepanelPosition(): boolean {\n const position = this.getEffectivePosition();\n return position.startsWith(\"sidepanel\");\n }\n\n protected isWindowPosition(): boolean {\n const position = this.getEffectivePosition();\n return position.startsWith(\"bottom\");\n }\n\n private showTypingIndicator(): void {\n if (!this.config.isTypingIndicator) return;\n\n if (this.config.isTypingIndicator.position === \"bottom\") {\n this.inputArea.showTypingIndicator(this.config.isTypingIndicator);\n } else {\n this.header.showTypingIndicator(this.config.isTypingIndicator);\n }\n }\n\n private hideTypingIndicator(): void {\n if (!this.config.isTypingIndicator) return;\n\n if (this.config.isTypingIndicator.position === \"bottom\") {\n this.inputArea.hideTypingIndicator();\n } else {\n this.header.hideTypingIndicator();\n }\n }\n\n private setupWaitForReplyTimeout(): void {\n if (!this.config.waitForReply) return;\n\n this.inputArea.setEnabled(false);\n\n this.waitForReplyTimeout = setTimeout(() => {\n this.inputArea.setEnabled(true);\n this.hideTypingIndicator();\n this.waitForReplyTimeout = null;\n }, this.config.waitForReply.timeout);\n }\n\n // Abstract methods to be implemented by subclasses\n protected abstract buildPanel(): HTMLElement;\n protected abstract showPanel(): void;\n protected abstract hidePanel(): void;\n}\n","import type { EventBus } from \"../core/EventBus\";\nimport type { ChatSession } from \"../core/ChatSession\";\nimport type { WebSocketManager } from \"../core/WebSocketManager\";\nimport type { ResolvedConfig } from \"../types/config\";\nimport { BaseWindow } from \"./BaseWindow\";\n\n/**\n * Pop-up chat window for bottom-right / bottom-left positions.\n */\nexport class ChatWindow extends BaseWindow {\n constructor(\n config: ResolvedConfig,\n eventBus: EventBus,\n session: ChatSession,\n wsManager: WebSocketManager\n ) {\n super(config, eventBus, session, wsManager);\n }\n\n protected buildPanel(): HTMLElement {\n const panel = this.createPanelElement();\n const isRight = this.config.style.position === \"bottom-right\";\n const isMobile = this.isMobileDevice();\n \n const width = this.config.style.width;\n const height = this.config.style.height;\n\n panel.className = \"font-sans fixed flex flex-col rounded-xl shadow-2xl overflow-hidden nimbus-fade-in\";\n \n if (isMobile) {\n panel.style.cssText = [\n \"display: none\",\n \"width: 100%\",\n `height: ${height}`,\n \"bottom: 84px\",\n \"left: 0\",\n \"right: 0\",\n \"border: 1px solid #e2e8f0\",\n \"border-radius: 12px 12px 0 0\",\n ].join(\";\");\n } else {\n panel.style.cssText = [\n \"display: none\",\n `width: ${width}`,\n `height: ${height}`,\n \"bottom: 84px\",\n isRight ? \"right: 20px\" : \"left: 20px\",\n \"border: 1px solid #e2e8f0\",\n ].join(\";\");\n }\n\n return panel;\n }\n\n protected showPanel(): void {\n this.panel.style.display = \"flex\";\n this.bubble.setOpen(true);\n \n if (this.config.bubble.autoHide) {\n this.bubble.el.style.display = \"none\";\n this.panel.style.bottom = \"20px\";\n }\n }\n\n protected hidePanel(): void {\n this.panel.style.display = \"none\";\n this.bubble.setOpen(false);\n \n if (this.config.bubble.autoHide) {\n this.bubble.el.style.display = \"flex\";\n this.panel.style.bottom = \"84px\";\n }\n }\n}","import type { EventBus } from \"../core/EventBus\";\nimport type { ChatSession } from \"../core/ChatSession\";\nimport type { WebSocketManager } from \"../core/WebSocketManager\";\nimport type { ResolvedConfig } from \"../types/config\";\nimport { BaseWindow } from \"./BaseWindow\";\n\n/**\n * Full-height sidepanel chat for sidepanel-left / sidepanel-right positions.\n */\nexport class SidepanelWindow extends BaseWindow {\n constructor(\n config: ResolvedConfig,\n eventBus: EventBus,\n session: ChatSession,\n wsManager: WebSocketManager\n ) {\n super(config, eventBus, session, wsManager);\n \n this.bubble.el.style.display = \"flex\";\n this.bubble.setOpen(false);\n }\n\n protected buildPanel(): HTMLElement {\n const panel = this.createPanelElement();\n const isRight = this.config.style.position === \"sidepanel-right\";\n const isMobile = this.isMobileDevice();\n \n const width = this.config.style.width;\n\n panel.className = \"font-sans fixed top-0 h-screen flex flex-col shadow-2xl overflow-hidden nimbus-panel-slide\";\n \n if (isMobile) {\n panel.style.cssText = [\n \"display: none\",\n \"width: 100%\",\n \"left: 0\",\n \"right: 0\",\n \"border: 1px solid #e2e8f0\",\n \"border-radius: 12px 12px 0 0\"\n ].join(\";\");\n } else {\n panel.style.cssText = [\n \"display: none\",\n `width: ${width}`,\n isRight ? \"right: 0\" : \"left: 0\",\n \"border: 1px solid #e2e8f0\",\n \"border-radius: 12px 12px 0 0\",\n ].join(\";\");\n }\n\n return panel;\n }\n\n protected showPanel(): void {\n this.panel.style.display = \"flex\";\n this.bubble.el.style.display = \"none\";\n this.bubble.setOpen(true);\n }\n\n protected hidePanel(): void {\n this.panel.style.display = \"none\";\n this.bubble.el.style.display = \"flex\";\n this.bubble.setOpen(false);\n }\n}","import type { NimbusChatConfig, ResolvedConfig } from \"./types/config\";\nimport type { ServerEvent } from \"./types/message\";\nimport { resolveConfig } from \"./types/config\";\nimport { validateConfig } from \"./utils/validator\";\nimport { isMobileDevice, setMobileBreakpoint } from \"./utils/device\";\nimport { createLogger, printBanner, type Logger } from \"./utils/logger\";\nimport { PerformanceTracker } from \"./utils/PerformanceTracker\";\nimport { EventBus } from \"./core/EventBus\";\nimport { ChatSession } from \"./core/ChatSession\";\nimport { WebSocketManager } from \"./core/WebSocketManager\";\nimport { ApiClient } from \"./core/ApiClient\";\nimport { ChatWindow } from \"./ui/ChatWindow\";\nimport { SidepanelWindow } from \"./ui/SidepanelWindow\";\n\n/**\n * Main SDK orchestrator.\n *\n * Usage (npm):\n * const chat = new NimbusChat({ agent_id: \"550e8400-e29b-41d4-a716-446655440000\" });\n * chat.open();\n *\n * Usage (CDN):\n * NimbusChat.init({ agent_id: \"550e8400-e29b-41d4-a716-446655440000\" });\n */\nexport class NimbusChat {\n private config: ResolvedConfig;\n private eventBus: EventBus;\n private session: ChatSession;\n private wsManager: WebSocketManager;\n private apiClient: ApiClient;\n private ui: ChatWindow | SidepanelWindow;\n private logger: Logger;\n private destroyed = false;\n private messageLimit: number;\n private hasMoreMessages = false;\n private perf: PerformanceTracker;\n\n constructor(config: NimbusChatConfig) {\n validateConfig(config);\n\n this.config = resolveConfig(config);\n\n if (this.config.debug) printBanner();\n this.logger = createLogger(\"Main\", this.config.debug);\n\n this.messageLimit = this.config.messagesPerPage;\n\n this.perf = new PerformanceTracker(this.config.debug);\n this.eventBus = new EventBus();\n this.session = new ChatSession(this.eventBus);\n this.wsManager = new WebSocketManager(\n this.config.dns,\n this.config.agent_id,\n this.eventBus,\n this.config.reconnect,\n this.config.debug,\n );\n this.apiClient = new ApiClient(this.config.dns, this.config.debug);\n\n setMobileBreakpoint(this.config.style.mobile?.breakpoint);\n\n const isMobile = isMobileDevice();\n const effectivePosition = isMobile && this.config.style.mobile?.position\n ? this.config.style.mobile.position\n : this.config.style.position;\n\n const isSidepanel = effectivePosition.startsWith(\"sidepanel\");\n if (isSidepanel) {\n this.ui = new SidepanelWindow(\n this.config,\n this.eventBus,\n this.session,\n this.wsManager\n );\n } else {\n this.ui = new ChatWindow(\n this.config,\n this.eventBus,\n this.session,\n this.wsManager\n );\n }\n\n this.setupEventHandlers();\n\n this.logger.info(\"Initialized\", this.config);\n }\n\n private setupEventHandlers(): void {\n this.eventBus.on(\"ws:request-connect\", () => {\n this.wsManager.connect();\n });\n\n this.eventBus.on(\"ws:message\", (event: ServerEvent) => {\n this.handleServerEvent(event);\n });\n\n this.eventBus.on(\"message:added\", (msg) => {\n if (msg.direction === \"INBOUND\") {\n this.perf.markMessageSent();\n }\n });\n\n // Only enable show-more if resumeConversation is enabled\n if (this.config.resumeConversation) {\n this.eventBus.on(\"ui:show-more\", () => {\n this.fetchAndDisplayHistory(true);\n });\n }\n\n this.eventBus.on(\"ui:start-new-chat\", () => {\n this.wsManager.clearSession();\n this.session.clear();\n this.perf.reset();\n this.messageLimit = this.config.messagesPerPage;\n this.hasMoreMessages = false;\n this.wsManager.connect(true);\n });\n }\n\n private async handleServerEvent(event: ServerEvent): Promise<void> {\n switch (event.type) {\n case \"connected\":\n this.session.setFlowId(event.flow_id);\n this.perf.markSessionStarted();\n this.eventBus.emit(\"session:connected\");\n\n // Fetch chat history if resume is enabled\n if (this.config.resumeConversation) {\n await this.fetchAndDisplayHistory();\n }\n break;\n\n case \"message\":\n this.perf.markBotMessageReceived();\n this.session.createBotMessage(event.content);\n break;\n }\n }\n\n // ── Public API ──────────────────────────────────────────────────\n\n /** Show the chat widget */\n open(): void {\n this.ui.open();\n }\n\n /** Hide the chat widget */\n close(): void {\n this.ui.close();\n }\n\n /** Toggle chat visibility */\n toggle(): void {\n this.ui.toggle();\n }\n\n /**\n * Completely remove the widget from the page and disconnect WebSocket.\n * After calling destroy(), this instance cannot be reused.\n */\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.apiClient.abort();\n this.wsManager.disconnect();\n this.ui.destroy();\n this.eventBus.removeAll();\n\n this.logger.info(\"Destroyed\");\n }\n\n private async fetchAndDisplayHistory(loadMore = false, mergeWithLocal = false): Promise<void> {\n const flowId = this.session.getFlowId() || this.wsManager.getFlowId();\n if (!flowId) return;\n\n try {\n this.logger.debug(`fetchAndDisplayHistory called with loadMore=${loadMore}`);\n\n if (loadMore) {\n this.messageLimit += this.config.messagesPerPage;\n this.logger.debug(`Incrementing limit to ${this.messageLimit}`);\n } else {\n this.messageLimit = this.config.messagesPerPage;\n this.logger.debug(`Resetting limit to ${this.messageLimit}`);\n }\n\n const history = await this.apiClient.fetchMessageHistory(flowId, this.messageLimit);\n\n this.logger.debug(`Received ${history.messages.length} messages from API`);\n\n this.hasMoreMessages = history.has_more;\n this.logger.debug(`has_more flag: ${this.hasMoreMessages}`);\n\n if (mergeWithLocal) {\n this.session.loadHistoryMessagesWithMerge(history.messages);\n } else {\n this.session.clearMessages();\n this.session.loadHistoryMessages(history.messages);\n }\n\n this.eventBus.emit(\"history:has_more\", this.hasMoreMessages);\n } catch (error) {\n this.logger.error(\"Failed to load message history:\", error);\n }\n }\n}\n"],"mappings":"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","names":["src_exports","__export","ICON_NAMES","ICON_SIZES","NimbusChat","src_default","getInstance","init","__toCommonJS","ICON_NAMES","ICON_SIZES","UI_TEXT","INPUT_CONSTANTS","Z_INDEX","WEBSOCKET_CLOSE_CODES","TIMEOUTS","WIDGET_DEFAULTS","UI_FACTORS","TIME_CONVERSION","DEFAULT_THEME","DEFAULT_MESSAGE_WIDTH","DEFAULT_USER_MESSAGE","DEFAULT_BOT_MESSAGE","DEFAULT_BUBBLE","DEFAULT_WELCOME","DEFAULT_SEND_BUTTON","DEFAULT_TYPING_INDICATOR","DEFAULT_WAIT_FOR_REPLY","DEFAULT_SHOW_MORE","DEFAULT_UPLOAD","DEFAULT_RECONNECT","DEFAULT_ERROR","DEFAULT_STYLE","DEFAULT_CONFIG","resolveConfig","config","s","SPACING","BORDER_RADIUS","ICON_SCALE","VALID_POSITIONS","VALID_BUBBLE_POSITIONS","validateTextConfig","value","path","validateIconConfig","icon","size","validateConfig","config","c","st","t","bub","msg","hdr","hc","inp","bg","btn","w","cachedBreakpoint","cachedResult","setMobileBreakpoint","breakpoint","isMobileDevice","userAgent","px","LEVEL_CONFIG","COMPONENT_COLORS","DEFAULT_COMPONENT_COLOR","BADGE_STYLE","bg","fg","COMPONENT_STYLE","color","TIMESTAMP_STYLE","MESSAGE_STYLE","getComponentColor","component","lower","key","sanitizeArgs","args","arg","obj","tryParseJSON","str","sanitizeFilePayload","message","wsMatch","prefix","payload","parsed","clean","Logger","_Logger","enabled","level","badge","consoleFn","time","sanitized","json","cleanArgs","parts","styles","allArgs","label","subComponent","data","createLogger","debug","bannerPrinted","printBanner","version","art","subtitle","PerformanceTracker","debug","createLogger","now","elapsed","EventBus","createLogger","event","handler","wrapper","args","err","ChatSession","eventBus","flowId","message","content","messageType","msg","TIME_CONVERSION","id","created_at","messages","localMessages","allMessages","uniqueMessages","key","b","STORAGE_KEYS","WebSocketManager","dns","agentId","eventBus","reconnectConfig","debug","createLogger","forceNew","closingExisting","WEBSOCKET_CLOSE_CODES","keepIntentionalClose","url","wsUrl","params","message","payload","event","parsed","err","delay","state","flowId","ApiClient","dns","debug","createLogger","sessionId","limit","WIDGET_DEFAULTS","before","url","response","data","error","COMPILED_CSS","DOMBuilder","_DOMBuilder","element","tag","props","children","builder","className","style","dataset","attrs","on","rest","key","value","event","handler","classes","styles","processChild","child","html","text","name","condition","callback","parent","ShadowContainer","theme","DOMBuilder","Z_INDEX","COMPILED_CSS","css","style","ThemeManager","config","c","bg","isImage","import_lucide","logger","createLogger","iconCache","toPascalCase","str","part","lookupIcon","name","pascalName","data","isUrl","renderIcon","iconSource","size","color","className","renderImageIcon","renderLucideIcon","src","DOMBuilder","iconData","svg","createElement","tagName","attributes","children","element","key","value","child","elementData","IconRenderer","_IconRenderer","icon","defaultIcon","ICON_NAMES","defaultSize","options","w","h","wrapperStyles","iconSource","iconElement","sizeFactor","UI_FACTORS","iconSize","iconColor","rawIcon","renderIcon","objectFit","DOMBuilder","iconName","size","color","exceptionConfig","reason","isUserMessage","className","exceptionEl","StyleUtils","_StyleUtils","element","textConfig","defaults","color","font","size","classes","styles","property","value","name","variables","ButtonBuilder","icon","config","variant","iconElement","IconRenderer","ICON_SIZES","className","DOMBuilder","text","onClick","title","BaseComponent","hooks","createLogger","element","event","handler","parent","data","Header","BaseComponent","headerConfig","onClose","allowNewChat","onNewChat","headerColor","leftChildren","iconCfg","IconRenderer","ICON_NAMES","ICON_SIZES","textCfg","titleEl","DOMBuilder","StyleUtils","UI_TEXT","leftSection","rightChildren","ButtonBuilder","rightSection","header","state","config","FileUtils","bytes","shortFormat","k","sizes","i","file","resolve","reject","reader","base64","MessageBubble","BaseComponent","message","options","isUser","bubbleWidth","UI_FACTORS","iconCfg","iconEl","IconRenderer","ICON_NAMES","ICON_SIZES","BORDER_RADIUS","SPACING","bubble","DOMBuilder","row","fileData","fileContent","parsed","content","text","container","downloadLink","icon","UI_TEXT","sharedFileMsg","filename","filesize","fileInfo","FileUtils","date","MessageList","BaseComponent","showMoreConfig","primaryColor","userMessageWidth","botMessageWidth","userIcon","botIcon","DOMBuilder","message","isUser","opts","bubble","MessageBubble","welcome","titleRow","preTitleValue","pre","titleValue","title","subtitleValue","subtitle","sub","container","element","el","StyleUtils","onClick","baseClasses","stickyClasses","buttonChildren","iconEl","IconRenderer","ICON_NAMES","ICON_SIZES","textSpan","UI_TEXT","hasMore","FileUploadButton","config","callbacks","debug","createLogger","DOMBuilder","iconEl","IconRenderer","ICON_NAMES","ICON_SIZES","UI_TEXT","file","maxSize","WIDGET_DEFAULTS","maxSizeFormatted","FileUtils","base64Content","error","enabled","FilePreview","onRemove","DOMBuilder","fileData","previewWrapper","removeBtn","e","fileIcon","IconRenderer","ICON_SIZES","fileName","fileSize","FileUtils","contentArea","InputArea","BaseComponent","config","callbacks","debug","createLogger","FilePreview","FileUploadButton","file","base64","error","iconEl","IconRenderer","ICON_NAMES","ICON_SIZES","DOMBuilder","UI_TEXT","sharedInputClasses","sharedKeydownHandler","e","container","inputStyle","inputWrapper","uploadClasses","sendClasses","innerWrap","style","currentLength","maxLength","textarea","scrollH","INPUT_CONSTANTS","text","show","hasFilePreview","enabled","message","duration","ChatBubble","BaseComponent","config","onToggle","DOMBuilder","UI_TEXT","open","IconRenderer","ICON_NAMES","ICON_SIZES","ICON_SCALE","closeIcon","openIcon","BaseWindow","config","eventBus","session","wsManager","theme","ThemeManager","ShadowContainer","Header","MessageList","InputArea","content","file","base64","description","ChatBubble","unsub","base64Content","fileContent","messagePayload","event","handler","msg","handleHistoryUpdate","messages","hasMore","state","message","DOMBuilder","isMobileDevice","ChatWindow","BaseWindow","config","eventBus","session","wsManager","panel","isRight","isMobile","width","height","SidepanelWindow","BaseWindow","config","eventBus","session","wsManager","panel","isRight","isMobile","width","NimbusChat","config","validateConfig","resolveConfig","printBanner","createLogger","PerformanceTracker","EventBus","ChatSession","WebSocketManager","ApiClient","setMobileBreakpoint","isMobileDevice","SidepanelWindow","ChatWindow","event","msg","loadMore","mergeWithLocal","flowId","history","error","logger","createLogger","globalInstance","init","config","NimbusChat","getInstance","src_default","NimbusChat","init","getInstance"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/types/config.ts","../src/utils/validator.ts","../src/utils/device.ts","../src/utils/logger.ts","../src/utils/PerformanceTracker.ts","../src/core/EventBus.ts","../src/core/ChatSession.ts","../src/core/WebSocketManager.ts","../src/core/ApiClient.ts","../src/styles/compiled.ts","../src/utils/DOMBuilder.ts","../src/ui/ShadowContainer.ts","../src/ui/ThemeManager.ts","../src/icons/lucide.ts","../src/utils/IconRenderer.ts","../src/utils/StyleUtils.ts","../src/utils/ButtonBuilder.ts","../src/utils/ComponentFactory.ts","../src/ui/Header.ts","../src/utils/FileUtils.ts","../src/ui/MessageBubble.ts","../src/ui/MessageList.ts","../src/ui/FileUploadButton.ts","../src/ui/FilePreview.ts","../src/ui/InputArea.ts","../src/ui/ChatBubble.ts","../src/ui/BaseWindow.ts","../src/ui/ChatWindow.ts","../src/ui/SidepanelWindow.ts","../src/NimbusChat.ts"],"sourcesContent":["import { NimbusChat } from \"./NimbusChat\";\nimport type { NimbusChatConfig } from \"./types/config\";\nimport { createLogger } from \"./utils/logger\";\n\nconst logger = createLogger('SDK');\n\n// ── Singleton for CDN / script-tag usage ────────────────────────\n\nlet globalInstance: NimbusChat | null = null;\n\n/**\n * Initialize the NimbusChat widget (singleton pattern).\n * Use this when loading via CDN <script> tag.\n *\n * NimbusChat.init({ agent_version_id: \"550e8400-e29b-41d4-a716-446655440000\" });\n */\nfunction init(config: NimbusChatConfig): NimbusChat {\n if (globalInstance) {\n logger.warn(\"Already initialized. Returning existing instance.\");\n return globalInstance;\n }\n globalInstance = new NimbusChat(config);\n return globalInstance;\n}\n\n/**\n * Get the existing singleton instance (or null if not initialized).\n */\nfunction getInstance(): NimbusChat | null {\n return globalInstance;\n}\n\n// ── Exports ─────────────────────────────────────────────────────\n\n// Named exports for npm users\nexport { NimbusChat, init, getInstance };\nexport type { NimbusChatConfig } from \"./types/config\";\nexport type { ChatPosition, BubblePosition, BubbleConfig, ResolvedConfig, MessageConfig, ThemeConfig, HeaderConfig, TextConfig, TextElement, ColorPair, IconConfig, WelcomeConfig, InputConfig, SendButtonConfig } from \"./types/config\";\nexport { ICON_NAMES, ICON_SIZES } from \"./types/config\";\nexport type {\n ServerMessage,\n UserMessage,\n ChatMessage,\n MessageType,\n ConnectionState,\n} from \"./types/message\";\n\n// Default export for CDN UMD — tsup's globalName:\"NimbusChat\" exposes this on window\nexport default { NimbusChat, init, getInstance };\n","export type ChatPosition =\n | \"bottom-right\"\n | \"bottom-left\"\n | \"sidepanel-left\"\n | \"sidepanel-right\";\n\nexport type BubblePosition = \"bottom-right\" | \"bottom-left\";\n\n\n/** Reusable text configuration used throughout the SDK. */\nexport interface TextConfig {\n /** Whether to display this text element. Not used everywhere. */\n display?: boolean;\n /** The text value / content. */\n value?: string;\n /** Text color (CSS color string). */\n color?: string;\n /** Font family override. */\n font?: string;\n /** Font size in px. */\n size?: number;\n}\n\n/** Unified text element with value and nested styling */\nexport interface TextElement {\n /** The text value / content. */\n value?: string;\n /** Text styling configuration */\n text?: TextConfig;\n}\n\n/** Reusable color pair configuration for primary/secondary colors */\nexport interface ColorPair {\n /** Primary color (usually background or main color) */\n primary?: string;\n /** Secondary color (usually foreground or accent color) */\n secondary?: string;\n}\n\n/**\n * Icon configuration used throughout the SDK.\n * Pass `null` to explicitly hide an icon.\n */\nexport interface IconConfig {\n /** Image URL for the icon. */\n img?: string;\n /** Size of the icon with width and height in pixels. */\n size?: { width: number; height: number };\n /** Icon stroke/fill color (CSS color string). Only applies to Lucide icons, ignored for image URLs. */\n color?: string;\n}\n\n\nexport interface ThemeConfig {\n /** Primary theme color — send button bg, header bg, bubble bg. */\n primary: string;\n /** Secondary theme color — send button icon, header text, bubble icon. */\n secondary: string;\n}\n\n\nexport interface HeaderConfig {\n /** Icon configuration for the header. Not shown if omitted/null. */\n icon?: IconConfig | null;\n /** Header text configuration. */\n text?: TextConfig;\n /** Header color configuration (primary = background, secondary = close icon). */\n color?: ColorPair;\n}\n\n\nexport interface WelcomeConfig {\n /** Whether to display the welcome message. Default: true */\n display?: boolean;\n /** Pre-title text (e.g. \"Welcome to \"). */\n preTitle?: TextElement | TextConfig;\n /** Title text (e.g. \"Nimbus\"). */\n title?: TextElement | TextConfig;\n /** Subtitle text (e.g. \"Send a message to start a conversation\"). */\n subtitle?: TextElement | TextConfig;\n}\n\n\nexport interface MessageConfig {\n /** Bubble background color. */\n background?: string;\n /** Width of the message bubble (e.g. \"90%\", \"300px\"). Default: \"70%\" */\n width?: string;\n /** Text configuration for the message. */\n text?: TextConfig;\n /** Avatar icon configuration. Pass null to hide, omit to not show. */\n icon?: IconConfig | null;\n}\n\n\nexport interface TypingIndicatorConfig {\n /** Where to display the indicator: \"top\" = header status area, \"bottom\" = above input. Default: \"top\" */\n position?: \"top\" | \"bottom\";\n /** Title configuration for typing indicator */\n title?: {\n value?: string;\n text?: TextConfig;\n };\n}\n\nexport interface WaitForReplyConfig {\n /** Timeout in milliseconds. Default: 10000 (10 seconds) */\n timeout?: number;\n /** Wait for bot's first message before allowing user to type in new conversation. Default: false */\n firstReply?: boolean;\n}\n\n\nexport interface MobileConfig {\n /** Widget position on mobile devices. If not set, uses the regular position. */\n position?: ChatPosition;\n /** Viewport width breakpoint to consider as mobile (e.g. \"480px\"). If set, overrides user-agent detection. */\n breakpoint?: string;\n}\n\nexport interface StyleConfig {\n /** Width of the chat window/sidepanel (e.g. \"380px\", \"100%\"). Default: \"380px\" */\n width?: string;\n /** Height of the chat window (e.g. \"560px\", \"100%\"). Only applies to window mode. Default: \"560px\" */\n height?: string;\n /** Widget position on the page. Default: \"bottom-right\" */\n position?: ChatPosition;\n /** Mobile-specific overrides */\n mobile?: MobileConfig;\n /** Global font family for the entire chat widget. No default (uses system font). */\n font?: string;\n /** Chat body background — can be a CSS color or an image URL. Default: \"#ffffff\" */\n background?: string;\n}\n\nexport interface ReconnectConfig {\n /** Maximum number of reconnection attempts. Default: 5 */\n attempts?: number;\n /** Delay between reconnection attempts in ms. Default: 5000 (5 seconds) */\n timeout?: number;\n}\n\n\n\n\nexport interface ShowMoreConfig {\n /** Button text. Default: \"Show More\" */\n value?: string;\n /** Whether button should be sticky at top. Default: true */\n sticky?: boolean;\n /** Button background color. Default: theme.secondary */\n background?: string;\n /** Text styling for the button */\n text?: TextConfig;\n /** Icon configuration for the button */\n icon?: IconConfig;\n}\n\n\nexport interface UploadConfig {\n /** Maximum file size in bytes. Default: 5MB */\n maxFileSize?: number;\n /** Array of allowed MIME types. */\n allowedFileTypes?: string[];\n /** Icon configuration for the upload button. */\n icon?: IconConfig | null;\n /** Duration in milliseconds for error message display. Default: 2000 */\n errorDisplayDuration?: number;\n}\n\nexport interface MaxCharactersConfig {\n /** Maximum number of characters allowed. If null/undefined, unlimited */\n limit: number;\n /** Text styling configuration */\n text?: TextConfig;\n}\n\nexport interface InputConfig {\n /** Placeholder text for the input. Default: \"Ask Nimbus\" */\n placeholder?: string;\n /** If true, the input expands to a textarea. Default: false */\n expandable?: boolean;\n /** Text styling for the input field. */\n text?: TextConfig;\n /** Background colors for the input (primary = input area, secondary = container). */\n background?: ColorPair;\n /** File upload configuration */\n upload?: UploadConfig;\n /** Character limit configuration with counter display */\n maxCharacters?: MaxCharactersConfig;\n}\n\n\nexport interface SendButtonConfig {\n /** Custom icon configuration. Pass null to hide, omit for default. */\n icon?: IconConfig | null;\n /** If true, align input and send button horizontally on the same row. Default: false */\n align?: boolean;\n}\n\n\n\nexport interface BubbleConfig {\n /** Position of the floating bubble. Default: \"bottom-right\" */\n position?: BubblePosition;\n /** If true, hide the bubble when the chat panel is open. Default: false */\n autoHide?: boolean;\n /** Custom icon configuration. Pass null to hide custom, omit for default. */\n icon?: IconConfig | null;\n}\n\nexport interface NimbusChatConfig {\n /** Required — the agent version ID (UUID) for this chat instance */\n agent_version_id: string;\n\n /** DNS endpoint for chat services. Default: \"api.nimbus.ai/api/v1/webchat\" */\n dns?: string;\n\n /** Whether to resume previous conversation on reconnect. Default: false */\n resumeConversation?: boolean;\n\n /** Style configuration (position, dimensions, font, background, mobile) */\n style?: StyleConfig;\n\n /** Number of messages to load per page. Default: 10 */\n messagesPerPage?: number;\n \n /** Bubble configuration (position, autoHide, icon) */\n bubble?: BubbleConfig;\n\n /** Theme colors (primary, secondary) */\n theme?: Partial<ThemeConfig>;\n\n /** User message styling */\n userMessage?: MessageConfig;\n\n /** Bot message styling */\n botMessage?: MessageConfig;\n\n /** Header configuration (icon, text, color) */\n header?: HeaderConfig;\n\n /** Input field configuration */\n input?: InputConfig;\n\n /** Send button configuration */\n sendButton?: SendButtonConfig;\n\n /** Welcome message configuration shown when chat is empty. */\n welcome?: WelcomeConfig;\n\n /** Enable test mode. When true, appends ?test=True to WebSocket connection. Default: false */\n test?: boolean;\n\n /** Enable debug logging to console. Default: false */\n debug?: boolean;\n\n /** Reconnection configuration for unexpected disconnections */\n reconnect?: ReconnectConfig;\n \n /** Show new chat button in header. Default: false */\n allowNewChat?: boolean;\n \n /** Wait for bot reply before allowing next user message */\n waitForReply?: WaitForReplyConfig;\n \n /** Typing indicator configuration. Independent from waitForReply. */\n isTypingIndicator?: TypingIndicatorConfig;\n \n /** Show More button styling */\n showMore?: ShowMoreConfig;\n}\n\n\nexport interface ResolvedConfig {\n agent_version_id: string;\n dns: string;\n resumeConversation: boolean;\n style: {\n position: ChatPosition;\n mobile?: {\n position?: ChatPosition;\n breakpoint?: string;\n };\n width: string;\n height: string;\n font?: string;\n background: string;\n };\n messagesPerPage: number;\n bubble: Required<Pick<BubbleConfig, \"position\" | \"autoHide\">> &\n Pick<BubbleConfig, \"icon\">;\n theme: ThemeConfig;\n userMessage: {\n background: string;\n width: string;\n text: TextConfig;\n icon?: IconConfig | null;\n };\n botMessage: {\n background: string;\n width: string;\n text: TextConfig;\n icon?: IconConfig | null;\n };\n header: HeaderConfig;\n input: {\n placeholder: string;\n expandable: boolean;\n text?: TextConfig;\n background?: ColorPair;\n upload?: UploadConfig;\n maxCharacters?: MaxCharactersConfig;\n };\n sendButton: Required<Pick<SendButtonConfig, \"align\">> &\n Pick<SendButtonConfig, \"icon\">;\n welcome: WelcomeConfig;\n test: boolean;\n debug: boolean;\n reconnect: ReconnectConfig;\n allowNewChat: boolean;\n waitForReply?: WaitForReplyConfig;\n isTypingIndicator?: TypingIndicatorConfig;\n showMore: ShowMoreConfig;\n}\n\n\n// ── Icon Constants ─────────────────────────────────────────────\n\n/** Standard icon names used throughout the SDK */\nexport const ICON_NAMES = {\n MESSAGE_CIRCLE: \"message-circle\",\n SEND: \"send\",\n CLOSE: \"x\",\n NEW_CHAT: \"rotate-cw\",\n CHEVRON_DOWN: \"chevron-down\",\n PAPERCLIP: \"paperclip\",\n TYPE: \"type\",\n} as const;\n\n/** Standard icon sizes used throughout the SDK */\nexport const ICON_SIZES = {\n BUTTON_SMALL: 16,\n BUTTON_MEDIUM: 18,\n AVATAR: 28,\n BUBBLE_OPEN: 26,\n} as const;\n\n/** Standard UI text labels used throughout the SDK */\nexport const UI_TEXT = {\n OPEN_CHAT: \"Open chat\",\n CLOSE_CHAT: \"Close chat\",\n NEW_CHAT: \"New Chat\",\n SEND: \"Send\",\n CONNECTED: \"Connected\",\n SHOW_MORE: \"Show More\",\n DOWNLOAD_FILE: \"Download file\",\n START_NEW_CHAT: \"Start new chat\",\n USE_HERE: \"Use here\",\n UPLOAD_FILE: \"Upload file\",\n} as const;\n\n/** Input area configuration constants */\nexport const INPUT_CONSTANTS = {\n MAX_LINES: 8,\n LINE_HEIGHT: 21,\n VERTICAL_PADDING: 20,\n get MAX_HEIGHT() {\n return this.MAX_LINES * this.LINE_HEIGHT + this.VERTICAL_PADDING;\n }\n} as const;\n\n/** Z-index values for proper layering */\nexport const Z_INDEX = {\n WIDGET_ROOT: 2147483647,\n} as const;\n\n/** WebSocket close codes */\nexport const WEBSOCKET_CLOSE_CODES = {\n NORMAL: 1000,\n SESSION_UNAVAILABLE: 4403,\n SESSION_CONFLICT: 4409,\n} as const;\n\n/** Default timeout values in milliseconds */\nexport const TIMEOUTS = {\n REPLY_WAIT: 10000,\n RECONNECT: 5000,\n} as const;\n\n/** Default widget dimensions and limits */\nexport const WIDGET_DEFAULTS = {\n WIDTH: \"380px\",\n HEIGHT: \"560px\",\n MESSAGE_LIMIT: 10,\n MESSAGES_PER_PAGE: 10,\n MAX_RECONNECT_ATTEMPTS: 5,\n MAX_FILE_SIZE: 5 * 1024 * 1024, // 5MB\n ERROR_DISPLAY_DURATION: 2000, // 2 seconds\n};\n\n/** UI sizing and scaling factors */\nexport const UI_FACTORS = {\n DEFAULT_BUBBLE_MAX_WIDTH: 280,\n DEFAULT_ICON_SIZE_FACTOR: 0.75,\n MESSAGE_AVATAR_SIZE_FACTOR: 1.0,\n} as const;\n\n/** Time conversion constants */\nexport const TIME_CONVERSION = {\n MILLISECONDS_TO_SECONDS: 1000,\n} as const;\n\n// ── Default Configurations ─────────────────────────────────────\n\nconst DEFAULT_THEME: ThemeConfig = {\n primary: \"#ffce1c\",\n secondary: \"#f3f1ef\",\n};\n\nconst DEFAULT_MESSAGE_WIDTH = \"80%\";\n\nconst DEFAULT_USER_MESSAGE = {\n background: \"#DCF8C6\",\n width: DEFAULT_MESSAGE_WIDTH,\n text: { color: \"#111B21\", size: 13 } as TextConfig,\n icon: { img: \"user\", size: { width: 20, height: 20 } } as IconConfig,\n};\n\nconst DEFAULT_BOT_MESSAGE = {\n background: \"#FFFFFF\",\n width: DEFAULT_MESSAGE_WIDTH,\n text: { color: \"#111B21\", size: 13 } as TextConfig,\n icon: { img: \"bot\", size: { width: 20, height: 20 } } as IconConfig,\n};\n\nconst DEFAULT_BUBBLE: BubbleConfig = {\n position: \"bottom-right\" as BubblePosition,\n autoHide: false,\n icon: {\n img: \"message-circle\",\n size: { width: 35, height: 35 },\n },\n};\n\nconst DEFAULT_WELCOME: WelcomeConfig = {\n display: true,\n preTitle: { value: \"Welcome to :\", text: { color: \"#1e293b\" } },\n title: { value: \" Nimbus Chat!\", text: { color: \"#1e293b\" } },\n subtitle: { value: \"Send a message to start a conversation\", text: { color: \"gray\" } },\n};\n\nconst DEFAULT_SEND_BUTTON: SendButtonConfig = {\n align: false,\n};\n\nconst DEFAULT_TYPING_INDICATOR: TypingIndicatorConfig = {\n position: \"top\",\n title: {\n value: \"AI Assistant is typing...\",\n text: {\n color: \"#1e293b\",\n font: \"\",\n }\n },\n};\n\nconst DEFAULT_WAIT_FOR_REPLY: WaitForReplyConfig = {\n timeout: TIMEOUTS.REPLY_WAIT,\n};\n\nconst DEFAULT_SHOW_MORE: ShowMoreConfig = {\n value: \"Show More\",\n sticky: true,\n background: \"#FFF4CC\",\n text: {\n color: \"black\",\n size: 13,\n },\n icon: {\n img: \"chevron-down\",\n size: { width: 16, height: 16 },\n },\n};\n\nconst DEFAULT_UPLOAD: UploadConfig = {\n maxFileSize: WIDGET_DEFAULTS.MAX_FILE_SIZE,\n errorDisplayDuration: WIDGET_DEFAULTS.ERROR_DISPLAY_DURATION,\n allowedFileTypes: [\n \"image/jpeg\",\n \"image/png\",\n \"image/gif\",\n \"image/webp\",\n \"application/pdf\",\n \"application/msword\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.ms-excel\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ],\n};\n\nconst DEFAULT_RECONNECT: ReconnectConfig = {\n attempts: WIDGET_DEFAULTS.MAX_RECONNECT_ATTEMPTS,\n timeout: TIMEOUTS.RECONNECT,\n};\n\n\nconst DEFAULT_STYLE = {\n position: \"bottom-right\" as ChatPosition,\n width: WIDGET_DEFAULTS.WIDTH as string,\n height: WIDGET_DEFAULTS.HEIGHT as string,\n background: \"#f3f1ef\",\n};\n\nexport const DEFAULT_CONFIG = {\n dns: \"api.nimbus.ai/api/v1/webchat\",\n style: DEFAULT_STYLE,\n messagesPerPage: WIDGET_DEFAULTS.MESSAGES_PER_PAGE,\n bubble: DEFAULT_BUBBLE,\n theme: DEFAULT_THEME,\n userMessage: DEFAULT_USER_MESSAGE,\n botMessage: DEFAULT_BOT_MESSAGE,\n header: {\n icon: null,\n text: { value: \"Nimbus AI\", color: \"#1e293b\" },\n color: { primary: \"#f3f1ef\", secondary: \"#ffce1c\" },\n },\n input: {\n placeholder: \"Ask Nimbus\",\n expandable: true,\n text: { color: \"#1e293b\" },\n background: { primary: \"white\", secondary: \"#f3f1ef\" },\n },\n sendButton: {\n ...DEFAULT_SEND_BUTTON,\n icon: { img: \"send\", size: { width: 20, height: 20 } },\n },\n welcome: DEFAULT_WELCOME,\n debug: false,\n reconnect: DEFAULT_RECONNECT,\n allowNewChat: false,\n waitForReply: DEFAULT_WAIT_FOR_REPLY,\n showMore: DEFAULT_SHOW_MORE,\n icon: {\n defaultSize: 24,\n defaultColor: \"currentColor\",\n },\n};\n\nexport function resolveConfig(config: NimbusChatConfig): ResolvedConfig {\n const s = config.style;\n const resolved = {\n agent_version_id: config.agent_version_id,\n dns: config.dns ?? DEFAULT_CONFIG.dns,\n resumeConversation: config.resumeConversation ?? false,\n style: {\n position: s?.position ?? DEFAULT_STYLE.position,\n mobile: s?.mobile ? {\n position: s.mobile.position,\n breakpoint: s.mobile.breakpoint,\n } : undefined,\n width: s?.width ?? DEFAULT_STYLE.width,\n height: s?.height ?? DEFAULT_STYLE.height,\n font: s?.font,\n background: s?.background ?? DEFAULT_STYLE.background,\n },\n messagesPerPage: config.messagesPerPage ?? DEFAULT_CONFIG.messagesPerPage,\n bubble: {\n position: config.bubble?.position ?? DEFAULT_BUBBLE.position!,\n autoHide: config.bubble?.autoHide ?? DEFAULT_BUBBLE.autoHide!,\n icon: config.bubble?.icon ?? DEFAULT_BUBBLE.icon,\n },\n theme: { ...DEFAULT_THEME, ...config.theme },\n userMessage: {\n background: config.userMessage?.background ?? DEFAULT_USER_MESSAGE.background,\n width: config.userMessage?.width ?? DEFAULT_USER_MESSAGE.width,\n text: { ...DEFAULT_USER_MESSAGE.text, ...config.userMessage?.text },\n icon: config.userMessage?.icon !== undefined ? config.userMessage.icon : DEFAULT_USER_MESSAGE.icon,\n },\n botMessage: {\n background: config.botMessage?.background ?? DEFAULT_BOT_MESSAGE.background,\n width: config.botMessage?.width ?? DEFAULT_BOT_MESSAGE.width,\n text: { ...DEFAULT_BOT_MESSAGE.text, ...config.botMessage?.text },\n icon: config.botMessage?.icon ?? DEFAULT_BOT_MESSAGE.icon,\n },\n header: {\n icon: config.header?.icon !== undefined ? config.header.icon : DEFAULT_CONFIG.header.icon,\n text: config.header?.text ?? DEFAULT_CONFIG.header.text,\n color: config.header?.color ?? DEFAULT_CONFIG.header.color,\n },\n input: {\n placeholder: config.input?.placeholder ?? DEFAULT_CONFIG.input.placeholder,\n expandable: config.input?.expandable ?? DEFAULT_CONFIG.input.expandable,\n text: config.input?.text ?? DEFAULT_CONFIG.input.text,\n background: config.input?.background ?? DEFAULT_CONFIG.input.background,\n upload: config.input?.upload ? {\n maxFileSize: config.input.upload.maxFileSize ?? DEFAULT_UPLOAD.maxFileSize,\n errorDisplayDuration: config.input.upload.errorDisplayDuration ?? DEFAULT_UPLOAD.errorDisplayDuration,\n allowedFileTypes: config.input.upload.allowedFileTypes ?? DEFAULT_UPLOAD.allowedFileTypes,\n icon: config.input.upload.icon,\n } : undefined,\n maxCharacters: config.input?.maxCharacters ? {\n limit: config.input.maxCharacters.limit,\n text: config.input.maxCharacters.text,\n } : undefined,\n },\n sendButton: {\n align: config.sendButton?.align ?? DEFAULT_SEND_BUTTON.align!,\n icon: config.sendButton?.icon ?? DEFAULT_CONFIG.sendButton.icon,\n },\n welcome: {\n display: config.welcome?.display ?? DEFAULT_WELCOME.display,\n preTitle: { ...DEFAULT_WELCOME.preTitle, ...config.welcome?.preTitle },\n title: { ...DEFAULT_WELCOME.title, ...config.welcome?.title },\n subtitle: { ...DEFAULT_WELCOME.subtitle, ...config.welcome?.subtitle },\n },\n test: config.test ?? false,\n debug: config.debug ?? DEFAULT_CONFIG.debug,\n reconnect: {\n attempts: config.reconnect?.attempts ?? DEFAULT_RECONNECT.attempts!,\n timeout: config.reconnect?.timeout ?? DEFAULT_RECONNECT.timeout!,\n },\n allowNewChat: config.allowNewChat ?? DEFAULT_CONFIG.allowNewChat,\n waitForReply: config.waitForReply ? {\n timeout: config.waitForReply.timeout ?? DEFAULT_WAIT_FOR_REPLY.timeout,\n firstReply: config.waitForReply.firstReply ?? false,\n } : undefined,\n isTypingIndicator: config.isTypingIndicator ? {\n position: config.isTypingIndicator.position ?? DEFAULT_TYPING_INDICATOR.position,\n title: {\n value: config.isTypingIndicator.title?.value ?? DEFAULT_TYPING_INDICATOR.title?.value,\n text: config.isTypingIndicator.title?.text ?? DEFAULT_TYPING_INDICATOR.title?.text,\n },\n } : undefined,\n showMore: {\n value: config.showMore?.value ?? DEFAULT_CONFIG.showMore.value,\n sticky: config.showMore?.sticky ?? DEFAULT_CONFIG.showMore.sticky,\n background: config.showMore?.background ?? ({ ...DEFAULT_THEME, ...config.theme }).secondary,\n text: {\n color: config.showMore?.text?.color ?? DEFAULT_CONFIG.showMore.text?.color,\n font: config.showMore?.text?.font ?? s?.font,\n size: config.showMore?.text?.size ?? DEFAULT_CONFIG.showMore.text?.size,\n },\n icon: config.showMore?.icon ?? DEFAULT_CONFIG.showMore.icon,\n },\n };\n \n return resolved;\n}\n\n/** UI positioning and spacing constants */\nexport const SPACING = {\n BUBBLE_OFFSET: 84, // Distance bubble is offset from bottom\n EDGE_DISTANCE: 20, // Distance from viewport edges \n BUTTON_OFFSET: 7, // Precise button positioning\n AVATAR_MARGIN: 2, // Avatar bottom margin\n} as const;\n\n/** Border radius values used throughout */\nexport const BORDER_RADIUS = {\n AVATAR: \"50%\", // Circular avatars\n PANEL: \"12px\", // Panel corners\n PANEL_MOBILE: \"12px 12px 0 0\", // Mobile panel corners\n} as const;\n\n/** Icon scaling factors */ \nexport const ICON_SCALE = {\n BUBBLE_ICON: 0.65, // Chat bubble icon scaling\n} as const;\n","import type { NimbusChatConfig, ChatPosition, BubblePosition } from \"../types/config\";\n\nconst VALID_POSITIONS: ChatPosition[] = [\n \"bottom-right\",\n \"bottom-left\",\n \"sidepanel-left\",\n \"sidepanel-right\",\n];\n\nconst VALID_BUBBLE_POSITIONS: BubblePosition[] = [\n \"bottom-right\",\n \"bottom-left\",\n];\n\n\n/** Validate a TextConfig value. */\nfunction validateTextConfig(value: unknown, path: string): void {\n if (typeof value !== \"object\" || value === null) {\n throw new Error(`[NimbusChat] ${path} must be an object`);\n }\n const t = value as Record<string, unknown>;\n if (t.display !== undefined && typeof t.display !== \"boolean\") {\n throw new Error(`[NimbusChat] ${path}.display must be a boolean`);\n }\n if (t.value !== undefined && typeof t.value !== \"string\") {\n throw new Error(`[NimbusChat] ${path}.value must be a string`);\n }\n if (t.color !== undefined && typeof t.color !== \"string\") {\n throw new Error(`[NimbusChat] ${path}.color must be a string`);\n }\n if (t.font !== undefined && typeof t.font !== \"string\") {\n throw new Error(`[NimbusChat] ${path}.font must be a string`);\n }\n if (t.size !== undefined && typeof t.size !== \"number\") {\n throw new Error(`[NimbusChat] ${path}.size must be a number`);\n }\n}\n\n/** Validate an IconConfig value (or null). */\nfunction validateIconConfig(value: unknown, path: string): void {\n if (value === null) return;\n if (typeof value !== \"object\") {\n throw new Error(`[NimbusChat] ${path} must be an object or null`);\n }\n const icon = value as Record<string, unknown>;\n if (icon.img !== undefined && typeof icon.img !== \"string\") {\n throw new Error(`[NimbusChat] ${path}.img must be a string`);\n }\n if (icon.size !== undefined) {\n if (typeof icon.size !== \"object\" || icon.size === null) {\n throw new Error(`[NimbusChat] ${path}.size must be an object`);\n }\n const size = icon.size as Record<string, unknown>;\n if (size.width !== undefined && typeof size.width !== \"number\") {\n throw new Error(`[NimbusChat] ${path}.size.width must be a number`);\n }\n if (size.height !== undefined && typeof size.height !== \"number\") {\n throw new Error(`[NimbusChat] ${path}.size.height must be a number`);\n }\n }\n}\n\nexport function validateConfig(config: unknown): asserts config is NimbusChatConfig {\n if (!config || typeof config !== \"object\") {\n throw new Error(\"[NimbusChat] Config must be an object\");\n }\n\n const c = config as Record<string, unknown>;\n\n if (!c.agent_version_id || typeof c.agent_version_id !== \"string\" || c.agent_version_id.trim() === \"\") {\n throw new Error(\"[NimbusChat] agent_version_id is required and must be a non-empty UUID string\");\n }\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (!uuidRegex.test(c.agent_version_id.trim())) {\n throw new Error(\"[NimbusChat] agent_version_id must be a valid UUID (e.g. 550e8400-e29b-41d4-a716-446655440000)\");\n }\n if (c.wsUrl !== undefined && typeof c.wsUrl !== \"string\") {\n throw new Error(\"[NimbusChat] wsUrl must be a string\");\n }\n if (c.style !== undefined) {\n if (typeof c.style !== \"object\" || c.style === null) {\n throw new Error(\"[NimbusChat] style must be an object\");\n }\n const st = c.style as Record<string, unknown>;\n if (st.position !== undefined && !VALID_POSITIONS.includes(st.position as ChatPosition)) {\n throw new Error(`[NimbusChat] style.position must be one of: ${VALID_POSITIONS.join(\", \")}`);\n }\n if (st.background !== undefined && typeof st.background !== \"string\") {\n throw new Error(\"[NimbusChat] style.background must be a string\");\n }\n if (st.font !== undefined && typeof st.font !== \"string\") {\n throw new Error(\"[NimbusChat] style.font must be a string\");\n }\n }\n\n if (c.theme !== undefined) {\n if (typeof c.theme !== \"object\" || c.theme === null) {\n throw new Error(\"[NimbusChat] theme must be an object\");\n }\n const t = c.theme as Record<string, unknown>;\n if (t.primary !== undefined && typeof t.primary !== \"string\") {\n throw new Error(\"[NimbusChat] theme.primary must be a string\");\n }\n if (t.secondary !== undefined && typeof t.secondary !== \"string\") {\n throw new Error(\"[NimbusChat] theme.secondary must be a string\");\n }\n }\n\n if (c.bubble !== undefined) {\n if (typeof c.bubble !== \"object\" || c.bubble === null) {\n throw new Error(\"[NimbusChat] bubble must be an object\");\n }\n const bub = c.bubble as Record<string, unknown>;\n if (bub.position !== undefined && !VALID_BUBBLE_POSITIONS.includes(bub.position as BubblePosition)) {\n throw new Error(`[NimbusChat] bubble.position must be one of: ${VALID_BUBBLE_POSITIONS.join(\", \")}`);\n }\n if (bub.autoHide !== undefined && typeof bub.autoHide !== \"boolean\") {\n throw new Error(\"[NimbusChat] bubble.autoHide must be a boolean\");\n }\n if (bub.icon !== undefined) validateIconConfig(bub.icon, \"bubble.icon\");\n }\n\n if (c.userMessage !== undefined) {\n if (typeof c.userMessage !== \"object\" || c.userMessage === null) {\n throw new Error(\"[NimbusChat] userMessage must be an object\");\n }\n const msg = c.userMessage as Record<string, unknown>;\n if (msg.background !== undefined && typeof msg.background !== \"string\") {\n throw new Error(\"[NimbusChat] userMessage.background must be a string\");\n }\n if (msg.text !== undefined) validateTextConfig(msg.text, \"userMessage.text\");\n if (msg.icon !== undefined) validateIconConfig(msg.icon, \"userMessage.icon\");\n }\n\n if (c.botMessage !== undefined) {\n if (typeof c.botMessage !== \"object\" || c.botMessage === null) {\n throw new Error(\"[NimbusChat] botMessage must be an object\");\n }\n const msg = c.botMessage as Record<string, unknown>;\n if (msg.background !== undefined && typeof msg.background !== \"string\") {\n throw new Error(\"[NimbusChat] botMessage.background must be a string\");\n }\n if (msg.text !== undefined) validateTextConfig(msg.text, \"botMessage.text\");\n if (msg.icon !== undefined) validateIconConfig(msg.icon, \"botMessage.icon\");\n }\n\n if (c.header !== undefined) {\n if (typeof c.header !== \"object\" || c.header === null) {\n throw new Error(\"[NimbusChat] header must be an object\");\n }\n const hdr = c.header as Record<string, unknown>;\n if (hdr.icon !== undefined) validateIconConfig(hdr.icon, \"header.icon\");\n if (hdr.text !== undefined) validateTextConfig(hdr.text, \"header.text\");\n if (hdr.color !== undefined) {\n if (typeof hdr.color !== \"object\" || hdr.color === null) {\n throw new Error(\"[NimbusChat] header.color must be an object\");\n }\n const hc = hdr.color as Record<string, unknown>;\n if (hc.primary !== undefined && typeof hc.primary !== \"string\") {\n throw new Error(\"[NimbusChat] header.color.primary must be a string\");\n }\n if (hc.secondary !== undefined && typeof hc.secondary !== \"string\") {\n throw new Error(\"[NimbusChat] header.color.secondary must be a string\");\n }\n }\n }\n\n if (c.input !== undefined) {\n if (typeof c.input !== \"object\" || c.input === null) {\n throw new Error(\"[NimbusChat] input must be an object\");\n }\n const inp = c.input as Record<string, unknown>;\n if (inp.placeholder !== undefined && typeof inp.placeholder !== \"string\") {\n throw new Error(\"[NimbusChat] input.placeholder must be a string\");\n }\n if (inp.expandable !== undefined && typeof inp.expandable !== \"boolean\") {\n throw new Error(\"[NimbusChat] input.expandable must be a boolean\");\n }\n if (inp.text !== undefined) validateTextConfig(inp.text, \"input.text\");\n if (inp.background !== undefined) {\n if (typeof inp.background !== \"object\" || inp.background === null) {\n throw new Error(\"[NimbusChat] input.background must be an object\");\n }\n const bg = inp.background as Record<string, unknown>;\n if (bg.primary !== undefined && typeof bg.primary !== \"string\") {\n throw new Error(\"[NimbusChat] input.background.primary must be a string\");\n }\n if (bg.secondary !== undefined && typeof bg.secondary !== \"string\") {\n throw new Error(\"[NimbusChat] input.background.secondary must be a string\");\n }\n }\n }\n\n if (c.sendButton !== undefined) {\n if (typeof c.sendButton !== \"object\" || c.sendButton === null) {\n throw new Error(\"[NimbusChat] sendButton must be an object\");\n }\n const btn = c.sendButton as Record<string, unknown>;\n if (btn.icon !== undefined) validateIconConfig(btn.icon, \"sendButton.icon\");\n if (btn.align !== undefined && typeof btn.align !== \"boolean\") {\n throw new Error(\"[NimbusChat] sendButton.align must be a boolean\");\n }\n }\n\n if (c.welcome !== undefined) {\n if (typeof c.welcome !== \"object\" || c.welcome === null) {\n throw new Error(\"[NimbusChat] welcome must be an object\");\n }\n const w = c.welcome as Record<string, unknown>;\n if (w.display !== undefined && typeof w.display !== \"boolean\") {\n throw new Error(\"[NimbusChat] welcome.display must be a boolean\");\n }\n if (w.preTitle !== undefined) validateTextConfig(w.preTitle, \"welcome.preTitle\");\n if (w.title !== undefined) validateTextConfig(w.title, \"welcome.title\");\n if (w.subtitle !== undefined) validateTextConfig(w.subtitle, \"welcome.subtitle\");\n }\n}\n","let cachedBreakpoint: string | undefined;\nlet cachedResult: boolean | null = null;\n\nexport function setMobileBreakpoint(breakpoint?: string): void {\n if (breakpoint) {\n cachedBreakpoint = breakpoint;\n cachedResult = null;\n }\n}\n\nexport function isMobileDevice(): boolean {\n if (cachedResult === null) {\n const userAgent = navigator.userAgent.toLowerCase();\n cachedResult = /mobile|android|iphone|ipad|ipod|blackberry|windows phone/.test(userAgent);\n }\n\n if (cachedBreakpoint) {\n const px = parseInt(cachedBreakpoint, 10);\n if (!isNaN(px) && window.innerWidth <= px) return true;\n }\n\n return cachedResult;\n}\n","type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst LEVEL_CONFIG: Record<LogLevel, { badge: string; bg: string; fg: string; consoleFn: \"log\" | \"info\" | \"warn\" | \"error\" }> = {\n debug: { badge: \"DBG\", bg: \"#6B7280\", fg: \"#fff\", consoleFn: \"log\" },\n info: { badge: \"INF\", bg: \"#3B82F6\", fg: \"#fff\", consoleFn: \"info\" },\n warn: { badge: \"WRN\", bg: \"#F59E0B\", fg: \"#000\", consoleFn: \"warn\" },\n error: { badge: \"ERR\", bg: \"#EF4444\", fg: \"#fff\", consoleFn: \"error\" },\n};\n\nconst COMPONENT_COLORS: Record<string, string> = {\n websocket: \"#10B981\",\n ws: \"#10B981\",\n api: \"#F97316\",\n ui: \"#EC4899\",\n window: \"#EC4899\",\n session: \"#06B6D4\",\n perf: \"#22C55E\",\n};\n\nconst DEFAULT_COMPONENT_COLOR = \"#8B5CF6\";\n\nconst BADGE_STYLE = (bg: string, fg: string) =>\n `background:${bg};color:${fg};padding:1px 6px;border-radius:3px;font-weight:600;font-size:11px`;\n\nconst COMPONENT_STYLE = (color: string) =>\n `background:${color}22;color:${color};padding:1px 6px;border-radius:3px;font-weight:600;font-size:11px`;\n\nconst TIMESTAMP_STYLE = \"color:#9CA3AF;font-size:10px;font-weight:400\";\nconst MESSAGE_STYLE = \"color:#E5E7EB;font-weight:400\";\n\nfunction getComponentColor(component: string): string {\n const lower = component.toLowerCase();\n for (const [key, color] of Object.entries(COMPONENT_COLORS)) {\n if (lower.includes(key)) return color;\n }\n return DEFAULT_COMPONENT_COLOR;\n}\n\nfunction sanitizeArgs(args: unknown[]): unknown[] {\n return args.map(arg => {\n if (arg && typeof arg === 'object') {\n const obj = arg as Record<string, unknown>;\n if (obj.type === 'file' && obj.content) {\n return { ...obj, content: '[base64 omitted]' };\n }\n }\n return arg;\n });\n}\n\nfunction tryParseJSON(str: string): unknown | null {\n try {\n return JSON.parse(str);\n } catch {\n return null;\n }\n}\n\nfunction sanitizeFilePayload(message: string): { sanitized: string; json: unknown | null } {\n const wsMatch = message.match(/^(Sending:|Received:)\\s*(.+)$/s);\n\n if (wsMatch) {\n const [, prefix, payload] = wsMatch;\n const parsed = tryParseJSON(payload);\n if (parsed && typeof parsed === 'object') {\n const obj = parsed as Record<string, unknown>;\n if (obj.type === 'file' && obj.content) {\n const clean = { ...obj, content: '[base64 omitted]' };\n return { sanitized: `${prefix}`, json: clean };\n }\n return { sanitized: `${prefix}`, json: parsed };\n }\n }\n\n const parsed = tryParseJSON(message);\n if (parsed && typeof parsed === 'object') {\n const obj = parsed as Record<string, unknown>;\n if (obj.type === 'file' && obj.content) {\n return { sanitized: '', json: { ...obj, content: '[base64 omitted]' } };\n }\n return { sanitized: '', json: parsed };\n }\n\n return { sanitized: message, json: null };\n}\n\nexport type { Logger };\n\nclass Logger {\n private component: string;\n private enabled: boolean;\n private color: string;\n\n constructor(component: string, enabled = false) {\n this.component = component;\n this.enabled = enabled;\n this.color = getComponentColor(component);\n }\n\n private log(level: LogLevel, message: string, ...args: unknown[]): void {\n if (!this.enabled) return;\n\n const { badge, bg, fg, consoleFn } = LEVEL_CONFIG[level];\n const time = new Date().toISOString().substring(11, 23);\n\n const { sanitized, json } = sanitizeFilePayload(message);\n const cleanArgs = sanitizeArgs(args);\n\n const parts = [\n `%c${badge}%c ${this.component} %c${time}`,\n ];\n const styles = [\n BADGE_STYLE(bg, fg),\n COMPONENT_STYLE(this.color),\n TIMESTAMP_STYLE,\n ];\n\n if (sanitized) {\n parts[0] += ` %c${sanitized}`;\n styles.push(MESSAGE_STYLE);\n }\n\n const allArgs: unknown[] = [parts[0], ...styles];\n\n if (json) {\n allArgs.push(json);\n }\n\n allArgs.push(...cleanArgs);\n\n console[consoleFn](...allArgs);\n }\n\n debug(message: string, ...args: unknown[]): void {\n this.log(\"debug\", message, ...args);\n }\n\n info(message: string, ...args: unknown[]): void {\n this.log(\"info\", message, ...args);\n }\n\n warn(message: string, ...args: unknown[]): void {\n this.log(\"warn\", message, ...args);\n }\n\n error(message: string, ...args: unknown[]): void {\n this.log(\"error\", message, ...args);\n }\n\n object(label: string, obj: unknown): void {\n if (!this.enabled) return;\n this.debug(label);\n console.dir(obj, { depth: 3, colors: true });\n }\n\n group(label: string): void {\n if (!this.enabled) return;\n console.groupCollapsed(\n `%c${this.component}%c ${label}`,\n COMPONENT_STYLE(this.color),\n MESSAGE_STYLE,\n );\n }\n\n groupEnd(): void {\n if (!this.enabled) return;\n console.groupEnd();\n }\n\n child(subComponent: string): Logger {\n return new Logger(`${this.component}:${subComponent}`, this.enabled);\n }\n\n setEnabled(enabled: boolean): void {\n this.enabled = enabled;\n }\n\n table(data: unknown): void {\n if (!this.enabled) return;\n console.table(data);\n }\n}\n\nexport function createLogger(component: string, debug = false): Logger {\n return new Logger(component, debug);\n}\n\nlet bannerPrinted = false;\n\nexport function printBanner(version?: string): void {\n if (bannerPrinted) return;\n bannerPrinted = true;\n\n const art = [\n \"\",\n \" %c\",\n \" ███╗ ██╗██╗███╗ ███╗██████╗ ██╗ ██╗███████╗\",\n \" ████╗ ██║██║████╗ ████║██╔══██╗██║ ██║██╔════╝\",\n \" ██╔██╗ ██║██║██╔████╔██║██████╔╝██║ ██║███████╗\",\n \" ██║╚██╗██║██║██║╚██╔╝██║██╔══██╗██║ ██║╚════██║\",\n \" ██║ ╚████║██║██║ ╚═╝ ██║██████╔╝╚██████╔╝███████║\",\n \" ╚═╝ ╚═══╝╚═╝╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚══════╝\",\n \"\",\n ].join(\"\\n\");\n\n const subtitle = version\n ? `%c WebChat SDK v${version}`\n : \"%c WebChat SDK\";\n const line = \"%c ──────────────────────────────────────────────\";\n\n console.log(\n art + \"\\n\" + subtitle + \"\\n\" + line + \"\\n\",\n \"color:#ffce1c;font-weight:bold\",\n \"color:#9CA3AF;font-size:11px\",\n \"color:#374151\",\n );\n}\n","import { createLogger, type Logger } from './logger';\n\n/**\n * Tracks timing metrics for chat interactions.\n * Only logs when debug mode is enabled.\n */\nexport class PerformanceTracker {\n private logger: Logger;\n private sessionStartTime: number | null = null;\n private messageSendTime: number | null = null;\n private waitingForFirstMessage = false;\n private waitingForReply = false;\n\n constructor(debug: boolean) {\n this.logger = createLogger('Perf', debug);\n }\n\n /** Call when a new session starts (session:started event) */\n markSessionStarted(): void {\n this.sessionStartTime = performance.now();\n this.waitingForFirstMessage = true;\n this.logger.debug('Session started — waiting for first AI message');\n }\n\n /** Call when the user sends a message */\n markMessageSent(): void {\n this.messageSendTime = performance.now();\n this.waitingForReply = true;\n this.logger.debug('User message sent — waiting for AI response');\n }\n\n /** Call when a bot (OUTBOUND) message is received */\n markBotMessageReceived(): void {\n const now = performance.now();\n\n if (this.waitingForFirstMessage && this.sessionStartTime !== null) {\n const elapsed = now - this.sessionStartTime;\n this.logger.info(`First AI message: ${elapsed.toFixed(0)}ms (from session start)`);\n this.waitingForFirstMessage = false;\n this.sessionStartTime = null;\n }\n\n if (this.waitingForReply && this.messageSendTime !== null) {\n const elapsed = now - this.messageSendTime;\n this.logger.info(`AI response time: ${elapsed.toFixed(0)}ms (from user message)`);\n this.waitingForReply = false;\n this.messageSendTime = null;\n }\n }\n\n /** Reset all tracking state */\n reset(): void {\n this.sessionStartTime = null;\n this.messageSendTime = null;\n this.waitingForFirstMessage = false;\n this.waitingForReply = false;\n }\n}\n","import type { ChatMessage, ConnectionState, ServerEvent } from \"../types/message\";\nimport { createLogger, type Logger } from \"../utils/logger\";\n\nexport interface EventMap {\n \"ws:open\": [];\n \"ws:close\": [event: globalThis.CloseEvent];\n \"ws:error\": [event: Event];\n \"ws:message\": [event: ServerEvent];\n \"ws:request-connect\": [];\n \"session:id\": [flowId: string];\n \"session:connected\": [];\n \"session:cleared\": [];\n \"message:added\": [message: ChatMessage];\n \"history:loaded\": [messages: ChatMessage[]];\n \"history:merged\": [messages: ChatMessage[]];\n \"history:has_more\": [hasMore: boolean];\n \"connection:state\": [state: ConnectionState];\n \"ui:toggle\": [];\n \"ui:show-error\": [message: string];\n \"ui:show-more\": [];\n \"ui:start-new-chat\": [];\n}\n\nexport type EventName = keyof EventMap;\nexport type EventHandler<K extends EventName = EventName> = (...args: EventMap[K]) => void;\n\nexport class EventBus {\n private listeners = new Map<EventName, Set<EventHandler<any>>>();\n private logger: Logger;\n\n constructor() {\n this.logger = createLogger(\"EventBus\", false);\n }\n\n on<K extends EventName>(event: K, handler: EventHandler<K>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(handler);\n }\n\n off<K extends EventName>(event: K, handler: EventHandler<K>): void {\n this.listeners.get(event)?.delete(handler);\n }\n\n once<K extends EventName>(event: K, handler: EventHandler<K>): void {\n const wrapper = ((...args: EventMap[K]) => {\n handler(...args);\n this.off(event, wrapper);\n }) as EventHandler<K>;\n this.on(event, wrapper);\n }\n\n emit<K extends EventName>(event: K, ...args: EventMap[K]): void {\n this.listeners.get(event)?.forEach((handler) => {\n try {\n handler(...args);\n } catch (err) {\n this.logger.error(`EventBus error in \"${event}\" handler:`, err);\n }\n });\n }\n\n removeAll(): void {\n this.listeners.clear();\n }\n}\n","import type { ChatMessage, MessageType } from \"../types/message\";\nimport type { EventBus } from \"./EventBus\";\nimport { TIME_CONVERSION } from \"../types/config\";\n\n/**\n * Chat session manager with message history support.\n * Manages current flow and message list.\n */\nexport class ChatSession {\n private flowId: string | null = null;\n private messages: ChatMessage[] = [];\n private eventBus: EventBus;\n\n constructor(eventBus: EventBus) {\n this.eventBus = eventBus;\n }\n\n // ── Flow ID ──────────────────────────────────────────────────\n\n setFlowId(flowId: string): void {\n this.flowId = flowId;\n this.eventBus.emit(\"session:id\", flowId);\n }\n\n getFlowId(): string | null {\n return this.flowId;\n }\n\n // ── Messages ────────────────────────────────────────────────────\n\n addMessage(message: ChatMessage): void {\n this.messages.push(message);\n this.eventBus.emit(\"message:added\", message);\n }\n\n getMessages(): ChatMessage[] {\n return [...this.messages];\n }\n\n clearMessages(): void {\n this.messages = [];\n }\n\n // ── Factory helpers ─────────────────────────────────────────────\n\n createUserMessage(content: string | object, messageType: MessageType = \"text\"): ChatMessage {\n const msg: ChatMessage = {\n id: crypto.randomUUID(),\n direction: \"INBOUND\",\n message_type: messageType,\n content,\n created_at: Date.now() / TIME_CONVERSION.MILLISECONDS_TO_SECONDS,\n };\n this.addMessage(msg);\n return msg;\n }\n\n createBotMessage(\n content: string,\n id?: string,\n created_at?: number,\n ): ChatMessage {\n const msg: ChatMessage = {\n id: id ?? crypto.randomUUID(),\n direction: \"OUTBOUND\",\n message_type: \"text\",\n content,\n created_at: created_at ?? Date.now() / TIME_CONVERSION.MILLISECONDS_TO_SECONDS,\n };\n this.addMessage(msg);\n return msg;\n }\n\n loadHistoryMessages(messages: ChatMessage[]): void {\n this.messages = messages;\n this.eventBus.emit(\"history:loaded\", this.messages);\n }\n\n loadHistoryMessagesWithMerge(messages: ChatMessage[]): void {\n const localMessages = [...this.messages];\n\n const fetchedMessages = messages;\n\n const allMessages = [...fetchedMessages, ...localMessages];\n\n const uniqueMessages = new Map<string, ChatMessage>();\n\n for (const msg of allMessages) {\n const key = msg.id;\n\n if (!uniqueMessages.has(key)) {\n uniqueMessages.set(key, msg);\n }\n }\n\n this.messages = Array.from(uniqueMessages.values())\n .sort((a, b) => a.created_at - b.created_at);\n\n this.eventBus.emit(\"history:merged\", this.messages);\n }\n\n // ── Lifecycle ───────────────────────────────────────────────────\n\n clear(): void {\n this.flowId = null;\n this.messages = [];\n this.eventBus.emit(\"session:cleared\");\n }\n}\n","import type { EventBus } from \"./EventBus\";\nimport type { ConnectionState, ServerEvent, UserMessage } from \"../types/message\";\nimport type { ReconnectConfig } from \"../types/config\";\nimport { WEBSOCKET_CLOSE_CODES } from \"../types/config\";\nimport { createLogger, type Logger } from \"../utils/logger\";\n\nconst STORAGE_KEYS = {\n FLOW_ID: 'nimbus_webchat_flow_id',\n} as const;\n\n/**\n * Manages the WebSocket lifecycle: connect, send, receive, reconnect.\n * Supports flow-based connections with optional flow restoration.\n */\nexport class WebSocketManager {\n private ws: WebSocket | null = null;\n private dns: string;\n private agentVersionId: string;\n private flowId: string | null = null;\n private eventBus: EventBus;\n private reconnectAttempts = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private _state: ConnectionState = \"idle\";\n private intentionalClose = false;\n private logger: Logger;\n private reconnectConfig: ReconnectConfig;\n private testMode: boolean;\n\n constructor(\n dns: string,\n agentVersionId: string,\n eventBus: EventBus,\n reconnectConfig: ReconnectConfig,\n debug: boolean,\n testMode: boolean = false,\n ) {\n this.dns = dns;\n this.agentVersionId = agentVersionId;\n this.eventBus = eventBus;\n this.reconnectConfig = reconnectConfig;\n this.testMode = testMode;\n this.logger = createLogger(\"WebSocket\", debug);\n\n // Always load saved flow_id to reuse existing conversation\n this.flowId = this.loadFlowId();\n }\n\n // ── Public API ──────────────────────────────────────────────────\n\n get state(): ConnectionState {\n return this._state;\n }\n\n getFlowId(): string | null {\n return this.flowId;\n }\n\n connect(forceNew = false): void {\n this.logger.debug(`connect() called with forceNew=${forceNew}, ws state=${this.ws?.readyState}, flowId=${this.flowId}`);\n\n this.clearReconnectTimer();\n\n if (!forceNew && this.isConnectedOrConnecting()) {\n this.logger.debug(\"Already connected or connecting, skipping new connection\");\n return;\n }\n\n const closingExisting = this.closeExistingConnection(forceNew);\n\n this.resetConnectionState(closingExisting);\n\n this.createWebSocketConnection();\n }\n\n private isConnectedOrConnecting(): boolean {\n return this.ws !== null &&\n (this.ws.readyState === WebSocket.OPEN ||\n this.ws.readyState === WebSocket.CONNECTING);\n }\n\n private closeExistingConnection(forceNew: boolean): boolean {\n if (!forceNew || !this.ws) return false;\n\n if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {\n this.intentionalClose = true;\n this.ws.onopen = null;\n this.ws.onmessage = null;\n this.ws.onerror = null;\n this.ws.onclose = null;\n this.ws.close(WEBSOCKET_CLOSE_CODES.NORMAL, \"Creating new connection\");\n this.ws = null;\n return true;\n }\n\n this.ws = null;\n return false;\n }\n\n private resetConnectionState(keepIntentionalClose: boolean): void {\n if (!keepIntentionalClose) {\n this.intentionalClose = false;\n }\n\n this.setState(\"connecting\");\n }\n\n private createWebSocketConnection(): void {\n const url = this.buildWebSocketUrl();\n this.logger.info(`Connecting to ${url}`);\n\n this.ws = new WebSocket(url);\n this.ws.onopen = this.onOpen.bind(this);\n this.ws.onmessage = this.onMessage.bind(this);\n this.ws.onerror = this.onError.bind(this);\n this.ws.onclose = this.onClose.bind(this);\n }\n\n private buildWebSocketUrl(): string {\n const wsUrl = `wss://${this.dns}`;\n const params = new URLSearchParams();\n\n params.set('agent_version_id', this.agentVersionId);\n if (this.flowId) {\n params.set('flow_id', this.flowId);\n }\n if (this.testMode) {\n params.set('test', 'True');\n }\n\n return `${wsUrl}?${params.toString()}`;\n }\n\n disconnect(): void {\n this.intentionalClose = true;\n this.clearReconnectTimer();\n\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n this.ws.close(WEBSOCKET_CLOSE_CODES.NORMAL, \"Client disconnect\");\n }\n\n this.ws = null;\n this.setState(\"disconnected\");\n }\n\n send(message: UserMessage): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n this.logger.warn(\"Cannot send message — WebSocket not connected\");\n return;\n }\n const payload = JSON.stringify(message);\n this.logger.debug(`Sending: ${payload}`);\n this.ws.send(payload);\n }\n\n // ── WebSocket event handlers ────────────────────────────────────\n\n private onOpen(): void {\n this.logger.info(\"Connected\");\n this.reconnectAttempts = 0;\n this.intentionalClose = false;\n this.setState(\"connected\");\n this.eventBus.emit(\"ws:open\");\n }\n\n private onMessage(event: MessageEvent): void {\n this.logger.debug(`Received: ${event.data}`);\n try {\n const parsed: ServerEvent = JSON.parse(event.data);\n\n if (parsed.type === \"connected\") {\n this.flowId = parsed.flow_id;\n this.saveFlowId(parsed.flow_id);\n this.logger.info(`Connected with flow_id: ${parsed.flow_id}`);\n } else if (parsed.type === \"message\") {\n // Validate message: must have direction \"outbound\" and non-empty content\n if (parsed.direction !== \"outbound\" || !parsed.content) {\n this.logger.debug(\"Ignoring invalid message: missing/empty content or wrong direction\");\n return;\n }\n } else {\n // Silently ignore unknown event types\n this.logger.debug(`Ignoring unknown event type: ${(parsed as any).type}`);\n return;\n }\n\n this.eventBus.emit(\"ws:message\", parsed);\n } catch (err) {\n this.logger.error(\"Failed to parse WebSocket message:\", err);\n }\n }\n\n private onError(event: Event): void {\n this.logger.error(\"WebSocket error:\", event);\n this.eventBus.emit(\"ws:error\", event);\n }\n\n private onClose(event: CloseEvent): void {\n this.logger.info(`Connection closed: code=${event.code} reason=\"${event.reason}\"`);\n if (this.ws === event.target) {\n this.ws = null;\n }\n this.eventBus.emit(\"ws:close\", event);\n\n if (!this.intentionalClose) {\n this.attemptReconnect();\n } else {\n this.setState(\"disconnected\");\n }\n }\n\n // ── Reconnection ───────────────────────────────────────────────\n\n private attemptReconnect(): void {\n if (this.hasExceededMaxReconnectAttempts()) {\n this.logger.error(\"Max reconnect attempts reached\");\n this.setState(\"disconnected\");\n return;\n }\n\n const delay = this.calculateReconnectDelay();\n this.scheduleReconnect(delay);\n }\n\n private hasExceededMaxReconnectAttempts(): boolean {\n return this.reconnectAttempts >= this.reconnectConfig.attempts!;\n }\n\n private calculateReconnectDelay(): number {\n return this.reconnectConfig.timeout!;\n }\n\n private scheduleReconnect(delay: number): void {\n this.setState(\"connecting\");\n this.reconnectAttempts++;\n\n this.logger.info(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.reconnectConfig.attempts})`);\n\n this.reconnectTimer = setTimeout(() => {\n this.connect();\n }, delay);\n }\n\n private clearReconnectTimer(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n // ── Helpers ────────────────────────────────────────────────────\n\n private setState(state: ConnectionState): void {\n this._state = state;\n this.eventBus.emit(\"connection:state\", state);\n }\n\n // ── Flow ID Management ──────────────────────────────────────────\n\n clearSession(): void {\n this.flowId = null;\n this.clearFlowId();\n this.eventBus.emit(\"session:cleared\");\n }\n\n private saveFlowId(flowId: string): void {\n try {\n localStorage.setItem(STORAGE_KEYS.FLOW_ID, flowId);\n } catch {\n }\n }\n\n private loadFlowId(): string | null {\n try {\n return localStorage.getItem(STORAGE_KEYS.FLOW_ID);\n } catch {\n return null;\n }\n }\n\n private clearFlowId(): void {\n try {\n localStorage.removeItem(STORAGE_KEYS.FLOW_ID);\n } catch {\n }\n }\n}\n","import type { ChatMessagesResponse } from \"../types/message\";\nimport { WIDGET_DEFAULTS } from \"../types/config\";\nimport { createLogger, type Logger } from \"../utils/logger\";\n\nexport class ApiClient {\n private dns: string;\n private logger: Logger;\n private abortController: AbortController | null = null;\n\n constructor(dns: string, debug: boolean) {\n this.dns = dns;\n this.logger = createLogger(\"API\", debug);\n }\n\n async fetchMessageHistory(\n sessionId: string,\n limit = WIDGET_DEFAULTS.MESSAGE_LIMIT,\n before?: number\n ): Promise<ChatMessagesResponse> {\n this.abortController?.abort();\n this.abortController = new AbortController();\n\n const url = new URL(`https://${this.dns}/sessions/${sessionId}/messages`);\n url.searchParams.set(\"limit\", limit.toString());\n if (before) {\n url.searchParams.set(\"before\", before.toString());\n }\n\n this.logger.debug(`Fetching history: ${url.toString()}`);\n\n try {\n const response = await fetch(url.toString(), {\n method: 'GET',\n headers: { 'Accept': 'application/json' },\n signal: this.abortController.signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const data: ChatMessagesResponse = await response.json();\n this.logger.debug(`Retrieved ${data.messages.length} messages, has_more: ${data.has_more}`);\n\n return data;\n } catch (error) {\n if (error instanceof DOMException && error.name === 'AbortError') {\n this.logger.debug(\"Request aborted\");\n throw error;\n }\n this.logger.error(\"Failed to fetch message history:\", error);\n throw error;\n }\n }\n\n abort(): void {\n this.abortController?.abort();\n this.abortController = null;\n }\n}\n","// AUTO-GENERATED — do not edit. Run \"npm run build:css\" to regenerate.\nexport const COMPILED_CSS = \"*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:\\\"\\\"}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.-right-1{right:-.25rem}.-top-1{top:-.25rem}.bottom-1{bottom:.25rem}.bottom-12{bottom:3rem}.bottom-2{bottom:.5rem}.bottom-5{bottom:1.25rem}.bottom-\\\\[10px\\\\]{bottom:10px}.left-0{left:0}.left-1{left:.25rem}.left-2{left:.5rem}.left-5{left:1.25rem}.right-0{right:0}.right-1{right:.25rem}.right-2{right:.5rem}.right-5{right:1.25rem}.top-0{top:0}.top-1{top:.25rem}.top-2{top:.5rem}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.my-3{margin-top:.75rem;margin-bottom:.75rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.ml-2{margin-left:.5rem}.mr-2{margin-right:.5rem}.mt-0{margin-top:0}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.contents{display:contents}.hidden{display:none}.h-14{height:3.5rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-screen{height:100vh}.max-h-48{max-height:12rem}.max-h-64{max-height:16rem}.w-14{width:3.5rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-full{width:100%}.max-w-\\\\[200px\\\\]{max-width:200px}.max-w-full{max-width:100%}.flex-1{flex:1 1 0%}.flex-shrink{flex-shrink:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-grow{flex-grow:1}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.resize{resize:both}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-2\\\\.5{gap:.625rem}.gap-3{gap:.75rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.rounded-t-xl{border-top-left-radius:.75rem;border-top-right-radius:.75rem}.rounded-bl-md{border-bottom-left-radius:.375rem}.rounded-br-md{border-bottom-right-radius:.375rem}.border{border-width:1px}.border-0{border-width:0}.border-t{border-top-width:1px}.border-slate-200{--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity,1))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.bg-black\\\\/10{background-color:rgba(0,0,0,.1)}.bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.bg-gray-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity,1))}.bg-green-400{--tw-bg-opacity:1;background-color:rgb(74 222 128/var(--tw-bg-opacity,1))}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity,1))}.bg-orange-400{--tw-bg-opacity:1;background-color:rgb(251 146 60/var(--tw-bg-opacity,1))}.bg-red-400{--tw-bg-opacity:1;background-color:rgb(248 113 113/var(--tw-bg-opacity,1))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-slate-100{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity,1))}.bg-slate-300{--tw-bg-opacity:1;background-color:rgb(203 213 225/var(--tw-bg-opacity,1))}.bg-slate-50{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity,1))}.bg-slate-600{--tw-bg-opacity:1;background-color:rgb(71 85 105/var(--tw-bg-opacity,1))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-yellow-400{--tw-bg-opacity:1;background-color:rgb(250 204 21/var(--tw-bg-opacity,1))}.bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(254 252 232/var(--tw-bg-opacity,1))}.object-cover{-o-object-fit:cover;object-fit:cover}.p-1{padding:.25rem}.p-1\\\\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\\\\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\\\\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\\\\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2{padding-bottom:.5rem}.pl-12{padding-left:3rem}.pl-4{padding-left:1rem}.pl-5{padding-left:1.25rem}.pl-6{padding-left:1.5rem}.pl-8{padding-left:2rem}.pr-12{padding-right:3rem}.pr-4{padding-right:1rem}.pr-6{padding-right:1.5rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.text-\\\\[10px\\\\]{font-size:10px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-red-50{--tw-text-opacity:1;color:rgb(254 242 242/var(--tw-text-opacity,1))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity,1))}.text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}.text-slate-500{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity,1))}.text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}.text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-yellow-700{--tw-text-opacity:1;color:rgb(161 98 7/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.opacity-50{opacity:.5}.opacity-75{opacity:.75}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-2xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring-blue-500{--tw-ring-opacity:1;--tw-ring-color:rgb(59 130 246/var(--tw-ring-opacity,1))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-shadow{transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.hover\\\\:scale-110:hover{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\\\\:bg-blue-600:hover{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.hover\\\\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.hover\\\\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.hover\\\\:bg-red-500:hover{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.hover\\\\:bg-slate-100:hover{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity,1))}.hover\\\\:bg-white\\\\/20:hover{background-color:hsla(0,0%,100%,.2)}.hover\\\\:text-blue-600:hover{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.hover\\\\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.hover\\\\:opacity-80:hover{opacity:.8}.focus\\\\:border-blue-300:focus{--tw-border-opacity:1;border-color:rgb(147 197 253/var(--tw-border-opacity,1))}.focus\\\\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\\\\:ring-blue-500\\\\/20:focus{--tw-ring-color:rgba(59,130,246,.2)}.disabled\\\\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\\\\:opacity-40:disabled{opacity:.4}\";\n","type ElementProps<T extends HTMLElement = HTMLElement> = Partial<Omit<T, 'style'>> & {\n className?: string;\n style?: Partial<CSSStyleDeclaration>;\n dataset?: Record<string, string>;\n attrs?: Record<string, string>;\n on?: Record<string, EventListenerOrEventListenerObject | ((e: never) => void)>;\n};\n\ntype Child = Node | DOMBuilder | string | null | undefined | false | Child[];\n\n/**\n * Fluent DOM builder for creating elements with a chainable API\n */\nexport class DOMBuilder<T extends HTMLElement = HTMLElement> {\n private element: T;\n\n constructor(element: T) {\n this.element = element;\n }\n\n /**\n * Create a new DOM element with optional properties and children\n */\n static create<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n props?: ElementProps<HTMLElementTagNameMap[K]>,\n ...children: Child[]\n ): DOMBuilder<HTMLElementTagNameMap[K]> {\n const element = document.createElement(tag);\n const builder = new DOMBuilder(element);\n \n if (props) {\n builder.props(props);\n }\n \n if (children.length > 0) {\n builder.append(...children);\n }\n \n return builder;\n }\n\n /**\n * Shorthand static methods for common elements\n */\n static div(props?: ElementProps<HTMLDivElement>, ...children: Child[]) {\n return this.create('div', props, ...children);\n }\n\n static span(props?: ElementProps<HTMLSpanElement>, ...children: Child[]) {\n return this.create('span', props, ...children);\n }\n\n static button(props?: ElementProps<HTMLButtonElement>, ...children: Child[]) {\n return this.create('button', props, ...children);\n }\n\n static img(props?: ElementProps<HTMLImageElement>) {\n return this.create('img', props);\n }\n\n static input(props?: ElementProps<HTMLInputElement>) {\n return this.create('input', props);\n }\n\n static textarea(props?: ElementProps<HTMLTextAreaElement>) {\n return this.create('textarea', props);\n }\n\n static form(props?: ElementProps<HTMLFormElement>, ...children: Child[]) {\n return this.create('form', props, ...children);\n }\n\n static label(props?: ElementProps<HTMLLabelElement>, ...children: Child[]) {\n return this.create('label', props, ...children);\n }\n\n /**\n * Apply properties to the element\n */\n props(props: ElementProps<T>): this {\n const { className, style, dataset, attrs, on, ...rest } = props;\n \n if (className) {\n this.element.className = className;\n }\n \n if (style) {\n Object.assign(this.element.style, style);\n }\n \n if (dataset) {\n Object.entries(dataset).forEach(([key, value]) => {\n this.element.dataset[key] = value;\n });\n }\n \n if (attrs) {\n Object.entries(attrs).forEach(([key, value]) => {\n this.element.setAttribute(key, value);\n });\n }\n \n if (on) {\n Object.entries(on).forEach(([event, handler]) => {\n this.element.addEventListener(event, handler as EventListenerOrEventListenerObject);\n });\n }\n \n // Apply remaining properties directly\n Object.entries(rest).forEach(([key, value]) => {\n (this.element as Record<string, unknown>)[key] = value;\n });\n \n return this;\n }\n\n /**\n * Add CSS classes\n */\n addClass(...classes: string[]): this {\n this.element.classList.add(...classes.filter(Boolean));\n return this;\n }\n\n /**\n * Set styles\n */\n setStyle(styles: Partial<CSSStyleDeclaration>): this {\n Object.assign(this.element.style, styles);\n return this;\n }\n\n /**\n * Add event listeners\n */\n on(event: string, handler: EventListener): this {\n this.element.addEventListener(event, handler);\n return this;\n }\n\n /**\n * Append children to the element\n */\n append(...children: Child[]): this {\n const processChild = (child: Child) => {\n if (child === null || child === undefined || child === false) return;\n \n if (Array.isArray(child)) {\n child.forEach(processChild);\n } else if (child instanceof DOMBuilder) {\n this.element.appendChild(child.build());\n } else if (child instanceof Node) {\n this.element.appendChild(child);\n } else if (typeof child === 'string') {\n this.element.appendChild(document.createTextNode(child));\n } else if (typeof child === 'number') {\n this.element.appendChild(document.createTextNode(String(child)));\n }\n };\n \n children.forEach(processChild);\n return this;\n }\n\n /**\n * Set innerHTML (use with caution)\n */\n html(html: string): this {\n this.element.innerHTML = html;\n return this;\n }\n\n /**\n * Set text content\n */\n text(text: string): this {\n this.element.textContent = text;\n return this;\n }\n\n /**\n * Set an attribute\n */\n attr(name: string, value: string): this {\n this.element.setAttribute(name, value);\n return this;\n }\n\n /**\n * Set data attribute\n */\n data(key: string, value: string): this {\n this.element.dataset[key] = value;\n return this;\n }\n\n /**\n * Conditionally apply modifications\n */\n if(condition: boolean, callback: (builder: DOMBuilder<T>) => void): this {\n if (condition) {\n callback(this);\n }\n return this;\n }\n\n /**\n * Get the built element\n */\n build(): T {\n return this.element;\n }\n\n /**\n * Mount the element to a parent\n */\n mountTo(parent: Element | DOMBuilder): T {\n const parentEl = parent instanceof DOMBuilder ? parent.build() : parent;\n parentEl.appendChild(this.element);\n return this.element;\n }\n}\n\n/**\n * Functional helper for creating elements (alternative to class-based approach)\n */\nexport function createElement<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n props?: ElementProps<HTMLElementTagNameMap[K]>,\n ...children: Child[]\n): HTMLElementTagNameMap[K] {\n return DOMBuilder.create(tag, props, ...children).build();\n}\n\n/**\n * Create a document fragment with multiple elements\n */\nexport function createFragment(...children: Child[]): DocumentFragment {\n const fragment = document.createDocumentFragment();\n const temp = DOMBuilder.create('div').append(...children).build();\n \n while (temp.firstChild) {\n fragment.appendChild(temp.firstChild);\n }\n \n return fragment;\n}","import { ThemeManager } from \"./ThemeManager\";\nimport { COMPILED_CSS } from \"../styles/compiled\";\nimport { Z_INDEX } from \"../types/config\";\nimport { DOMBuilder } from \"../utils/DOMBuilder\";\n\n/**\n * Creates the host element and Shadow DOM root.\n * Injects compiled Tailwind CSS + theme CSS variables into the shadow root.\n */\nexport class ShadowContainer {\n readonly host: HTMLElement;\n readonly root: ShadowRoot;\n\n constructor(theme: ThemeManager) {\n // Create host element using DOMBuilder\n this.host = DOMBuilder.div({\n id: \"nimbus-chat-root\",\n style: {\n all: \"initial\",\n position: \"fixed\",\n zIndex: String(Z_INDEX.WIDGET_ROOT)\n }\n }).mountTo(document.body);\n\n // Attach shadow root\n this.root = this.host.attachShadow({ mode: \"open\" });\n\n // Inject compiled Tailwind CSS (imported as string from build step)\n this.injectCSS(\n COMPILED_CSS ||\n \"*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\"\n );\n\n // Inject theme CSS custom properties\n this.injectCSS(theme.getCSSVariables());\n\n // Inject base component styles that use CSS variables\n this.injectCSS(this.getComponentCSS());\n }\n\n private injectCSS(css: string): void {\n const style = DOMBuilder.create('style')\n .text(css)\n .build();\n this.root.appendChild(style);\n }\n\n /**\n * Component-level styles that reference CSS custom properties for theming.\n */\n private getComponentCSS(): string {\n return `\n :host {\n font-family: var(--nimbus-font, inherit);\n font-size: 14px;\n line-height: 1.5;\n color: #1e293b;\n }\n\n .nimbus-header {\n background-color: var(--nimbus-header-bg, var(--nimbus-theme-primary, #2563eb));\n color: var(--nimbus-theme-secondary, #ffffff);\n }\n\n .nimbus-header button svg {\n color: var(--nimbus-header-close, var(--nimbus-theme-secondary, #ffffff));\n stroke: var(--nimbus-header-close, var(--nimbus-theme-secondary, #ffffff));\n }\n\n .nimbus-sidepanel {\n background-color: var(--nimbus-bg-color, #ffffff);\n }\n\n .nimbus-bubble {\n background-color: var(--nimbus-theme-primary, #2563eb);\n }\n\n .nimbus-bubble svg {\n color: var(--nimbus-theme-secondary, #ffffff);\n stroke: var(--nimbus-theme-secondary, #ffffff);\n }\n\n .nimbus-send-btn {\n background-color: var(--nimbus-theme-primary, #2563eb);\n }\n\n .nimbus-send-btn:hover {\n filter: brightness(0.9);\n }\n\n .nimbus-send-btn svg {\n color: var(--nimbus-theme-secondary, #ffffff);\n stroke: var(--nimbus-theme-secondary, #ffffff);\n }\n\n .nimbus-msg-user {\n background-color: var(--nimbus-user-msg-bg, #2563eb);\n color: var(--nimbus-user-msg-text, #fff);\n font-family: var(--nimbus-user-msg-font, inherit);\n font-size: var(--nimbus-user-msg-size, inherit);\n }\n\n .nimbus-msg-bot {\n background-color: var(--nimbus-bot-msg-bg, #f1f5f9);\n color: var(--nimbus-bot-msg-text, #1e293b);\n font-family: var(--nimbus-bot-msg-font, inherit);\n font-size: var(--nimbus-bot-msg-size, inherit);\n }\n\n .nimbus-input-field {\n font-family: var(--nimbus-input-font, inherit);\n background-color: var(--nimbus-input-bg, #f8fafc);\n }\n\n .nimbus-input-container {\n background-color: var(--nimbus-input-container-bg, #ffffff);\n }\n\n .nimbus-send-btn-square {\n border-radius: 0.375rem;\n }\n\n .nimbus-send-btn-text {\n display: flex;\n align-items: center;\n gap: 6px;\n color: var(--nimbus-theme-secondary, #fff);\n font-size: 13px;\n font-weight: 600;\n padding: 8px 16px;\n }\n\n /* Auto-expanding textarea (no manual resize) */\n .nimbus-textarea {\n resize: none;\n overflow-y: hidden;\n min-height: 45px;\n padding-right: 52px; /* room for send button inside */\n line-height: 1.5;\n transition: height 0.1s ease;\n }\n\n /* Scrollable once it hits max height */\n .nimbus-textarea.nimbus-textarea-scroll {\n overflow-y: auto !important;\n }\n\n .nimbus-textarea::-webkit-scrollbar {\n width: 4px;\n }\n .nimbus-textarea::-webkit-scrollbar-track {\n background: transparent;\n margin-right: 4px;\n }\n .nimbus-textarea::-webkit-scrollbar-thumb {\n background: #cbd5e1;\n border-radius: 2px;\n }\n\n /* Textarea wrapper for send btn */\n .nimbus-textarea-wrap {\n position: relative;\n }\n\n /* Send button positioned inside textarea (bottom-right) */\n .nimbus-send-inside {\n position: absolute;\n bottom: 9px;\n right: 5px;\n height: 35px;\n width: 35px;\n }\n\n .nimbus-chat-body {\n background-color: var(--nimbus-bg-color, #ffffff);\n background-image: var(--nimbus-bg-image, none);\n background-size: cover;\n background-position: center;\n }\n\n /* Scrollbar */\n .nimbus-message-list::-webkit-scrollbar {\n width: 6px;\n }\n .nimbus-message-list::-webkit-scrollbar-track {\n background: transparent;\n }\n .nimbus-message-list::-webkit-scrollbar-thumb {\n background: #cbd5e1;\n border-radius: 3px;\n }\n\n /* Typing indicator animation */\n @keyframes nimbus-bounce {\n 0%, 80%, 100% { transform: scale(0); }\n 40% { transform: scale(1); }\n }\n .nimbus-typing-dot {\n animation: nimbus-bounce 1.4s infinite ease-in-out both;\n }\n .nimbus-typing-dot:nth-child(1) { animation-delay: -0.32s; }\n .nimbus-typing-dot:nth-child(2) { animation-delay: -0.16s; }\n\n /* Loader spin */\n @keyframes nimbus-spin {\n to { transform: rotate(360deg); }\n }\n .nimbus-spin {\n animation: nimbus-spin 1s linear infinite;\n }\n\n /* Bubble pulse */\n @keyframes nimbus-pulse {\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(1.05); }\n }\n\n /* Transition helpers */\n .nimbus-fade-in {\n animation: nimbus-fade 0.2s ease-out;\n }\n @keyframes nimbus-fade {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n `;\n }\n\n /**\n * Remove the widget from the DOM entirely.\n */\n destroy(): void {\n this.host.remove();\n }\n}\n","import type { ResolvedConfig } from \"../types/config\";\n\n/**\n * Generates and manages CSS custom properties for runtime theming.\n */\nexport class ThemeManager {\n private config: ResolvedConfig;\n\n constructor(config: ResolvedConfig) {\n this.config = config;\n }\n\n /**\n * Returns a <style> block that sets CSS custom properties on :host.\n */\n getCSSVariables(): string {\n const c = this.config;\n\n // Detect if background is a URL (image) or a plain color\n const bg = c.style.background;\n const isImage = bg.startsWith(\"url(\") || bg.startsWith(\"http\") || bg.startsWith(\"/\");\n\n const vars = [\n ` --nimbus-theme-primary: ${c.theme.primary};`,\n ` --nimbus-theme-secondary: ${c.theme.secondary};`,\n ` --nimbus-bg-color: ${isImage ? \"transparent\" : bg};`,\n ` --nimbus-bg-image: ${isImage ? (bg.startsWith(\"url(\") ? bg : `url(${bg})`) : \"none\"};`,\n ` --nimbus-user-msg-bg: ${c.userMessage.background};`,\n ` --nimbus-user-msg-text: ${c.userMessage.text.color};`,\n ` --nimbus-bot-msg-bg: ${c.botMessage.background};`,\n ` --nimbus-bot-msg-text: ${c.botMessage.text.color};`,\n c.style.font ? ` --nimbus-font: ${c.style.font};` : null,\n c.userMessage.text.font ? ` --nimbus-user-msg-font: ${c.userMessage.text.font};` : null,\n c.userMessage.text.size ? ` --nimbus-user-msg-size: ${c.userMessage.text.size}px;` : null,\n c.botMessage.text.font ? ` --nimbus-bot-msg-font: ${c.botMessage.text.font};` : null,\n c.botMessage.text.size ? ` --nimbus-bot-msg-size: ${c.botMessage.text.size}px;` : null,\n c.input.text?.font ? ` --nimbus-input-font: ${c.input.text.font};` : null,\n c.input.background?.primary ? ` --nimbus-input-bg: ${c.input.background.primary};` : null,\n c.input.background?.secondary ? ` --nimbus-input-container-bg: ${c.input.background.secondary};` : null,\n ].filter(Boolean).join(\"\\n\");\n\n return `:host {\\n${vars}\\n}`;\n }\n}\n","import { icons } from 'lucide';\nimport { createLogger } from '../utils/logger';\nimport { DOMBuilder } from '../utils/DOMBuilder';\n\nconst logger = createLogger('Icons');\n\ntype SVGAttributes = Record<string, string | number>;\ntype SVGElementData = [string, SVGAttributes, ...SVGElementData[]];\ntype LucideIconData = SVGElementData[];\n\nconst iconCache = new Map<string, LucideIconData>();\n\nfunction toPascalCase(str: string): string {\n return str\n .split('-')\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n\nfunction lookupIcon(name: string): LucideIconData | undefined {\n if (iconCache.has(name)) {\n return iconCache.get(name);\n }\n\n const pascalName = toPascalCase(name);\n const data = (icons as Record<string, LucideIconData>)[pascalName];\n\n if (data) {\n iconCache.set(name, data);\n }\n\n return data;\n}\n\nfunction isUrl(str: string): boolean {\n return str.startsWith('http://') || str.startsWith('https://');\n}\n\nexport function renderIcon(\n iconSource: string,\n size: number,\n color: string = \"currentColor\",\n className?: string\n): SVGSVGElement | HTMLImageElement {\n if (isUrl(iconSource)) {\n return renderImageIcon(iconSource, size, className);\n } else {\n return renderLucideIcon(iconSource, size, color, className);\n }\n}\n\nfunction renderImageIcon(\n src: string,\n size: number,\n className?: string\n): HTMLImageElement {\n return DOMBuilder.img({\n src,\n width: size,\n height: size,\n className: className || '',\n style: { display: 'block' }\n }).build() as HTMLImageElement;\n}\n\nfunction renderLucideIcon(\n name: string,\n size: number,\n color: string,\n className?: string\n): SVGSVGElement {\n const iconData = lookupIcon(name);\n\n if (!iconData) {\n logger.warn(`Unknown icon: ${name}`);\n const empty = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n return empty;\n }\n\n const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 24 24\");\n svg.setAttribute(\"width\", String(size));\n svg.setAttribute(\"height\", String(size));\n svg.setAttribute(\"fill\", \"none\");\n svg.setAttribute(\"stroke\", color);\n svg.setAttribute(\"stroke-width\", \"2\");\n svg.setAttribute(\"stroke-linecap\", \"round\");\n svg.setAttribute(\"stroke-linejoin\", \"round\");\n if (className) svg.setAttribute(\"class\", className);\n\n function createElement(data: SVGElementData): Element {\n const [tagName, attributes, ...children] = data;\n const element = document.createElementNS(\"http://www.w3.org/2000/svg\", tagName);\n\n if (attributes && typeof attributes === 'object') {\n Object.entries(attributes).forEach(([key, value]) => {\n element.setAttribute(key, String(value));\n });\n }\n\n children.forEach(child => {\n if (Array.isArray(child)) {\n element.appendChild(createElement(child));\n }\n });\n\n return element;\n }\n\n iconData.forEach(elementData => {\n svg.appendChild(createElement(elementData));\n });\n\n return svg;\n}\n","import type { IconConfig } from \"../types/config\";\nimport { ICON_NAMES, UI_FACTORS } from \"../types/config\";\nimport { renderIcon } from \"../icons/lucide\";\nimport { DOMBuilder } from \"./DOMBuilder\";\n\nexport class IconRenderer {\n /**\n * Creates a wrapped icon element with proper sizing and styling.\n * \n * @param icon - Icon configuration (can be null/undefined)\n * @param defaultIcon - Default icon name to use if no icon.img provided\n * @param defaultSize - Default size in pixels\n * @param options - Additional styling options\n * @returns HTMLElement wrapper containing the icon\n */\n static create(\n icon: IconConfig | null | undefined,\n defaultIcon: string = ICON_NAMES.MESSAGE_CIRCLE,\n defaultSize: number = 24,\n options: {\n borderRadius?: string;\n marginBottom?: string;\n objectFit?: \"contain\" | \"cover\";\n sizeFactor?: number; // Multiplier for icon size within wrapper (0.65-0.75)\n } = {}\n ): HTMLElement {\n const w = icon?.size?.width ?? defaultSize;\n const h = icon?.size?.height ?? defaultSize;\n \n // Build wrapper styles\n const wrapperStyles: Partial<CSSStyleDeclaration> = {\n alignItems: \"center\",\n justifySelf: \"anchor-center\",\n width: `${w}px`,\n height: `${h}px`,\n overflow: \"hidden\",\n flexShrink: \"0\",\n };\n \n // Add optional styles\n if (options.borderRadius) {\n wrapperStyles.borderRadius = options.borderRadius;\n }\n if (options.marginBottom) {\n wrapperStyles.marginBottom = options.marginBottom;\n }\n \n // Only render icon if we have an image or default\n const iconSource = icon?.img ?? defaultIcon;\n let iconElement: HTMLElement | null = null;\n \n if (iconSource) {\n const sizeFactor = options.sizeFactor ?? UI_FACTORS.DEFAULT_ICON_SIZE_FACTOR;\n const iconSize = Math.min(w, h) * sizeFactor;\n const iconColor = icon?.color ?? \"currentColor\";\n const rawIcon = renderIcon(iconSource, iconSize, iconColor);\n \n // Apply consistent icon styling\n const objectFit = options.objectFit ?? \"contain\";\n rawIcon.style.cssText = `width: 100%; height: 100%; object-fit: ${objectFit}; border: none;`;\n \n iconElement = rawIcon as HTMLElement;\n }\n \n // Create wrapper using DOMBuilder\n return DOMBuilder.div({ style: wrapperStyles }, iconElement).build();\n }\n\n /**\n * Creates a simple icon without wrapper (used for buttons like close, new chat).\n * \n * @param iconName - Lucide icon name\n * @param size - Size in pixels\n * @param color - Icon color\n * @returns SVG or IMG element\n */\n static createSimple(\n iconName: string,\n size: number = 18,\n color: string = \"currentColor\"\n ): SVGSVGElement | HTMLImageElement {\n return renderIcon(iconName, size, color);\n }\n \n /**\n * Creates an exception icon with tooltip for error messages.\n * \n * @param exceptionConfig - Exception icon configuration\n * @param reason - Error reason to show in tooltip\n * @param isUserMessage - Whether this is a user message (affects positioning)\n * @param className - Optional CSS class name\n * @returns HTMLElement wrapper containing the exception icon with tooltip\n */\n static createExceptionIcon(\n exceptionConfig: IconConfig | undefined,\n reason: string,\n isUserMessage: boolean,\n className?: string\n ): HTMLElement | null {\n if (!exceptionConfig) return null;\n \n const iconName = exceptionConfig.img || \"alert-triangle\";\n const iconSize = exceptionConfig.size?.width || 20;\n const iconColor = exceptionConfig.color;\n \n const exceptionEl = IconRenderer.createSimple(\n iconName,\n iconSize,\n iconColor\n );\n \n if (!exceptionEl) return null;\n \n // Wrap icon in a span for better tooltip display\n const wrapper = DOMBuilder.span({\n title: reason,\n className: className,\n style: {\n marginLeft: isUserMessage ? \"0\" : \"4px\",\n marginRight: isUserMessage ? \"4px\" : \"0\",\n display: \"inline-flex\",\n alignItems: \"center\"\n }\n }, exceptionEl).build();\n \n return wrapper;\n }\n}","import type { TextConfig } from \"../types/config\";\n\nexport class StyleUtils {\n /**\n * Applies text styling from TextConfig to an HTML element.\n * \n * @param element - The HTML element to style\n * @param textConfig - Text configuration with color, font, size options\n * @param defaults - Optional default values to apply if not specified in config\n */\n static applyTextStyle(\n element: HTMLElement, \n textConfig: TextConfig | undefined,\n defaults?: Partial<TextConfig>\n ): void {\n if (!textConfig && !defaults) return;\n\n const color = textConfig?.color ?? defaults?.color;\n if (color) element.style.color = color;\n\n const font = textConfig?.font ?? defaults?.font;\n if (font) element.style.fontFamily = font;\n\n const size = textConfig?.size ?? defaults?.size;\n if (size) element.style.fontSize = `${size}px`;\n }\n\n /**\n * Builds a CSS class string from an array of class names, filtering out falsy values.\n * \n * @param classes - Array of class names or conditional expressions\n * @returns Space-separated class string\n */\n static buildClassNames(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(\" \");\n }\n\n /**\n * Applies multiple CSS properties to an element via style object.\n * \n * @param element - The HTML element to style\n * @param styles - Object with CSS property names as keys\n */\n static applyStyles(element: HTMLElement, styles: Record<string, string>): void {\n Object.entries(styles).forEach(([property, value]) => {\n if (value) {\n element.style.setProperty(property, value);\n }\n });\n }\n\n /**\n * Creates a CSS variable name with the nimbus prefix.\n * \n * @param name - Variable name without prefix\n * @returns CSS variable string with --nimbus- prefix\n */\n static cssVar(name: string): string {\n return `--nimbus-${name}`;\n }\n\n /**\n * Sets CSS variables on an element.\n * \n * @param element - The element to set variables on\n * @param variables - Object with variable names (without --nimbus- prefix) as keys\n */\n static setCssVariables(element: HTMLElement, variables: Record<string, string>): void {\n Object.entries(variables).forEach(([name, value]) => {\n if (value) {\n element.style.setProperty(StyleUtils.cssVar(name), value);\n }\n });\n }\n}","import { DOMBuilder } from \"./DOMBuilder\";\nimport { IconRenderer } from \"./IconRenderer\";\nimport { ICON_SIZES } from \"../types/config\";\n\nexport type ButtonVariant = 'header' | 'action' | 'remove' | 'primary' | 'secondary';\n\nexport interface ButtonConfig {\n icon?: string;\n iconSize?: number;\n title?: string;\n className?: string;\n onClick: () => void;\n}\n\n/**\n * Utility for creating consistent buttons throughout the application\n */\nexport class ButtonBuilder {\n private static readonly VARIANTS: Record<ButtonVariant, string> = {\n header: \"p-1 rounded-lg hover:bg-white/20 transition-colors cursor-pointer\",\n action: \"px-4 py-2 rounded transition-opacity hover:opacity-80\",\n remove: \"w-5 h-5 rounded-full bg-slate-300 hover:bg-red-500 text-slate-600 hover:text-white transition-colors\",\n primary: \"px-3 py-1 bg-blue-500 text-white rounded text-sm hover:bg-blue-600\",\n secondary: \"block w-full text-left p-2 border rounded hover:bg-gray-50\"\n };\n\n /**\n * Create an icon button with specified variant styling\n */\n static createIconButton(\n icon: string,\n config: ButtonConfig,\n variant: ButtonVariant = 'action'\n ): HTMLButtonElement {\n const iconElement = IconRenderer.createSimple(\n icon, \n config.iconSize || ICON_SIZES.BUTTON_MEDIUM\n );\n\n const className = [\n this.VARIANTS[variant],\n config.className || \"\"\n ].filter(Boolean).join(\" \");\n\n return DOMBuilder.button({\n className,\n title: config.title,\n type: \"button\",\n on: { click: config.onClick }\n }, iconElement).build() as HTMLButtonElement;\n }\n\n /**\n * Create a text button with specified variant styling\n */\n static createTextButton(\n text: string,\n config: ButtonConfig,\n variant: ButtonVariant = 'action'\n ): HTMLButtonElement {\n const className = [\n this.VARIANTS[variant],\n config.className || \"\"\n ].filter(Boolean).join(\" \");\n\n return DOMBuilder.button({\n className,\n title: config.title,\n type: \"button\",\n on: { click: config.onClick }\n }).text(text).build() as HTMLButtonElement;\n }\n\n /**\n * Create a button with both icon and text\n */\n static createIconTextButton(\n icon: string,\n text: string,\n config: ButtonConfig,\n variant: ButtonVariant = 'action'\n ): HTMLButtonElement {\n const iconElement = IconRenderer.createSimple(\n icon, \n config.iconSize || ICON_SIZES.BUTTON_SMALL\n );\n\n const className = [\n this.VARIANTS[variant],\n \"flex items-center gap-2\",\n config.className || \"\"\n ].filter(Boolean).join(\" \");\n\n return DOMBuilder.button({\n className,\n title: config.title,\n type: \"button\",\n on: { click: config.onClick }\n }, iconElement, DOMBuilder.span().text(text)).build() as HTMLButtonElement;\n }\n\n /**\n * Create a close/remove button (common pattern)\n */\n static createCloseButton(onClick: () => void, title: string = \"Close\"): HTMLButtonElement {\n return this.createTextButton(\"×\", {\n onClick,\n title,\n className: \"absolute top-2 right-2 z-10 flex items-center justify-center\"\n }, 'remove');\n }\n}","import { createLogger } from './logger';\n\n/**\n * Component lifecycle hooks\n */\nexport interface ComponentHooks {\n onMount?: () => void;\n onUnmount?: () => void;\n onDestroy?: () => void;\n onUpdate?: (data: unknown) => void;\n}\n\n/**\n * Base component interface\n */\nexport interface Component {\n element: HTMLElement;\n mount(parent: HTMLElement): void;\n unmount(): void;\n destroy(): void;\n update?(data: unknown): void;\n}\n\n/**\n * Abstract base class for all components\n */\nexport abstract class BaseComponent implements Component {\n protected _element: HTMLElement | null = null;\n protected hooks: ComponentHooks;\n protected logger: ReturnType<typeof createLogger>;\n protected destroyed = false;\n protected mounted = false;\n protected eventListeners: Array<{\n element: EventTarget;\n event: string;\n handler: EventListener;\n }> = [];\n\n constructor(hooks: ComponentHooks = {}) {\n this.hooks = hooks;\n this.logger = createLogger(this.constructor.name);\n }\n\n get element(): HTMLElement {\n if (!this._element) {\n this._element = this.render();\n this.afterRender();\n }\n return this._element;\n }\n\n /**\n * Abstract method to render the component\n */\n protected abstract render(): HTMLElement;\n\n /**\n * Called after render, useful for setting up event listeners\n */\n protected afterRender(): void {\n // Override in subclasses\n }\n\n /**\n * Add event listener with automatic cleanup\n */\n protected addEventListener(\n element: EventTarget,\n event: string,\n handler: EventListener\n ): void {\n element.addEventListener(event, handler);\n this.eventListeners.push({ element, event, handler });\n }\n\n /**\n * Mount component to parent\n */\n mount(parent: HTMLElement): void {\n if (this.mounted || this.destroyed) return;\n \n parent.appendChild(this.element);\n this.mounted = true;\n this.hooks.onMount?.();\n }\n\n /**\n * Unmount component from DOM\n */\n unmount(): void {\n if (!this.mounted || this.destroyed) return;\n \n this.element.remove();\n this.mounted = false;\n this.hooks.onUnmount?.();\n }\n\n /**\n * Update component with new data\n */\n update(data: unknown): void {\n if (this.destroyed) return;\n this.hooks.onUpdate?.(data);\n }\n\n /**\n * Destroy component and cleanup\n */\n destroy(): void {\n if (this.destroyed) return;\n \n this.unmount();\n \n // Remove all event listeners\n this.eventListeners.forEach(({ element, event, handler }) => {\n element.removeEventListener(event, handler);\n });\n this.eventListeners = [];\n \n this.hooks.onDestroy?.();\n this.destroyed = true;\n this._element = null;\n }\n}\n\n","import type { ConnectionState } from \"../types/message\";\nimport type { HeaderConfig, TypingIndicatorConfig } from \"../types/config\";\nimport { IconRenderer } from \"../utils/IconRenderer\";\nimport { StyleUtils } from \"../utils/StyleUtils\";\nimport { ButtonBuilder } from \"../utils/ButtonBuilder\";\nimport { ICON_NAMES, ICON_SIZES, UI_TEXT } from \"../types/config\";\nimport { DOMBuilder } from \"../utils/DOMBuilder\";\nimport { BaseComponent } from \"../utils/ComponentFactory\";\n\nexport class Header extends BaseComponent {\n readonly el: HTMLElement;\n private statusDot!: HTMLElement;\n private statusText?: HTMLElement;\n private currentConnectionState: ConnectionState = \"idle\";\n private isTyping: boolean = false;\n private headerConfig: HeaderConfig;\n private onClose: () => void;\n private allowNewChat: boolean;\n private onNewChat?: () => void;\n\n constructor(headerConfig: HeaderConfig, onClose: () => void, allowNewChat: boolean, onNewChat?: () => void) {\n super();\n this.headerConfig = headerConfig;\n this.onClose = onClose;\n this.allowNewChat = allowNewChat;\n this.onNewChat = onNewChat;\n this.el = this.element;\n }\n\n protected render(): HTMLElement {\n const headerColor = this.headerConfig.color;\n \n // Build left section components\n const leftChildren = [];\n \n // Add icon if configured\n const iconCfg = this.headerConfig.icon;\n if (iconCfg) {\n leftChildren.push(\n IconRenderer.create(iconCfg, ICON_NAMES.MESSAGE_CIRCLE, ICON_SIZES.AVATAR)\n );\n }\n\n // Add title text if configured\n const textCfg = this.headerConfig.text;\n const showText = textCfg?.display !== false;\n if (showText) {\n const titleEl = DOMBuilder.span({\n className: \"font-semibold truncate\"\n }).text(textCfg?.value ?? \"Nimbus\").build();\n \n StyleUtils.applyTextStyle(titleEl, textCfg, { size: 14 });\n leftChildren.push(titleEl);\n }\n\n // Create status dot\n this.statusDot = DOMBuilder.span({\n className: \"inline-block w-2 h-2 rounded-full bg-green-400 shrink-0\",\n title: UI_TEXT.CONNECTED\n }).build();\n leftChildren.push(this.statusDot);\n \n // Create status text (Online/Typing)\n this.statusText = DOMBuilder.span({\n className: \"text-xs\"\n }).text(\"Online\").build();\n \n // Apply text styling if header text config exists\n if (textCfg) {\n StyleUtils.applyTextStyle(this.statusText, textCfg, { size: 12 });\n }\n leftChildren.push(this.statusText);\n\n // Build left section\n const leftSection = DOMBuilder.div({\n className: \"flex items-center gap-2.5\"\n }, ...leftChildren);\n\n // Build right section components\n const rightChildren = [];\n \n // Add new chat button if allowed\n if (this.allowNewChat && this.onNewChat) {\n rightChildren.push(\n ButtonBuilder.createIconButton(ICON_NAMES.NEW_CHAT, {\n onClick: this.onNewChat,\n title: UI_TEXT.NEW_CHAT,\n iconSize: ICON_SIZES.BUTTON_MEDIUM\n }, 'header')\n );\n }\n\n // Add close button\n rightChildren.push(\n ButtonBuilder.createIconButton(ICON_NAMES.CLOSE, {\n onClick: this.onClose,\n title: \"Close\",\n iconSize: ICON_SIZES.BUTTON_MEDIUM\n }, 'header')\n );\n\n // Build right section\n const rightSection = DOMBuilder.div({\n className: \"flex items-center gap-1\"\n }, ...rightChildren);\n\n // Build main header container\n const header = DOMBuilder.div({\n className: \"nimbus-header flex items-center justify-between px-4 py-3 rounded-t-xl select-none\",\n style: headerColor?.primary ? { backgroundColor: headerColor.primary } : undefined\n }, leftSection, rightSection).build();\n\n // Set secondary color property if configured\n if (headerColor?.secondary) {\n header.style.setProperty(\"--nimbus-header-close\", headerColor.secondary);\n }\n\n return header;\n }\n\n\n setConnectionState(state: ConnectionState): void {\n this.currentConnectionState = state;\n \n // Update status dot\n this.statusDot.className = `inline-block w-2 h-2 rounded-full shrink-0 ${this.getStatusDotColor()}`;\n this.statusDot.title = state.charAt(0).toUpperCase() + state.slice(1);\n \n // Update status text if not typing\n if (!this.isTyping && this.statusText) {\n this.statusText.textContent = this.getStatusText();\n }\n }\n\n\n showTypingIndicator(config: TypingIndicatorConfig): void {\n this.isTyping = true;\n \n if (this.statusText && config.title?.value) {\n this.statusText.textContent = config.title.value;\n if (config.title.text) {\n StyleUtils.applyTextStyle(this.statusText, config.title.text, { size: 12 });\n }\n }\n }\n\n hideTypingIndicator(): void {\n this.isTyping = false;\n \n if (this.statusText) {\n this.statusText.textContent = this.getStatusText();\n StyleUtils.applyTextStyle(this.statusText, this.headerConfig.text, { size: 12 });\n }\n }\n\n\n private getStatusText(): string {\n switch (this.currentConnectionState) {\n case \"connected\": return \"Online\";\n case \"connecting\": return \"Connecting...\";\n case \"disconnected\": return \"Offline\";\n default: return \"Offline\";\n }\n }\n\n private getStatusDotColor(): string {\n const colors: Record<ConnectionState, string> = {\n idle: \"bg-gray-400\",\n connected: \"bg-green-400\",\n connecting: \"bg-yellow-400\",\n disconnected: \"bg-red-400\",\n };\n return colors[this.currentConnectionState];\n }\n}\n","/**\n * Shared utilities for file operations\n */\nexport class FileUtils {\n /**\n * Format file size in bytes to human-readable format\n * @param bytes - Size in bytes\n * @param shortFormat - Use short format (B, KB) vs long format (Bytes, KB)\n */\n static formatFileSize(bytes: number, shortFormat = false): string {\n if (bytes === 0) return shortFormat ? '0 B' : '0 Bytes';\n \n const k = 1024;\n const sizes = shortFormat ? ['B', 'KB', 'MB', 'GB'] : ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n \n return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];\n }\n\n static fileToBase64(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n if (typeof reader.result === \"string\") {\n const base64 = reader.result.split(\",\")[1];\n resolve(base64);\n } else {\n reject(new Error(\"Failed to read file as base64\"));\n }\n };\n reader.onerror = reject;\n reader.readAsDataURL(file);\n });\n }\n}","import type { ChatMessage, FileContent } from \"../types/message\";\nimport type { IconConfig } from \"../types/config\";\nimport { IconRenderer } from \"../utils/IconRenderer\";\nimport { FileUtils } from \"../utils/FileUtils\";\nimport { ICON_NAMES, ICON_SIZES, UI_TEXT, UI_FACTORS, BORDER_RADIUS, SPACING } from \"../types/config\";\nimport { DOMBuilder } from \"../utils/DOMBuilder\";\nimport { BaseComponent } from \"../utils/ComponentFactory\";\n\nexport interface MessageBubbleOptions {\n /** Avatar icon configuration. Pass null to hide. Omit to not show. */\n icon?: IconConfig | null;\n /** Width of the message bubble (e.g. \"90%\", \"300px\"). Default: \"70%\" */\n width?: string;\n}\n\n/**\n * Renders a single chat message bubble with optional avatar icon.\n */\nexport class MessageBubble extends BaseComponent {\n readonly el: HTMLElement;\n private message: ChatMessage;\n private options?: MessageBubbleOptions;\n\n constructor(message: ChatMessage, options?: MessageBubbleOptions) {\n super();\n this.message = message;\n this.options = options;\n this.el = this.element;\n }\n\n protected render(): HTMLElement {\n const isUser = this.message.direction === \"INBOUND\";\n const bubbleWidth = this.options?.width ?? `${UI_FACTORS.DEFAULT_BUBBLE_MAX_WIDTH}px`;\n\n // Build the row with avatar and bubble\n const iconCfg = this.options?.icon;\n const iconEl = iconCfg ? IconRenderer.create(\n iconCfg, \n ICON_NAMES.MESSAGE_CIRCLE, \n ICON_SIZES.AVATAR,\n { \n borderRadius: BORDER_RADIUS.AVATAR, \n marginBottom: `${SPACING.AVATAR_MARGIN}px`, \n objectFit: \"cover\",\n sizeFactor: UI_FACTORS.MESSAGE_AVATAR_SIZE_FACTOR\n }\n ) : null;\n\n // Create bubble with time\n const bubble = DOMBuilder.div({\n className: [\n isUser ? \"nimbus-msg-user\" : \"nimbus-msg-bot\",\n \"px-3.5 py-2.5 text-sm leading-relaxed\",\n isUser ? \"rounded-2xl rounded-br-md\" : \"rounded-2xl rounded-bl-md\",\n \"break-words whitespace-pre-wrap overflow-wrap-anywhere\",\n ].join(\" \"),\n style: {\n maxWidth: \"100%\",\n wordBreak: \"break-word\"\n }\n },\n this.renderContent(this.message),\n DOMBuilder.div({\n className: \"text-[10px] mt-1 opacity-50 select-none\"\n }).text(this.formatTime(new Date(this.message.created_at * 1000)))\n );\n\n // Create row with conditional avatar placement\n const row = DOMBuilder.div({\n className: `flex items-end gap-2 ${isUser ? \"flex-row-reverse\" : \"flex-row\"}`,\n style: { maxWidth: bubbleWidth }\n });\n\n if (iconEl) row.append(iconEl);\n row.append(bubble);\n\n // Create main container\n return DOMBuilder.div({\n className: `flex ${isUser ? \"justify-end\" : \"justify-start\"} nimbus-fade-in`\n }, row).build();\n }\n\n private renderContent(message: ChatMessage): Node {\n switch (message.message_type) {\n case \"json\":\n return this.renderJson(message.content);\n case \"file\":\n // Handle SHARED_FILE content - use message metadata directly\n if (message.content === \"SHARED_FILE\") {\n return this.renderSharedFile(message);\n }\n \n // For AI messages with file metadata, construct FileContent from message\n if (message.direction === \"OUTBOUND\" && message.filename) {\n const fileData: FileContent = {\n content: typeof message.content === 'string' ? message.content : JSON.stringify(message.content),\n filename: message.filename ?? undefined,\n filetype: message.filetype ?? undefined,\n filesize: message.filesize ?? undefined,\n description: message.description ?? undefined\n };\n return this.renderFile(fileData);\n }\n \n // For file messages, try to parse if it's a string\n let fileContent = message.content;\n if (typeof fileContent === 'string') {\n const parsed = this.tryParseJson(fileContent);\n if (parsed) {\n fileContent = parsed;\n }\n }\n return this.renderFile(fileContent);\n case \"text\":\n default:\n return this.renderText(typeof message.content === 'string' ? message.content : JSON.stringify(message.content));\n }\n }\n\n private renderText(content: string): Node {\n return DOMBuilder.span().text(content).build();\n }\n\n private renderJson(content: string | object): Node {\n let text: string;\n if (typeof content === 'object') {\n text = JSON.stringify(content, null, 2);\n } else {\n try {\n text = JSON.stringify(JSON.parse(content), null, 2);\n } catch {\n text = content;\n }\n }\n return DOMBuilder.create('pre', {\n className: \"text-xs font-mono bg-black/10 rounded p-2 overflow-x-auto\"\n }).text(text).build();\n }\n\n private renderSharedFile(message: ChatMessage): Node {\n const container = DOMBuilder.div({ className: \"space-y-2\" });\n\n // Display description FIRST if provided (like a caption)\n if (message.description && message.description.trim()) {\n container.append(\n DOMBuilder.div({ className: \"text-sm mb-2\" }).text(message.description)\n );\n }\n \n // Display file info\n container.append(this.createFileInfo(message.filename, message.filesize));\n \n // Show \"Shared a file\" message since no content is available\n container.append(this.createSharedFileIndicator());\n\n return container.build();\n }\n\n private renderFile(content: string | object): Node {\n if (typeof content === 'object') {\n return this.renderFileObject(content as FileContent);\n } else if (typeof content === 'string') {\n // Try to parse as JSON first\n const parsed = this.tryParseJson(content);\n if (parsed) {\n return this.renderFileObject(parsed);\n }\n \n // Handle raw base64 content (images or data URLs)\n return this.renderRawContent(content);\n }\n\n // If we can't parse the content, show error\n const container = DOMBuilder.div({ className: \"space-y-2\" });\n container.append(\n DOMBuilder.div({ className: \"text-red-500 text-sm\" }).text(\"Invalid file format\")\n );\n return container.build();\n }\n\n private renderFileObject(fileData: FileContent): Node {\n const container = DOMBuilder.div({ className: \"space-y-2\" });\n \n // Display description FIRST if provided (like a caption)\n if (fileData.description && fileData.description.trim()) {\n container.append(\n DOMBuilder.div({ className: \"text-sm mb-2\" }).text(fileData.description)\n );\n }\n \n // Display file info\n container.append(this.createFileInfo(fileData.filename, fileData.filesize));\n \n // Check if we have actual file content (base64)\n const hasFileContent = fileData.content && fileData.content.trim();\n\n if (hasFileContent) {\n container.append(this.renderFileContent(fileData));\n } else {\n container.append(this.createSharedFileIndicator());\n }\n\n return container.build();\n }\n\n private renderFileContent(fileData: FileContent): HTMLElement {\n const isUser = this.message.direction === \"INBOUND\";\n \n // Display image if it's an image type\n if (fileData.filetype && fileData.filetype.startsWith('image/')) {\n return DOMBuilder.img({\n src: `data:${fileData.filetype};base64,${fileData.content}`,\n className: \"max-w-full rounded-lg max-h-64\",\n alt: fileData.filename || \"Uploaded image\"\n }).build();\n } else {\n // For user files (INBOUND), show \"Shared a file\" instead of download link\n if (isUser) {\n return this.createSharedFileIndicator();\n }\n \n // For AI assistant files (OUTBOUND), show download link\n const downloadLink = DOMBuilder.create('a', {\n href: `data:${fileData.filetype || 'application/octet-stream'};base64,${fileData.content}`,\n download: fileData.filename || 'download',\n className: \"inline-flex items-center gap-2 px-3 py-2 bg-gray-100 rounded hover:bg-gray-200\"\n });\n \n const icon = IconRenderer.create(null, \"file\", ICON_SIZES.BUTTON_SMALL);\n downloadLink.append(icon);\n downloadLink.append(\n DOMBuilder.span().text(UI_TEXT.DOWNLOAD_FILE)\n );\n \n return downloadLink.build();\n }\n }\n\n\n\n private createSharedFileIndicator(): HTMLElement {\n const sharedFileMsg = DOMBuilder.div({\n className: \"inline-flex items-center gap-2 px-3 py-2 bg-gray-100 rounded text-gray-600\"\n });\n \n const icon = IconRenderer.create(null, \"file\", ICON_SIZES.BUTTON_SMALL);\n sharedFileMsg.append(icon);\n sharedFileMsg.append(\n DOMBuilder.span().text(\"Shared a file\")\n );\n \n return sharedFileMsg.build();\n }\n\n private createFileInfo(filename?: string | null, filesize?: number | null): HTMLElement {\n const fileInfo = DOMBuilder.div({ className: \"text-xs opacity-75 mb-1\" });\n fileInfo.append(\n DOMBuilder.span().text(`${filename || 'File'} `),\n DOMBuilder.span({ className: \"text-gray-500\" }).text(\n filesize ? `(${FileUtils.formatFileSize(filesize)})` : ''\n )\n );\n return fileInfo.build();\n }\n\n private tryParseJson(content: string): FileContent | null {\n try {\n return JSON.parse(content) as FileContent;\n } catch {\n return null;\n }\n }\n\n private renderRawContent(content: string): Node {\n const isUser = this.message.direction === \"INBOUND\";\n const container = DOMBuilder.div({ className: \"space-y-2\" });\n\n // Handle data URLs (data:image/png;base64,...)\n if (content.startsWith(\"data:\")) {\n if (content.startsWith(\"data:image\")) {\n container.append(\n DOMBuilder.img({\n src: content,\n className: \"max-w-full rounded-lg max-h-64\",\n alt: \"Shared image\"\n })\n );\n } else {\n // Non-image data URL - show \"Shared a file\" for user files, download link for AI files\n if (isUser) {\n container.append(this.createSharedFileIndicator());\n } else {\n container.append(\n DOMBuilder.create('a', {\n href: content,\n download: \"file\",\n className: \"inline-flex items-center gap-2 px-3 py-2 bg-gray-100 rounded hover:bg-gray-200\"\n }, \n IconRenderer.create(null, \"file\", ICON_SIZES.BUTTON_SMALL),\n DOMBuilder.span().text(UI_TEXT.DOWNLOAD_FILE)\n )\n );\n }\n }\n }\n // Handle raw base64 strings (assume image if it looks like base64)\n else if (content.match(/^[A-Za-z0-9+/=]+$/)) {\n container.append(\n DOMBuilder.img({\n src: `data:image/png;base64,${content}`,\n className: \"max-w-full rounded-lg max-h-64\",\n alt: \"Shared image\"\n })\n );\n }\n // Handle regular URLs - only show download for AI files\n else if (content.startsWith(\"http\")) {\n if (isUser) {\n container.append(this.createSharedFileIndicator());\n } else {\n container.append(\n DOMBuilder.create('a', {\n href: content,\n className: \"underline text-blue-500 hover:text-blue-600\",\n target: \"_blank\",\n rel: \"noopener noreferrer\"\n }).text(UI_TEXT.DOWNLOAD_FILE)\n );\n }\n }\n // Fallback - show as text\n else {\n container.append(\n DOMBuilder.div({ className: \"text-gray-600 text-sm\" }).text(`File: ${content.substring(0, 50)}${content.length > 50 ? '...' : ''}`)\n );\n }\n\n return container.build();\n }\n\n private formatTime(date: Date): string {\n return date.toLocaleTimeString(undefined, {\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n }\n}\n","import type { ChatMessage } from \"../types/message\";\nimport type { IconConfig, WelcomeConfig, TextConfig, TextElement, ShowMoreConfig } from \"../types/config\";\nimport { MessageBubble } from \"./MessageBubble\";\nimport type { MessageBubbleOptions } from \"./MessageBubble\";\nimport { IconRenderer } from \"../utils/IconRenderer\";\nimport { StyleUtils } from \"../utils/StyleUtils\";\nimport { ICON_NAMES, ICON_SIZES, UI_TEXT } from \"../types/config\";\nimport { DOMBuilder } from \"../utils/DOMBuilder\";\nimport { BaseComponent } from \"../utils/ComponentFactory\";\n\n/**\n * Scrollable container for chat messages.\n */\nexport class MessageList extends BaseComponent {\n readonly el: HTMLElement;\n private userIcon?: IconConfig | null;\n private botIcon?: IconConfig | null;\n private showMoreConfig: ShowMoreConfig;\n private primaryColor: string;\n private showMoreButton?: HTMLButtonElement;\n private hasMoreMessages = false;\n private userMessageWidth: string;\n private botMessageWidth: string;\n\n constructor(\n showMoreConfig: ShowMoreConfig,\n primaryColor: string,\n userMessageWidth: string,\n botMessageWidth: string,\n userIcon?: IconConfig | null,\n botIcon?: IconConfig | null\n ) {\n super();\n this.userIcon = userIcon;\n this.botIcon = botIcon;\n this.userMessageWidth = userMessageWidth;\n this.botMessageWidth = botMessageWidth;\n this.showMoreConfig = showMoreConfig;\n this.primaryColor = primaryColor;\n this.el = this.element;\n }\n\n protected render(): HTMLElement {\n return DOMBuilder.div({\n className: \"nimbus-message-list nimbus-chat-body flex-1 overflow-y-auto p-4 space-y-3\"\n }).build();\n }\n\n addMessage(message: ChatMessage): void {\n this.hideWelcome();\n const isUser = message.direction === \"INBOUND\";\n const opts: MessageBubbleOptions = {\n icon: isUser ? this.userIcon : this.botIcon,\n width: isUser ? this.userMessageWidth : this.botMessageWidth,\n };\n const bubble = new MessageBubble(message, opts);\n bubble.el.setAttribute('data-message-id', message.id);\n this.el.appendChild(bubble.el);\n this.scrollToBottom();\n }\n\n clear(): void {\n this.el.replaceChildren();\n this.showMoreButton = undefined;\n }\n\n showWelcome(welcome: WelcomeConfig): void {\n if (welcome.display === false) return;\n\n const titleRow = DOMBuilder.div();\n\n const preTitleValue = this.getWelcomeTextValue(welcome.preTitle);\n if (preTitleValue) {\n const pre = DOMBuilder.span().text(preTitleValue).build();\n this.applyWelcomeTextStyle(pre, welcome.preTitle);\n titleRow.append(pre);\n }\n\n const titleValue = this.getWelcomeTextValue(welcome.title);\n if (titleValue) {\n const title = DOMBuilder.create('strong').text(titleValue).build();\n this.applyWelcomeTextStyle(title, welcome.title);\n titleRow.append(title);\n }\n\n const subtitleValue = this.getWelcomeTextValue(welcome.subtitle);\n const subtitle = subtitleValue ? (() => {\n const sub = DOMBuilder.div({ className: \"mt-1 text-xs\" }).text(subtitleValue).build();\n this.applyWelcomeTextStyle(sub, welcome.subtitle);\n return sub;\n })() : null;\n\n const container = DOMBuilder.div({\n className: \"nimbus-welcome text-center text-sm py-8 select-none\"\n }, titleRow, subtitle).build();\n\n this.el.appendChild(container);\n }\n\n private hideWelcome(): void {\n const welcome = this.el.querySelector(\".nimbus-welcome\");\n if (welcome) welcome.remove();\n }\n\n private getWelcomeTextValue(element: TextElement | TextConfig | undefined): string | undefined {\n return (element as TextElement)?.value ?? (element as TextConfig)?.value;\n }\n\n private applyWelcomeTextStyle(el: HTMLElement, element: TextElement | TextConfig | undefined): void {\n if (!element) return;\n\n if ('text' in element && element.text) {\n StyleUtils.applyTextStyle(el, element.text);\n } else {\n StyleUtils.applyTextStyle(el, element as TextConfig);\n }\n }\n\n showError(message: string): void {\n const className = \"nimbus-system-message text-center p-3 rounded bg-red-50 text-red-700\";\n const container = DOMBuilder.div({ className },\n DOMBuilder.div().text(message)\n ).build();\n this.el.appendChild(container);\n }\n\n showShowMoreButton(onClick: () => void): void {\n this.hideShowMoreButton();\n\n const baseClasses = \"nimbus-show-more mx-auto mb-4 px-4 py-2 rounded-full text-sm transition-colors flex items-center gap-2\";\n const stickyClasses = this.showMoreConfig.sticky ? \"sticky top-0 z-10\" : \"\";\n\n const buttonChildren = [];\n\n if (this.showMoreConfig.icon) {\n const iconEl = IconRenderer.create(\n this.showMoreConfig.icon,\n ICON_NAMES.CHEVRON_DOWN,\n ICON_SIZES.BUTTON_SMALL,\n { borderRadius: \"50%\" }\n );\n if (iconEl) buttonChildren.push(iconEl);\n }\n\n const textSpan = DOMBuilder.span().text(\n this.showMoreConfig.value || UI_TEXT.SHOW_MORE\n ).build();\n\n if (this.showMoreConfig.text) {\n StyleUtils.applyTextStyle(textSpan, this.showMoreConfig.text);\n }\n buttonChildren.push(textSpan);\n\n this.showMoreButton = DOMBuilder.button({\n className: `${baseClasses} ${stickyClasses}`.trim(),\n style: {\n backgroundColor: this.showMoreConfig.background || \"#ffffff\",\n border: `1px solid ${this.primaryColor}`\n },\n type: \"button\",\n on: { click: onClick }\n }, ...buttonChildren).build() as HTMLButtonElement;\n\n this.el.insertBefore(this.showMoreButton, this.el.firstChild);\n }\n\n hideShowMoreButton(): void {\n if (this.showMoreButton) {\n this.showMoreButton.remove();\n this.showMoreButton = undefined;\n }\n }\n\n updateHasMore(hasMore: boolean): void {\n this.hasMoreMessages = hasMore;\n if (!hasMore) {\n this.hideShowMoreButton();\n }\n }\n\n private scrollToBottom(): void {\n requestAnimationFrame(() => {\n this.el.scrollTop = this.el.scrollHeight;\n });\n }\n\n scrollToTop(): void {\n requestAnimationFrame(() => {\n this.el.scrollTop = 0;\n });\n }\n}\n","import type { IconConfig, UploadConfig } from \"../types/config\";\nimport { IconRenderer } from \"../utils/IconRenderer\";\nimport { ICON_NAMES, ICON_SIZES, UI_TEXT, WIDGET_DEFAULTS } from \"../types/config\";\nimport { DOMBuilder } from \"../utils/DOMBuilder\";\nimport { FileUtils } from \"../utils/FileUtils\";\nimport { createLogger, type Logger } from \"../utils/logger\";\n\nexport interface FileUploadCallbacks {\n onFileSelected: (file: File, base64Content: string) => void;\n onError: (error: string) => void;\n}\n\nexport class FileUploadButton {\n private buttonEl: HTMLButtonElement;\n private inputEl: HTMLInputElement;\n private config: UploadConfig;\n private callbacks: FileUploadCallbacks;\n private logger: Logger;\n\n constructor(config: UploadConfig, callbacks: FileUploadCallbacks, debug: boolean) {\n this.config = config;\n this.callbacks = callbacks;\n this.logger = createLogger(\"FileUploadButton\", debug);\n \n // Create hidden file input\n this.inputEl = DOMBuilder.input({\n type: \"file\",\n className: \"hidden\",\n accept: this.config.allowedFileTypes?.join(\",\") || \"*\",\n on: {\n change: () => this.handleFileChange()\n }\n }).build() as HTMLInputElement;\n \n // Create upload button with icon\n const iconEl = IconRenderer.create(\n this.config.icon,\n ICON_NAMES.PAPERCLIP,\n ICON_SIZES.BUTTON_SMALL\n );\n \n this.buttonEl = DOMBuilder.button({\n className: [\n \"nimbus-upload-btn\",\n \"p-2\",\n \"rounded-full\",\n \"transition-all cursor-pointer\",\n \"hover:bg-slate-100\",\n \"disabled:opacity-40 disabled:cursor-not-allowed\",\n \"flex items-center justify-center\",\n ].join(\" \"),\n title: UI_TEXT.UPLOAD_FILE,\n type: \"button\",\n on: {\n click: () => this.openFileDialog()\n }\n }, iconEl, this.inputEl).build() as HTMLButtonElement;\n }\n\n get element(): HTMLElement {\n // Return just the button, input is already a child from constructor\n return this.buttonEl;\n }\n\n private openFileDialog(): void {\n this.inputEl.click();\n }\n\n private async handleFileChange(): Promise<void> {\n const file = this.inputEl.files?.[0];\n if (!file) return;\n\n // Validate file size\n const maxSize = this.config.maxFileSize ?? WIDGET_DEFAULTS.MAX_FILE_SIZE;\n if (file.size > maxSize) {\n const maxSizeFormatted = FileUtils.formatFileSize(maxSize);\n this.callbacks.onError(`Max File Size: ${maxSizeFormatted}`);\n this.inputEl.value = \"\";\n return;\n }\n\n // Validate file type\n if (this.config.allowedFileTypes && this.config.allowedFileTypes.length > 0) {\n if (!this.config.allowedFileTypes.includes(file.type)) {\n this.callbacks.onError(`File type ${file.type} is not allowed`);\n this.inputEl.value = \"\";\n return;\n }\n }\n\n try {\n // Convert file to base64\n const base64Content = await FileUtils.fileToBase64(file);\n this.logger.debug(`File selected: ${file.name} (${file.size} bytes)`);\n \n // Trigger callback with file and base64 content\n this.callbacks.onFileSelected(file, base64Content);\n \n // Reset input for next selection\n this.inputEl.value = \"\";\n } catch (error) {\n this.logger.error(\"Error processing file:\", error);\n this.callbacks.onError(\"Failed to process file\");\n this.inputEl.value = \"\";\n }\n }\n\n enable(): void {\n this.buttonEl.disabled = false;\n }\n\n disable(): void {\n this.buttonEl.disabled = true;\n }\n\n setEnabled(enabled: boolean): void {\n this.buttonEl.disabled = !enabled;\n }\n}","import { DOMBuilder } from \"../utils/DOMBuilder\";\nimport { IconRenderer } from \"../utils/IconRenderer\";\nimport { FileUtils } from \"../utils/FileUtils\";\nimport { ICON_NAMES, ICON_SIZES } from \"../types/config\";\n\nexport interface FilePreviewData {\n file: File;\n base64Content: string;\n}\n\nexport class FilePreview {\n private container: HTMLElement;\n private fileData: FilePreviewData | null = null;\n private onRemove: () => void;\n\n constructor(onRemove: () => void) {\n this.onRemove = onRemove;\n this.container = this.createContainer();\n }\n\n private createContainer(): HTMLElement {\n return DOMBuilder.div({\n className: \"nimbus-file-preview hidden\"\n }).build();\n }\n\n setFile(fileData: FilePreviewData | null): void {\n this.fileData = fileData;\n \n if (!fileData) {\n this.container.classList.add(\"hidden\");\n this.container.innerHTML = \"\";\n return;\n }\n\n // Clear existing content\n this.container.innerHTML = \"\";\n this.container.classList.remove(\"hidden\");\n\n // Create full-width file preview bar\n const previewWrapper = DOMBuilder.div({\n className: \"relative w-full px-3 py-2 bg-slate-100 border border-slate-200 rounded-lg\"\n }).build();\n\n // Close button in top right corner\n const removeBtn = DOMBuilder.button({\n className: [\n \"absolute top-1 right-1\",\n \"flex items-center justify-center\",\n \"w-5 h-5 rounded-full\",\n \"bg-slate-300 hover:bg-red-500 text-slate-600 hover:text-white\",\n \"transition-colors duration-200\",\n \"text-xs leading-none\"\n ].join(\" \"),\n type: \"button\",\n title: \"Remove file\",\n on: {\n click: (e: Event) => {\n e.preventDefault();\n e.stopPropagation();\n this.removeFile();\n }\n }\n }).text(\"×\").build();\n previewWrapper.appendChild(removeBtn);\n\n // File icon\n const fileIcon = IconRenderer.create(\n null,\n \"file-text\", // Generic file icon\n ICON_SIZES.BUTTON_MEDIUM\n );\n\n // File name\n const fileName = DOMBuilder.span({\n className: \"text-sm font-medium text-slate-700 truncate max-w-[200px]\"\n }).text(fileData.file.name);\n\n // File size\n const fileSize = DOMBuilder.span({\n className: \"text-xs text-slate-500 flex-shrink-0\"\n }).text(FileUtils.formatFileSize(fileData.file.size, true));\n\n // Build content area using DOMBuilder\n const contentArea = DOMBuilder.div({\n className: \"flex items-center justify-between pr-6\" // Right padding to avoid close button\n },\n // Left side: Icon and file name\n DOMBuilder.div({\n className: \"flex items-center gap-3\"\n }, fileIcon, fileName),\n // Right side: File size\n fileSize\n );\n\n previewWrapper.appendChild(contentArea.build());\n\n this.container.appendChild(previewWrapper);\n }\n\n\n private removeFile(): void {\n this.setFile(null);\n this.onRemove();\n }\n\n getFileData(): FilePreviewData | null {\n return this.fileData;\n }\n\n get element(): HTMLElement {\n return this.container;\n }\n}","import type { IconConfig, TextConfig, ColorPair, UploadConfig, MaxCharactersConfig, TypingIndicatorConfig } from \"../types/config\";\nimport { IconRenderer } from \"../utils/IconRenderer\";\nimport { ICON_NAMES, ICON_SIZES, UI_TEXT, INPUT_CONSTANTS } from \"../types/config\";\nimport { DOMBuilder } from \"../utils/DOMBuilder\";\nimport { BaseComponent } from \"../utils/ComponentFactory\";\nimport { FileUploadButton } from \"./FileUploadButton\";\nimport { FilePreview, type FilePreviewData } from \"./FilePreview\";\nimport { createLogger, type Logger } from \"../utils/logger\";\n\nexport interface InputAreaConfig {\n placeholder: string;\n expandable: boolean;\n text?: TextConfig;\n background?: ColorPair;\n sendButton: {\n icon?: IconConfig | null;\n align: boolean;\n };\n upload?: UploadConfig;\n maxCharacters?: MaxCharactersConfig;\n}\n\nexport interface InputAreaCallbacks {\n onSend: (content: string) => void;\n onFileUpload?: (file: File, base64Content: string, description?: string) => void;\n}\n\nexport class InputArea extends BaseComponent {\n readonly el: HTMLElement;\n private inputEl!: HTMLInputElement | HTMLTextAreaElement;\n private sendBtn!: HTMLButtonElement;\n private uploadButton?: FileUploadButton;\n private filePreview?: FilePreview;\n private pendingFile: FilePreviewData | null = null;\n private config: InputAreaConfig;\n private callbacks: InputAreaCallbacks;\n private expandable: boolean;\n protected logger: Logger;\n private debug: boolean;\n private errorContainer: HTMLElement | null = null;\n private errorTimeout: number | null = null;\n private bottomArea!: HTMLElement;\n private characterCounter?: HTMLElement;\n private typingBar?: HTMLElement;\n\n constructor(\n config: InputAreaConfig,\n callbacks: InputAreaCallbacks,\n debug: boolean\n ) {\n super();\n this.config = config;\n this.debug = debug;\n this.logger = createLogger(\"InputArea\", debug);\n this.callbacks = callbacks;\n this.expandable = config.expandable;\n this.el = this.element;\n }\n\n protected render(): HTMLElement {\n // Create file preview component\n if (this.config.upload) {\n this.filePreview = new FilePreview(() => {\n this.pendingFile = null;\n this.toggleUploadButton(true);\n });\n }\n\n // Create upload button if enabled\n if (this.config.upload && this.callbacks.onFileUpload) {\n this.uploadButton = new FileUploadButton(\n this.config.upload,\n {\n onFileSelected: (file, base64) => {\n // Store the file and show preview instead of sending immediately\n this.pendingFile = { file, base64Content: base64 };\n this.filePreview?.setFile(this.pendingFile);\n this.toggleUploadButton(false);\n },\n onError: (error) => {\n this.logger.error(\"File upload error:\", error);\n this.showError(error);\n }\n },\n this.debug\n );\n }\n\n // Create send button\n const iconEl = IconRenderer.create(this.config.sendButton.icon, ICON_NAMES.SEND, ICON_SIZES.BUTTON_SMALL);\n this.sendBtn = DOMBuilder.button({\n className: [\n \"nimbus-send-btn\",\n \"rounded-full\",\n \"p-1.5\",\n \"transition-all cursor-pointer shrink-0\",\n \"disabled:opacity-40 disabled:cursor-not-allowed\",\n ].join(\" \"),\n title: UI_TEXT.SEND,\n type: \"button\",\n on: { click: () => this.handleSend() }\n }, iconEl).build() as HTMLButtonElement;\n\n // Calculate shared values once\n const hasUpload = this.config.upload && this.uploadButton;\n const paddingClasses = hasUpload ? \"pl-12 pr-12\" : \"pl-4 pr-12\";\n const sharedInputClasses = [\n \"nimbus-input-field\",\n \"w-full py-2.5 text-sm outline-none\",\n \"focus:ring-2 focus:ring-blue-500/20 focus:border-blue-300 transition-all\",\n paddingClasses\n ];\n \n const sharedKeydownHandler = (e: KeyboardEvent) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n this.handleSend();\n }\n };\n\n this.typingBar = DOMBuilder.div({\n className: \"nimbus-typing-bar hidden px-4 py-1.5 text-xs border-t border-slate-200\"\n }).build();\n\n // Create container\n const container = DOMBuilder.div({\n className: \"nimbus-input-container border-t border-slate-200\"\n });\n\n const inputStyle: Partial<CSSStyleDeclaration> = {};\n if (this.config.text?.color) inputStyle.color = this.config.text.color;\n if (this.config.text?.font) inputStyle.fontFamily = this.config.text.font;\n if (this.config.text?.size) inputStyle.fontSize = `${this.config.text.size}px`;\n\n if (this.config.expandable) {\n this.inputEl = DOMBuilder.textarea({\n placeholder: this.config.placeholder,\n rows: 1,\n className: [\n ...sharedInputClasses,\n \"nimbus-textarea border border-slate-200 rounded-xl\",\n ].join(\" \"),\n style: inputStyle,\n on: {\n keydown: sharedKeydownHandler,\n input: () => {\n this.autoResize(this.inputEl as HTMLTextAreaElement);\n this.updateCharacterCounter();\n }\n }\n }).build() as HTMLTextAreaElement;\n } else {\n this.inputEl = DOMBuilder.input({\n type: \"text\",\n placeholder: this.config.placeholder,\n className: [\n ...sharedInputClasses,\n \"border border-slate-200 rounded-full\",\n ].join(\" \"),\n style: inputStyle,\n on: {\n keydown: sharedKeydownHandler,\n input: () => this.updateCharacterCounter()\n }\n }).build() as HTMLInputElement;\n }\n\n // Create wrapper to position buttons (unified for both input types)\n const inputWrapper = DOMBuilder.div({\n className: this.config.expandable ? \"relative w-full\" : \"relative w-full flex items-center\"\n }).build();\n \n // Add input element\n inputWrapper.appendChild(this.inputEl);\n \n // Add upload button positioned absolutely on the left inside the input\n if (this.uploadButton) {\n const uploadClasses = this.config.expandable \n ? [\"absolute\", \"left-1\", \"bottom-2\", \"z-10\"]\n : [\"absolute\", \"left-1\", \"z-10\"];\n this.uploadButton.element.classList.add(...uploadClasses);\n inputWrapper.appendChild(this.uploadButton.element);\n }\n \n // Add send button positioned absolutely on the right inside the input\n const sendClasses = [\"absolute\", \"right-1\", \"bottom-[10px]\", \"z-10\"];\n this.sendBtn.classList.add(...sendClasses);\n inputWrapper.appendChild(this.sendBtn);\n \n container.append(this.typingBar);\n \n const innerWrap = DOMBuilder.div({ className: \"p-3\" });\n innerWrap.append(inputWrapper);\n\n // Add file preview / error area below the input\n this.errorContainer = null;\n this.bottomArea = DOMBuilder.div({ className: \"nimbus-bottom-area\" }).build();\n if (this.filePreview) {\n this.bottomArea.appendChild(this.filePreview.element);\n }\n innerWrap.append(this.bottomArea);\n\n // Add character counter if enabled\n if (this.config.maxCharacters) {\n this.characterCounter = this.createCharacterCounter();\n innerWrap.append(this.characterCounter);\n }\n\n container.append(innerWrap);\n\n return container.build();\n }\n\n\n private createCharacterCounter(): HTMLElement {\n const config = this.config.maxCharacters!;\n const style: Partial<CSSStyleDeclaration> = {};\n if (config.text?.color) style.color = config.text.color;\n if (config.text?.size) style.fontSize = `${config.text.size}px`;\n if (config.text?.font) style.fontFamily = config.text.font;\n return DOMBuilder.div({\n className: \"nimbus-character-counter text-right px-3 py-1\",\n style\n })\n .text(`0/${config.limit}`)\n .build();\n }\n\n private updateCharacterCounter(): void {\n if (!this.characterCounter || !this.config.maxCharacters) return;\n \n const currentLength = this.inputEl.value.length;\n const maxLength = this.config.maxCharacters.limit;\n \n this.characterCounter.textContent = `${currentLength}/${maxLength}`;\n \n // Apply color based on limit proximity\n if (currentLength > maxLength) {\n this.characterCounter.style.color = 'red';\n } else if (currentLength > maxLength * 0.9) {\n this.characterCounter.style.color = 'orange';\n } else {\n this.characterCounter.style.color = this.config.maxCharacters.text?.color || '#6b7280';\n }\n }\n\n private autoResize(textarea: HTMLTextAreaElement): void {\n textarea.style.height = \"auto\";\n const scrollH = textarea.scrollHeight;\n if (scrollH > INPUT_CONSTANTS.MAX_HEIGHT) {\n textarea.style.height = INPUT_CONSTANTS.MAX_HEIGHT + \"px\";\n textarea.classList.add(\"nimbus-textarea-scroll\");\n } else {\n textarea.style.height = scrollH + \"px\";\n textarea.classList.remove(\"nimbus-textarea-scroll\");\n }\n }\n\n private handleSend(): void {\n const text = this.inputEl.value.trim();\n \n // Check character limit validation\n if (this.config.maxCharacters && text.length > this.config.maxCharacters.limit) {\n this.logger.warn(`Message exceeds character limit: ${text.length}/${this.config.maxCharacters.limit}`);\n return; // Prevent sending messages that exceed the limit\n }\n \n // If there's a pending file, send it with the description\n if (this.pendingFile) {\n if (this.callbacks.onFileUpload) {\n // Send file with description (text as description)\n this.callbacks.onFileUpload(this.pendingFile.file, this.pendingFile.base64Content, text);\n }\n // Clear file preview\n this.pendingFile = null;\n this.filePreview?.setFile(null);\n this.toggleUploadButton(true);\n } else if (text) {\n // Send regular text message\n this.callbacks.onSend(text);\n } else {\n // No text and no file, don't send anything\n return;\n }\n \n // Clear input and update counter\n this.inputEl.value = \"\";\n this.updateCharacterCounter();\n if (this.expandable && this.inputEl instanceof HTMLTextAreaElement) {\n this.inputEl.style.height = \"auto\";\n this.inputEl.classList.remove(\"nimbus-textarea-scroll\");\n }\n this.inputEl.focus();\n }\n\n private toggleUploadButton(show: boolean): void {\n if (this.uploadButton) {\n this.uploadButton.element.classList.toggle(\"hidden\", !show);\n }\n // Update input padding based on whether upload button is shown\n this.updateInputPadding(!show);\n }\n\n private updateInputPadding(hasFilePreview: boolean): void {\n if (hasFilePreview) {\n // When file preview is active, remove left padding\n this.inputEl.classList.remove(\"pl-12\");\n this.inputEl.classList.add(\"pl-4\");\n } else {\n // When upload button is visible, add left padding\n if (this.config.upload) {\n this.inputEl.classList.remove(\"pl-4\");\n this.inputEl.classList.add(\"pl-12\");\n }\n }\n }\n\n disable(): void {\n this.inputEl.disabled = true;\n this.sendBtn.disabled = true;\n this.uploadButton?.disable();\n }\n\n enable(): void {\n this.inputEl.disabled = false;\n this.sendBtn.disabled = false;\n this.uploadButton?.enable();\n }\n\n setEnabled(enabled: boolean): void {\n if (enabled) {\n this.enable();\n } else {\n this.disable();\n }\n }\n\n focus(): void {\n this.inputEl.focus();\n }\n\n showTypingIndicator(config: TypingIndicatorConfig): void {\n if (!this.typingBar) return;\n this.typingBar.textContent = config.title?.value ?? \"\";\n if (config.title?.text?.color) {\n this.typingBar.style.color = config.title.text.color;\n }\n if (config.title?.text?.font) {\n this.typingBar.style.fontFamily = config.title.text.font;\n }\n if (config.title?.text?.size) {\n this.typingBar.style.fontSize = `${config.title.text.size}px`;\n }\n this.typingBar.classList.remove(\"hidden\");\n }\n\n hideTypingIndicator(): void {\n if (!this.typingBar) return;\n this.typingBar.classList.add(\"hidden\");\n }\n\n private showError(message: string): void {\n this.hideError();\n\n this.errorContainer = DOMBuilder.div({\n className: \"nimbus-upload-error bg-red-500 text-white text-sm px-3 py-2 mt-2 rounded-lg\"\n }).text(message).build();\n\n this.bottomArea.appendChild(this.errorContainer);\n\n const duration = this.config.upload?.errorDisplayDuration || 2000;\n this.errorTimeout = setTimeout(() => {\n this.hideError();\n }, duration);\n }\n\n private hideError(): void {\n if (this.errorTimeout) {\n clearTimeout(this.errorTimeout);\n this.errorTimeout = null;\n }\n\n if (this.errorContainer) {\n this.errorContainer.remove();\n this.errorContainer = null;\n }\n }\n}\n","import type { BubblePosition, IconConfig } from \"../types/config\";\nimport { IconRenderer } from \"../utils/IconRenderer\";\nimport { ICON_NAMES, ICON_SIZES, UI_TEXT, ICON_SCALE } from \"../types/config\";\nimport { DOMBuilder } from \"../utils/DOMBuilder\";\nimport { BaseComponent } from \"../utils/ComponentFactory\";\n\nexport interface ChatBubbleConfig {\n position: BubblePosition;\n autoHide: boolean;\n icon?: IconConfig | null;\n}\n\n/**\n * Floating action button that opens/closes the chat window.\n * Supports custom icon (IconConfig), null (no custom icon), or default chat icon.\n */\nexport class ChatBubble extends BaseComponent {\n readonly el: HTMLElement;\n private isOpen = false;\n private config: ChatBubbleConfig;\n private onToggle: () => void;\n\n constructor(config: ChatBubbleConfig, onToggle: () => void) {\n super();\n this.config = config;\n this.onToggle = onToggle;\n this.el = this.element;\n }\n\n protected render(): HTMLElement {\n const button = DOMBuilder.button({\n className: [\n \"nimbus-bubble\",\n \"fixed z-50 w-14 h-14 shadow-lg\",\n \"rounded-full\",\n \"flex items-center justify-center cursor-pointer\",\n \"transition-all duration-200 hover:scale-110\",\n this.config.position === \"bottom-right\" ? \"right-5 bottom-5\" : \"left-5 bottom-5\",\n ].join(\" \"),\n title: UI_TEXT.OPEN_CHAT,\n type: \"button\",\n on: {\n click: () => {\n this.isOpen = !this.isOpen;\n this.updateIcon();\n this.onToggle();\n }\n }\n }, this.createBubbleIcon()).build();\n\n return button;\n }\n\n setOpen(open: boolean): void {\n this.isOpen = open;\n this.updateIcon();\n }\n\n private createBubbleIcon(): HTMLElement | null {\n return IconRenderer.create(\n this.config.icon, \n ICON_NAMES.MESSAGE_CIRCLE, \n ICON_SIZES.AVATAR,\n { sizeFactor: ICON_SCALE.BUBBLE_ICON }\n );\n }\n\n private updateIcon(): void {\n // Clear current content\n this.el.replaceChildren();\n \n if (this.isOpen) {\n const closeIcon = IconRenderer.createSimple(ICON_NAMES.CHEVRON_DOWN, ICON_SIZES.BUBBLE_OPEN);\n this.el.appendChild(closeIcon);\n this.el.title = UI_TEXT.CLOSE_CHAT;\n } else {\n const openIcon = this.createBubbleIcon();\n if (openIcon) {\n this.el.appendChild(openIcon);\n }\n this.el.title = UI_TEXT.OPEN_CHAT;\n }\n }\n}\n","import type { EventBus } from \"../core/EventBus\";\nimport type { ChatSession } from \"../core/ChatSession\";\nimport type { WebSocketManager } from \"../core/WebSocketManager\";\nimport type { ResolvedConfig } from \"../types/config\";\nimport type { ChatMessage, ConnectionState, UserMessage } from \"../types/message\";\nimport { isMobileDevice } from \"../utils/device\";\nimport { ShadowContainer } from \"./ShadowContainer\";\nimport { ThemeManager } from \"./ThemeManager\";\nimport { Header } from \"./Header\";\nimport { MessageList } from \"./MessageList\";\nimport { InputArea } from \"./InputArea\";\nimport { ChatBubble } from \"./ChatBubble\";\nimport { DOMBuilder } from \"../utils/DOMBuilder\";\n\n/**\n * Base class for chat window implementations.\n * Contains all shared functionality between ChatWindow and SidepanelWindow.\n */\nexport abstract class BaseWindow {\n protected config: ResolvedConfig;\n protected eventBus: EventBus;\n protected session: ChatSession;\n protected wsManager: WebSocketManager;\n\n protected shadow!: ShadowContainer;\n protected bubble!: ChatBubble;\n protected panel!: HTMLElement;\n protected header!: Header;\n protected messageList!: MessageList;\n protected inputArea!: InputArea;\n protected isOpen = false;\n protected hasRequestedConnect = false;\n protected waitForReplyTimeout: ReturnType<typeof setTimeout> | null = null;\n private shouldScrollToTop = false;\n private waitingForFirstReply = false;\n private firstReplyTimeout: ReturnType<typeof setTimeout> | null = null;\n private eventUnsubscribers: Array<() => void> = [];\n\n constructor(\n config: ResolvedConfig,\n eventBus: EventBus,\n session: ChatSession,\n wsManager: WebSocketManager\n ) {\n this.config = config;\n this.eventBus = eventBus;\n this.session = session;\n this.wsManager = wsManager;\n\n this.initializeComponents();\n\n this.panel = this.buildPanel();\n\n this.setupDOM();\n\n this.messageList.showWelcome(config.welcome);\n\n this.bindEvents();\n }\n\n protected initializeComponents(): void {\n const theme = new ThemeManager(this.config);\n this.shadow = new ShadowContainer(theme);\n\n this.header = new Header(\n this.config.header,\n () => this.close(),\n this.config.allowNewChat,\n () => this.eventBus.emit(\"ui:start-new-chat\")\n );\n\n this.messageList = new MessageList(\n this.config.showMore,\n this.config.theme.primary,\n this.config.userMessage.width,\n this.config.botMessage.width,\n this.config.userMessage.icon,\n this.config.botMessage.icon\n );\n this.inputArea = new InputArea(\n {\n placeholder: this.config.input.placeholder,\n expandable: this.config.input.expandable,\n text: this.config.input.text,\n background: this.config.input.background,\n sendButton: this.config.sendButton,\n upload: this.config.input.upload,\n maxCharacters: this.config.input.maxCharacters,\n },\n {\n onSend: (content) => this.handleSend(content),\n onFileUpload: this.config.input.upload\n ? (file, base64, description) => this.handleFileUpload(file, base64, description)\n : undefined\n },\n this.config.debug\n );\n\n this.bubble = new ChatBubble(\n {\n position: this.config.bubble.position,\n autoHide: this.config.bubble.autoHide,\n icon: this.config.bubble.icon,\n },\n () => this.toggle()\n );\n }\n\n protected setupDOM(): void {\n this.shadow.root.appendChild(this.bubble.el);\n this.shadow.root.appendChild(this.panel);\n }\n\n open(): void {\n if (!this.hasRequestedConnect) {\n this.hasRequestedConnect = true;\n this.eventBus.emit(\"ws:request-connect\");\n }\n\n this.isOpen = true;\n this.showPanel();\n this.inputArea.focus();\n }\n\n close(): void {\n this.isOpen = false;\n this.hidePanel();\n }\n\n toggle(): void {\n this.isOpen ? this.close() : this.open();\n }\n\n destroy(): void {\n if (this.waitForReplyTimeout) {\n clearTimeout(this.waitForReplyTimeout);\n this.waitForReplyTimeout = null;\n }\n if (this.firstReplyTimeout) {\n clearTimeout(this.firstReplyTimeout);\n this.firstReplyTimeout = null;\n }\n this.eventUnsubscribers.forEach(unsub => unsub());\n this.eventUnsubscribers = [];\n this.shadow.destroy();\n }\n\n private onMessageSent(): void {\n if (this.config.waitForReply) {\n this.setupWaitForReplyTimeout();\n }\n this.showTypingIndicator();\n }\n\n protected handleFileUpload(file: File, base64Content: string, description?: string): void {\n const fileContent = {\n filename: file.name,\n filetype: file.type,\n filesize: file.size,\n content: base64Content,\n description: description || \"\"\n };\n\n this.session.createUserMessage(fileContent, \"file\");\n this.onMessageSent();\n this.wsManager.send({\n type: \"message\",\n direction: \"inbound\",\n content: JSON.stringify(fileContent),\n });\n }\n\n protected handleSend(content: string): void {\n // Create local message\n this.session.createUserMessage(content, \"text\");\n\n // Build payload with simplified schema\n const messagePayload: UserMessage = {\n type: \"message\",\n direction: \"inbound\",\n content,\n };\n\n this.onMessageSent();\n this.wsManager.send(messagePayload);\n }\n\n private subscribe<K extends import(\"../core/EventBus\").EventName>(\n event: K,\n handler: import(\"../core/EventBus\").EventHandler<K>\n ): void {\n this.eventBus.on(event, handler);\n this.eventUnsubscribers.push(() => this.eventBus.off(event, handler));\n }\n\n protected bindEvents(): void {\n this.subscribe(\"message:added\", (msg: ChatMessage) => {\n this.messageList.addMessage(msg);\n\n if (msg.direction === \"OUTBOUND\") {\n if (this.waitingForFirstReply) {\n this.waitingForFirstReply = false;\n this.inputArea.setEnabled(true);\n this.hideTypingIndicator();\n if (this.firstReplyTimeout) {\n clearTimeout(this.firstReplyTimeout);\n this.firstReplyTimeout = null;\n }\n } else if (this.waitForReplyTimeout) {\n clearTimeout(this.waitForReplyTimeout);\n this.waitForReplyTimeout = null;\n this.inputArea.setEnabled(true);\n this.hideTypingIndicator();\n } else {\n this.hideTypingIndicator();\n }\n }\n });\n\n const handleHistoryUpdate = (messages: ChatMessage[]) => {\n this.messageList.clear();\n if (messages.length === 0) {\n this.messageList.showWelcome(this.config.welcome);\n } else {\n messages.forEach(msg => this.messageList.addMessage(msg));\n if (this.shouldScrollToTop) {\n this.messageList.scrollToTop();\n this.shouldScrollToTop = false;\n }\n }\n };\n\n this.subscribe(\"history:loaded\", handleHistoryUpdate);\n this.subscribe(\"history:merged\", handleHistoryUpdate);\n\n // Only show \"show more\" button if resumeConversation is enabled\n if (this.config.resumeConversation) {\n this.subscribe(\"history:has_more\", (hasMore: boolean) => {\n if (hasMore) {\n this.messageList.showShowMoreButton(() => {\n this.shouldScrollToTop = true;\n this.eventBus.emit(\"ui:show-more\");\n });\n } else {\n this.messageList.hideShowMoreButton();\n }\n this.messageList.updateHasMore(hasMore);\n });\n }\n\n this.subscribe(\"connection:state\", (state: ConnectionState) => {\n this.header.setConnectionState(state);\n if (state === \"connected\") {\n if (!this.waitingForFirstReply) {\n this.inputArea.setEnabled(true);\n }\n }\n });\n\n this.subscribe(\"session:connected\", () => {\n if (this.config.waitForReply?.firstReply) {\n this.waitingForFirstReply = true;\n this.inputArea.setEnabled(false);\n this.showTypingIndicator();\n\n this.firstReplyTimeout = setTimeout(() => {\n this.waitingForFirstReply = false;\n this.inputArea.setEnabled(true);\n this.hideTypingIndicator();\n this.firstReplyTimeout = null;\n }, this.config.waitForReply.timeout);\n }\n });\n\n this.subscribe(\"session:cleared\", () => {\n this.messageList.clear();\n this.messageList.showWelcome(this.config.welcome);\n\n if (!this.config.waitForReply?.firstReply) {\n this.inputArea.setEnabled(true);\n }\n });\n\n this.subscribe(\"ui:show-error\", (message: string) => {\n this.messageList.showError(message);\n });\n }\n\n protected createPanelElement(): HTMLElement {\n return DOMBuilder.div({},\n this.header.el,\n this.messageList.el,\n this.inputArea.el\n ).build();\n }\n\n protected isMobileDevice(): boolean {\n return isMobileDevice();\n }\n\n protected getEffectivePosition(): string {\n if (isMobileDevice() && this.config.style.mobile?.position) {\n return this.config.style.mobile.position;\n }\n return this.config.style.position;\n }\n\n protected isSidepanelPosition(): boolean {\n const position = this.getEffectivePosition();\n return position.startsWith(\"sidepanel\");\n }\n\n protected isWindowPosition(): boolean {\n const position = this.getEffectivePosition();\n return position.startsWith(\"bottom\");\n }\n\n private showTypingIndicator(): void {\n if (!this.config.isTypingIndicator) return;\n\n if (this.config.isTypingIndicator.position === \"bottom\") {\n this.inputArea.showTypingIndicator(this.config.isTypingIndicator);\n } else {\n this.header.showTypingIndicator(this.config.isTypingIndicator);\n }\n }\n\n private hideTypingIndicator(): void {\n if (!this.config.isTypingIndicator) return;\n\n if (this.config.isTypingIndicator.position === \"bottom\") {\n this.inputArea.hideTypingIndicator();\n } else {\n this.header.hideTypingIndicator();\n }\n }\n\n private setupWaitForReplyTimeout(): void {\n if (!this.config.waitForReply) return;\n\n this.inputArea.setEnabled(false);\n\n this.waitForReplyTimeout = setTimeout(() => {\n this.inputArea.setEnabled(true);\n this.hideTypingIndicator();\n this.waitForReplyTimeout = null;\n }, this.config.waitForReply.timeout);\n }\n\n // Abstract methods to be implemented by subclasses\n protected abstract buildPanel(): HTMLElement;\n protected abstract showPanel(): void;\n protected abstract hidePanel(): void;\n}\n","import type { EventBus } from \"../core/EventBus\";\nimport type { ChatSession } from \"../core/ChatSession\";\nimport type { WebSocketManager } from \"../core/WebSocketManager\";\nimport type { ResolvedConfig } from \"../types/config\";\nimport { BaseWindow } from \"./BaseWindow\";\n\n/**\n * Pop-up chat window for bottom-right / bottom-left positions.\n */\nexport class ChatWindow extends BaseWindow {\n constructor(\n config: ResolvedConfig,\n eventBus: EventBus,\n session: ChatSession,\n wsManager: WebSocketManager\n ) {\n super(config, eventBus, session, wsManager);\n }\n\n protected buildPanel(): HTMLElement {\n const panel = this.createPanelElement();\n const isRight = this.config.style.position === \"bottom-right\";\n const isMobile = this.isMobileDevice();\n \n const width = this.config.style.width;\n const height = this.config.style.height;\n\n panel.className = \"font-sans fixed flex flex-col rounded-xl shadow-2xl overflow-hidden nimbus-fade-in\";\n \n if (isMobile) {\n panel.style.cssText = [\n \"display: none\",\n \"width: 100%\",\n `height: ${height}`,\n \"bottom: 84px\",\n \"left: 0\",\n \"right: 0\",\n \"border: 1px solid #e2e8f0\",\n \"border-radius: 12px 12px 0 0\",\n ].join(\";\");\n } else {\n panel.style.cssText = [\n \"display: none\",\n `width: ${width}`,\n `height: ${height}`,\n \"bottom: 84px\",\n isRight ? \"right: 20px\" : \"left: 20px\",\n \"border: 1px solid #e2e8f0\",\n ].join(\";\");\n }\n\n return panel;\n }\n\n protected showPanel(): void {\n this.panel.style.display = \"flex\";\n this.bubble.setOpen(true);\n \n if (this.config.bubble.autoHide) {\n this.bubble.el.style.display = \"none\";\n this.panel.style.bottom = \"20px\";\n }\n }\n\n protected hidePanel(): void {\n this.panel.style.display = \"none\";\n this.bubble.setOpen(false);\n \n if (this.config.bubble.autoHide) {\n this.bubble.el.style.display = \"flex\";\n this.panel.style.bottom = \"84px\";\n }\n }\n}","import type { EventBus } from \"../core/EventBus\";\nimport type { ChatSession } from \"../core/ChatSession\";\nimport type { WebSocketManager } from \"../core/WebSocketManager\";\nimport type { ResolvedConfig } from \"../types/config\";\nimport { BaseWindow } from \"./BaseWindow\";\n\n/**\n * Full-height sidepanel chat for sidepanel-left / sidepanel-right positions.\n */\nexport class SidepanelWindow extends BaseWindow {\n constructor(\n config: ResolvedConfig,\n eventBus: EventBus,\n session: ChatSession,\n wsManager: WebSocketManager\n ) {\n super(config, eventBus, session, wsManager);\n \n this.bubble.el.style.display = \"flex\";\n this.bubble.setOpen(false);\n }\n\n protected buildPanel(): HTMLElement {\n const panel = this.createPanelElement();\n const isRight = this.config.style.position === \"sidepanel-right\";\n const isMobile = this.isMobileDevice();\n \n const width = this.config.style.width;\n\n panel.className = \"font-sans fixed top-0 h-screen flex flex-col shadow-2xl overflow-hidden nimbus-panel-slide\";\n \n if (isMobile) {\n panel.style.cssText = [\n \"display: none\",\n \"width: 100%\",\n \"left: 0\",\n \"right: 0\",\n \"border: 1px solid #e2e8f0\",\n \"border-radius: 12px 12px 0 0\"\n ].join(\";\");\n } else {\n panel.style.cssText = [\n \"display: none\",\n `width: ${width}`,\n isRight ? \"right: 0\" : \"left: 0\",\n \"border: 1px solid #e2e8f0\",\n \"border-radius: 12px 12px 0 0\",\n ].join(\";\");\n }\n\n return panel;\n }\n\n protected showPanel(): void {\n this.panel.style.display = \"flex\";\n this.bubble.el.style.display = \"none\";\n this.bubble.setOpen(true);\n }\n\n protected hidePanel(): void {\n this.panel.style.display = \"none\";\n this.bubble.el.style.display = \"flex\";\n this.bubble.setOpen(false);\n }\n}","import type { NimbusChatConfig, ResolvedConfig } from \"./types/config\";\nimport type { ServerEvent } from \"./types/message\";\nimport { resolveConfig } from \"./types/config\";\nimport { validateConfig } from \"./utils/validator\";\nimport { isMobileDevice, setMobileBreakpoint } from \"./utils/device\";\nimport { createLogger, printBanner, type Logger } from \"./utils/logger\";\nimport { PerformanceTracker } from \"./utils/PerformanceTracker\";\nimport { EventBus } from \"./core/EventBus\";\nimport { ChatSession } from \"./core/ChatSession\";\nimport { WebSocketManager } from \"./core/WebSocketManager\";\nimport { ApiClient } from \"./core/ApiClient\";\nimport { ChatWindow } from \"./ui/ChatWindow\";\nimport { SidepanelWindow } from \"./ui/SidepanelWindow\";\n\n/**\n * Main SDK orchestrator.\n *\n * Usage (npm):\n * const chat = new NimbusChat({ agent_version_id: \"550e8400-e29b-41d4-a716-446655440000\" });\n * chat.open();\n *\n * Usage (CDN):\n * NimbusChat.init({ agent_version_id: \"550e8400-e29b-41d4-a716-446655440000\" });\n */\nexport class NimbusChat {\n private config: ResolvedConfig;\n private eventBus: EventBus;\n private session: ChatSession;\n private wsManager: WebSocketManager;\n private apiClient: ApiClient;\n private ui: ChatWindow | SidepanelWindow;\n private logger: Logger;\n private destroyed = false;\n private messageLimit: number;\n private hasMoreMessages = false;\n private perf: PerformanceTracker;\n\n constructor(config: NimbusChatConfig) {\n validateConfig(config);\n\n this.config = resolveConfig(config);\n\n if (this.config.debug) printBanner();\n this.logger = createLogger(\"Main\", this.config.debug);\n\n this.messageLimit = this.config.messagesPerPage;\n\n this.perf = new PerformanceTracker(this.config.debug);\n this.eventBus = new EventBus();\n this.session = new ChatSession(this.eventBus);\n this.wsManager = new WebSocketManager(\n this.config.dns,\n this.config.agent_version_id,\n this.eventBus,\n this.config.reconnect,\n this.config.debug,\n this.config.test,\n );\n this.apiClient = new ApiClient(this.config.dns, this.config.debug);\n\n setMobileBreakpoint(this.config.style.mobile?.breakpoint);\n\n const isMobile = isMobileDevice();\n const effectivePosition = isMobile && this.config.style.mobile?.position\n ? this.config.style.mobile.position\n : this.config.style.position;\n\n const isSidepanel = effectivePosition.startsWith(\"sidepanel\");\n if (isSidepanel) {\n this.ui = new SidepanelWindow(\n this.config,\n this.eventBus,\n this.session,\n this.wsManager\n );\n } else {\n this.ui = new ChatWindow(\n this.config,\n this.eventBus,\n this.session,\n this.wsManager\n );\n }\n\n this.setupEventHandlers();\n\n this.logger.info(\"Initialized\", this.config);\n }\n\n private setupEventHandlers(): void {\n this.eventBus.on(\"ws:request-connect\", () => {\n this.wsManager.connect();\n });\n\n this.eventBus.on(\"ws:message\", (event: ServerEvent) => {\n this.handleServerEvent(event);\n });\n\n this.eventBus.on(\"message:added\", (msg) => {\n if (msg.direction === \"INBOUND\") {\n this.perf.markMessageSent();\n }\n });\n\n // Only enable show-more if resumeConversation is enabled\n if (this.config.resumeConversation) {\n this.eventBus.on(\"ui:show-more\", () => {\n this.fetchAndDisplayHistory(true);\n });\n }\n\n this.eventBus.on(\"ui:start-new-chat\", () => {\n this.wsManager.clearSession();\n this.session.clear();\n this.perf.reset();\n this.messageLimit = this.config.messagesPerPage;\n this.hasMoreMessages = false;\n this.wsManager.connect(true);\n });\n }\n\n private async handleServerEvent(event: ServerEvent): Promise<void> {\n switch (event.type) {\n case \"connected\":\n this.session.setFlowId(event.flow_id);\n this.perf.markSessionStarted();\n this.eventBus.emit(\"session:connected\");\n\n // Fetch chat history if resume is enabled\n if (this.config.resumeConversation) {\n await this.fetchAndDisplayHistory();\n }\n break;\n\n case \"message\":\n this.perf.markBotMessageReceived();\n this.session.createBotMessage(event.content);\n break;\n }\n }\n\n // ── Public API ──────────────────────────────────────────────────\n\n /** Show the chat widget */\n open(): void {\n this.ui.open();\n }\n\n /** Hide the chat widget */\n close(): void {\n this.ui.close();\n }\n\n /** Toggle chat visibility */\n toggle(): void {\n this.ui.toggle();\n }\n\n /**\n * Completely remove the widget from the page and disconnect WebSocket.\n * After calling destroy(), this instance cannot be reused.\n */\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.apiClient.abort();\n this.wsManager.disconnect();\n this.ui.destroy();\n this.eventBus.removeAll();\n\n this.logger.info(\"Destroyed\");\n }\n\n private async fetchAndDisplayHistory(loadMore = false, mergeWithLocal = false): Promise<void> {\n const flowId = this.session.getFlowId() || this.wsManager.getFlowId();\n if (!flowId) return;\n\n try {\n this.logger.debug(`fetchAndDisplayHistory called with loadMore=${loadMore}`);\n\n if (loadMore) {\n this.messageLimit += this.config.messagesPerPage;\n this.logger.debug(`Incrementing limit to ${this.messageLimit}`);\n } else {\n this.messageLimit = this.config.messagesPerPage;\n this.logger.debug(`Resetting limit to ${this.messageLimit}`);\n }\n\n const history = await this.apiClient.fetchMessageHistory(flowId, this.messageLimit);\n\n this.logger.debug(`Received ${history.messages.length} messages from API`);\n\n this.hasMoreMessages = history.has_more;\n this.logger.debug(`has_more flag: ${this.hasMoreMessages}`);\n\n if (mergeWithLocal) {\n this.session.loadHistoryMessagesWithMerge(history.messages);\n } else {\n this.session.clearMessages();\n this.session.loadHistoryMessages(history.messages);\n }\n\n this.eventBus.emit(\"history:has_more\", this.hasMoreMessages);\n } catch (error) {\n this.logger.error(\"Failed to load message history:\", error);\n }\n }\n}\n"],"mappings":"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","names":["src_exports","__export","ICON_NAMES","ICON_SIZES","NimbusChat","src_default","getInstance","init","__toCommonJS","ICON_NAMES","ICON_SIZES","UI_TEXT","INPUT_CONSTANTS","Z_INDEX","WEBSOCKET_CLOSE_CODES","TIMEOUTS","WIDGET_DEFAULTS","UI_FACTORS","TIME_CONVERSION","DEFAULT_THEME","DEFAULT_MESSAGE_WIDTH","DEFAULT_USER_MESSAGE","DEFAULT_BOT_MESSAGE","DEFAULT_BUBBLE","DEFAULT_WELCOME","DEFAULT_SEND_BUTTON","DEFAULT_TYPING_INDICATOR","DEFAULT_WAIT_FOR_REPLY","DEFAULT_SHOW_MORE","DEFAULT_UPLOAD","DEFAULT_RECONNECT","DEFAULT_STYLE","DEFAULT_CONFIG","resolveConfig","config","s","SPACING","BORDER_RADIUS","ICON_SCALE","VALID_POSITIONS","VALID_BUBBLE_POSITIONS","validateTextConfig","value","path","validateIconConfig","icon","size","validateConfig","config","c","st","t","bub","msg","hdr","hc","inp","bg","btn","w","cachedBreakpoint","cachedResult","setMobileBreakpoint","breakpoint","isMobileDevice","userAgent","px","LEVEL_CONFIG","COMPONENT_COLORS","DEFAULT_COMPONENT_COLOR","BADGE_STYLE","bg","fg","COMPONENT_STYLE","color","TIMESTAMP_STYLE","MESSAGE_STYLE","getComponentColor","component","lower","key","sanitizeArgs","args","arg","obj","tryParseJSON","str","sanitizeFilePayload","message","wsMatch","prefix","payload","parsed","clean","Logger","_Logger","enabled","level","badge","consoleFn","time","sanitized","json","cleanArgs","parts","styles","allArgs","label","subComponent","data","createLogger","debug","bannerPrinted","printBanner","version","art","subtitle","PerformanceTracker","debug","createLogger","now","elapsed","EventBus","createLogger","event","handler","wrapper","args","err","ChatSession","eventBus","flowId","message","content","messageType","msg","TIME_CONVERSION","id","created_at","messages","localMessages","allMessages","uniqueMessages","key","b","STORAGE_KEYS","WebSocketManager","dns","agentVersionId","eventBus","reconnectConfig","debug","testMode","createLogger","forceNew","closingExisting","WEBSOCKET_CLOSE_CODES","keepIntentionalClose","url","wsUrl","params","message","payload","event","parsed","err","delay","state","flowId","ApiClient","dns","debug","createLogger","sessionId","limit","WIDGET_DEFAULTS","before","url","response","data","error","COMPILED_CSS","DOMBuilder","_DOMBuilder","element","tag","props","children","builder","className","style","dataset","attrs","on","rest","key","value","event","handler","classes","styles","processChild","child","html","text","name","condition","callback","parent","ShadowContainer","theme","DOMBuilder","Z_INDEX","COMPILED_CSS","css","style","ThemeManager","config","c","bg","isImage","import_lucide","logger","createLogger","iconCache","toPascalCase","str","part","lookupIcon","name","pascalName","data","isUrl","renderIcon","iconSource","size","color","className","renderImageIcon","renderLucideIcon","src","DOMBuilder","iconData","svg","createElement","tagName","attributes","children","element","key","value","child","elementData","IconRenderer","_IconRenderer","icon","defaultIcon","ICON_NAMES","defaultSize","options","w","h","wrapperStyles","iconSource","iconElement","sizeFactor","UI_FACTORS","iconSize","iconColor","rawIcon","renderIcon","objectFit","DOMBuilder","iconName","size","color","exceptionConfig","reason","isUserMessage","className","exceptionEl","StyleUtils","_StyleUtils","element","textConfig","defaults","color","font","size","classes","styles","property","value","name","variables","ButtonBuilder","icon","config","variant","iconElement","IconRenderer","ICON_SIZES","className","DOMBuilder","text","onClick","title","BaseComponent","hooks","createLogger","element","event","handler","parent","data","Header","BaseComponent","headerConfig","onClose","allowNewChat","onNewChat","headerColor","leftChildren","iconCfg","IconRenderer","ICON_NAMES","ICON_SIZES","textCfg","titleEl","DOMBuilder","StyleUtils","UI_TEXT","leftSection","rightChildren","ButtonBuilder","rightSection","header","state","config","FileUtils","bytes","shortFormat","k","sizes","i","file","resolve","reject","reader","base64","MessageBubble","BaseComponent","message","options","isUser","bubbleWidth","UI_FACTORS","iconCfg","iconEl","IconRenderer","ICON_NAMES","ICON_SIZES","BORDER_RADIUS","SPACING","bubble","DOMBuilder","row","fileData","fileContent","parsed","content","text","container","downloadLink","icon","UI_TEXT","sharedFileMsg","filename","filesize","fileInfo","FileUtils","date","MessageList","BaseComponent","showMoreConfig","primaryColor","userMessageWidth","botMessageWidth","userIcon","botIcon","DOMBuilder","message","isUser","opts","bubble","MessageBubble","welcome","titleRow","preTitleValue","pre","titleValue","title","subtitleValue","subtitle","sub","container","element","el","StyleUtils","onClick","baseClasses","stickyClasses","buttonChildren","iconEl","IconRenderer","ICON_NAMES","ICON_SIZES","textSpan","UI_TEXT","hasMore","FileUploadButton","config","callbacks","debug","createLogger","DOMBuilder","iconEl","IconRenderer","ICON_NAMES","ICON_SIZES","UI_TEXT","file","maxSize","WIDGET_DEFAULTS","maxSizeFormatted","FileUtils","base64Content","error","enabled","FilePreview","onRemove","DOMBuilder","fileData","previewWrapper","removeBtn","e","fileIcon","IconRenderer","ICON_SIZES","fileName","fileSize","FileUtils","contentArea","InputArea","BaseComponent","config","callbacks","debug","createLogger","FilePreview","FileUploadButton","file","base64","error","iconEl","IconRenderer","ICON_NAMES","ICON_SIZES","DOMBuilder","UI_TEXT","sharedInputClasses","sharedKeydownHandler","e","container","inputStyle","inputWrapper","uploadClasses","sendClasses","innerWrap","style","currentLength","maxLength","textarea","scrollH","INPUT_CONSTANTS","text","show","hasFilePreview","enabled","message","duration","ChatBubble","BaseComponent","config","onToggle","DOMBuilder","UI_TEXT","open","IconRenderer","ICON_NAMES","ICON_SIZES","ICON_SCALE","closeIcon","openIcon","BaseWindow","config","eventBus","session","wsManager","theme","ThemeManager","ShadowContainer","Header","MessageList","InputArea","content","file","base64","description","ChatBubble","unsub","base64Content","fileContent","messagePayload","event","handler","msg","handleHistoryUpdate","messages","hasMore","state","message","DOMBuilder","isMobileDevice","ChatWindow","BaseWindow","config","eventBus","session","wsManager","panel","isRight","isMobile","width","height","SidepanelWindow","BaseWindow","config","eventBus","session","wsManager","panel","isRight","isMobile","width","NimbusChat","config","validateConfig","resolveConfig","printBanner","createLogger","PerformanceTracker","EventBus","ChatSession","WebSocketManager","ApiClient","setMobileBreakpoint","isMobileDevice","SidepanelWindow","ChatWindow","event","msg","loadMore","mergeWithLocal","flowId","history","error","logger","createLogger","globalInstance","init","config","NimbusChat","getInstance","src_default","NimbusChat","init","getInstance"]}