@newfold/wp-module-ai-chat 1.0.1 → 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.
Files changed (54) hide show
  1. package/build/index-rtl.css +1 -0
  2. package/build/index.asset.php +1 -0
  3. package/build/index.css +1 -0
  4. package/build/index.js +8 -0
  5. package/package.json +11 -9
  6. package/src/components/chat/ChatHeader.jsx +0 -63
  7. package/src/components/chat/ChatHistoryDropdown.jsx +0 -182
  8. package/src/components/chat/ChatHistoryList.jsx +0 -257
  9. package/src/components/chat/ChatInput.jsx +0 -157
  10. package/src/components/chat/ChatMessage.jsx +0 -157
  11. package/src/components/chat/ChatMessages.jsx +0 -137
  12. package/src/components/chat/WelcomeScreen.jsx +0 -115
  13. package/src/components/icons/CloseIcon.jsx +0 -27
  14. package/src/components/icons/SparklesOutlineIcon.jsx +0 -30
  15. package/src/components/icons/index.js +0 -5
  16. package/src/components/ui/AILogo.jsx +0 -47
  17. package/src/components/ui/BluBetaHeading.jsx +0 -18
  18. package/src/components/ui/ErrorAlert.jsx +0 -30
  19. package/src/components/ui/HeaderBar.jsx +0 -34
  20. package/src/components/ui/SuggestionButton.jsx +0 -28
  21. package/src/components/ui/ToolExecutionList.jsx +0 -264
  22. package/src/components/ui/TypingIndicator.jsx +0 -268
  23. package/src/constants/nfdAgents/input.js +0 -13
  24. package/src/constants/nfdAgents/storageKeys.js +0 -102
  25. package/src/constants/nfdAgents/typingStatus.js +0 -40
  26. package/src/constants/nfdAgents/websocket.js +0 -44
  27. package/src/hooks/useAIChat.js +0 -432
  28. package/src/hooks/useNfdAgentsWebSocket.js +0 -964
  29. package/src/index.js +0 -66
  30. package/src/services/mcpClient.js +0 -433
  31. package/src/services/openaiClient.js +0 -416
  32. package/src/styles/_branding.scss +0 -151
  33. package/src/styles/_history.scss +0 -180
  34. package/src/styles/_input.scss +0 -170
  35. package/src/styles/_messages.scss +0 -272
  36. package/src/styles/_mixins.scss +0 -21
  37. package/src/styles/_typing-indicator.scss +0 -162
  38. package/src/styles/_ui.scss +0 -173
  39. package/src/styles/_vars.scss +0 -103
  40. package/src/styles/_welcome.scss +0 -81
  41. package/src/styles/app.scss +0 -10
  42. package/src/utils/helpers.js +0 -75
  43. package/src/utils/markdownParser.js +0 -319
  44. package/src/utils/nfdAgents/archiveConversation.js +0 -82
  45. package/src/utils/nfdAgents/chatHistoryList.js +0 -130
  46. package/src/utils/nfdAgents/configFetcher.js +0 -137
  47. package/src/utils/nfdAgents/greeting.js +0 -55
  48. package/src/utils/nfdAgents/jwtUtils.js +0 -59
  49. package/src/utils/nfdAgents/messageHandler.js +0 -328
  50. package/src/utils/nfdAgents/storage.js +0 -112
  51. package/src/utils/nfdAgents/typingIndicatorToolDisplay.js +0 -180
  52. package/src/utils/nfdAgents/url.js +0 -101
  53. package/src/utils/restApi.js +0 -87
  54. package/src/utils/sanitizeHtml.js +0 -94
