Skip to content

Conversation

@tbwksm
Copy link

@tbwksm tbwksm commented Oct 24, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Smart text truncation with language-aware width calculations for accurate text display.
    • Added dropdown selectors for choosing GPT models and MDM modules.
    • New programmatic API to control chat window (open, close, toggle).
    • Enhanced message support for menus, searches, and advanced interactions.
    • Closeable chat window with optional close button.
  • UI/UX Improvements

    • Localized interface text to Korean.
    • Updated avatar and bubble styling.
    • Enhanced keyboard accessibility and navigation.
  • Chores

    • Configured Git line-ending handling across environments.

tbwksm and others added 30 commits August 18, 2025 11:23
출처 박스 변경. 디자인 반영.
…ubble components. Adjust styling and improve menu alias display formatting.
- Added dynamic dropdown width calculation based on option labels.
- Introduced `dropdownMinWidth` signal and `triggerRef` for measurement.
- Updated input field and dropdown styling for better alignment and responsiveness.
lesliejjytbw and others added 26 commits September 22, 2025 17:00
…` handling in BotBubble

- Added `observeMastClick` observer to `observersConfigType`.
- Enhanced UI and interactivity for `mastSearches` in BotBubble component.
…le components

- Introduced `calledTools` state with corresponding signal and update logic.
- Enhanced LoadingBubble to display active tool calls dynamically.
- Added logic to reset `calledTools` state during relevant Bot events.
- Introduced `clipboardSrc` property in BotBubble and related types.
- Updated copy-to-clipboard logic to use custom `clipboardSrc` image if provided.
…ents

- Introduced `thoughts` property in Bot state and incorporated parsing logic for `<think>` tags.
- Enhanced menu sorting logic to prioritize mentions in messages for better context relevance.
- Updated LoadingBubble with avatar display options and improved interaction handling.
…ast searches in BotBubble

- Introduced ordered state management for menus, sources, and mast searches.
- Enhanced link handling logic to prioritize relevance and improve user interaction.
- Updated styles and added dynamic rendering for ordered items.
- Introduced `docNumberMap` for consistent document numbering across sessions.
- Adjusted link handling to assign and display unique numbers based on document IDs.
- Fixed duplicated source ordering issue by refining `orderedCurrentSources` updates.
- Removed unused button rendering for menus and mast IDs.
- Simplified the component's structure by eliminating redundant interactivity.
- Eliminated redundant span element and inline styles for unused icon rendering in `SourceBubble`.
- Simplified component structure by cleaning up unnecessary code.
…e BotBubble styles

- Eliminated unnecessary `sourceDocuments` references in `apiMessage` objects for cleaner state updates.
- Simplified and consolidated inline styling logic in BotBubble component.
- Ensured proper handling of null/undefined `sourceDocuments` for consistent message formatting.
… message merge logic

- Implemented caching for `choose_one_property` selections within the session to streamline responses.
- Enhanced action handling for `choose_one_property` and search-related operations, ensuring appropriate AI message formatting and updates.
- Improved consistency in merging and updating consecutive AI messages.
- Updated BotBubble component styles for better rendering of vertical buttons in `choose_one_property` actions.
…erty` logic

- Introduced `isAppending` prop to refine LoadingBubble rendering, improving message transitions.
- Simplified and optimized `choose_one_property` action handling, including caching and fallback logic.
- Enhanced Bot component interactivity and removed redundant code for better maintainability.
- Cleaned up debug logs from `choose_one_property` and `search` actions for better code maintainability.
- Ensured focus is maintained for Backspace key events.
- Added event listeners to stop propagation for Backspace and deleteContentBackward actions during capture phase.
- Refactored ref assignment to handle user-provided refs and cleanup listeners effectively.
- Added support for external element triggers to toggle bot visibility via `externalTriggerElementId`.
- Introduced event dispatching for open, close, and toggle actions (`flowise:open`, `flowise:close`, `flowise:toggle`).
- Enhanced `Bubble.tsx` to conditionally render button and tooltip based on the `hideButton` property.
- Updated window exports to include toggle control methods.
- Introduced `CloseButton` component with customizable `closeButtonColor` and observer close functionality (`useObserverClose`).
- Added `showCloseButton` prop for conditional rendering of the close button in Bot and Bubble components.
- Updated event listeners in Bubble to improve external trigger handling.
@coderabbitai
Copy link

coderabbitai bot commented Oct 30, 2025

Walkthrough

This pull request enhances a chatbot widget system with expanded streaming message handling, observer callbacks for user interactions, Korean language localization, language-aware text truncation utilities, a new ComboBox input component, additional UI refinements (avatars, bubbles, buttons), and new window-level control functions (open/close/toggle) for external bot triggering. The Bot component gains support for model/module selectors, close buttons, and metadata preservation across multiple message types.

Changes

