@sybilion/uilib 1.2.15 → 1.2.17
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/dist/esm/components/ui/Chat/Chat.js +4 -3
- package/dist/esm/components/ui/Chat/Chat.styl.js +2 -2
- package/dist/esm/components/ui/Chat/ChatChrome/ChatChrome.js +20 -3
- package/dist/esm/components/ui/Chat/ChatChrome/ChatChrome.styl.js +2 -2
- package/dist/esm/components/ui/Chat/ChatEmptyState/ChatEmptyState.js +3 -2
- package/dist/esm/components/ui/Chat/ChatEmptyState/ChatEmptyState.styl.js +1 -1
- package/dist/esm/components/ui/Chat/ChatEmptyState/icons/sparkles.svg.js +77 -0
- package/dist/esm/components/ui/Chat/ChatPrompt/ChatPrompt.js +3 -13
- package/dist/esm/components/ui/Chat/ChatPrompt/ChatPrompt.styl.js +2 -2
- package/dist/esm/components/ui/Chat/ChatSheet/ChatSelector.js +14 -3
- package/dist/esm/components/ui/Chat/ChatSheet/ChatSelector.styl.js +7 -0
- package/dist/esm/components/ui/Chat/ChatSheet/ChatSheet.js +2 -1
- package/dist/esm/components/ui/Chat/ChatSheet/useChatPanelChromeModel.js +2 -15
- package/dist/esm/components/ui/Page/AppShell/AppShell.styl.js +1 -1
- package/dist/esm/components/ui/Page/PageContent/PageContent.styl.js +1 -1
- package/dist/esm/types/src/components/ui/Chat/Chat.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/Chat.types.d.ts +3 -2
- package/dist/esm/types/src/components/ui/Chat/ChatChrome/ChatChrome.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/ChatChrome/ChatChrome.types.d.ts +0 -1
- package/dist/esm/types/src/components/ui/Chat/ChatEmptyState/ChatEmptyState.d.ts +2 -1
- package/dist/esm/types/src/components/ui/Chat/ChatEmptyState/ChatEmptyState.types.d.ts +2 -0
- package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPrompt.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/ChatSheet/ChatSelector.d.ts +2 -1
- package/dist/esm/types/src/components/ui/Chat/ChatSheet/ChatSheet.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.d.ts +4 -1
- package/package.json +1 -6
- package/src/components/ui/Chat/Chat.styl +10 -0
- package/src/components/ui/Chat/Chat.styl.d.ts +1 -0
- package/src/components/ui/Chat/Chat.tsx +14 -1
- package/src/components/ui/Chat/Chat.types.ts +3 -2
- package/src/components/ui/Chat/ChatChrome/ChatChrome.styl +10 -12
- package/src/components/ui/Chat/ChatChrome/ChatChrome.styl.d.ts +1 -3
- package/src/components/ui/Chat/ChatChrome/ChatChrome.tsx +24 -5
- package/src/components/ui/Chat/ChatChrome/ChatChrome.types.ts +0 -1
- package/src/components/ui/Chat/ChatEmptyState/ChatEmptyState.styl +5 -5
- package/src/components/ui/Chat/ChatEmptyState/ChatEmptyState.tsx +10 -3
- package/src/components/ui/Chat/ChatEmptyState/ChatEmptyState.types.ts +2 -0
- package/src/components/ui/Chat/ChatEmptyState/icons/sparkles.svg +22 -0
- package/src/components/ui/Chat/ChatPrompt/ChatPrompt.styl +0 -19
- package/src/components/ui/Chat/ChatPrompt/ChatPrompt.styl.d.ts +0 -2
- package/src/components/ui/Chat/ChatPrompt/ChatPrompt.tsx +1 -26
- package/src/components/ui/Chat/ChatSheet/ChatSelector.styl +24 -0
- package/src/components/ui/Chat/ChatSheet/ChatSelector.styl.d.ts +10 -0
- package/src/components/ui/Chat/ChatSheet/ChatSelector.tsx +59 -21
- package/src/components/ui/Chat/ChatSheet/ChatSheet.tsx +2 -0
- package/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.tsx +5 -16
- package/src/components/ui/Page/AppShell/AppShell.styl +2 -0
- package/src/components/ui/Page/PageContent/PageContent.styl +0 -1
- package/src/docs/docsHeaderActions.tsx +8 -0
- package/src/docs/index.tsx +3 -1
- package/src/docs/pages/ChatPage.tsx +93 -42
- package/dist/standalone/vite-sybilion-standalone-dev.d.ts +0 -13
- package/dist/standalone/vite-sybilion-standalone-dev.js +0 -49
- package/src/standalone/vite-sybilion-standalone-dev.ts +0 -65
|
@@ -1,23 +1,13 @@
|
|
|
1
1
|
@import 'lib/theme.styl'
|
|
2
2
|
|
|
3
|
-
.
|
|
3
|
+
.root
|
|
4
4
|
position relative
|
|
5
5
|
display flex
|
|
6
6
|
flex-direction column
|
|
7
7
|
height 100%
|
|
8
8
|
min-height 0
|
|
9
9
|
overflow hidden
|
|
10
|
-
|
|
11
|
-
&.variant-sidebar
|
|
12
|
-
.footer
|
|
13
|
-
backdrop-filter blur(30px)
|
|
14
|
-
background-color var(--background-alpha-800)
|
|
15
|
-
box-shadow 0 0 20px 16px var(--background)
|
|
16
|
-
border-top 1px solid var(--border)
|
|
17
|
-
|
|
18
|
-
&.variant-default
|
|
19
|
-
.footer
|
|
20
|
-
--chat-chrome-footer-variant default
|
|
10
|
+
border-radius var(--p-4)
|
|
21
11
|
|
|
22
12
|
.chatResizeHandle
|
|
23
13
|
position: absolute;
|
|
@@ -44,7 +34,10 @@
|
|
|
44
34
|
|
|
45
35
|
.panelHeader
|
|
46
36
|
position absolute
|
|
37
|
+
display flex
|
|
38
|
+
align-items center
|
|
47
39
|
width 100%
|
|
40
|
+
min-height 70px
|
|
48
41
|
z-index 100
|
|
49
42
|
|
|
50
43
|
flex-shrink 0
|
|
@@ -84,6 +77,7 @@
|
|
|
84
77
|
|
|
85
78
|
.scrollInner
|
|
86
79
|
padding-top var(--p-10)
|
|
80
|
+
min-height 100%
|
|
87
81
|
// padding-bottom 320px // goes under prompt
|
|
88
82
|
|
|
89
83
|
&::after
|
|
@@ -103,6 +97,10 @@
|
|
|
103
97
|
bottom 0
|
|
104
98
|
width 100%;
|
|
105
99
|
|
|
100
|
+
backdrop-filter blur(30px)
|
|
101
|
+
background-color var(--background-alpha-800)
|
|
102
|
+
border-top 1px solid var(--border)
|
|
103
|
+
box-shadow 0 0 20px 16px var(--background)
|
|
106
104
|
|
|
107
105
|
.loader
|
|
108
106
|
// z-index 1
|
|
@@ -9,13 +9,11 @@ interface CssExports {
|
|
|
9
9
|
'loader': string;
|
|
10
10
|
'panelClose': string;
|
|
11
11
|
'panelHeader': string;
|
|
12
|
-
'
|
|
12
|
+
'root': string;
|
|
13
13
|
'scroll': string;
|
|
14
14
|
'scrollInner': string;
|
|
15
15
|
'scrollWrapper': string;
|
|
16
16
|
'scrollbar': string;
|
|
17
|
-
'variant-default': string;
|
|
18
|
-
'variant-sidebar': string;
|
|
19
17
|
}
|
|
20
18
|
export const cssExports: CssExports;
|
|
21
19
|
export default cssExports;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import cn from 'classnames';
|
|
2
|
+
import { useEffect } from 'react';
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
displayLabelForBranchKeyFromMessages,
|
|
@@ -42,12 +43,28 @@ export function ChatChrome({
|
|
|
42
43
|
onPromptSubmit,
|
|
43
44
|
onChatDeleted,
|
|
44
45
|
promptPrefill,
|
|
45
|
-
variant = 'sidebar',
|
|
46
46
|
footerClassName,
|
|
47
47
|
emptyState,
|
|
48
48
|
}: ChatChromeProps) {
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (isEmpty) return;
|
|
51
|
+
|
|
52
|
+
const scrollToBottom = () => {
|
|
53
|
+
const inner = scrollRef.current?.innerElem as HTMLDivElement | undefined;
|
|
54
|
+
if (inner) {
|
|
55
|
+
inner.scrollTo({
|
|
56
|
+
top: inner.scrollHeight + 1000,
|
|
57
|
+
behavior: 'smooth',
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const inner = scrollRef.current?.innerElem as HTMLDivElement | undefined;
|
|
63
|
+
if (inner) setTimeout(scrollToBottom, 100);
|
|
64
|
+
}, [isEmpty, messages.length]);
|
|
65
|
+
|
|
49
66
|
return (
|
|
50
|
-
<div className={
|
|
67
|
+
<div className={S.root}>
|
|
51
68
|
{showResizeHandle && resizeHandle ? (
|
|
52
69
|
<PanelResizeHandle
|
|
53
70
|
edge="leading"
|
|
@@ -74,7 +91,11 @@ export function ChatChrome({
|
|
|
74
91
|
) : null}
|
|
75
92
|
</div>
|
|
76
93
|
<div className={S.content}>
|
|
77
|
-
<Chat
|
|
94
|
+
<Chat
|
|
95
|
+
isEmpty={isEmpty}
|
|
96
|
+
scopeId={effectiveScopeId}
|
|
97
|
+
onChatDeleted={onChatDeleted}
|
|
98
|
+
>
|
|
78
99
|
{isEmpty ? (
|
|
79
100
|
<>
|
|
80
101
|
<Chat.EmptyState {...emptyState} />
|
|
@@ -174,10 +195,8 @@ export function ChatChrome({
|
|
|
174
195
|
|
|
175
196
|
<div className={cn(S.footer, footerClassName)}>
|
|
176
197
|
<Chat.Prompt
|
|
177
|
-
id={effectiveScopeId}
|
|
178
198
|
onSubmit={onPromptSubmit}
|
|
179
199
|
disabled={isLoading}
|
|
180
|
-
onChatDeleted={onChatDeleted}
|
|
181
200
|
prefillMessage={promptPrefill ?? undefined}
|
|
182
201
|
/>
|
|
183
202
|
</div>
|
|
@@ -44,7 +44,6 @@ export interface ChatChromeProps {
|
|
|
44
44
|
onChatDeleted: (sessionId: string) => void;
|
|
45
45
|
/** `?prompt=` deep link text for one-shot composer pre-fill. */
|
|
46
46
|
promptPrefill?: string | null;
|
|
47
|
-
variant?: 'default' | 'sidebar';
|
|
48
47
|
footerClassName?: string;
|
|
49
48
|
emptyState?: ChatEmptyStateProps;
|
|
50
49
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
flex-direction column
|
|
4
4
|
align-items center
|
|
5
5
|
justify-content center
|
|
6
|
-
gap var(--p-
|
|
6
|
+
gap var(--p-10)
|
|
7
7
|
|
|
8
8
|
// height 450px
|
|
9
9
|
flex 1
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
color var(--text-secondary)
|
|
15
15
|
|
|
16
16
|
.icon
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
&
|
|
18
|
+
& > svg
|
|
19
|
+
width 32px
|
|
20
|
+
height 32px
|
|
@@ -1,16 +1,23 @@
|
|
|
1
|
+
import { PropsWithChildren } from 'react';
|
|
2
|
+
|
|
1
3
|
import S from './ChatEmptyState.styl';
|
|
2
4
|
import type { ChatEmptyStateProps } from './ChatEmptyState.types';
|
|
5
|
+
import SparklesIcon from './icons/sparkles.svg';
|
|
3
6
|
|
|
4
7
|
export function ChatEmptyState({
|
|
5
|
-
icon
|
|
6
|
-
title,
|
|
8
|
+
icon = <SparklesIcon />,
|
|
9
|
+
title = 'Start a conversation',
|
|
7
10
|
description,
|
|
8
|
-
|
|
11
|
+
additionalContent,
|
|
12
|
+
children,
|
|
13
|
+
}: PropsWithChildren<ChatEmptyStateProps>) {
|
|
9
14
|
return (
|
|
10
15
|
<div className={S.root}>
|
|
11
16
|
{icon && <div className={S.icon}>{icon}</div>}
|
|
12
17
|
{title && <h2>{title}</h2>}
|
|
13
18
|
{description && <p>{description}</p>}
|
|
19
|
+
{additionalContent}
|
|
20
|
+
{children}
|
|
14
21
|
</div>
|
|
15
22
|
);
|
|
16
23
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M13.0843 21.2051L12 25L10.9157 21.2051C10.3462 19.2118 8.78817 17.6538 6.79492 17.0843L3 16L6.79492 14.9157C8.78817 14.3462 10.3462 12.7882 10.9157 10.7949L12 7L13.0843 10.7949C13.6538 12.7882 15.2118 14.3462 17.2051 14.9157L21 16L17.2051 17.0843C15.2118 17.6538 13.6538 19.2118 13.0843 21.2051Z" stroke="url(#paint0_linear_3178_5168)" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round" />
|
|
3
|
+
<path d="M24.3452 11.6194L24 13L23.6548 11.6194C23.2518 10.0071 21.9929 8.74822 20.3806 8.34515L19 8L20.3806 7.65485C21.9929 7.25178 23.2518 5.9929 23.6548 4.38062L24 3L24.3452 4.38062C24.7482 5.9929 26.0071 7.25178 27.6194 7.65485L29 8L27.6194 8.34515C26.0071 8.74822 24.7482 10.0071 24.3452 11.6194Z" stroke="url(#paint1_linear_3178_5168)" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round" />
|
|
4
|
+
<path d="M22.5257 27.423L22 29L21.4743 27.423C21.1757 26.5272 20.4728 25.8243 19.577 25.5257L18 25L19.577 24.4743C20.4728 24.1757 21.1757 23.4728 21.4743 22.577L22 21L22.5257 22.577C22.8243 23.4728 23.5272 24.1757 24.423 24.4743L26 25L24.423 25.5257C23.5272 25.8243 22.8243 26.5272 22.5257 27.423Z" stroke="url(#paint2_linear_3178_5168)" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round" />
|
|
5
|
+
<defs>
|
|
6
|
+
<linearGradient id="paint0_linear_3178_5168" x1="3" y1="16" x2="29" y2="16" gradientUnits="userSpaceOnUse">
|
|
7
|
+
<stop stop-color="#19BFE9" />
|
|
8
|
+
<stop offset="0.673077" stop-color="#C670E8" />
|
|
9
|
+
<stop offset="1" stop-color="#DA62AB" />
|
|
10
|
+
</linearGradient>
|
|
11
|
+
<linearGradient id="paint1_linear_3178_5168" x1="3" y1="16" x2="29" y2="16" gradientUnits="userSpaceOnUse">
|
|
12
|
+
<stop stop-color="#19BFE9" />
|
|
13
|
+
<stop offset="0.673077" stop-color="#C670E8" />
|
|
14
|
+
<stop offset="1" stop-color="#DA62AB" />
|
|
15
|
+
</linearGradient>
|
|
16
|
+
<linearGradient id="paint2_linear_3178_5168" x1="3" y1="16" x2="29" y2="16" gradientUnits="userSpaceOnUse">
|
|
17
|
+
<stop stop-color="#19BFE9" />
|
|
18
|
+
<stop offset="0.673077" stop-color="#C670E8" />
|
|
19
|
+
<stop offset="1" stop-color="#DA62AB" />
|
|
20
|
+
</linearGradient>
|
|
21
|
+
</defs>
|
|
22
|
+
</svg>
|
|
@@ -53,25 +53,6 @@
|
|
|
53
53
|
background-color var(--page-color)
|
|
54
54
|
box-shadow 0 0 20px var(--background)
|
|
55
55
|
|
|
56
|
-
.selectorWrapper
|
|
57
|
-
display flex
|
|
58
|
-
align-items center
|
|
59
|
-
gap var(--p-2)
|
|
60
|
-
min-width 200px
|
|
61
|
-
z-index 1000
|
|
62
|
-
|
|
63
|
-
& > button:first-child
|
|
64
|
-
flex 1
|
|
65
|
-
& > button:last-child
|
|
66
|
-
border-radius 50px
|
|
67
|
-
opacity 0.3
|
|
68
|
-
|
|
69
|
-
&:hover > button:last-child
|
|
70
|
-
opacity 1
|
|
71
|
-
|
|
72
|
-
.selector
|
|
73
|
-
box-shadow 0 0 0 1px var(--border) !important
|
|
74
|
-
|
|
75
56
|
.notice
|
|
76
57
|
font-size var(--text-xs)
|
|
77
58
|
text-align center
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import cn from 'classnames';
|
|
2
2
|
import { FormEvent, useEffect, useState } from 'react';
|
|
3
3
|
|
|
4
|
-
import { ChatSelector } from '#uilib/components/ui/Chat/ChatSheet/ChatSelector';
|
|
5
|
-
import { useChatsForScopeId } from '#uilib/contexts/chat-context';
|
|
6
4
|
import useEvent from '#uilib/hooks/useEvent';
|
|
7
|
-
import { SendHorizontalIcon
|
|
5
|
+
import { SendHorizontalIcon } from 'lucide-react';
|
|
8
6
|
|
|
9
7
|
import { Button } from '../../Button';
|
|
10
8
|
import { Input } from '../../Input';
|
|
@@ -16,12 +14,9 @@ export function ChatPrompt({
|
|
|
16
14
|
placeholder,
|
|
17
15
|
className,
|
|
18
16
|
footer,
|
|
19
|
-
id,
|
|
20
|
-
onChatDeleted,
|
|
21
17
|
prefillMessage,
|
|
22
18
|
}: ChatPromptProps) {
|
|
23
19
|
const [message, setMessage] = useState('');
|
|
24
|
-
const { currentChatId, deleteChat, chats } = useChatsForScopeId(id);
|
|
25
20
|
|
|
26
21
|
useEffect(() => {
|
|
27
22
|
if (prefillMessage != null && prefillMessage !== '') {
|
|
@@ -29,13 +24,6 @@ export function ChatPrompt({
|
|
|
29
24
|
}
|
|
30
25
|
}, [prefillMessage]);
|
|
31
26
|
|
|
32
|
-
const handleDeleteChat = () => {
|
|
33
|
-
if (!currentChatId) return;
|
|
34
|
-
const sid = currentChatId;
|
|
35
|
-
deleteChat(sid);
|
|
36
|
-
onChatDeleted?.(sid);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
27
|
const handleSubmit = (e: FormEvent | KeyboardEvent) => {
|
|
40
28
|
const trimmedMessage = message.trim();
|
|
41
29
|
|
|
@@ -82,19 +70,6 @@ export function ChatPrompt({
|
|
|
82
70
|
>
|
|
83
71
|
<PaperclipIcon size={16} />
|
|
84
72
|
</Button> */}
|
|
85
|
-
<div className={S.selectorWrapper}>
|
|
86
|
-
<ChatSelector id={id} className={S.selector} />
|
|
87
|
-
<Button
|
|
88
|
-
type="button"
|
|
89
|
-
variant="ghost"
|
|
90
|
-
size="sm"
|
|
91
|
-
aria-label="Delete chat"
|
|
92
|
-
disabled={!currentChatId || chats.length === 0}
|
|
93
|
-
onClick={handleDeleteChat}
|
|
94
|
-
>
|
|
95
|
-
<Trash2Icon size={16} />
|
|
96
|
-
</Button>
|
|
97
|
-
</div>
|
|
98
73
|
</div>
|
|
99
74
|
|
|
100
75
|
{footer}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
@import 'lib/theme.styl';
|
|
2
|
+
|
|
3
|
+
.wrapper
|
|
4
|
+
display flex
|
|
5
|
+
align-items center
|
|
6
|
+
gap var(--p-2)
|
|
7
|
+
min-width 200px
|
|
8
|
+
z-index 1000
|
|
9
|
+
|
|
10
|
+
.selectGrow
|
|
11
|
+
flex 1
|
|
12
|
+
min-width 0
|
|
13
|
+
|
|
14
|
+
.selectTrigger
|
|
15
|
+
// box-shadow 0 0 0 1px var(--border) !important
|
|
16
|
+
width 100%
|
|
17
|
+
|
|
18
|
+
.deleteBtn
|
|
19
|
+
flex-shrink 0
|
|
20
|
+
border-radius 50px
|
|
21
|
+
opacity 0.3
|
|
22
|
+
|
|
23
|
+
.wrapper:hover .deleteBtn
|
|
24
|
+
opacity 1
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// This file is automatically generated.
|
|
2
|
+
// Please do not change this file!
|
|
3
|
+
interface CssExports {
|
|
4
|
+
'deleteBtn': string;
|
|
5
|
+
'selectGrow': string;
|
|
6
|
+
'selectTrigger': string;
|
|
7
|
+
'wrapper': string;
|
|
8
|
+
}
|
|
9
|
+
export const cssExports: CssExports;
|
|
10
|
+
export default cssExports;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import cn from 'classnames';
|
|
2
|
+
|
|
1
3
|
import { Chat } from '#uilib/components/ui/Chat/Chat.types';
|
|
2
4
|
import { useChatsForScopeId } from '#uilib/contexts/chat-context';
|
|
5
|
+
import { Trash2Icon } from 'lucide-react';
|
|
3
6
|
|
|
7
|
+
import { Button } from '../../Button';
|
|
4
8
|
import {
|
|
5
9
|
Select,
|
|
6
10
|
SelectContent,
|
|
@@ -8,14 +12,20 @@ import {
|
|
|
8
12
|
SelectTrigger,
|
|
9
13
|
SelectValue,
|
|
10
14
|
} from '../../Select/Select';
|
|
15
|
+
import S from './ChatSelector.styl';
|
|
11
16
|
|
|
12
17
|
export interface ChatSelectorProps {
|
|
13
18
|
id: string;
|
|
14
19
|
className?: string;
|
|
20
|
+
onChatDeleted?: (sessionId: string) => void;
|
|
15
21
|
}
|
|
16
22
|
|
|
17
|
-
export function ChatSelector({
|
|
18
|
-
|
|
23
|
+
export function ChatSelector({
|
|
24
|
+
id,
|
|
25
|
+
className,
|
|
26
|
+
onChatDeleted,
|
|
27
|
+
}: ChatSelectorProps) {
|
|
28
|
+
const { chats, currentChatId, setCurrentChatId, newChat, deleteChat } =
|
|
19
29
|
useChatsForScopeId(id);
|
|
20
30
|
|
|
21
31
|
const handleValueChange = (value: string) => {
|
|
@@ -26,6 +36,13 @@ export function ChatSelector({ id, className }: ChatSelectorProps) {
|
|
|
26
36
|
}
|
|
27
37
|
};
|
|
28
38
|
|
|
39
|
+
const handleDeleteChat = () => {
|
|
40
|
+
if (!currentChatId) return;
|
|
41
|
+
const sid = currentChatId;
|
|
42
|
+
deleteChat(sid);
|
|
43
|
+
onChatDeleted?.(sid);
|
|
44
|
+
};
|
|
45
|
+
|
|
29
46
|
const getChatDisplayName = (chat: Chat) => {
|
|
30
47
|
if (chat.name) {
|
|
31
48
|
return chat.name;
|
|
@@ -41,25 +58,46 @@ export function ChatSelector({ id, className }: ChatSelectorProps) {
|
|
|
41
58
|
};
|
|
42
59
|
|
|
43
60
|
return (
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
value={chat.session_id.toString()}
|
|
57
|
-
selected={chat.session_id === currentChatId}
|
|
61
|
+
<div className={S.wrapper}>
|
|
62
|
+
<div className={S.selectGrow}>
|
|
63
|
+
<Select
|
|
64
|
+
variant="clear"
|
|
65
|
+
value={currentChatId?.toString() ?? ''}
|
|
66
|
+
onValueChange={handleValueChange}
|
|
67
|
+
>
|
|
68
|
+
<SelectTrigger
|
|
69
|
+
size="sm"
|
|
70
|
+
className={
|
|
71
|
+
className ? cn(S.selectTrigger, className) : S.selectTrigger
|
|
72
|
+
}
|
|
58
73
|
>
|
|
59
|
-
|
|
60
|
-
</
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
<SelectValue placeholder="Select chat" />
|
|
75
|
+
</SelectTrigger>
|
|
76
|
+
<SelectContent>
|
|
77
|
+
<SelectItem value="new">+ New Chat</SelectItem>
|
|
78
|
+
{chats.map(chat => (
|
|
79
|
+
<SelectItem
|
|
80
|
+
key={chat.session_id}
|
|
81
|
+
value={chat.session_id.toString()}
|
|
82
|
+
selected={chat.session_id === currentChatId}
|
|
83
|
+
>
|
|
84
|
+
{getChatDisplayName(chat)}
|
|
85
|
+
</SelectItem>
|
|
86
|
+
))}
|
|
87
|
+
</SelectContent>
|
|
88
|
+
</Select>
|
|
89
|
+
</div>
|
|
90
|
+
<Button
|
|
91
|
+
type="button"
|
|
92
|
+
variant="ghost"
|
|
93
|
+
size="sm"
|
|
94
|
+
className={S.deleteBtn}
|
|
95
|
+
aria-label="Delete chat"
|
|
96
|
+
disabled={!currentChatId || chats.length === 0}
|
|
97
|
+
onClick={handleDeleteChat}
|
|
98
|
+
>
|
|
99
|
+
<Trash2Icon size={16} />
|
|
100
|
+
</Button>
|
|
101
|
+
</div>
|
|
64
102
|
);
|
|
65
103
|
}
|
|
@@ -43,6 +43,7 @@ export function ChatSheet({
|
|
|
43
43
|
onScriptComplete,
|
|
44
44
|
onGenerateDashboard,
|
|
45
45
|
renderMessageChart,
|
|
46
|
+
emptyState,
|
|
46
47
|
inline = false,
|
|
47
48
|
}: ChatSheetProps) {
|
|
48
49
|
const model = useChatPanelChromeModel({
|
|
@@ -53,6 +54,7 @@ export function ChatSheet({
|
|
|
53
54
|
onScriptComplete,
|
|
54
55
|
onGenerateDashboard,
|
|
55
56
|
renderMessageChart,
|
|
57
|
+
emptyState,
|
|
56
58
|
});
|
|
57
59
|
|
|
58
60
|
if (actionsRef) {
|
|
@@ -34,6 +34,7 @@ import { ScrollRef } from '@homecode/ui';
|
|
|
34
34
|
import { useSidebar } from '../../Sidebar/Sidebar';
|
|
35
35
|
import { Chat } from '../Chat';
|
|
36
36
|
import type { ChatChromeProps } from '../ChatChrome';
|
|
37
|
+
import type { ChatEmptyStateProps } from '../ChatEmptyState/ChatEmptyState.types';
|
|
37
38
|
|
|
38
39
|
export type UseChatPanelChromeModelInput = {
|
|
39
40
|
/** When true, skip sidebar chat slot, URL `?chat=`, and portal behavior (e.g. page main content). */
|
|
@@ -48,6 +49,8 @@ export type UseChatPanelChromeModelInput = {
|
|
|
48
49
|
onGenerateDashboard?: (transcript: string) => void | Promise<void>;
|
|
49
50
|
/** Renders `[CHART]` tokens in assistant messages. */
|
|
50
51
|
renderMessageChart?: () => React.ReactNode;
|
|
52
|
+
/** Forwarded to `ChatChrome` when the thread is empty. */
|
|
53
|
+
emptyState?: ChatEmptyStateProps;
|
|
51
54
|
};
|
|
52
55
|
|
|
53
56
|
export type UseChatPanelChromeModelResult = {
|
|
@@ -92,6 +95,7 @@ export function useChatPanelChromeModel({
|
|
|
92
95
|
onScriptComplete,
|
|
93
96
|
onGenerateDashboard,
|
|
94
97
|
renderMessageChart,
|
|
98
|
+
emptyState,
|
|
95
99
|
}: UseChatPanelChromeModelInput): UseChatPanelChromeModelResult {
|
|
96
100
|
const effectiveScopeId = scopeId ?? NO_SCOPE_FALLBACK;
|
|
97
101
|
const isMobile = useIsMobile();
|
|
@@ -234,22 +238,6 @@ export function useChatPanelChromeModel({
|
|
|
234
238
|
],
|
|
235
239
|
);
|
|
236
240
|
|
|
237
|
-
const scrollToBottom = () => {
|
|
238
|
-
const inner = scrollRef.current?.innerElem as HTMLDivElement;
|
|
239
|
-
if (inner) {
|
|
240
|
-
inner.scrollTo({
|
|
241
|
-
top: inner.scrollHeight + 1000,
|
|
242
|
-
behavior: 'smooth',
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
useEffect(() => {
|
|
248
|
-
const inner = scrollRef.current?.innerElem as HTMLDivElement;
|
|
249
|
-
|
|
250
|
-
if (inner) setTimeout(scrollToBottom, 100);
|
|
251
|
-
}, [chat?.messages.length]);
|
|
252
|
-
|
|
253
241
|
const onOpenChange = (open: boolean) => {
|
|
254
242
|
if (embedAsPage) {
|
|
255
243
|
return;
|
|
@@ -1009,6 +997,7 @@ export function useChatPanelChromeModel({
|
|
|
1009
997
|
onPromptSubmit: handlePromptSubmit,
|
|
1010
998
|
onChatDeleted: endLocalDemoFlow,
|
|
1011
999
|
promptPrefill: promptLinkPrefill,
|
|
1000
|
+
emptyState,
|
|
1012
1001
|
};
|
|
1013
1002
|
|
|
1014
1003
|
const toggleOpen = () => onOpenChange(!isOpen);
|
|
@@ -13,6 +13,14 @@ export function DocsHeaderActions() {
|
|
|
13
13
|
</>
|
|
14
14
|
}
|
|
15
15
|
scopeId={DOCS_CHAT_SCOPE_ID}
|
|
16
|
+
emptyState={{
|
|
17
|
+
title: 'Start a conversation',
|
|
18
|
+
description:
|
|
19
|
+
'Send a message below. This demo appends a canned reply after a short delay.',
|
|
20
|
+
additionalContent: (
|
|
21
|
+
<p>Optional empty-state slot via additionalContent.</p>
|
|
22
|
+
),
|
|
23
|
+
}}
|
|
16
24
|
/>
|
|
17
25
|
);
|
|
18
26
|
}
|
package/src/docs/index.tsx
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createRoot } from 'react-dom/client';
|
|
2
2
|
import { BrowserRouter } from 'react-router-dom';
|
|
3
3
|
|
|
4
|
+
import { DEFAULT_THEME_ACTIVE_COLOR } from '#uilib/docs/lib/theme';
|
|
5
|
+
|
|
4
6
|
import App from './App/App';
|
|
5
7
|
import { ThemeProvider } from './contexts/theme-context';
|
|
6
8
|
|
|
@@ -9,7 +11,7 @@ const root = createRoot(elem);
|
|
|
9
11
|
|
|
10
12
|
root.render(
|
|
11
13
|
<BrowserRouter>
|
|
12
|
-
<ThemeProvider activeColor=
|
|
14
|
+
<ThemeProvider activeColor={DEFAULT_THEME_ACTIVE_COLOR}>
|
|
13
15
|
<App />
|
|
14
16
|
</ThemeProvider>
|
|
15
17
|
</BrowserRouter>,
|