@@ -1,157 +0,0 @@
1
- /**
2
- * WordPress dependencies
3
- */
4
- import { useMemo, useState, useEffect, useRef } from "@wordpress/element";
5
-
6
- /**
7
- * Internal dependencies
8
- */
9
- import classnames from "classnames";
10
- import { containsHtml, sanitizeHtml } from "../../utils/sanitizeHtml";
11
- import { containsMarkdown, parseMarkdown, linkifyUrls } from "../../utils/markdownParser";
12
- import { unescapeAiResponse } from "../../utils/helpers";
13
- import ToolExecutionList from "../ui/ToolExecutionList";
14
-
15
- /** Typing animation: chars to reveal per tick, tick interval ms */
16
- const TYPING_CHARS_PER_TICK = 2;
17
- const TYPING_TICK_MS = 35;
18
-
19
- /**
20
- * ChatMessage Component
21
- *
22
- * Displays a single message in the chat with appropriate styling.
23
- * Supports HTML and Markdown rendering for assistant messages.
24
- *
25
- * @param {Object} props - The component props.
26
- * @param {string} props.message - The message content to display.
27
- * @param {string} [props.type="assistant"] - The message type ("user" or "assistant").
28
- * @param {boolean} [props.animateTyping] - When true, reveal assistant content with a typing effect.
29
- * @param {Function} [props.onContentGrow] - Called when displayed content grows (e.g. for scroll-into-view).
30
- * @param {Array} [props.executedTools=[]] - List of executed tools to show inline.
31
- * @param {Array} [props.toolResults=[]] - Results from tool executions.
32
- * @return {JSX.Element} The ChatMessage component.
33
- */
34
- const ChatMessage = ({
35
- message,
36
- type = "assistant",
37
- animateTyping = false,
38
- onContentGrow,
39
- executedTools = [],
40
- toolResults = [],
41
- }) => {
42
- // Treat approval_request as assistant so the thread still displays
43
- const displayType = type === "approval_request" ? "assistant" : type;
44
- const isUser = displayType === "user";
45
-
46
- const fullLength = (message || "").length;
47
- const [displayedLength, setDisplayedLength] = useState(() => (animateTyping ? 0 : fullLength));
48
-
49
- // When not animating, show full message; when animating, drive displayedLength toward fullLength and clear interval when done
50
- const intervalRef = useRef(null);
51
- useEffect(() => {
52
- if (!animateTyping) {
53
- setDisplayedLength(fullLength);
54
- return;
55
- }
56
- intervalRef.current = setInterval(() => {
57
- setDisplayedLength((prev) => {
58
- if (prev >= fullLength) {
59
- if (intervalRef.current) {
60
- clearInterval(intervalRef.current);
61
- intervalRef.current = null;
62
- }
63
- return prev;
64
- }
65
- return Math.min(prev + TYPING_CHARS_PER_TICK, fullLength);
66
- });
67
- }, TYPING_TICK_MS);
68
- return () => {
69
- if (intervalRef.current) {
70
- clearInterval(intervalRef.current);
71
- intervalRef.current = null;
72
- }
73
- };
74
- }, [animateTyping, fullLength]);
75
-
76
- // Notify parent when content grows (for auto-scroll), throttled to avoid excessive updates
77
- const lastGrowCallRef = useRef(0);
78
- useEffect(() => {
79
- if (!onContentGrow || !animateTyping || displayedLength === 0) {
80
- return;
81
- }
82
- const now = Date.now();
83
- if (now - lastGrowCallRef.current < 80) {
84
- return;
85
- }
86
- lastGrowCallRef.current = now;
87
- onContentGrow();
88
- }, [displayedLength, animateTyping, onContentGrow]);
89
-
90
- const effectiveMessage = animateTyping
91
- ? (message || "").slice(0, displayedLength)
92
- : message || "";
93
-
94
- // Choose rendering path: plain user text, sanitized HTML, parsed markdown, or linkified plain text.
95
- const { content, isRichContent } = useMemo(() => {
96
- if (!effectiveMessage) {
97
- return { content: "", isRichContent: false };
98
- }
99
-
100
- const raw = unescapeAiResponse(effectiveMessage);
101
-
102
- if (isUser) {
103
- return { content: raw, isRichContent: false };
104
- }
105
-
106
- if (containsHtml(raw)) {
107
- return { content: sanitizeHtml(raw), isRichContent: true };
108
- }
109
-
110
- if (containsMarkdown(raw)) {
111
- const parsed = parseMarkdown(raw);
112
- return { content: sanitizeHtml(parsed), isRichContent: true };
113
- }
114
-
115
- // Plain text: linkify bare URLs and preserve newlines
116
- const linkified = linkifyUrls(raw);
117
- if (linkified !== raw) {
118
- const withBreaks = linkified.replace(/\n/g, "<br>");
119
- return { content: sanitizeHtml(withBreaks), isRichContent: true };
120
- }
121
- return { content: raw, isRichContent: false };
122
- }, [effectiveMessage, isUser]);
123
-
124
- // Don't render if there's no content and no tools.
125
- if (!content && (!executedTools || executedTools.length === 0)) {
126
- return null;
127
- }
128
-
129
- const rootClassName = classnames("nfd-ai-chat-message", `nfd-ai-chat-message--${displayType}`);
130
-
131
- return (
132
- <div className={rootClassName}>
133
- {content &&
134
- (isRichContent ? (
135
- <>
136
- {/* content is sanitized in the useMemo above (sanitizeHtml) */}
137
- <div
138
- className="nfd-ai-chat-message__content nfd-ai-chat-message__content--rich"
139
- dangerouslySetInnerHTML={{ __html: content }}
140
- />
141
- </>
142
- ) : (
143
- <div
144
- className="nfd-ai-chat-message__content nfd-ai-chat-message__content--pre-wrap"
145
- style={{ whiteSpace: "pre-wrap" }}
146
- >
147
- {content}
148
- </div>
149
- ))}
150
- {executedTools && executedTools.length > 0 && (
151
- <ToolExecutionList executedTools={executedTools} toolResults={toolResults} />
152
- )}
153
- </div>
154
- );
155
- };
156
-
157
- export default ChatMessage;
@@ -1,137 +0,0 @@
1
- /**
2
- * WordPress dependencies
3
- */
4
- import { useEffect, useRef, useCallback, useState } from "@wordpress/element";
5
- import { __ } from "@wordpress/i18n";
6
-
7
- /**
8
- * Internal dependencies
9
- */
10
- import classnames from "classnames";
11
- import ErrorAlert from "../ui/ErrorAlert";
12
- import TypingIndicator from "../ui/TypingIndicator";
13
- import ChatMessage from "./ChatMessage";
14
-
15
- /**
16
- * ChatMessages Component
17
- *
18
- * Scrollable container for all chat messages.
19
- * Auto-scrolls to bottom when new messages arrive or when the last message content grows (e.g. typing animation).
20
- *
21
- * @param {Object} props - The component props.
22
- * @param {Array} props.messages - The messages to display.
23
- * @param {boolean} props.isLoading - Whether the AI is generating a response.
24
- * @param {string} props.error - Error message to display (optional).
25
- * @param {string} props.status - The current status.
26
- * @param {Object} props.activeToolCall - The currently executing tool call (optional).
27
- * @param {string} props.toolProgress - Real-time progress message (optional).
28
- * @param {Array} props.executedTools - List of completed tool executions (optional).
29
- * @param {Array} props.pendingTools - List of pending tools to execute (optional).
30
- * @param {Function} [props.onRetry] - Callback when user clicks Retry (e.g. after connection failed).
31
- * @param {boolean} [props.connectionFailed] - Whether connection has failed (show Retry without red error).
32
- * @param {boolean} [props.isConnectingOrReconnecting] - When true, show a single "Connecting...." assistant message.
33
- * @param {string} [props.messageBubbleStyle] - 'bubbles' (default) or 'minimal'. Controls container and message bubble styling.
34
- * @return {JSX.Element} The ChatMessages component.
35
- */
36
- const ChatMessages = ({
37
- messages = [],
38
- isLoading = false,
39
- error = null,
40
- status = null,
41
- activeToolCall = null,
42
- toolProgress = null,
43
- executedTools = [],
44
- pendingTools = [],
45
- onRetry,
46
- connectionFailed = false,
47
- isConnectingOrReconnecting = false,
48
- messageBubbleStyle = "bubbles",
49
- }) => {
50
- const scrollContainerRef = useRef(null);
51
- const [scrollTrigger, setScrollTrigger] = useState(0);
52
-
53
- const scrollToBottom = useCallback(() => {
54
- const el = scrollContainerRef.current;
55
- if (!el) {
56
- return;
57
- }
58
- el.scrollTo({
59
- top: el.scrollHeight - el.clientHeight,
60
- behavior: "smooth",
61
- });
62
- }, []);
63
-
64
- useEffect(() => {
65
- scrollToBottom();
66
- }, [messages, isLoading, toolProgress, scrollTrigger, scrollToBottom]);
67
-
68
- // Bump scroll trigger so the scroll effect runs again when the last message's content grows (e.g. typing).
69
- const onContentGrow = useCallback(() => {
70
- setScrollTrigger((t) => t + 1);
71
- }, []);
72
-
73
- // Only show executed tools in TypingIndicator when there is active or pending tool work.
74
- const hasActiveToolExecution =
75
- activeToolCall || executedTools.length > 0 || pendingTools.length > 0;
76
-
77
- const messagesClassName = classnames("nfd-ai-chat-messages", {
78
- "nfd-ai-chat-messages--minimal": messageBubbleStyle === "minimal",
79
- });
80
-
81
- return (
82
- <div ref={scrollContainerRef} className={messagesClassName}>
83
- {messages.length > 0 &&
84
- messages.map((msg, index) => {
85
- // Animate typing only for the last assistant message that was received live (not loaded from history/restore)
86
- const isLastAssistant =
87
- index === messages.length - 1 && (msg.type === "assistant" || msg.role === "assistant");
88
- return (
89
- <ChatMessage
90
- key={msg.id || index}
91
- message={msg.content}
92
- type={msg.type}
93
- animateTyping={isLastAssistant && msg.animateTyping === true}
94
- onContentGrow={isLastAssistant ? onContentGrow : undefined}
95
- executedTools={msg.executedTools}
96
- toolResults={msg.toolResults}
97
- />
98
- );
99
- })}
100
- {isConnectingOrReconnecting && !connectionFailed && (
101
- <div className="nfd-ai-chat-message nfd-ai-chat-message--assistant">
102
- <div className="nfd-ai-chat-message__content">
103
- <div className="nfd-ai-chat-typing-indicator">
104
- <span className="nfd-ai-chat-typing-indicator__dots" aria-hidden="true">
105
- <span></span>
106
- <span></span>
107
- <span></span>
108
- </span>
109
- <span className="nfd-ai-chat-typing-indicator__text">
110
- {__("Connecting....", "wp-module-ai-chat")}
111
- </span>
112
- </div>
113
- </div>
114
- </div>
115
- )}
116
- {error && <ErrorAlert message={error} />}
117
- {onRetry && (error || connectionFailed) && (
118
- <p className="nfd-ai-chat-messages__retry">
119
- <button type="button" className="nfd-ai-chat-messages__retry-button" onClick={onRetry}>
120
- {__("Retry", "wp-module-ai-chat")}
121
- </button>
122
- </p>
123
- )}
124
- {isLoading && (
125
- <TypingIndicator
126
- status={status}
127
- activeToolCall={activeToolCall}
128
- toolProgress={toolProgress}
129
- executedTools={hasActiveToolExecution ? executedTools : []}
130
- pendingTools={pendingTools}
131
- />
132
- )}
133
- </div>
134
- );
135
- };
136
-
137
- export default ChatMessages;
@@ -1,115 +0,0 @@
1
- /**
2
- * WordPress dependencies
3
- */
4
- import { __ } from "@wordpress/i18n";
5
- import { useState, useEffect, useRef } from "@wordpress/element";
6
-
7
- /**
8
- * Internal dependencies
9
- */
10
- import AILogo from "../ui/AILogo";
11
- import SuggestionButton from "../ui/SuggestionButton";
12
-
13
- const TYPING_SPEED_MS = 40;
14
-
15
- /**
16
- * WelcomeScreen Component
17
- *
18
- * Displays the welcome screen with AI avatar, introduction message, and suggestion tags.
19
- *
20
- * @param {Object} props - The component props.
21
- * @param {Function} props.onSendMessage - Function to call when a suggestion is clicked.
22
- * @param {string} props.title - Custom welcome title (optional).
23
- * @param {string} props.subtitle - Custom welcome subtitle (optional).
24
- * @param {Array} props.suggestions - Custom suggestions array (optional).
25
- * @param {boolean} props.showSuggestions - Whether to show suggestions (default: false).
26
- * @param {boolean} props.animateWelcome - Whether to type the welcome text (default: false).
27
- * @return {JSX.Element} The WelcomeScreen component.
28
- */
29
- const WelcomeScreen = ({
30
- onSendMessage,
31
- title,
32
- subtitle,
33
- suggestions = [],
34
- showSuggestions = false,
35
- animateWelcome = false,
36
- }) => {
37
- const defaultTitle = __("Hi, I'm your AI assistant.", "wp-module-ai-chat");
38
- const defaultSubtitle = __("How can I help you today?", "wp-module-ai-chat");
39
-
40
- const titleText = title || defaultTitle;
41
- const subtitleText = subtitle || defaultSubtitle;
42
- const fullText = `${titleText} ${subtitleText}`;
43
-
44
- const [displayedLength, setDisplayedLength] = useState(0);
45
- const timeoutRef = useRef(null);
46
-
47
- useEffect(() => {
48
- if (!animateWelcome) {
49
- setDisplayedLength(fullText.length);
50
- return;
51
- }
52
- setDisplayedLength(0);
53
- const animate = () => {
54
- setDisplayedLength((prev) => {
55
- if (prev >= fullText.length) {
56
- return prev;
57
- }
58
- timeoutRef.current = setTimeout(animate, TYPING_SPEED_MS);
59
- return prev + 1;
60
- });
61
- };
62
- timeoutRef.current = setTimeout(animate, TYPING_SPEED_MS);
63
- return () => {
64
- if (timeoutRef.current) {
65
- clearTimeout(timeoutRef.current);
66
- }
67
- };
68
- }, [animateWelcome, fullText]);
69
-
70
- const showTyping = animateWelcome && displayedLength < fullText.length;
71
- const displayedTitle =
72
- showTyping && displayedLength <= titleText.length
73
- ? titleText.slice(0, displayedLength)
74
- : titleText;
75
- // fullText is "title + space + subtitle"; the -1 below accounts for the space when slicing into subtitle.
76
- let displayedSubtitle = subtitleText;
77
- if (showTyping && displayedLength > titleText.length) {
78
- displayedSubtitle = subtitleText.slice(0, displayedLength - titleText.length - 1);
79
- } else if (showTyping) {
80
- displayedSubtitle = "";
81
- }
82
-
83
- return (
84
- <div className="nfd-ai-chat-welcome">
85
- <div className="nfd-ai-chat-welcome__content">
86
- <div className="nfd-ai-chat-welcome__avatar">
87
- <AILogo width={64} height={64} />
88
- </div>
89
- <div className="nfd-ai-chat-welcome__message">
90
- <div className="nfd-ai-chat-welcome__title">{displayedTitle}</div>
91
- <div className="nfd-ai-chat-welcome__subtitle">
92
- {displayedSubtitle}
93
- {showTyping && displayedLength < fullText.length && (
94
- <span className="nfd-ai-chat-welcome__cursor" aria-hidden="true" />
95
- )}
96
- </div>
97
- </div>
98
- </div>
99
- {showSuggestions && suggestions.length > 0 && (
100
- <div className="nfd-ai-chat-suggestions">
101
- {suggestions.map((suggestion, index) => (
102
- <SuggestionButton
103
- key={suggestion.action ?? suggestion.text ?? index}
104
- icon={suggestion.icon}
105
- text={suggestion.text}
106
- onClick={() => onSendMessage(suggestion.action || suggestion.text)}
107
- />
108
- ))}
109
- </div>
110
- )}
111
- </div>
112
- );
113
- };
114
-
115
- export default WelcomeScreen;
@@ -1,27 +0,0 @@
1
- /**
2
- * Close (×) icon for actions. Inline SVG for accessibility.
3
- *
4
- * @param {Object} props - SVG props (e.g. className, aria-*).
5
- * @return {JSX.Element} Close icon element.
6
- */
7
- const CloseIcon = (props) => (
8
- <svg
9
- width="16"
10
- height="16"
11
- viewBox="0 0 24 24"
12
- xmlns="http://www.w3.org/2000/svg"
13
- aria-hidden="true"
14
- focusable="false"
15
- {...props}
16
- >
17
- <path
18
- d="M18 6L6 18M6 6l12 12"
19
- stroke="currentColor"
20
- strokeWidth="2"
21
- strokeLinecap="round"
22
- strokeLinejoin="round"
23
- />
24
- </svg>
25
- );
26
-
27
- export default CloseIcon;
@@ -1,30 +0,0 @@
1
- /**
2
- * Outline sparkles icon (decorative). Inline SVG for accessibility.
3
- *
4
- * @param {Object} props - SVG props (e.g. width, height, className).
5
- * @return {JSX.Element} Sparkles icon element.
6
- */
7
- const SparklesOutlineIcon = (props) => (
8
- <svg
9
- xmlns="http://www.w3.org/2000/svg"
10
- width="24"
11
- height="24"
12
- viewBox="0 0 24 24"
13
- fill="none"
14
- stroke="currentColor"
15
- strokeWidth="2"
16
- strokeLinecap="round"
17
- strokeLinejoin="round"
18
- aria-hidden="true"
19
- focusable="false"
20
- {...props}
21
- >
22
- <path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" />
23
- <path d="M5 3v4" />
24
- <path d="M19 17v4" />
25
- <path d="M3 5h4" />
26
- <path d="M17 19h4" />
27
- </svg>
28
- );
29
-
30
- export default SparklesOutlineIcon;
@@ -1,5 +0,0 @@
1
- /**
2
- * Icon components. Export individually for tree-shaking and clear imports.
3
- */
4
- export { default as CloseIcon } from "./CloseIcon";
5
- export { default as SparklesOutlineIcon } from "./SparklesOutlineIcon";
@@ -1,47 +0,0 @@
1
- /**
2
- * Filled sparks icon (matches editor-chat sparks.svg).
3
- * Inlined to avoid SVG loader dependency across consuming projects.
4
- *
5
- * @param {Object} props - Props to spread onto the SVG element.
6
- * @param {number} [props.width=24] - SVG width in pixels.
7
- * @param {number} [props.height=24] - SVG height in pixels.
8
- * @return {JSX.Element} Inline SVG sparks icon.
9
- */
10
- const SparksIcon = ({ width = 24, height = 24, ...props }) => (
11
- <svg
12
- xmlns="http://www.w3.org/2000/svg"
13
- viewBox="0 0 30 30"
14
- width={width}
15
- height={height}
16
- {...props}
17
- >
18
- <path d="M14.217,19.707l-1.112,2.547c-0.427,0.979-1.782,0.979-2.21,0l-1.112-2.547c-0.99-2.267-2.771-4.071-4.993-5.057L1.73,13.292c-0.973-0.432-0.973-1.848,0-2.28l2.965-1.316C6.974,8.684,8.787,6.813,9.76,4.47l1.126-2.714c0.418-1.007,1.81-1.007,2.228,0L14.24,4.47c0.973,2.344,2.786,4.215,5.065,5.226l2.965,1.316c0.973,0.432,0.973,1.848,0,2.28l-3.061,1.359C16.988,15.637,15.206,17.441,14.217,19.707z" />
19
- <path d="M24.481,27.796l-0.339,0.777c-0.248,0.569-1.036,0.569-1.284,0l-0.339-0.777c-0.604-1.385-1.693-2.488-3.051-3.092l-1.044-0.464c-0.565-0.251-0.565-1.072,0-1.323l0.986-0.438c1.393-0.619,2.501-1.763,3.095-3.195l0.348-0.84c0.243-0.585,1.052-0.585,1.294,0l0.348,0.84c0.594,1.432,1.702,2.576,3.095,3.195l0.986,0.438c0.565,0.251,0.565,1.072,0,1.323l-1.044,0.464C26.174,25.308,25.085,26.411,24.481,27.796z" />
20
- </svg>
21
- );
22
-
23
- /**
24
- * AILogo Component
25
- *
26
- * Displays the filled sparks icon inside a gradient circle.
27
- * Used on welcome screens and as the main AI assistant avatar.
28
- *
29
- * @param {Object} props - The component props.
30
- * @param {number} props.width - The width of the logo (default: 24).
31
- * @param {number} props.height - The height of the logo (default: 24).
32
- * @return {JSX.Element} Avatar wrapper with gradient circle and sparks icon.
33
- */
34
- const AILogo = ({ width = 24, height = 24 }) => (
35
- <div
36
- className="nfd-ai-chat-avatar"
37
- style={{
38
- width,
39
- height,
40
- }}
41
- >
42
- {/* Scale icon to ~62.5% of avatar so it fits inside the gradient circle. */}
43
- <SparksIcon width={width * 0.625} height={height * 0.625} />
44
- </div>
45
- );
46
-
47
- export default AILogo;
@@ -1,18 +0,0 @@
1
- /**
2
- * WordPress dependencies
3
- */
4
- import { __ } from "@wordpress/i18n";
5
-
6
- /**
7
- * BluBetaHeading Component
8
- *
9
- * Single solid dark blue BETA badge for the chat header (matches screenshot and
10
- * editor-chat: AILogo + "Blu Chat" plain text + this BETA pill).
11
- *
12
- * @return {JSX.Element} BETA badge span for the chat header.
13
- */
14
- const BluBetaHeading = () => (
15
- <span className="nfd-ai-chat-blu-beta-badge">{__("BETA", "wp-module-ai-chat")}</span>
16
- );
17
-
18
- export default BluBetaHeading;
@@ -1,30 +0,0 @@
1
- /**
2
- * External dependencies
3
- */
4
- import { CircleX } from "lucide-react";
5
-
6
- /**
7
- * ErrorAlert Component
8
- *
9
- * A reusable error alert component that displays error messages
10
- * in a red box with an exclamation mark icon.
11
- *
12
- * @param {Object} props - The component props.
13
- * @param {string} props.message - The error message to display.
14
- * @param {string} props.className - Additional CSS classes (optional).
15
- * @return {JSX.Element} The ErrorAlert component.
16
- */
17
- const ErrorAlert = ({ message, className = "" }) => {
18
- return (
19
- <div className={`nfd-ai-chat-error-alert ${className}`}>
20
- <div className="nfd-ai-chat-error-alert__icon">
21
- <CircleX width={16} height={16} />
22
- </div>
23
- <div className="nfd-ai-chat-error-alert__content">
24
- <div className="nfd-ai-chat-error-alert__message">{message}</div>
25
- </div>
26
- </div>
27
- );
28
- };
29
-
30
- export default ErrorAlert;
@@ -1,34 +0,0 @@
1
- /**
2
- * HeaderBar Component
3
- *
4
- * Shared header bar layout with configurable title, badge, and action slots.
5
- * Used by ChatHeader (help center / modal) and SidebarHeader (editor chat).
6
- *
7
- * @param {Object} props - Component props.
8
- * @param {string|import('react').ReactNode} [props.title] - Title content (e.g. "Blu Chat").
9
- * @param {import('react').ReactNode} [props.badge] - Badge node (e.g. BETA pill).
10
- * @param {import('react').ReactNode} [props.logo] - Optional logo/icon left of title.
11
- * @param {import('react').ReactNode} [props.leftActions] - Optional actions on the left side of the actions area.
12
- * @param {import('react').ReactNode} [props.rightActions] - Actions on the right (e.g. New chat, Close).
13
- * @param {string} [props.className] - Optional extra class for the root.
14
- * @return {JSX.Element} Shared header bar with configurable title, badge, and action slots.
15
- */
16
- const HeaderBar = ({ title, badge, logo, leftActions, rightActions, className = "" }) => (
17
- <div className={`nfd-ai-chat-header ${className}`.trim()} role="banner">
18
- <div className="nfd-ai-chat-header__brand">
19
- {logo}
20
- {title !== undefined && title !== null && (
21
- <span className="nfd-ai-chat-header__title">{title}</span>
22
- )}
23
- {badge}
24
- </div>
25
- {(leftActions || rightActions) && (
26
- <div className="nfd-ai-chat-header__actions">
27
- {leftActions}
28
- {rightActions}
29
- </div>
30
- )}
31
- </div>
32
- );
33
-
34
- export default HeaderBar;
@@ -1,28 +0,0 @@
1
- /**
2
- * WordPress dependencies
3
- */
4
- import { Button } from "@wordpress/components";
5
-
6
- /**
7
- * SuggestionButton Component
8
- *
9
- * A reusable suggestion button component that can be used in various contexts.
10
- * Takes an icon, text, and onClick action as parameters.
11
- *
12
- * @param {Object} props - The component props.
13
- * @param {JSX.Element} props.icon - The icon element to display.
14
- * @param {string} props.text - The text to display.
15
- * @param {Function} props.onClick - The function to call when clicked.
16
- * @param {string} props.className - Additional CSS classes (optional).
17
- * @return {JSX.Element} The SuggestionButton component.
18
- */
19
- const SuggestionButton = ({ icon, text, onClick, className = "" }) => {
20
- return (
21
- <Button className={`nfd-ai-chat-suggestion ${className}`} onClick={onClick}>
22
- <div className="nfd-ai-chat-suggestion__icon">{icon}</div>
23
- <div className="nfd-ai-chat-suggestion__text">{text}</div>
24
- </Button>
25
- );
26
- };
27
-
28
- export default SuggestionButton;