orchid-ai 2.0.1 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/orchid-ai.css +66 -25
- package/package.json +1 -1
- package/src/components/ChatWindow.jsx +36 -22
- package/src/components/Message.jsx +57 -15
- package/src/hooks/useOrchidAiChat.js +7 -6
- package/src/index.d.ts +18 -3
package/orchid-ai.css
CHANGED
|
@@ -244,6 +244,9 @@
|
|
|
244
244
|
border: 1px solid #e5e7eb;
|
|
245
245
|
border-radius: 12px;
|
|
246
246
|
padding: 16px 20px;
|
|
247
|
+
width: 100%;
|
|
248
|
+
max-width: 420px;
|
|
249
|
+
box-sizing: border-box;
|
|
247
250
|
}
|
|
248
251
|
|
|
249
252
|
.ai-chat-suggestions span {
|
|
@@ -260,6 +263,7 @@
|
|
|
260
263
|
display: flex;
|
|
261
264
|
flex-direction: column;
|
|
262
265
|
gap: 8px;
|
|
266
|
+
padding: 0;
|
|
263
267
|
}
|
|
264
268
|
|
|
265
269
|
.ai-chat-suggestions li {
|
|
@@ -271,6 +275,8 @@
|
|
|
271
275
|
border-radius: 8px;
|
|
272
276
|
cursor: pointer;
|
|
273
277
|
transition: background 0.15s, border-color 0.15s, color 0.15s;
|
|
278
|
+
width: 100%;
|
|
279
|
+
box-sizing: border-box;
|
|
274
280
|
}
|
|
275
281
|
|
|
276
282
|
.ai-chat-suggestions li:hover {
|
|
@@ -279,6 +285,17 @@
|
|
|
279
285
|
color: #1eaaf1;
|
|
280
286
|
}
|
|
281
287
|
|
|
288
|
+
.ai-chat-suggestions--disabled li {
|
|
289
|
+
opacity: 0.45;
|
|
290
|
+
cursor: not-allowed;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.ai-chat-suggestions--disabled li:hover {
|
|
294
|
+
background: #f9fafb;
|
|
295
|
+
border-color: #e5e7eb;
|
|
296
|
+
color: #4b5563;
|
|
297
|
+
}
|
|
298
|
+
|
|
282
299
|
/* ── Messages ── */
|
|
283
300
|
|
|
284
301
|
.ai-chat-message {
|
|
@@ -347,6 +364,16 @@
|
|
|
347
364
|
border-bottom-right-radius: 4px;
|
|
348
365
|
}
|
|
349
366
|
|
|
367
|
+
.ai-chat-bubble.user .ai-chat-user-link {
|
|
368
|
+
color: #e0f2fe;
|
|
369
|
+
text-decoration: underline;
|
|
370
|
+
word-break: break-all;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.ai-chat-bubble.user .ai-chat-user-link:hover {
|
|
374
|
+
color: #ffffff;
|
|
375
|
+
}
|
|
376
|
+
|
|
350
377
|
.ai-chat-bubble.assistant {
|
|
351
378
|
position: relative;
|
|
352
379
|
background: #ffffff;
|
|
@@ -1917,47 +1944,61 @@
|
|
|
1917
1944
|
/* ── Print (Single Response) ── */
|
|
1918
1945
|
|
|
1919
1946
|
@media print {
|
|
1920
|
-
@page {
|
|
1921
|
-
|
|
1947
|
+
@page { margin: 12mm; }
|
|
1948
|
+
|
|
1949
|
+
/*
|
|
1950
|
+
* visibility:hidden on body allows #ai-cortex-print-section descendants to
|
|
1951
|
+
* override with visibility:visible — this is a CSS guarantee that display:none
|
|
1952
|
+
* does NOT offer (Chrome's print engine ignores the specificity override for
|
|
1953
|
+
* display:none !important on body > *).
|
|
1954
|
+
*
|
|
1955
|
+
* Siblings are collapsed to height:0 so no blank space precedes the section.
|
|
1956
|
+
* position:absolute (not fixed) allows content to paginate across pages.
|
|
1957
|
+
*/
|
|
1958
|
+
body.ai-chat-printing {
|
|
1959
|
+
visibility: hidden !important;
|
|
1960
|
+
position: relative !important;
|
|
1922
1961
|
}
|
|
1923
1962
|
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1963
|
+
body.ai-chat-printing > *:not(#ai-cortex-print-section) {
|
|
1964
|
+
height: 0 !important;
|
|
1965
|
+
overflow: hidden !important;
|
|
1927
1966
|
}
|
|
1928
1967
|
|
|
1929
1968
|
body.ai-chat-printing #ai-cortex-print-section {
|
|
1969
|
+
visibility: visible !important;
|
|
1930
1970
|
display: block !important;
|
|
1971
|
+
position: absolute !important;
|
|
1972
|
+
top: 0 !important;
|
|
1973
|
+
left: 0 !important;
|
|
1974
|
+
width: 100% !important;
|
|
1975
|
+
padding: 0 !important;
|
|
1976
|
+
background: #ffffff;
|
|
1977
|
+
color: #1f2937;
|
|
1978
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
1979
|
+
font-size: 14px;
|
|
1980
|
+
line-height: 1.6;
|
|
1931
1981
|
}
|
|
1932
1982
|
|
|
1933
|
-
/*
|
|
1934
|
-
* Kill all CSS animations and transitions inside the print section.
|
|
1935
|
-
* Browsers reset animations during the print reflow, catching elements at
|
|
1936
|
-
* their `from` keyframe (opacity: 0, scale: 0) rather than their final state.
|
|
1937
|
-
* Disabling animations lets elements fall back to their base CSS styles,
|
|
1938
|
-
* which are always the fully-visible final appearance.
|
|
1939
|
-
*
|
|
1940
|
-
* print-color-adjust: exact forces Chrome to render background colours and
|
|
1941
|
-
* images — without it Chrome strips inline background fills (e.g. dot colours
|
|
1942
|
-
* on scatter/dot charts) and the dots appear as white outlines only.
|
|
1943
|
-
*/
|
|
1944
1983
|
body.ai-chat-printing #ai-cortex-print-section * {
|
|
1984
|
+
visibility: visible !important;
|
|
1945
1985
|
animation: none !important;
|
|
1946
1986
|
transition: none !important;
|
|
1987
|
+
/* Kill any opacity:0 left by a stopped animation */
|
|
1988
|
+
opacity: 1 !important;
|
|
1947
1989
|
-webkit-print-color-adjust: exact !important;
|
|
1948
1990
|
print-color-adjust: exact !important;
|
|
1949
1991
|
}
|
|
1950
1992
|
|
|
1951
|
-
/* Clean up the bubble for print */
|
|
1952
1993
|
body.ai-chat-printing #ai-cortex-print-section .ai-chat-bubble.assistant {
|
|
1953
|
-
max-width: none;
|
|
1954
|
-
width: 100
|
|
1955
|
-
border: none;
|
|
1956
|
-
border-radius: 0;
|
|
1957
|
-
padding: 0;
|
|
1958
|
-
background: #ffffff;
|
|
1959
|
-
color: #
|
|
1960
|
-
box-shadow: none;
|
|
1994
|
+
max-width: none !important;
|
|
1995
|
+
width: 100% !important;
|
|
1996
|
+
border: none !important;
|
|
1997
|
+
border-radius: 0 !important;
|
|
1998
|
+
padding: 0 !important;
|
|
1999
|
+
background: #ffffff !important;
|
|
2000
|
+
color: #1f2937 !important;
|
|
2001
|
+
box-shadow: none !important;
|
|
1961
2002
|
}
|
|
1962
2003
|
}
|
|
1963
2004
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import React, { useRef, useEffect } from 'react';
|
|
2
2
|
import Message from './Message';
|
|
3
3
|
|
|
4
|
+
const DEFAULT_SUGGESTIONS = [
|
|
5
|
+
'Give me brief tips for navigating iLink.',
|
|
6
|
+
'What should I check before starting a dispatch?',
|
|
7
|
+
'How do I narrow down a search in Hermes command search (⌘K)?',
|
|
8
|
+
];
|
|
9
|
+
|
|
4
10
|
/**
|
|
5
|
-
*
|
|
11
|
+
* Orchid AI chat window (Markdown + optional orchid-ai-chart fenced blocks).
|
|
6
12
|
*/
|
|
7
13
|
export default function ChatWindow({
|
|
8
14
|
messages,
|
|
@@ -11,7 +17,13 @@ export default function ChatWindow({
|
|
|
11
17
|
onSuggestionClick,
|
|
12
18
|
aiEnabled,
|
|
13
19
|
organisationName,
|
|
20
|
+
appName = "Hermes Chat",
|
|
21
|
+
unavailableMessage,
|
|
22
|
+
emptyDescription,
|
|
23
|
+
suggestions = DEFAULT_SUGGESTIONS,
|
|
24
|
+
suggestionsDisabled = false,
|
|
14
25
|
}) {
|
|
26
|
+
const exportPrefix = appName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
15
27
|
const bottomRef = useRef(null);
|
|
16
28
|
|
|
17
29
|
useEffect(() => {
|
|
@@ -20,6 +32,8 @@ export default function ChatWindow({
|
|
|
20
32
|
|
|
21
33
|
const renderEmptyState = () => {
|
|
22
34
|
if (!aiEnabled) {
|
|
35
|
+
const msg = unavailableMessage ??
|
|
36
|
+
`${appName} needs an Anthropic API key on the server. Contact your administrator if this persists.`;
|
|
23
37
|
return (
|
|
24
38
|
<div className="ai-chat-empty">
|
|
25
39
|
<div className="ai-chat-empty-icon">
|
|
@@ -37,16 +51,14 @@ export default function ChatWindow({
|
|
|
37
51
|
<line x1="4.93" y1="4.93" x2="19.07" y2="19.07" />
|
|
38
52
|
</svg>
|
|
39
53
|
</div>
|
|
40
|
-
<h2>
|
|
41
|
-
<p>
|
|
42
|
-
Hermes Chat needs an Anthropic API key on the server and an active organisation. Contact your administrator
|
|
43
|
-
if this persists.
|
|
44
|
-
</p>
|
|
54
|
+
<h2>{appName} unavailable</h2>
|
|
55
|
+
<p>{msg}</p>
|
|
45
56
|
</div>
|
|
46
57
|
);
|
|
47
58
|
}
|
|
48
59
|
|
|
49
60
|
const scope = organisationName || 'your organisation';
|
|
61
|
+
const description = emptyDescription ?? `Ask about ${scope} — shipments, schedules, data insights, or what to explore next.`;
|
|
50
62
|
|
|
51
63
|
return (
|
|
52
64
|
<div className="ai-chat-empty">
|
|
@@ -67,21 +79,23 @@ export default function ChatWindow({
|
|
|
67
79
|
</svg>
|
|
68
80
|
</div>
|
|
69
81
|
<h2>How can I help?</h2>
|
|
70
|
-
<p>{
|
|
71
|
-
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
<p>{description}</p>
|
|
83
|
+
{suggestions.length > 0 && (
|
|
84
|
+
<div className={`ai-chat-suggestions${suggestionsDisabled ? ' ai-chat-suggestions--disabled' : ''}`}>
|
|
85
|
+
<span>Try asking:</span>
|
|
86
|
+
<ul>
|
|
87
|
+
{suggestions.map((s) => (
|
|
88
|
+
<li
|
|
89
|
+
key={s}
|
|
90
|
+
onClick={suggestionsDisabled ? undefined : () => onSuggestionClick(s)}
|
|
91
|
+
aria-disabled={suggestionsDisabled || undefined}
|
|
92
|
+
>
|
|
93
|
+
{s}
|
|
94
|
+
</li>
|
|
95
|
+
))}
|
|
96
|
+
</ul>
|
|
97
|
+
</div>
|
|
98
|
+
)}
|
|
85
99
|
</div>
|
|
86
100
|
);
|
|
87
101
|
};
|
|
@@ -90,7 +104,7 @@ export default function ChatWindow({
|
|
|
90
104
|
<div className="ai-chat-window">
|
|
91
105
|
{messages?.length === 0 && !loading && renderEmptyState()}
|
|
92
106
|
{(messages ?? []).map((msg, i) => (
|
|
93
|
-
<Message key={i} role={msg.role} content={msg.content} truncated={msg.truncated} />
|
|
107
|
+
<Message key={i} role={msg.role} content={msg.content} truncated={msg.truncated} exportPrefix={exportPrefix} />
|
|
94
108
|
))}
|
|
95
109
|
{loading && (
|
|
96
110
|
<div className="ai-chat-message assistant">
|
|
@@ -9,7 +9,46 @@ const IS_DEV = process.env.NODE_ENV === "development";
|
|
|
9
9
|
|
|
10
10
|
const TITLE_RE = /<!--\s*title:\s*([^-][^>]*?)\s*-->/i;
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
/** Split on http(s) URLs for lightweight linkify in user bubbles (plain text, not full markdown). */
|
|
13
|
+
const URL_INLINE_RE = /(https?:\/\/[^\s<>`]+)/gi;
|
|
14
|
+
|
|
15
|
+
function trimTrailingUrlPunctuation(href) {
|
|
16
|
+
return href.replace(/[),.;:!?'"\]}>]+$/g, "");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function linkifyUserLine(line) {
|
|
20
|
+
const parts = String(line).split(URL_INLINE_RE);
|
|
21
|
+
return parts.map((part, i) => {
|
|
22
|
+
if (part === "") return null;
|
|
23
|
+
if (/^https?:\/\//i.test(part)) {
|
|
24
|
+
const href = trimTrailingUrlPunctuation(part);
|
|
25
|
+
return (
|
|
26
|
+
<a
|
|
27
|
+
key={i}
|
|
28
|
+
href={href}
|
|
29
|
+
target="_blank"
|
|
30
|
+
rel="noopener noreferrer"
|
|
31
|
+
className="ai-chat-user-link"
|
|
32
|
+
>
|
|
33
|
+
{part}
|
|
34
|
+
</a>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return <React.Fragment key={i}>{part}</React.Fragment>;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function UserBubbleContent({ content }) {
|
|
42
|
+
const lines = String(content).split("\n");
|
|
43
|
+
return lines.map((line, li) => (
|
|
44
|
+
<React.Fragment key={li}>
|
|
45
|
+
{li > 0 ? <br /> : null}
|
|
46
|
+
{linkifyUserLine(line)}
|
|
47
|
+
</React.Fragment>
|
|
48
|
+
));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default function Message({ role, content, truncated, exportPrefix = "orchid-ai" }) {
|
|
13
52
|
const isUser = role === "user";
|
|
14
53
|
const [copied, setCopied] = useState(false);
|
|
15
54
|
const [isPrinting, setIsPrinting] = useState(false);
|
|
@@ -56,23 +95,21 @@ export default function Message({ role, content, truncated }) {
|
|
|
56
95
|
const pad = (v) => String(v).padStart(2, "0");
|
|
57
96
|
const timestamp = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}`;
|
|
58
97
|
|
|
98
|
+
setIsPrinting(true);
|
|
99
|
+
|
|
100
|
+
const clone = bubbleContent.cloneNode(true);
|
|
101
|
+
clone.querySelectorAll(".ai-chart-export-actions, .ai-chat-message-actions").forEach((n) => n.remove());
|
|
102
|
+
|
|
59
103
|
setIsPrinting(true);
|
|
60
104
|
const previousTitle = document.title;
|
|
61
|
-
document.title =
|
|
105
|
+
document.title = `${exportPrefix}-${slug}-${timestamp}`;
|
|
62
106
|
|
|
63
|
-
// Clone the bubble content into an isolated print section at the body root.
|
|
64
|
-
// This keeps the layout clean (no surrounding app chrome) while the CSS
|
|
65
|
-
// animation: none rule ensures chart elements render at their final visible
|
|
66
|
-
// state rather than being caught mid-animation by the print reflow.
|
|
67
107
|
let printSection = document.getElementById("ai-cortex-print-section");
|
|
68
108
|
if (!printSection) {
|
|
69
109
|
printSection = document.createElement("div");
|
|
70
110
|
printSection.id = "ai-cortex-print-section";
|
|
71
111
|
document.body.appendChild(printSection);
|
|
72
112
|
}
|
|
73
|
-
|
|
74
|
-
const clone = bubbleContent.cloneNode(true);
|
|
75
|
-
clone.querySelectorAll(".ai-chart-export-actions, .ai-chat-message-actions").forEach((n) => n.remove());
|
|
76
113
|
printSection.innerHTML = "";
|
|
77
114
|
printSection.appendChild(clone);
|
|
78
115
|
document.body.classList.add("ai-chat-printing");
|
|
@@ -147,6 +184,16 @@ export default function Message({ role, content, truncated }) {
|
|
|
147
184
|
</code>
|
|
148
185
|
);
|
|
149
186
|
},
|
|
187
|
+
a({ href, children, ...props }) {
|
|
188
|
+
if (typeof href !== "string" || !/^https?:\/\//i.test(href)) {
|
|
189
|
+
return <span {...props}>{children}</span>;
|
|
190
|
+
}
|
|
191
|
+
return (
|
|
192
|
+
<a {...props} href={href} target="_blank" rel="noopener noreferrer">
|
|
193
|
+
{children}
|
|
194
|
+
</a>
|
|
195
|
+
);
|
|
196
|
+
},
|
|
150
197
|
};
|
|
151
198
|
|
|
152
199
|
return (
|
|
@@ -157,12 +204,7 @@ export default function Message({ role, content, truncated }) {
|
|
|
157
204
|
<div className={`ai-chat-bubble ${role}`}>
|
|
158
205
|
<div className="ai-chat-message-content">
|
|
159
206
|
{isUser ? (
|
|
160
|
-
content
|
|
161
|
-
<React.Fragment key={i}>
|
|
162
|
-
{line}
|
|
163
|
-
{i < content.split("\n").length - 1 && <br />}
|
|
164
|
-
</React.Fragment>
|
|
165
|
-
))
|
|
207
|
+
<UserBubbleContent content={content} />
|
|
166
208
|
) : (
|
|
167
209
|
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>{renderContent}</ReactMarkdown>
|
|
168
210
|
)}
|
|
@@ -20,20 +20,21 @@ export const ORCHID_AI_DEFAULT_STATUS = {
|
|
|
20
20
|
*
|
|
21
21
|
* @param {object} opts
|
|
22
22
|
* @param {string} opts.endpoint - POST endpoint URL
|
|
23
|
-
* @param {(userMessage: string, history: Array) => object} opts.buildBody - builds the request body
|
|
23
|
+
* @param {(userMessage: string, history: Array, sendOptions?: object) => object} opts.buildBody - builds the request body
|
|
24
24
|
* @param {() => object} [opts.getHeaders] - returns extra request headers (e.g. CSRF token)
|
|
25
25
|
* @param {boolean} [opts.showStatus=true] - set false to suppress status text entirely
|
|
26
|
+
* @param {Array} [opts.initialMessages=[]] - seed the conversation (e.g. from localStorage)
|
|
26
27
|
*
|
|
27
28
|
* @returns {{ messages, loading, statusText, sendMessage, clearMessages }}
|
|
28
29
|
*/
|
|
29
|
-
export function useOrchidAiChat({ endpoint, buildBody, getHeaders, showStatus = true }) {
|
|
30
|
-
const [messages, setMessages] = useState(
|
|
30
|
+
export function useOrchidAiChat({ endpoint, buildBody, getHeaders, showStatus = true, initialMessages = [] }) {
|
|
31
|
+
const [messages, setMessages] = useState(initialMessages);
|
|
31
32
|
const [loading, setLoading] = useState(false);
|
|
32
33
|
const [statusText, setStatusText] = useState('');
|
|
33
34
|
|
|
34
35
|
// Track messages in a ref so sendMessage always reads the latest without needing
|
|
35
36
|
// messages in its dependency array (avoids capturing stale history).
|
|
36
|
-
const messagesRef = useRef(
|
|
37
|
+
const messagesRef = useRef(initialMessages);
|
|
37
38
|
|
|
38
39
|
// Keep latest callbacks in refs so sendMessage identity stays stable regardless
|
|
39
40
|
// of whether the parent re-creates buildBody/getHeaders each render.
|
|
@@ -51,7 +52,7 @@ export function useOrchidAiChat({ endpoint, buildBody, getHeaders, showStatus =
|
|
|
51
52
|
}, []);
|
|
52
53
|
|
|
53
54
|
const sendMessage = useCallback(
|
|
54
|
-
async (userMessage) => {
|
|
55
|
+
async (userMessage, sendOptions = {}) => {
|
|
55
56
|
const history = messagesRef.current.slice();
|
|
56
57
|
addMessage({ role: 'user', content: userMessage });
|
|
57
58
|
setLoading(true);
|
|
@@ -64,7 +65,7 @@ export function useOrchidAiChat({ endpoint, buildBody, getHeaders, showStatus =
|
|
|
64
65
|
'Content-Type': 'application/json',
|
|
65
66
|
...getHeadersRef.current?.(),
|
|
66
67
|
},
|
|
67
|
-
body: JSON.stringify(buildBodyRef.current(userMessage, history)),
|
|
68
|
+
body: JSON.stringify(buildBodyRef.current(userMessage, history, sendOptions)),
|
|
68
69
|
});
|
|
69
70
|
|
|
70
71
|
const contentType = response.headers.get('content-type') ?? '';
|
package/src/index.d.ts
CHANGED
|
@@ -39,11 +39,24 @@ export const Message: React.FC<MessageProps>;
|
|
|
39
39
|
|
|
40
40
|
// ─── useOrchidAiChat ─────────────────────────────────────────────────────────
|
|
41
41
|
|
|
42
|
+
/** Optional flags merged into the request by `buildBody` (e.g. web research for Hermes). */
|
|
43
|
+
export interface OrchidAiChatSendOptions {
|
|
44
|
+
allowWebResearch?: boolean;
|
|
45
|
+
urlsToCheck?: string[];
|
|
46
|
+
}
|
|
47
|
+
|
|
42
48
|
export interface OrchidAiChatOptions {
|
|
43
49
|
/** POST endpoint that accepts the chat message */
|
|
44
50
|
endpoint: string;
|
|
45
|
-
/**
|
|
46
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Builds the request body from the user message, history, and per-send options.
|
|
53
|
+
* The third argument is only present when callers use `sendMessage(text, options)`.
|
|
54
|
+
*/
|
|
55
|
+
buildBody: (
|
|
56
|
+
userMessage: string,
|
|
57
|
+
history: ChatMessage[],
|
|
58
|
+
sendOptions?: OrchidAiChatSendOptions
|
|
59
|
+
) => Record<string, unknown>;
|
|
47
60
|
/** Returns extra request headers (e.g. CSRF token) */
|
|
48
61
|
getHeaders?: () => Record<string, string>;
|
|
49
62
|
/**
|
|
@@ -51,6 +64,8 @@ export interface OrchidAiChatOptions {
|
|
|
51
64
|
* Set to false to suppress all status text. Defaults to true.
|
|
52
65
|
*/
|
|
53
66
|
showStatus?: boolean;
|
|
67
|
+
/** Initial transcript when mounting the hook */
|
|
68
|
+
initialMessages?: ChatMessage[];
|
|
54
69
|
}
|
|
55
70
|
|
|
56
71
|
export interface OrchidAiChatState {
|
|
@@ -58,7 +73,7 @@ export interface OrchidAiChatState {
|
|
|
58
73
|
loading: boolean;
|
|
59
74
|
/** Live status text emitted by the server (e.g. "Thinking", "Compiling response") */
|
|
60
75
|
statusText: string;
|
|
61
|
-
sendMessage: (userMessage: string) => Promise<void>;
|
|
76
|
+
sendMessage: (userMessage: string, sendOptions?: OrchidAiChatSendOptions) => Promise<void>;
|
|
62
77
|
clearMessages: () => void;
|
|
63
78
|
}
|
|
64
79
|
|