kyd-shared-badge 0.3.5 → 0.3.7
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/package.json
CHANGED
package/src/chat/ChatWidget.tsx
CHANGED
|
@@ -47,33 +47,11 @@ export default function ChatWidget({ api = '/api/chat', title = 'KYD Bot', hintT
|
|
|
47
47
|
const [headerTop, setHeaderTop] = useState(0);
|
|
48
48
|
const [showHint, setShowHint] = useState(false);
|
|
49
49
|
const { messages, input, setInput, sending, sendMessage, cancel } = useChatStreaming({ api, badgeId });
|
|
50
|
-
const
|
|
51
|
-
const [autoScroll, setAutoScroll] = useState(true);
|
|
50
|
+
const listRef = useRef<HTMLDivElement>(null);
|
|
52
51
|
|
|
53
|
-
// Detect user scroll position to toggle auto-scroll
|
|
54
52
|
useEffect(() => {
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
-
if (!root) return;
|
|
58
|
-
const scrollEl = root.querySelector('.cs-message-list') as HTMLElement | null;
|
|
59
|
-
if (!scrollEl) return;
|
|
60
|
-
const onScroll = () => {
|
|
61
|
-
const nearBottom = scrollEl.scrollTop + scrollEl.clientHeight >= scrollEl.scrollHeight - 8;
|
|
62
|
-
setAutoScroll(nearBottom);
|
|
63
|
-
};
|
|
64
|
-
scrollEl.addEventListener('scroll', onScroll, { passive: true } as AddEventListenerOptions);
|
|
65
|
-
return () => scrollEl.removeEventListener('scroll', onScroll);
|
|
66
|
-
}, [open]);
|
|
67
|
-
|
|
68
|
-
// Keep scrolled to bottom only when autoScroll is enabled
|
|
69
|
-
useEffect(() => {
|
|
70
|
-
if (!open) return;
|
|
71
|
-
if (!autoScroll) return;
|
|
72
|
-
const root = containerRef.current;
|
|
73
|
-
if (!root) return;
|
|
74
|
-
const scrollEl = root.querySelector('.cs-message-list') as HTMLElement | null;
|
|
75
|
-
if (scrollEl) scrollEl.scrollTop = scrollEl.scrollHeight;
|
|
76
|
-
}, [messages, open, autoScroll]);
|
|
53
|
+
if (listRef.current) listRef.current.scrollTop = listRef.current.scrollHeight;
|
|
54
|
+
}, [messages, open]);
|
|
77
55
|
|
|
78
56
|
// Optional hint (only shows if user hasn't dismissed before and when collapsed)
|
|
79
57
|
useEffect(() => {
|
|
@@ -230,7 +208,6 @@ export default function ChatWidget({ api = '/api/chat', title = 'KYD Bot', hintT
|
|
|
230
208
|
aria-label={'Chat sidebar'}
|
|
231
209
|
style={{ position: 'fixed', top: headerTop, right: 0, bottom: 0, width, maxWidth: '92vw' }}
|
|
232
210
|
className={'shadow-xl border flex flex-col overflow-hidden'}
|
|
233
|
-
ref={containerRef}
|
|
234
211
|
>
|
|
235
212
|
{/* Left-edge resizer */}
|
|
236
213
|
<div
|
|
@@ -245,7 +222,7 @@ export default function ChatWidget({ api = '/api/chat', title = 'KYD Bot', hintT
|
|
|
245
222
|
<div className={'flex-1'} style={{ minHeight: 0 }}>
|
|
246
223
|
<MainContainer style={{ height: '100%', position: 'relative', background: 'var(--content-card-background)', border: 'none'}}>
|
|
247
224
|
<ChatContainer style={{ height: '100%' }}>
|
|
248
|
-
<MessageList typingIndicator={undefined} autoScrollToBottom
|
|
225
|
+
<MessageList typingIndicator={undefined} autoScrollToBottom>
|
|
249
226
|
{messages.length === 0 && (
|
|
250
227
|
<Message model={{ message: 'Start a conversation. I can answer questions about this developer’s report.', sender: 'KYD', direction: 'incoming', position: 'single' }} />
|
|
251
228
|
)}
|
|
@@ -295,7 +272,7 @@ export default function ChatWidget({ api = '/api/chat', title = 'KYD Bot', hintT
|
|
|
295
272
|
</ChatContainer>
|
|
296
273
|
</MainContainer>
|
|
297
274
|
</div>
|
|
298
|
-
<form onSubmit={(e) => { e.preventDefault(); if (!sending && input.trim())
|
|
275
|
+
<form onSubmit={(e) => { e.preventDefault(); if (!sending && input.trim()) sendMessage(); }}>
|
|
299
276
|
<div className={'flex items-end gap-2 p-2 border-t'} style={{ borderColor: 'var(--icon-button-secondary)', background: 'var(--content-card-background)' }}>
|
|
300
277
|
<input
|
|
301
278
|
aria-label={'Type your message'}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useEffect, useRef
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
4
4
|
import './chat-overrides.css';
|
|
5
5
|
import { FiSend } from 'react-icons/fi';
|
|
6
6
|
import { useChatStreaming } from './useChatStreaming';
|
|
@@ -8,24 +8,14 @@ import { useChatStreaming } from './useChatStreaming';
|
|
|
8
8
|
export default function ChatWindowStreaming({ api = '/api/chat', badgeId }: { api?: string, badgeId: string }) {
|
|
9
9
|
const { messages, input, setInput, sending, sendMessage } = useChatStreaming({ api, badgeId });
|
|
10
10
|
const listRef = useRef<HTMLDivElement>(null);
|
|
11
|
-
const autoScrollRef = useRef<boolean>(true);
|
|
12
|
-
|
|
13
|
-
const handleScroll = () => {
|
|
14
|
-
const el = listRef.current;
|
|
15
|
-
if (!el) return;
|
|
16
|
-
const nearBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 8;
|
|
17
|
-
autoScrollRef.current = nearBottom;
|
|
18
|
-
};
|
|
19
11
|
|
|
20
12
|
useEffect(() => {
|
|
21
|
-
if (listRef.current
|
|
22
|
-
listRef.current.scrollTop = listRef.current.scrollHeight;
|
|
23
|
-
}
|
|
13
|
+
if (listRef.current) listRef.current.scrollTop = listRef.current.scrollHeight;
|
|
24
14
|
}, [messages]);
|
|
25
15
|
|
|
26
16
|
return (
|
|
27
17
|
<div className="flex flex-col border rounded-lg overflow-hidden" style={{ background: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)'}}>
|
|
28
|
-
<div ref={listRef} className="p-4 space-y-3 h-72 overflow-auto"
|
|
18
|
+
<div ref={listRef} className="p-4 space-y-3 h-72 overflow-auto">
|
|
29
19
|
{messages.map(m => (
|
|
30
20
|
<div key={m.id} className={m.role === 'user' ? 'text-right' : 'text-left'}>
|
|
31
21
|
<div
|
|
@@ -45,13 +35,13 @@ export default function ChatWindowStreaming({ api = '/api/chat', badgeId }: { ap
|
|
|
45
35
|
<input
|
|
46
36
|
value={input}
|
|
47
37
|
onChange={e=>setInput(e.target.value)}
|
|
48
|
-
onKeyDown={e=>{ if (e.key==='Enter')
|
|
38
|
+
onKeyDown={e=>{ if (e.key==='Enter') sendMessage(); }}
|
|
49
39
|
className="flex-1 px-3 py-2 rounded border kyd-chat-input"
|
|
50
40
|
style={{ background: 'var(--input-background)', color: 'var(--text-main)', borderColor: 'var(--icon-button-secondary)' }}
|
|
51
41
|
placeholder="Ask KYD…"
|
|
52
42
|
/>
|
|
53
43
|
<button
|
|
54
|
-
onClick={()=>
|
|
44
|
+
onClick={()=>sendMessage()}
|
|
55
45
|
disabled={sending}
|
|
56
46
|
className="px-3 py-2 rounded disabled:opacity-60 kyd-chat-button"
|
|
57
47
|
style={{ background: 'var(--gradient-start)', color: '#fff' }}
|