Cohort / File(s) Change Summary
Configuration & Metadata
.gitattributes, .gitignore
Added Git line-ending normalization configuration (LF for source files, CRLF for batch files, binary markers for archives/executables). Added dist/ to ignore list.
Documentation
README.md
Added "Smart Text Truncation" feature section with usage examples for truncateTextByWidth and getTextWidth functions supporting multi-language width calculations.
Public HTML & Initialization
public/index.html
Replaced minimal Chatbot.init() with comprehensive Chatbot.initFull() call including nested configuration for environment variables, UI themes, avatars, input placeholders, and observer callbacks.
Styles
src/assets/index.css, src/components/bubbles/GuestBubble.tsx
Updated .chatbot-host-bubble background from #f7f8ff to #ffffff and removed border. Increased guest bubble left margin from 50px to 100px and added border class.
Avatar Component
src/components/avatars/Avatar.tsx
Removed rounded-full CSS class, changing avatar styling to object-cover w-full h-full.
Core Bot Component
src/components/Bot.tsx
Major expansion: added menus/mastSearches/thoughts metadata support to message types; introduced observer callbacks for user input, loading, messages, source clicks, menu clicks, mast clicks, and close events; added streaming message appending and merging logic; new helper functions for message logging and normalization; added ComboBox selections for gptModels and mdmModules; new CloseButton with configurable visibility; persisted metadata to localStorage.
Feedback Dialog
src/components/FeedbackContentDialog.tsx
Localized UI text to Korean: "Provide additional feedback" → "추가 피드백 작성"; placeholder updated; button label changed to "피드백 보내기"; simplified input validation to use reportValidity() only.
BotBubble Component
src/components/bubbles/BotBubble.tsx
Added optional props: avatarLoadingSrc, avatarInfoSrc, avatarEmptySrc, backgroundColorEmphasize, observeSourceClick, observeMenuClick, observeMastClick, langCode. Enhanced anchor handling for specialized link types (menu:, document:, master:). Localized clipboard notification to Korean.
SourceBubble Component
src/components/bubbles/SourceBubble.tsx
Replaced pageContent/metadata props with index, chunkContent, title, imageSrc. Redesigned rendering with index badge, truncated title header, and conditional image preview section.
LoadingBubble Component
src/components/bubbles/LoadingBubble.tsx
Converted to prop-driven component with LoadingBubbleProps type. Added CalledTool type and conditional rendering of tool call list with LoadingDots animation per tool. Avatar rendering now conditional on showAvatar prop.
StarterPromptBubble
src/components/bubbles/StarterPromptBubble.tsx
Added required backgroundColor: string prop. Changed border-radius from 15px to 4px; added 1px solid border (#CED4DA) and inline background styling.
Button Components
src/components/buttons/FeedbackButtons.tsx, src/components/buttons/SendButton.tsx
Updated FeedbackButtons titles to Korean ("클립보드로 복사", "좋아요", "별로에요"). Added new CloseButton component with inline SVG close icon. Updated DeleteButton icon to inline SVG sourced from props.sendButtonColor.
Icon Components
src/components/icons/ChevronDownIcon.tsx, src/components/icons/ClipboardIcon.tsx, src/components/icons/index.ts
Added new ChevronDownIcon component. Updated ClipboardIcon from 24x24 stroke-based to 16x16 fill-based SVG. Re-exported new ChevronDownIcon in index.
ComboBox Input
src/components/inputs/ComboBox.tsx, src/components/inputs/index.ts
New reusable ComboBox component with controlled/uncontrolled value, keyboard accessibility (Enter/Escape), dynamic dropdown width calculation, and search filtering. Added ComboBoxOption and Props types. Re-exported from inputs index.
ShortTextInput
src/components/inputs/textInput/components/ShortTextInput.tsx
Enhanced ref compatibility for both function and object refs. Added Backspace handling and capture-phase event listeners for delete operations.
Bubble Feature
src/features/bubble/components/Bubble.tsx, src/features/bubble/types.ts
Added external trigger event listeners (flowise:open/close/toggle) and global event dispatch. Added conditional button visibility via hideButton. Extended BotProps with showCloseButton, useObserverClose, gptModels, mdmModules, closeButtonColor. Expanded theme types with ComboBoxTheme, new avatar sources, and emphasis colors.
Full Feature
src/features/full/components/Full.tsx
Forwarded new Bot props (gptModels, mdmModules, showCloseButton, useObserverClose, closeButtonColor) from theme to Bot component.
Message Logging Query
src/queries/sendMessageQuery.ts
Added optional aimlHost field to MessageRequest type. New sendMessageLog() function posts to ${aimlHost}/api/v1/log/{chatflowid} endpoint.
Text Truncation Utilities
src/utils/textTruncator.ts, src/utils/textTruncator.test.ts, src/utils/index.ts
New utility module with truncateTextByWidth() and getTextWidth() functions supporting Korean Hangul, full-width, and ASCII character width calculations. Added comprehensive test suite with language-specific test cases. Re-exported utilities from utils index.
Window API
src/window.ts
Added open(), close(), and toggle() functions that dispatch bot-control events (flowise:open/close/toggle). Extended Chatbot type and parseChatbot() return with these new public functions.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant BubbleUI as Bubble UI
    participant Bot as Bot Component
    participant Observer as Observer Callbacks
    participant Storage as LocalStorage

    User->>BubbleUI: Triggers external element or flowise:open event
    BubbleUI->>Bot: toggleBot() / dispatch event
    Bot->>Bot: Handle streaming message<br/>(append vs. new)
    Bot->>Bot: Merge menus/mastSearches<br/>into last AI message
    Bot->>Observer: observeMessages(updated messages)
    Bot->>Storage: Persist messages with<br/>new metadata
    Bot->>BotBubble: Render with menus/sources<br/>and specialized links
    
    User->>BotBubble: Click menu: link
    BotBubble->>Observer: observeMenuClick(menu)
    Observer->>User: Callback triggered

    User->>BubbleUI: Select gptModel from ComboBox
    BubbleUI->>Bot: onChange(modelValue)
    Bot->>Bot: Save to chatflowConfig.gptModel
    Bot->>Storage: Update config
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • src/components/Bot.tsx: Dense logic for streaming message handling, metadata merging, message normalization, observer callbacks, and localStorage persistence. Cross-verify that consecutive message consolidation and cache eviction logic are sound.
  • src/utils/textTruncator.ts: Character width calculation logic for Korean Hangul, full-width characters, and mixed scripts. Verify width unit assignments are correct for all supported languages/character types and test edge cases (empty strings, newlines, mixed content).
  • src/components/inputs/ComboBox.tsx: New controlled/uncontrolled value model, keyboard accessibility, and dynamic width calculation. Ensure dropdown positioning and focus management work across scenarios.
  • src/components/bubbles/SourceBubble.tsx: Props interface completely changed; verify that all call sites (parent components) are updated to pass new index, chunkContent, title, imageSrc parameters instead of old pageContent, metadata.
  • Observer callbacks chaining: Verify that multiple observer types (observeUserInput, observeLoading, observeMessages, observeSourceClick, etc.) are called in the correct order and don't cause race conditions or re-entrancy issues.
  • External trigger event dispatch (flowise:open/close/toggle): Ensure event listeners are properly cleaned up on unmount and that the CustomEvent dispatch doesn't conflict with existing bot control flow.

Poem

🐰 A chatbot springs to life with Korean grace,
With menus, models, truncated space,
ComboBoxes choose, observers hear,
Streaming messages merge so clear!
Text widths calculated, bubble by bubble,
Our fuzzy friends solved bot troubles!

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The pull request title "Ksm" is a vague, non-descriptive term that does not convey any meaningful information about the changeset. While the PR objectives show this matches the branch name, the title fails to summarize or describe the actual changes. The changeset includes substantial modifications across multiple components (new ComboBox input, text truncation utilities, enhanced Bot configuration, UI refinements, Korean localization, new window API functions, and theme extensions), but the title provides no indication of these changes. Using just a branch name or abbreviation as a title does not meet the expectation of being clear and specific enough for teammates scanning history. Consider revising the pull request title to clearly describe the main focus of these changes. For example, a more descriptive title could be something like "Add ComboBox component, text truncation utilities, and enhance Bot configuration with observers and themes" or focus on a primary theme if one feature dominates. The title should be specific enough that teammates reviewing the repository history can quickly understand what was changed without needing to examine the full changeset.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/components/buttons/SendButton.tsx (1)

3-3: Remove unused import.

The DeleteIcon component is imported but no longer used in this file, as it has been replaced with an inline SVG in the DeleteButton component.

Apply this diff to remove the unused import:

-import { DeleteIcon, SendIcon } from '../icons';
+import { SendIcon } from '../icons';
src/components/bubbles/StarterPromptBubble.tsx (1)

4-26: Use the new backgroundColor prop.

Line 24 keeps the background hard-coded to white while Props now requires backgroundColor. Callers must supply a value that is never applied, so the new API is broken. Please bind the style to the prop.

-          'background-color': 'white',
+          'background-color': props.backgroundColor,
♻️ Duplicate comments (1)
src/components/buttons/FeedbackButtons.tsx (1)

25-25: Consider using an i18n/l10n system for button titles.

Like in FeedbackContentDialog.tsx, the hardcoded Korean tooltip titles make the UI Korean-only. For a maintainable multilingual approach, use an i18n library with language selection.

Example refactor:

const { t } = useI18n();

// CopyToClipboardButton
title={t('buttons.copyToClipboard')}

// ThumbsUpButton  
title={t('buttons.thumbsUp')}

// ThumbsDownButton
title={t('buttons.thumbsDown')}

Also applies to: 45-45, 65-65

🧹 Nitpick comments (13)
src/components/FeedbackContentDialog.tsx (1)

48-48: Consider using an i18n/l10n system instead of hardcoded Korean strings.

Hardcoding Korean text makes the UI Korean-only and difficult to maintain for multilingual support. Consider using an i18n library (e.g., solid-i18n, i18next) with language detection/selection.

Additionally, there's a spelling error on line 83: "메세지" should be "메시지" (correct Korean spelling for "message").

Example with an i18n system:

import { useI18n } from './i18n'; // hypothetical

const FeedbackContentDialog = (props: FeedbackContentDialogProps) => {
  const { t } = useI18n();
  
  return (
    // ...
    <span>{t('feedback.title')}</span>
    // ...
    <textarea placeholder={t('feedback.placeholder')} />
    // ...
    <button>{t('feedback.submit')}</button>
  );
};

Also applies to: 83-83, 93-93

src/components/buttons/SendButton.tsx (1)

59-83: LGTM! Consider extracting common button logic.

The CloseButton implementation is solid and follows the established pattern. The use of type="button" is correct for a non-submit action, and the default title provides good accessibility.

However, there's significant code duplication across SendButton, DeleteButton, and CloseButton (button structure, class composition, loading state handling, style attributes). Consider extracting a base button component or a shared composition function to reduce duplication and improve maintainability.

src/components/icons/ClipboardIcon.tsx (1)

4-30: Remove the commented legacy SVG.

The old 24×24 SVG is now commented out, which adds noise without serving a fallback. Let’s delete the unused block so the component stays lean.

-  // <svg
-  //   xmlns="http://www.w3.org/2000/svg"
-  //   class="icon icon-tabler icon-tabler-refresh w-4 h-4"
-  //   width="24"
-  //   height="24"
-  //   viewBox="0 0 24 24"
-  //   fill="none"
-  //   stroke={props.color ?? defaultButtonColor}
-  //   stroke-width="2"
-  //   stroke-linecap="round"
-  //   stroke-linejoin="round"
-  // >
-  //   <rect width="8" height="4" x="8" y="2" rx="1" ry="1" />
-  //   <path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" />
-  // </svg>
src/components/icons/ChevronDownIcon.tsx (1)

3-11: Drop the unused isCurrentColor prop.

isCurrentColor is declared but never referenced, which invites confusion. Please remove it (or wire it through) so the public API only exposes meaningful props.

-type Props = {
-  class?: string;
-  isCurrentColor?: boolean;
-};
+type Props = {
+  class?: string;
+};
src/features/bubble/types.ts (1)

140-144: Consider optional fields for ComboBoxTheme.

All fields in ComboBoxTheme are required, which might be restrictive. Consider:

  • Making defaultValue optional with a fallback to the first value in the array
  • Making label optional for cases where a label isn't needed

This would provide more flexibility while maintaining type safety.

 export type ComboBoxTheme = {
-  label: string;
-  defaultValue: string;
+  label?: string;
+  defaultValue?: string;
   values: { value: string; label: string }[];
 };
src/utils/index.ts (1)

154-155: Use English comments for consistency.

The exports are correct, but the Korean comment should be translated to English for codebase consistency.

-// 텍스트 자르기 관련 유틸리티 함수들
+// Text truncation utility functions
 export { truncateTextByWidth, getTextWidth } from './textTruncator';
src/utils/textTruncator.test.ts (2)

1-66: Use a proper testing framework with assertions.

This test file uses console.log statements instead of a proper testing framework. Consider migrating to Jest, Vitest, or another testing framework with assertions.

Example with Jest/Vitest:

import { describe, it, expect } from 'vitest';
import { truncateTextByWidth, getTextWidth } from './textTruncator';

describe('Text Truncation Utilities', () => {
  describe('truncateTextByWidth', () => {
    it('should truncate English text correctly', () => {
      const result = truncateTextByWidth('Hello World!', 10);
      expect(getTextWidth(result)).toBeLessThanOrEqual(10);
    });

    it('should handle Korean text', () => {
      const result = truncateTextByWidth('안녕하세요 반갑습니다', 10);
      expect(getTextWidth(result)).toBeLessThanOrEqual(10);
    });

    it('should handle empty strings', () => {
      expect(truncateTextByWidth('', 10)).toBe('');
    });
  });

  describe('getTextWidth', () => {
    it('should calculate width correctly for mixed text', () => {
      const width = getTextWidth('Hello 안녕');
      expect(width).toBeGreaterThan(0);
    });
  });
});

Additionally, translate Korean comments to English for consistency:

  • '=== 텍스트 자르기 함수 테스트 ===' → '=== Text Truncation Function Tests ==='
  • '영어 텍스트 (10글자 너비)' → 'English text (10 width units)'
  • etc.

68-76: Integrate tests into the build pipeline.

The tests are only executed manually in development mode in the browser. Consider:

  • Adding a test script to package.json
  • Running tests as part of CI/CD
  • Removing the conditional execution since test runners handle environment concerns

This would ensure tests run consistently across all environments and catch regressions early.

public/index.html (1)

22-138: Consider externalizing configuration for maintainability.

The initialization contains a large, deeply nested configuration object with many hardcoded values (URLs, user-specific data, UI settings). While functional, this makes the file difficult to maintain and update.

Consider:

  • Moving the configuration to a separate JSON or JS module
  • Using environment-specific configuration files
  • Documenting which values are intended to be customized vs. defaults

This would improve reusability and make it easier to maintain different deployment configurations.

src/components/bubbles/SourceBubble.tsx (3)

85-85: Magic number for truncation width needs clarification.

The maxWidth value of 50 appears arbitrary. Consider:

  • Extracting this as a named constant with a descriptive name (e.g., MAX_TEXT_WIDTH_WITH_IMAGE)
  • Documenting why this specific value was chosen
  • Calculating it dynamically based on the container dimensions if possible

Example:

+const MAX_TEXT_WIDTH_WITH_IMAGE = 50; // Chosen to leave room for 74px image below
+
 export const SourceBubble = (props: Props) => (
   <>
     <div
       ...
     >
       ...
-          {props.imageSrc && props.imageSrc.trim() !== '' ? truncateTextByWidth(props.chunkContent, 50) : props.chunkContent}
+          {props.imageSrc && props.imageSrc.trim() !== '' ? truncateTextByWidth(props.chunkContent, MAX_TEXT_WIDTH_WITH_IMAGE) : props.chunkContent}

23-32: Fixed dimensions may limit responsiveness.

The hardcoded width and height (139px) restrict the component's ability to adapt to different screen sizes or container contexts. While this may be intentional for a uniform grid layout, consider whether responsive sizing would be beneficial.

If responsiveness is desired, consider using percentage-based dimensions or CSS Grid/Flexbox for flexible layouts.


88-96: Consider error handling for invalid base64 images.

If props.imageSrc contains invalid base64 data, the image will fail to render without user feedback. Consider adding error handling to gracefully handle this scenario.

Example:

<img
  style={{ ... }}
  src={`data:image/png;base64,${props.imageSrc}`}
  alt="source"
  onError={(e) => {
    // Hide broken image or show placeholder
    (e.target as HTMLImageElement).style.display = 'none';
  }}
/>
src/features/bubble/components/Bubble.tsx (1)

74-90: Simplify external trigger binding logic.

The external trigger implementation has two code paths: direct element binding (lines 76-81) and document-level delegation (lines 82-89). The fallback delegation is only needed when the element isn't immediately available, but both paths always execute, creating unnecessary complexity.

Consider simplifying by using only the delegation approach, which handles both cases:

   createEffect(() => {
     if (!externalTriggerElementId) return;
-    const el = document.getElementById(externalTriggerElementId);
-    if (el) {
-      const handler = () => toggleBot();
-      el.addEventListener('click', handler);
-      return () => el.removeEventListener('click', handler);
-    }
     const handler = (e: Event) => {
       const target = e.target as HTMLElement | null;
       if (!target) return;
       const el = target.closest(`#${externalTriggerElementId}`);
       if (el) toggleBot();
     };
     document.addEventListener('click', handler, true);
     return () => document.removeEventListener('click', handler, true);
   });

Or, if direct binding is preferred for performance, choose one approach based on whether the element exists at initialization.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 596759e and f7bfe5d.

⛔ Files ignored due to path filters (154)
  • dist/components/Badge.d.ts is excluded by !**/dist/**
  • dist/components/Badge.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/Bot.d.ts is excluded by !**/dist/**
  • dist/components/Bot.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/FeedbackContentDialog.d.ts is excluded by !**/dist/**
  • dist/components/FeedbackContentDialog.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/ImageUploadButton.d.ts is excluded by !**/dist/**
  • dist/components/ImageUploadButton.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/RecordAudioButton.d.ts is excluded by !**/dist/**
  • dist/components/RecordAudioButton.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/RichTreeView.d.ts is excluded by !**/dist/**
  • dist/components/RichTreeView.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/SendButton.d.ts is excluded by !**/dist/**
  • dist/components/SendButton.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/TreeViewDemo.d.ts is excluded by !**/dist/**
  • dist/components/TreeViewDemo.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/TypingBubble.d.ts is excluded by !**/dist/**
  • dist/components/TypingBubble.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/avatars/Avatar.d.ts is excluded by !**/dist/**
  • dist/components/avatars/Avatar.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/avatars/DefaultAvatar.d.ts is excluded by !**/dist/**
  • dist/components/avatars/DefaultAvatar.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/bubbles/AgentReasoningBubble.d.ts is excluded by !**/dist/**
  • dist/components/bubbles/AgentReasoningBubble.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/bubbles/BotBubble.d.ts is excluded by !**/dist/**
  • dist/components/bubbles/BotBubble.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/bubbles/FollowUpPromptBubble.d.ts is excluded by !**/dist/**
  • dist/components/bubbles/FollowUpPromptBubble.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/bubbles/GuestBubble.d.ts is excluded by !**/dist/**
  • dist/components/bubbles/GuestBubble.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/bubbles/LeadCaptureBubble.d.ts is excluded by !**/dist/**
  • dist/components/bubbles/LeadCaptureBubble.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/bubbles/LoadingBubble.d.ts is excluded by !**/dist/**
  • dist/components/bubbles/LoadingBubble.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/bubbles/SourceBubble.d.ts is excluded by !**/dist/**
  • dist/components/bubbles/SourceBubble.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/bubbles/StarterPromptBubble.d.ts is excluded by !**/dist/**
  • dist/components/bubbles/StarterPromptBubble.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/buttons/AttachmentUploadButton.d.ts is excluded by !**/dist/**
  • dist/components/buttons/AttachmentUploadButton.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/buttons/CancelButton.d.ts is excluded by !**/dist/**
  • dist/components/buttons/CancelButton.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/buttons/FeedbackButtons.d.ts is excluded by !**/dist/**
  • dist/components/buttons/FeedbackButtons.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/buttons/ImageUploadButton.d.ts is excluded by !**/dist/**
  • dist/components/buttons/ImageUploadButton.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/buttons/LeadCaptureButtons.d.ts is excluded by !**/dist/**
  • dist/components/buttons/LeadCaptureButtons.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/buttons/RecordAudioButton.d.ts is excluded by !**/dist/**
  • dist/components/buttons/RecordAudioButton.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/buttons/SendButton.d.ts is excluded by !**/dist/**
  • dist/components/buttons/SendButton.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/examples/TreeViewExample.d.ts is excluded by !**/dist/**
  • dist/components/examples/TreeViewExample.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/examples/index.d.ts is excluded by !**/dist/**
  • dist/components/examples/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/icons/AddImageIcon.d.ts is excluded by !**/dist/**
  • dist/components/icons/AddImageIcon.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/icons/AttachmentIcon.d.ts is excluded by !**/dist/**
  • dist/components/icons/AttachmentIcon.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/icons/CircleDotIcon.d.ts is excluded by !**/dist/**
  • dist/components/icons/CircleDotIcon.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/icons/ClipboardIcon.d.ts is excluded by !**/dist/**
  • dist/components/icons/ClipboardIcon.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/icons/DeleteIcon.d.ts is excluded by !**/dist/**
  • dist/components/icons/DeleteIcon.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/icons/RecordIcon.d.ts is excluded by !**/dist/**
  • dist/components/icons/RecordIcon.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/icons/SendIcon.d.ts is excluded by !**/dist/**
  • dist/components/icons/SendIcon.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/icons/SparklesIcon.d.ts is excluded by !**/dist/**
  • dist/components/icons/SparklesIcon.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/icons/ThumbsDownIcon.d.ts is excluded by !**/dist/**
  • dist/components/icons/ThumbsDownIcon.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/icons/ThumbsUpIcon.d.ts is excluded by !**/dist/**
  • dist/components/icons/ThumbsUpIcon.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/icons/TickIcon.d.ts is excluded by !**/dist/**
  • dist/components/icons/TickIcon.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/icons/TrashIcon.d.ts is excluded by !**/dist/**
  • dist/components/icons/TrashIcon.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/icons/XIcon.d.ts is excluded by !**/dist/**
  • dist/components/icons/XIcon.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/icons/index.d.ts is excluded by !**/dist/**
  • dist/components/icons/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/index.d.ts is excluded by !**/dist/**
  • dist/components/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/inputs/textInput/components/FilePreview.d.ts is excluded by !**/dist/**
  • dist/components/inputs/textInput/components/FilePreview.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/inputs/textInput/components/ShortTextInput.d.ts is excluded by !**/dist/**
  • dist/components/inputs/textInput/components/ShortTextInput.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/inputs/textInput/components/TextInput.d.ts is excluded by !**/dist/**
  • dist/components/inputs/textInput/components/TextInput.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/inputs/textInput/index.d.ts is excluded by !**/dist/**
  • dist/components/inputs/textInput/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/treeview/DataTransformer.d.ts is excluded by !**/dist/**
  • dist/components/treeview/DataTransformer.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/treeview/RichTreeView.d.ts is excluded by !**/dist/**
  • dist/components/treeview/RichTreeView.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/treeview/TreeView.d.ts is excluded by !**/dist/**
  • dist/components/treeview/TreeView.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/components/treeview/WorkflowTreeView.d.ts is excluded by !**/dist/**
  • dist/components/treeview/WorkflowTreeView.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/constants.d.ts is excluded by !**/dist/**
  • dist/constants.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/features/bubble/components/Bubble.d.ts is excluded by !**/dist/**
  • dist/features/bubble/components/Bubble.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/features/bubble/components/BubbleButton.d.ts is excluded by !**/dist/**
  • dist/features/bubble/components/BubbleButton.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/features/bubble/components/Tooltip.d.ts is excluded by !**/dist/**
  • dist/features/bubble/components/Tooltip.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/features/bubble/components/index.d.ts is excluded by !**/dist/**
  • dist/features/bubble/components/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/features/bubble/index.d.ts is excluded by !**/dist/**
  • dist/features/bubble/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/features/bubble/types.d.ts is excluded by !**/dist/**
  • dist/features/bubble/types.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/features/full/components/Full.d.ts is excluded by !**/dist/**
  • dist/features/full/components/Full.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/features/full/components/index.d.ts is excluded by !**/dist/**
  • dist/features/full/components/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/features/full/index.d.ts is excluded by !**/dist/**
  • dist/features/full/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/features/popup/components/DisclaimerPopup.d.ts is excluded by !**/dist/**
  • dist/features/popup/components/DisclaimerPopup.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/features/popup/components/Popup.d.ts is excluded by !**/dist/**
  • dist/features/popup/components/Popup.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/features/popup/components/index.d.ts is excluded by !**/dist/**
  • dist/features/popup/components/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/features/popup/index.d.ts is excluded by !**/dist/**
  • dist/features/popup/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/features/popup/types.d.ts is excluded by !**/dist/**
  • dist/features/popup/types.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/index.d.ts is excluded by !**/dist/**
  • dist/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/queries/sendMessageQuery.d.ts is excluded by !**/dist/**
  • dist/queries/sendMessageQuery.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/register.d.ts is excluded by !**/dist/**
  • dist/register.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/types.d.ts is excluded by !**/dist/**
  • dist/types.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/utils/audioRecording.d.ts is excluded by !**/dist/**
  • dist/utils/audioRecording.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/utils/chatInputHistory.d.ts is excluded by !**/dist/**
  • dist/utils/chatInputHistory.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/utils/index.d.ts is excluded by !**/dist/**
  • dist/utils/index.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/utils/isMobileSignal.d.ts is excluded by !**/dist/**
  • dist/utils/isMobileSignal.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/web.d.ts is excluded by !**/dist/**
  • dist/web.d.ts.map is excluded by !**/dist/**, !**/*.map
  • dist/web.js is excluded by !**/dist/**
  • dist/web.umd.js is excluded by !**/dist/**
  • dist/window.d.ts is excluded by !**/dist/**
  • dist/window.d.ts.map is excluded by !**/dist/**, !**/*.map
📒 Files selected for processing (29)
  • .gitattributes (1 hunks)
  • .gitignore (1 hunks)
  • README.md (1 hunks)
  • public/index.html (1 hunks)
  • src/assets/index.css (1 hunks)
  • src/components/Bot.tsx (40 hunks)
  • src/components/FeedbackContentDialog.tsx (4 hunks)
  • src/components/avatars/Avatar.tsx (1 hunks)
  • src/components/bubbles/BotBubble.tsx (12 hunks)
  • src/components/bubbles/GuestBubble.tsx (1 hunks)
  • src/components/bubbles/LoadingBubble.tsx (1 hunks)
  • src/components/bubbles/SourceBubble.tsx (2 hunks)
  • src/components/bubbles/StarterPromptBubble.tsx (2 hunks)
  • src/components/buttons/FeedbackButtons.tsx (3 hunks)
  • src/components/buttons/SendButton.tsx (1 hunks)
  • src/components/icons/ChevronDownIcon.tsx (1 hunks)
  • src/components/icons/ClipboardIcon.tsx (1 hunks)
  • src/components/icons/index.ts (1 hunks)
  • src/components/inputs/ComboBox.tsx (1 hunks)
  • src/components/inputs/index.ts (1 hunks)
  • src/components/inputs/textInput/components/ShortTextInput.tsx (1 hunks)
  • src/features/bubble/components/Bubble.tsx (4 hunks)
  • src/features/bubble/types.ts (4 hunks)
  • src/features/full/components/Full.tsx (1 hunks)
  • src/queries/sendMessageQuery.ts (2 hunks)
  • src/utils/index.ts (1 hunks)
  • src/utils/textTruncator.test.ts (1 hunks)
  • src/utils/textTruncator.ts (1 hunks)
  • src/window.ts (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
src/components/buttons/SendButton.tsx (1)
src/components/buttons/RecordAudioButton.tsx (1)
  • Spinner (31-47)
src/utils/textTruncator.ts (1)
src/utils/index.ts (2)
  • truncateTextByWidth (155-155)
  • getTextWidth (155-155)
src/components/bubbles/SourceBubble.tsx (2)
src/utils/textTruncator.ts (1)
  • truncateTextByWidth (7-35)
src/utils/index.ts (1)
  • truncateTextByWidth (155-155)
src/components/bubbles/LoadingBubble.tsx (3)
src/utils/isMobileSignal.ts (1)
  • isMobile (3-3)
src/components/avatars/Avatar.tsx (1)
  • Avatar (6-24)
src/components/TypingBubble.tsx (1)
  • TypingBubble (1-7)
src/utils/textTruncator.test.ts (2)
src/utils/textTruncator.ts (2)
  • getTextWidth (84-90)
  • truncateTextByWidth (7-35)
src/utils/index.ts (2)
  • getTextWidth (155-155)
  • truncateTextByWidth (155-155)
src/components/inputs/ComboBox.tsx (2)
src/components/inputs/index.ts (1)
  • ComboBox (2-2)
src/components/icons/ChevronDownIcon.tsx (1)
  • ChevronDownIcon (8-12)
src/components/bubbles/BotBubble.tsx (4)
src/components/Bot.tsx (1)
  • MessageType (116-138)
src/queries/sendMessageQuery.ts (1)
  • updateFeedbackQuery (73-79)
src/components/avatars/Avatar.tsx (1)
  • Avatar (6-24)
src/components/bubbles/SourceBubble.tsx (1)
  • SourceBubble (11-101)
src/components/buttons/FeedbackButtons.tsx (2)
src/components/buttons/SendButton.tsx (1)
  • Spinner (85-101)
src/components/icons/ThumbsDownIcon.tsx (1)
  • ThumbsDownIcon (3-19)
src/features/bubble/components/Bubble.tsx (1)
src/features/bubble/components/BubbleButton.tsx (1)
  • BubbleButton (20-146)
src/queries/sendMessageQuery.ts (1)
src/utils/index.ts (1)
  • sendRequest (9-73)
src/components/Bot.tsx (5)
src/features/bubble/types.ts (1)
  • ComboBoxTheme (140-144)
src/queries/sendMessageQuery.ts (1)
  • sendMessageLog (134-140)
src/components/inputs/ComboBox.tsx (1)
  • ComboBox (31-251)
src/components/inputs/index.ts (1)
  • ComboBox (2-2)
src/components/bubbles/LoadingBubble.tsx (1)
  • LoadingBubble (28-50)
🪛 Gitleaks (8.28.0)
public/index.html

[high] 33-33: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (20)
.gitignore (1)

2-2: LGTM!

Adding dist to .gitignore is standard practice for build output directories. This aligns well with the PR's expanded UI component system and build artifacts.

.gitattributes (1)

1-42: Good addition for cross-platform consistency.

This .gitattributes file properly configures Git to normalize line endings across the project. The setup—with text=auto as a default, explicit LF enforcement for source files and configs, and binary declarations—is a solid best practice that will help prevent line-ending inconsistencies across contributors on different operating systems.

The coverage is appropriate for a TypeScript/JavaScript web project.

src/components/FeedbackContentDialog.tsx (1)

20-20: Validation change may not be effective without HTML5 constraints.

The switch from checking inputValue() to inputRef?.reportValidity() is a step toward HTML5 validation, but the textarea element (lines 74-85) has no validation attributes like required, minlength, etc. Without constraints, reportValidity() will always return true, making this check effectively a no-op.

If validation is needed, add constraints to the textarea:

 <textarea
   onInput={(e) => handleInput(e.currentTarget.value)}
   ref={inputRef as HTMLTextAreaElement}
   rows="4"
+  required
   class="block p-2.5 rounded-lg border focus:ring-blue-500 focus:border-blue-500 bg-transparent flex-1 w-full feedback-input disabled:opacity-50 disabled:cursor-not-allowed disabled:brightness-100 font-normal"

Otherwise, if no validation is required, you can revert to the simpler check or remove it entirely.

src/queries/sendMessageQuery.ts (2)

18-18: LGTM!

Adding aimlHost to the BaseRequest type is a clean design choice that allows all request functions to optionally target a separate AIML host.


134-140: The code is syntactically valid; the review comment's "syntax error" claim is incorrect.

The missing space in chatflowid,aimlHost on line 134 is a style inconsistency, not a syntax error—both {a,b} and {a, b} are valid JavaScript/TypeScript. The code parses and executes without errors.

The endpoint is properly integrated: Bot.tsx (lines 555–560) calls sendMessageLog(), passing aimlHost from config variables (vars.aimlUrl), which overrides the default port 8443 fallback shown in public/index.html line 28.

Likely an incorrect or invalid review comment.

src/components/buttons/SendButton.tsx (1)

48-54: LGTM!

The inline SVG implementation is clean and consistent with the CloseButton approach. Using props.sendButtonColor for dynamic fill color provides good flexibility.

src/components/inputs/textInput/components/ShortTextInput.tsx (1)

40-49: Address Korean comments and clarify multi-layered Backspace handling; note that ref cleanup is valid SolidJS pattern.

The review comment contains factually incorrect analysis. The ref callback returning a cleanup function is the correct SolidJS pattern—not a memory leak. SolidJS ref callbacks properly support cleanup functions, and the removeEventListener calls are appropriate resource cleanup.

However, the following concerns remain valid:

  1. Three layers of Backspace handling (lines 41–49, 63–71, 74–80) appear redundant. Please clarify why all three are necessary, or consolidate to a single approach.

  2. Korean comments should be replaced with English for international collaboration (lines 41, 44, 55, 63, 66, 69, 74, 83).

  3. Type inconsistency: props.ref is typed as HTMLInputElement | HTMLTextAreaElement | undefined (line 6), but the code treats it as a callback function (line 57: typeof props.ref === 'function') and assigns to it directly (line 58: (props.ref as any) = node). Either update the type definition or fix the assignment logic.

Likely an incorrect or invalid review comment.

src/assets/index.css (1)

202-203: LGTM! Clean visual polish.

The removal of the border and change to pure white background (#ffffff) creates a cleaner, more modern look for host bubbles.

src/features/full/components/Full.tsx (1)

96-100: LGTM! Consistent prop forwarding.

The new props are properly forwarded from theme.chatWindow to the Bot component, following the established pattern in this file.

src/components/bubbles/GuestBubble.tsx (1)

88-90: Verify mobile layout with increased margin.

The left margin increase from 50px to 100px provides better visual separation on desktop, but may cause layout issues on mobile devices with narrow viewports.

Consider testing on mobile/narrow viewports to ensure the guest bubble container doesn't overflow or create horizontal scrolling.

src/components/inputs/index.ts (1)

1-2: LGTM! Clean barrel exports.

The exports follow standard patterns and provide a clean public API for input components.

src/features/bubble/types.ts (3)

31-31: LGTM! Speech-to-text feature flag.

The addition of isSpeechToTextEnabled provides a clean way to toggle speech-to-text functionality via theme configuration.


43-49: LGTM! Enhanced bot message theming.

The new properties enable richer bot message presentation with state-specific avatars and emphasized backgrounds, while maintaining backward compatibility through optional fields.


90-94: LGTM! Model/module selectors and close button controls.

The addition of gptModels and mdmModules (typed as ComboBoxTheme) provides a structured way to configure model and module selection, while the close button properties offer flexible control over window dismissal behavior.

README.md (1)

87-108: LGTM! Clear and helpful documentation.

The new "Smart Text Truncation" section provides clear explanations of the feature with practical usage examples. The width unit specifications for different character types are well-documented.

src/utils/textTruncator.ts (1)

7-35: Consider edge case: empty string after truncation.

If maxWidth is very small (e.g., less than the width of a single character), the function will return only '...' without any original content. While this may be acceptable, consider whether a minimum width validation or a different behavior (e.g., returning at least one character) would be more appropriate.

Verify the expected behavior when maxWidth is smaller than the first character's width. If this is intentional, consider adding a comment documenting this edge case.

src/components/bubbles/LoadingBubble.tsx (2)

16-26: Good implementation of loading animation.

The LoadingDots component correctly manages its interval lifecycle with onMount and onCleanup. The modulo arithmetic creates a smooth 0-3 dots animation pattern.


30-34: Avatar placeholder alignment is well-handled.

The fallback div maintains proper spacing when isAppending is true by matching the avatar dimensions. This ensures consistent layout during message streaming.

src/features/bubble/components/Bubble.tsx (2)

60-72: External event integration is well-implemented.

The lifecycle management for flowise:open, flowise:close, and flowise:toggle events properly registers listeners in a createEffect and cleans them up on unmount. This enables external code to control the bot programmatically.


138-138: Clarify positioning calculation—not a circular dependency, but verify button drag behavior.

The concern about "circular dependency" is incorrect. The variables chatWindowBottom, buttonSize, and buttonBottom are all statically calculated once; they don't create recursive dependencies.

However, there is a real consistency issue: chatWindowBottom is a fixed value computed at mount, but buttonPosition() is reactive and changes when the button is dragged. The constraint Math.min(..., window.innerHeight - chatWindowBottom) therefore uses an outdated reference frame after dragging.

The Tooltip component (line 51 in Tooltip.tsx) uses the correct dynamic formula: position.bottom + buttonSize + 10. The chat window should do the same, or document why a static constraint is intentional.

Verify:

  • Does button drag behavior cause the chat window to misalign or overlap?
  • Is the static constraint intentional, or should it recalculate on button position changes?

langCode: "KO",
mdmUrl: "https://rnd.tobeway.com:8000/tbwapi",
userId: "ksm",
userToken: "gejDolc+90dtL1t+mszjAPT0bIMdgqHCplmFf66mJ9s="
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove hardcoded credentials from source code.

The userToken field contains what appears to be a sensitive authentication token. Hardcoding credentials in source files, especially in HTML that may be committed to version control, poses a significant security risk.

Consider these alternatives:

  • Use environment variables or a configuration service to inject credentials at runtime
  • For demo/test purposes, use placeholder values and document where real credentials should be provided
  • If this is meant to be a template, replace with a clear placeholder like "YOUR_TOKEN_HERE"

Apply this approach for other sensitive values as well (lines 24, 28, 31, 32).

🧰 Tools
🪛 Gitleaks (8.28.0)

[high] 33-33: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🤖 Prompt for AI Agents
public/index.html lines 24, 28, 31, 32 and specifically line 33: remove the
hardcoded sensitive token and any other hardcoded credentials and replace them
with clear placeholder values (e.g. "YOUR_TOKEN_HERE"); update the page to
obtain real credentials at runtime via environment-backed injection or a secure
configuration endpoint (server-side templating, build-time env substitution, or
fetching a secure config API) and add a short comment documenting where/how to
supply real credentials for deployment; ensure no real secrets remain in the
committed file.

}
>
<img src={avatarSrc()} alt="Bot avatar" class="rounded-full object-cover w-full h-full" />
<img src={avatarSrc()} alt="Bot avatar" class="object-cover w-full h-full" />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restore circular mask on avatar image.

Line 20 drops rounded-full from the <img>, and because the parent <figure> doesn’t hide overflow, the image now renders with square corners outside the circular frame. Please reinstate the masking so avatars remain round.

-        <img src={avatarSrc()} alt="Bot avatar" class="object-cover w-full h-full" />
+        <img src={avatarSrc()} alt="Bot avatar" class="rounded-full object-cover w-full h-full" />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<img src={avatarSrc()} alt="Bot avatar" class="object-cover w-full h-full" />
<img src={avatarSrc()} alt="Bot avatar" class="rounded-full object-cover w-full h-full" />
🤖 Prompt for AI Agents
In src/components/avatars/Avatar.tsx around line 20, the <img> lost its circular
mask and now shows square corners because the parent <figure> doesn't hide
overflow; restore the avatar's round shape by adding the rounded-full Tailwind
class back onto the image (preserve the existing object-cover w-full h-full
classes) and ensure you use the JSX prop name className instead of class if this
is a React/TSX file.

Comment on lines +2521 to +2524
uploadsConfig={{
...uploadsConfig(),
isSpeechToTextEnabled: props.textInput?.isSpeechToTextEnabled ?? uploadsConfig()?.isSpeechToTextEnabled ?? false
}}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Guard against spreading undefined uploadsConfig

uploadsConfig() is undefined on the first render, so spreading it throws a TypeError. This breaks input rendering until the config arrives. Wrap the spread in a fallback object before decorating it.

-                  uploadsConfig={{
-                    ...uploadsConfig(),
-                    isSpeechToTextEnabled: props.textInput?.isSpeechToTextEnabled ?? uploadsConfig()?.isSpeechToTextEnabled ?? false
-                  }}
+                  uploadsConfig={{
+                    ...(uploadsConfig() ?? {}),
+                    isSpeechToTextEnabled: props.textInput?.isSpeechToTextEnabled ?? uploadsConfig()?.isSpeechToTextEnabled ?? false
+                  }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
uploadsConfig={{
...uploadsConfig(),
isSpeechToTextEnabled: props.textInput?.isSpeechToTextEnabled ?? uploadsConfig()?.isSpeechToTextEnabled ?? false
}}
uploadsConfig={{
...(uploadsConfig() ?? {}),
isSpeechToTextEnabled: props.textInput?.isSpeechToTextEnabled ?? uploadsConfig()?.isSpeechToTextEnabled ?? false
}}
🤖 Prompt for AI Agents
In src/components/Bot.tsx around lines 2521 to 2524, spreading uploadsConfig()
can throw when uploadsConfig() is undefined on first render; fix by falling back
to an empty object before spreading (e.g., use (uploadsConfig() ?? {}) as the
spread source) then set isSpeechToTextEnabled using
props.textInput?.isSpeechToTextEnabled ??
(uploadsConfig()?.isSpeechToTextEnabled ?? false) so the spread never receives
undefined and the property still falls back to false.

Comment on lines 32 to +33
const isFullChatbot = document.querySelector('flowise-fullchatbot') !== null;
const paddingClass = isFullChatbot ? 'px-4' : 'px-12';
const paddingClass = 'px-2'; // isFullChatbot ? 'px-2' : 'px-12';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused variable.

The isFullChatbot variable is computed but never used since the padding is now hardcoded. This is dead code that should be removed.

Apply this diff to remove the unused code:

-  // Check if <flowise-fullchatbot> is present in the DOM
-  const isFullChatbot = document.querySelector('flowise-fullchatbot') !== null;
   const paddingClass = 'px-2'; // isFullChatbot ? 'px-2' : 'px-12';

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/components/buttons/SendButton.tsx around lines 32 to 33, remove the
unused dead variable declaration `const isFullChatbot =
document.querySelector('flowise-fullchatbot') !== null;` since padding is
hardcoded; simply delete that line and leave the paddingClass assignment as-is,
ensuring no other references to isFullChatbot exist in the file.

Comment on lines +170 to +175
createEffect(() => {
if (isOpen()) {
document.addEventListener('click', handleOutsideClick);
return () => document.removeEventListener('click', handleOutsideClick);
}
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix leaked outside-click listener

createEffect in Solid does not use the return value for cleanup, so every open cycles adds another document listener that never detaches. Use onCleanup to unregister the handler and stop the leak.

-import { createSignal, Show, For, createEffect } from 'solid-js';
+import { createSignal, Show, For, createEffect, onCleanup } from 'solid-js';
@@
   createEffect(() => {
     if (isOpen()) {
       document.addEventListener('click', handleOutsideClick);
-      return () => document.removeEventListener('click', handleOutsideClick);
+      onCleanup(() => document.removeEventListener('click', handleOutsideClick));
     }
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
createEffect(() => {
if (isOpen()) {
document.addEventListener('click', handleOutsideClick);
return () => document.removeEventListener('click', handleOutsideClick);
}
});
createEffect(() => {
if (isOpen()) {
document.addEventListener('click', handleOutsideClick);
onCleanup(() => document.removeEventListener('click', handleOutsideClick));
}
});

Comment on lines +54 to +89
ref={(el) => {
// 사용자 ref 호환
const assign = (node: HTMLTextAreaElement | null) => {
if (typeof props.ref === 'function') props.ref(node as any);
else if (props.ref) (props.ref as any) = node;
};
assign(el);

if (el) {
// keydown 캡처 단계에서 Backspace 전파 차단
const keydownCapture = (e: KeyboardEvent) => {
if (e.key === 'Backspace') {
// 포커스 보장
if (document.activeElement !== el) el.focus();
e.stopPropagation();
// preventDefault는 하지 않음(삭제 동작 허용)
}
};
el.addEventListener('keydown', keydownCapture, { capture: true });

// beforeinput 캡처 단계에서 deleteContentBackward 전파 차단
const beforeInputCapture = (e: Event) => {
const ie = e as InputEvent;
if ((ie as any).inputType === 'deleteContentBackward') {
e.stopPropagation();
}
};
el.addEventListener('beforeinput', beforeInputCapture as EventListener, { capture: true });

// 클린업
return () => {
el.removeEventListener('keydown', keydownCapture, { capture: true } as any);
el.removeEventListener('beforeinput', beforeInputCapture as EventListener, { capture: true } as any);
};
}
}}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Memory leak from improper cleanup and incorrect ref assignment.

This ref callback implementation has critical issues:

  1. Memory leak: SolidJS ref callbacks don't support returning cleanup functions like React's useEffect. The cleanup function returned on lines 84-88 will never be called, so the event listeners added on lines 72 and 81 will never be removed. This causes a memory leak every time the component is mounted.

  2. Incorrect object ref assignment: Line 58 attempts (props.ref as any) = node; which won't work for object refs. You cannot reassign a const reference; you need to assign to its current property if it's a mutable ref object.

  3. Complexity: This adds the second and third layers of Backspace/deletion handling (capture-phase keydown and beforeinput), making the logic difficult to understand and maintain.

Solution: Use SolidJS's proper cleanup mechanism:

-  return (
-    <textarea
-      ref={(el) => {
-        // 사용자 ref 호환
-        const assign = (node: HTMLTextAreaElement | null) => {
-          if (typeof props.ref === 'function') props.ref(node as any);
-          else if (props.ref) (props.ref as any) = node;
-        };
-        assign(el);
-
-        if (el) {
-          // keydown 캡처 단계에서 Backspace 전파 차단
-          const keydownCapture = (e: KeyboardEvent) => {
-            if (e.key === 'Backspace') {
-              // 포커스 보장
-              if (document.activeElement !== el) el.focus();
-              e.stopPropagation();
-              // preventDefault는 하지 않음(삭제 동작 허용)
-            }
-          };
-          el.addEventListener('keydown', keydownCapture, { capture: true });
-
-          // beforeinput 캡처 단계에서 deleteContentBackward 전파 차단
-          const beforeInputCapture = (e: Event) => {
-            const ie = e as InputEvent;
-            if ((ie as any).inputType === 'deleteContentBackward') {
-              e.stopPropagation();
-            }
-          };
-          el.addEventListener('beforeinput', beforeInputCapture as EventListener, { capture: true });
-
-          // 클린업
-          return () => {
-            el.removeEventListener('keydown', keydownCapture, { capture: true } as any);
-            el.removeEventListener('beforeinput', beforeInputCapture as EventListener, { capture: true } as any);
-          };
-        }
-      }}
+  let textareaRef: HTMLTextAreaElement | undefined;
+
+  createEffect(() => {
+    const el = textareaRef;
+    if (!el) return;
+
+    // Capture-phase listener for Backspace
+    const keydownCapture = (e: KeyboardEvent) => {
+      if (e.key === 'Backspace') {
+        if (document.activeElement !== el) el.focus();
+        e.stopPropagation();
+      }
+    };
+    el.addEventListener('keydown', keydownCapture, { capture: true });
+
+    // Capture-phase listener for deleteContentBackward
+    const beforeInputCapture = (e: Event) => {
+      const ie = e as InputEvent;
+      if (ie.inputType === 'deleteContentBackward') {
+        e.stopPropagation();
+      }
+    };
+    el.addEventListener('beforeinput', beforeInputCapture, { capture: true });
+
+    // Proper cleanup in SolidJS
+    onCleanup(() => {
+      el.removeEventListener('keydown', keydownCapture, { capture: true });
+      el.removeEventListener('beforeinput', beforeInputCapture, { capture: true });
+    });
+  });
+
+  return (
+    <textarea
+      ref={(el) => {
+        textareaRef = el;
+        // Assign to user's ref
+        if (typeof props.ref === 'function') {
+          props.ref(el as any);
+        } else if (props.ref && 'current' in props.ref) {
+          (props.ref as any).current = el;
+        }
+      }}

Note: You'll need to import createEffect and onCleanup from 'solid-js'.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/components/inputs/textInput/components/ShortTextInput.tsx around lines
54-89: the ref callback currently returns a cleanup (which SolidJS never calls)
and assigns object refs incorrectly, causing leaked event listeners and broken
object-ref behavior. Change the ref callback to only set a local element
variable and forward refs by calling props.ref(node) if it's a function or
setting (props.ref as any).current = node if it's an object ref; then move the
addEventListener logic into a Solid effect (createEffect) that watches the local
element and use onCleanup to remove the keydown and beforeinput listeners when
the element changes/unmounts. Ensure you import createEffect and onCleanup from
'solid-js' and remove the returned cleanup from the ref callback.

Comment on lines +57 to +58
const hideButton = (bubbleProps.theme as any)?.button?.hideButton ?? false;
const externalTriggerElementId: string | undefined = (bubbleProps.theme as any)?.button?.externalTriggerElementId;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Type assertions bypass type safety.

Using (bubbleProps.theme as any) to access hideButton and externalTriggerElementId indicates these properties are missing from the theme type definition. This bypasses TypeScript's type checking and can lead to runtime errors.

Update the theme type definition to include these properties:

// In the theme types file (e.g., src/features/bubble/types.ts)
interface ButtonTheme {
  // ... existing properties
  hideButton?: boolean;
  externalTriggerElementId?: string;
}

Then remove the type assertions:

-  const hideButton = (bubbleProps.theme as any)?.button?.hideButton ?? false;
-  const externalTriggerElementId: string | undefined = (bubbleProps.theme as any)?.button?.externalTriggerElementId;
+  const hideButton = bubbleProps.theme?.button?.hideButton ?? false;
+  const externalTriggerElementId = bubbleProps.theme?.button?.externalTriggerElementId;
🤖 Prompt for AI Agents
In src/features/bubble/components/Bubble.tsx around lines 57-58, the code uses
(bubbleProps.theme as any) to access button.hideButton and
button.externalTriggerElementId which bypasses TypeScript safety; update the
theme types (e.g., src/features/bubble/types.ts) to add hideButton?: boolean and
externalTriggerElementId?: string on the ButtonTheme interface (or the
appropriate theme interface), ensure BubbleProps.theme is typed to include that
ButtonTheme, then remove the type assertions in Bubble.tsx and access
bubbleProps.theme.button.hideButton and
bubbleProps.theme.button.externalTriggerElementId directly.

Comment on lines +75 to +77
// 영어, 숫자, 일반 특수문자: 1.0
return 1.5;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix inconsistency between comment and implementation.

The comment on line 75 states that English, numbers, and general special characters should have a width of 1.0, but line 76 returns 1.5. This inconsistency will cause incorrect width calculations for ASCII characters.

Apply this diff to align the implementation with the documented behavior:

-  // 영어, 숫자, 일반 특수문자: 1.0
-  return 1.5;
+  // 영어, 숫자, 일반 특수문자: 1.0
+  return 1.0;

Alternatively, if 1.5 is the intended value, update the comment to reflect the actual behavior.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 영어, 숫자, 일반 특수문자: 1.0
return 1.5;
}
// 영어, 숫자, 일반 특수문자: 1.0
return 1.0;
}
🤖 Prompt for AI Agents
In src/utils/textTruncator.ts around lines 75 to 77, the comment says
English/numeric/general special characters have width 1.0 but the function
returns 1.5; to fix, make the implementation match the comment by changing the
returned value from 1.5 to 1.0, or if 1.5 is intended, update the comment to
state that ASCII characters are treated as width 1.5—pick one and keep comment
and return value consistent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant