From 219c8ca4e7fcaf9975bbd29b597750bc450f56a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=83=81=EC=97=B0?= Date: Wed, 13 Aug 2025 16:52:10 +0900 Subject: [PATCH 1/2] =?UTF-8?q?alarm=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Buttons/AudioButton/AudioButton.tsx | 2 +- .../ChatBotButton/ChatBotButton.tsx | 2 +- src/components/ChatBotContainer.tsx | 328 +++++++++--------- src/context/BotStatesContext.tsx | 1 - src/types/Message.ts | 2 +- 5 files changed, 171 insertions(+), 164 deletions(-) diff --git a/src/components/Buttons/AudioButton/AudioButton.tsx b/src/components/Buttons/AudioButton/AudioButton.tsx index 3905fd3..cfb6b2a 100644 --- a/src/components/Buttons/AudioButton/AudioButton.tsx +++ b/src/components/Buttons/AudioButton/AudioButton.tsx @@ -68,7 +68,7 @@ const AudioButton = () => { tabIndex={0} // ← 키보드 포커스 가능 onMouseDown={async (event: MouseEvent) => { event.preventDefault(); - if (audioToggledOn) cancelTTS(); // 🔴 토글 OFF 직전 즉시 중단 + if (audioToggledOn) cancelTTS(); // 토글 OFF 직전 즉시 중단 await toggleAudio(); }} onKeyDown={async (e) => { diff --git a/src/components/ChatBotButton/ChatBotButton.tsx b/src/components/ChatBotButton/ChatBotButton.tsx index 4a26a4b..f2f80f2 100644 --- a/src/components/ChatBotButton/ChatBotButton.tsx +++ b/src/components/ChatBotButton/ChatBotButton.tsx @@ -9,7 +9,7 @@ import "./ChatBotButton.css"; * Toggles opening and closing of the chat window when general.embedded is false. */ const ChatBotButton = () => { - // ✅ 디버그 토글: true면 배지를 무조건 보여줌(표시 여부 문제 확인용) + const FORCE_BADGE_DEBUG = true; // handles settings diff --git a/src/components/ChatBotContainer.tsx b/src/components/ChatBotContainer.tsx index d913761..3ac3563 100644 --- a/src/components/ChatBotContainer.tsx +++ b/src/components/ChatBotContainer.tsx @@ -22,6 +22,9 @@ import { Slots } from "../types/Slots"; import "./ChatBotContainer.css"; +// ★ 추가: 히스토리 스토리지 제어 +import { setHistoryMessages } from "../services/ChatHistoryService"; // ← 여기 추가 + /** * Integrates, loads plugins and contains the various components that makeup the chatbot. * @@ -29,168 +32,173 @@ import "./ChatBotContainer.css"; * @param slots slots to inject custom headers, footers etc */ const ChatBotContainer = ({ - plugins, - slots, + plugins, + slots, }: { - plugins?: Array; - slots?: Slots; + plugins?: Array; + slots?: Slots; }) => { - // handles platform - const isDesktop = useIsDesktopInternal(); - - // handles settings - const { settings } = useSettingsContext(); - - // handles styles - const { styles } = useStylesContext(); - - // handles bot states - const { hasFlowStarted, setHasFlowStarted } = useBotStatesContext(); - - // handles bot refs - const { inputRef } = useBotRefsContext(); - - // handles chat window - const { - viewportHeight, - viewportWidth, - isChatWindowOpen, - } = useChatWindowInternal(); - - // handles paths - const { goToPath } = usePathsInternal(); - - // buttons to show in header, chat input and footer - const { headerButtons, chatInputButtons, footerButtons } = useButtonInternal(); - - // loads all use effects - useBotEffectsInternal(); - - // loads plugins - usePluginsInternal(plugins); - - // adds start path when flow is started - useEffect(() => { - if (hasFlowStarted || settings.general?.flowStartTrigger === "ON_LOAD") { - goToPath("start"); - } - }, [hasFlowStarted, settings.general?.flowStartTrigger]); - - /** - * Retrieves class name for window state. - */ - const windowStateClass = useMemo(() => { - const windowClass = "rcb-chatbot-global "; - if (settings.general?.embedded) { - return windowClass + "rcb-window-embedded"; - } else if (isChatWindowOpen) { - return windowClass + "rcb-window-open"; - } - return windowClass + "rcb-window-close"; - }, [settings, isChatWindowOpen]); - - /** - * Retrieves styles for chat window. - */ - const getChatWindowStyle = () => { - if (!isDesktop && !settings.general?.embedded) { - return { - ...styles.chatWindowStyle, - borderRadius: "0px", - left: "0px", - right: "auto", - top: "0px", - bottom: "auto", - width: `${viewportWidth}px`, - height: `${viewportHeight}px`, - zIndex: 10000, - } - } - - // if not embedded, add z-index - if (!settings.general?.embedded) { - return { - ...styles.chatWindowStyle, - zIndex: 10000, - }; - } - - return {...styles.chatWindowStyle}; - } - - /** - * Checks if chatbot should be shown depending on platform. - */ - const shouldShowChatBot = () => { - return (isDesktop && settings.device?.desktopEnabled) - || (!isDesktop && settings.device?.mobileEnabled); - } - - return ( - <> - {shouldShowChatBot() && -
{ - // checks if user is interacting with chatbot for the first time - if (!hasFlowStarted && settings.general?.flowStartTrigger === "ON_CHATBOT_INTERACT") { - setHasFlowStarted(true); - } - - // if not on mobile, should remove focus - isDesktop ? inputRef.current?.blur() : event?.preventDefault(); - }} - className={windowStateClass} - > - - - {/* styles and prevents background from scrolling on mobile when chat window is open */} - {isChatWindowOpen && !isDesktop && !settings.general?.embedded && - <> - -
-
- - } -
- {(() => { - const HeaderComponent = slots?.chatBotHeader || ChatBotHeader; - const BodyComponent = slots?.chatBotBody || ChatBotBody; - const InputComponent = slots?.chatBotInput || ChatBotInput; - const FooterComponent = slots?.chatBotFooter || ChatBotFooter; - - return ( - <> - {settings.general?.showHeader && } - - - {settings.general?.showInputRow && } - {settings.general?.showFooter && } - - ); - })()} -
-
- } - - ); + // handles platform + const isDesktop = useIsDesktopInternal(); + + // handles settings + const { settings } = useSettingsContext(); + + // handles styles + const { styles } = useStylesContext(); + + // handles bot states + const { hasFlowStarted, setHasFlowStarted } = useBotStatesContext(); + + // handles bot refs + const { inputRef } = useBotRefsContext(); + + // handles chat window + const { + viewportHeight, + viewportWidth, + isChatWindowOpen, + } = useChatWindowInternal(); + + // handles paths + const { goToPath } = usePathsInternal(); + + // buttons to show in header, chat input and footer + const { headerButtons, chatInputButtons, footerButtons } = useButtonInternal(); + + // loads all use effects + useBotEffectsInternal(); + + // loads plugins + usePluginsInternal(plugins); + + // ★ 추가: 페이지 첫 진입/새로고침마다 히스토리 초기화 + useEffect(() => { + setHistoryMessages([]); // ← 이것만으로 과거 히스토리(버튼으로 보이던 것) 싹 비워짐 + }, []); + + // adds start path when flow is started + useEffect(() => { + if (hasFlowStarted || settings.general?.flowStartTrigger === "ON_LOAD") { + goToPath("start"); + } + }, [hasFlowStarted, settings.general?.flowStartTrigger]); + + /** + * Retrieves class name for window state. + */ + const windowStateClass = useMemo(() => { + const windowClass = "rcb-chatbot-global "; + if (settings.general?.embedded) { + return windowClass + "rcb-window-embedded"; + } else if (isChatWindowOpen) { + return windowClass + "rcb-window-open"; + } + return windowClass + "rcb-window-close"; + }, [settings, isChatWindowOpen]); + + /** + * Retrieves styles for chat window. + */ + const getChatWindowStyle = () => { + if (!isDesktop && !settings.general?.embedded) { + return { + ...styles.chatWindowStyle, + borderRadius: "0px", + left: "0px", + right: "auto", + top: "0px", + bottom: "auto", + width: `${viewportWidth}px`, + height: `${viewportHeight}px`, + zIndex: 10000, + } + } + + // if not embedded, add z-index + if (!settings.general?.embedded) { + return { + ...styles.chatWindowStyle, + zIndex: 10000, + }; + } + + return { ...styles.chatWindowStyle }; + } + + /** + * Checks if chatbot should be shown depending on platform. + */ + const shouldShowChatBot = () => { + return (isDesktop && settings.device?.desktopEnabled) + || (!isDesktop && settings.device?.mobileEnabled); + } + + return ( + <> + {shouldShowChatBot() && +
{ + // checks if user is interacting with chatbot for the first time + if (!hasFlowStarted && settings.general?.flowStartTrigger === "ON_CHATBOT_INTERACT") { + setHasFlowStarted(true); + } + + // if not on mobile, should remove focus + isDesktop ? inputRef.current?.blur() : event?.preventDefault(); + }} + className={windowStateClass} + > + + + {/* styles and prevents background from scrolling on mobile when chat window is open */} + {isChatWindowOpen && !isDesktop && !settings.general?.embedded && + <> + +
+
+ + } +
+ {(() => { + const HeaderComponent = slots?.chatBotHeader || ChatBotHeader; + const BodyComponent = slots?.chatBotBody || ChatBotBody; + const InputComponent = slots?.chatBotInput || ChatBotInput; + const FooterComponent = slots?.chatBotFooter || ChatBotFooter; + + return ( + <> + {settings.general?.showHeader && } + + + {settings.general?.showInputRow && } + {settings.general?.showFooter && } + + ); + })()} +
+
+ } + + ); }; export default ChatBotContainer; diff --git a/src/context/BotStatesContext.tsx b/src/context/BotStatesContext.tsx index f94491b..e1b2bc9 100644 --- a/src/context/BotStatesContext.tsx +++ b/src/context/BotStatesContext.tsx @@ -121,7 +121,6 @@ const BotStatesProvider = ({ ?? window.innerWidth); useEffect(() => { setUnreadCount(0); // ✅ 앱 처음 켰을 때 무조건 0부터 시작 - // setHasFlowStarted(false); // 기본값이 false면 생략 }, []); // ✅ 앱 최초 진입 시각 기록 diff --git a/src/types/Message.ts b/src/types/Message.ts index d82b710..a697e3a 100644 --- a/src/types/Message.ts +++ b/src/types/Message.ts @@ -7,7 +7,7 @@ export type Message = { timestamp: string; tags?: Array; contentWrapper?: React.ComponentType<{ children: React.ReactNode }>; - // 👇 추가 + // 밑에 추가 isHistory?: boolean; // 과거 로드된 메시지 isRead?: boolean; // 읽음 여부 } From ffd1db4b32203d4e62900deb76f28e02265e0c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=91=EC=83=81=EC=97=B0?= Date: Wed, 13 Aug 2025 20:58:34 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=ED=9E=88=EC=8A=A4=ED=86=A0=EB=A6=AC=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ChatBotContainer.tsx | 330 +++++++++--------- .../ChatHistoryButton/ChatHistoryButton.tsx | 73 +--- 2 files changed, 165 insertions(+), 238 deletions(-) diff --git a/src/components/ChatBotContainer.tsx b/src/components/ChatBotContainer.tsx index 3ac3563..eb55904 100644 --- a/src/components/ChatBotContainer.tsx +++ b/src/components/ChatBotContainer.tsx @@ -22,9 +22,6 @@ import { Slots } from "../types/Slots"; import "./ChatBotContainer.css"; -// ★ 추가: 히스토리 스토리지 제어 -import { setHistoryMessages } from "../services/ChatHistoryService"; // ← 여기 추가 - /** * Integrates, loads plugins and contains the various components that makeup the chatbot. * @@ -32,173 +29,168 @@ import { setHistoryMessages } from "../services/ChatHistoryService"; // ← 여 * @param slots slots to inject custom headers, footers etc */ const ChatBotContainer = ({ - plugins, - slots, + plugins, + slots, }: { - plugins?: Array; - slots?: Slots; + plugins?: Array; + slots?: Slots; }) => { - // handles platform - const isDesktop = useIsDesktopInternal(); - - // handles settings - const { settings } = useSettingsContext(); - - // handles styles - const { styles } = useStylesContext(); - - // handles bot states - const { hasFlowStarted, setHasFlowStarted } = useBotStatesContext(); - - // handles bot refs - const { inputRef } = useBotRefsContext(); - - // handles chat window - const { - viewportHeight, - viewportWidth, - isChatWindowOpen, - } = useChatWindowInternal(); - - // handles paths - const { goToPath } = usePathsInternal(); - - // buttons to show in header, chat input and footer - const { headerButtons, chatInputButtons, footerButtons } = useButtonInternal(); - - // loads all use effects - useBotEffectsInternal(); - - // loads plugins - usePluginsInternal(plugins); - - // ★ 추가: 페이지 첫 진입/새로고침마다 히스토리 초기화 - useEffect(() => { - setHistoryMessages([]); // ← 이것만으로 과거 히스토리(버튼으로 보이던 것) 싹 비워짐 - }, []); - - // adds start path when flow is started - useEffect(() => { - if (hasFlowStarted || settings.general?.flowStartTrigger === "ON_LOAD") { - goToPath("start"); - } - }, [hasFlowStarted, settings.general?.flowStartTrigger]); - - /** - * Retrieves class name for window state. - */ - const windowStateClass = useMemo(() => { - const windowClass = "rcb-chatbot-global "; - if (settings.general?.embedded) { - return windowClass + "rcb-window-embedded"; - } else if (isChatWindowOpen) { - return windowClass + "rcb-window-open"; - } - return windowClass + "rcb-window-close"; - }, [settings, isChatWindowOpen]); - - /** - * Retrieves styles for chat window. - */ - const getChatWindowStyle = () => { - if (!isDesktop && !settings.general?.embedded) { - return { - ...styles.chatWindowStyle, - borderRadius: "0px", - left: "0px", - right: "auto", - top: "0px", - bottom: "auto", - width: `${viewportWidth}px`, - height: `${viewportHeight}px`, - zIndex: 10000, - } - } - - // if not embedded, add z-index - if (!settings.general?.embedded) { - return { - ...styles.chatWindowStyle, - zIndex: 10000, - }; - } - - return { ...styles.chatWindowStyle }; - } - - /** - * Checks if chatbot should be shown depending on platform. - */ - const shouldShowChatBot = () => { - return (isDesktop && settings.device?.desktopEnabled) - || (!isDesktop && settings.device?.mobileEnabled); - } - - return ( - <> - {shouldShowChatBot() && -
{ - // checks if user is interacting with chatbot for the first time - if (!hasFlowStarted && settings.general?.flowStartTrigger === "ON_CHATBOT_INTERACT") { - setHasFlowStarted(true); - } - - // if not on mobile, should remove focus - isDesktop ? inputRef.current?.blur() : event?.preventDefault(); - }} - className={windowStateClass} - > - - - {/* styles and prevents background from scrolling on mobile when chat window is open */} - {isChatWindowOpen && !isDesktop && !settings.general?.embedded && - <> - -
-
- - } -
- {(() => { - const HeaderComponent = slots?.chatBotHeader || ChatBotHeader; - const BodyComponent = slots?.chatBotBody || ChatBotBody; - const InputComponent = slots?.chatBotInput || ChatBotInput; - const FooterComponent = slots?.chatBotFooter || ChatBotFooter; - - return ( - <> - {settings.general?.showHeader && } - - - {settings.general?.showInputRow && } - {settings.general?.showFooter && } - - ); - })()} -
-
- } - - ); + // handles platform + const isDesktop = useIsDesktopInternal(); + + // handles settings + const { settings } = useSettingsContext(); + + // handles styles + const { styles } = useStylesContext(); + + // handles bot states + const { hasFlowStarted, setHasFlowStarted } = useBotStatesContext(); + + // handles bot refs + const { inputRef } = useBotRefsContext(); + + // handles chat window + const { + viewportHeight, + viewportWidth, + isChatWindowOpen, + } = useChatWindowInternal(); + + // handles paths + const { goToPath } = usePathsInternal(); + + // buttons to show in header, chat input and footer + const { headerButtons, chatInputButtons, footerButtons } = useButtonInternal(); + + // loads all use effects + useBotEffectsInternal(); + + // loads plugins + usePluginsInternal(plugins); + + // adds start path when flow is started + useEffect(() => { + if (hasFlowStarted || settings.general?.flowStartTrigger === "ON_LOAD") { + goToPath("start"); + } + }, [hasFlowStarted, settings.general?.flowStartTrigger]); + + /** + * Retrieves class name for window state. + */ + const windowStateClass = useMemo(() => { + const windowClass = "rcb-chatbot-global "; + if (settings.general?.embedded) { + return windowClass + "rcb-window-embedded"; + } else if (isChatWindowOpen) { + return windowClass + "rcb-window-open"; + } + return windowClass + "rcb-window-close"; + }, [settings, isChatWindowOpen]); + + /** + * Retrieves styles for chat window. + */ + const getChatWindowStyle = () => { + if (!isDesktop && !settings.general?.embedded) { + return { + ...styles.chatWindowStyle, + borderRadius: "0px", + left: "0px", + right: "auto", + top: "0px", + bottom: "auto", + width: `${viewportWidth}px`, + height: `${viewportHeight}px`, + zIndex: 10000, + } + } + + // if not embedded, add z-index + if (!settings.general?.embedded) { + return { + ...styles.chatWindowStyle, + zIndex: 10000, + }; + } + + return {...styles.chatWindowStyle}; + } + + /** + * Checks if chatbot should be shown depending on platform. + */ + const shouldShowChatBot = () => { + return (isDesktop && settings.device?.desktopEnabled) + || (!isDesktop && settings.device?.mobileEnabled); + } + + return ( + <> + {shouldShowChatBot() && +
{ + // checks if user is interacting with chatbot for the first time + if (!hasFlowStarted && settings.general?.flowStartTrigger === "ON_CHATBOT_INTERACT") { + setHasFlowStarted(true); + } + + // if not on mobile, should remove focus + isDesktop ? inputRef.current?.blur() : event?.preventDefault(); + }} + className={windowStateClass} + > + + + {/* styles and prevents background from scrolling on mobile when chat window is open */} + {isChatWindowOpen && !isDesktop && !settings.general?.embedded && + <> + +
+
+ + } +
+ {(() => { + const HeaderComponent = slots?.chatBotHeader || ChatBotHeader; + const BodyComponent = slots?.chatBotBody || ChatBotBody; + const InputComponent = slots?.chatBotInput || ChatBotInput; + const FooterComponent = slots?.chatBotFooter || ChatBotFooter; + + return ( + <> + {settings.general?.showHeader && } + + + {settings.general?.showInputRow && } + {settings.general?.showFooter && } + + ); + })()} +
+
+ } + + ); }; -export default ChatBotContainer; +export default ChatBotContainer; \ No newline at end of file diff --git a/src/components/ChatHistoryButton/ChatHistoryButton.tsx b/src/components/ChatHistoryButton/ChatHistoryButton.tsx index a5c15c4..2399956 100644 --- a/src/components/ChatHistoryButton/ChatHistoryButton.tsx +++ b/src/components/ChatHistoryButton/ChatHistoryButton.tsx @@ -1,69 +1,4 @@ -import { useState, MouseEvent } from "react"; - -import { useChatHistoryInternal } from "../../hooks/internal/useChatHistoryInternal"; -import { useSettingsContext } from "../../context/SettingsContext"; -import { useStylesContext } from "../../context/StylesContext"; - -import "./ChatHistoryButton.css"; - -/** - * Supports viewing of old messages. - */ -const ChatHistoryButton = () => { - // handles settings - const { settings } = useSettingsContext(); - - // handles styles - const { styles } = useStylesContext(); - - // handles chat history - const { showChatHistory } = useChatHistoryInternal(); - - // tracks if view history button is hovered - const [isHovered, setIsHovered] = useState(false); - - // styles for view chat history hovered button - const chatHistoryButtonHoveredStyle: React.CSSProperties = { - color: settings.general?.primaryColor, - borderColor: settings.general?.primaryColor, - ...styles.chatHistoryButtonStyle, // by default inherit the base style - ...styles.chatHistoryButtonHoveredStyle - }; - - /** - * Handles mouse enter event on view chat history button. - */ - const handleMouseEnter = () => { - setIsHovered(true); - }; - - /** - * Handles mouse leave event on view chat history button. - */ - const handleMouseLeave = () => { - setIsHovered(false); - }; - - return ( -
-
{ - event.preventDefault(); - showChatHistory(); - }} - className="rcb-view-history-button" - role="button" - tabIndex={0} - > -

- {settings.chatHistory?.viewChatHistoryButtonText} -

-
-
- ); -}; - -export default ChatHistoryButton; +// ChatHistoryButton.tsx — 히스토리 버튼 완전 비활성화(렌더 X) +export default function ChatHistoryButton() { + return null; +}