@sybilion/uilib 1.2.15 → 1.2.16
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/useChatPanelChromeModel.js +0 -14
- package/dist/esm/components/ui/Page/AppShell/AppShell.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/ChatPrompt/ChatPrompt.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/ChatSheet/ChatSelector.d.ts +2 -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 +8 -3
- 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/useChatPanelChromeModel.tsx +0 -16
- package/src/components/ui/Page/AppShell/AppShell.styl +2 -0
- package/src/docs/index.tsx +3 -1
- package/src/docs/pages/ChatPage.tsx +90 -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,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,21 @@
|
|
|
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
|
+
children,
|
|
12
|
+
}: PropsWithChildren<ChatEmptyStateProps>) {
|
|
9
13
|
return (
|
|
10
14
|
<div className={S.root}>
|
|
11
15
|
{icon && <div className={S.icon}>{icon}</div>}
|
|
12
16
|
{title && <h2>{title}</h2>}
|
|
13
17
|
{description && <p>{description}</p>}
|
|
18
|
+
{children}
|
|
14
19
|
</div>
|
|
15
20
|
);
|
|
16
21
|
}
|
|
@@ -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
|
}
|
|
@@ -234,22 +234,6 @@ export function useChatPanelChromeModel({
|
|
|
234
234
|
],
|
|
235
235
|
);
|
|
236
236
|
|
|
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
237
|
const onOpenChange = (open: boolean) => {
|
|
254
238
|
if (embedAsPage) {
|
|
255
239
|
return;
|
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>,
|
|
@@ -1,18 +1,67 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
Chat,
|
|
5
|
-
ChatMessage,
|
|
6
|
-
ChatPrompt,
|
|
7
|
-
MessageRole,
|
|
8
|
-
} from '#uilib/components/ui/Chat';
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { ChatChrome, Message, MessageRole } from '#uilib/components/ui/Chat';
|
|
9
4
|
import { PageContentSection } from '#uilib/components/ui/Page';
|
|
5
|
+
import { ScrollRef } from '@homecode/ui';
|
|
10
6
|
|
|
11
7
|
import { AppPageHeader } from '../components/AppPageHeader/AppPageHeader';
|
|
12
8
|
import { DocsHeaderActions } from '../docsHeaderActions';
|
|
13
9
|
|
|
10
|
+
const NO_QUICK_REPLY_KEYS: ReadonlySet<string> = new Set();
|
|
11
|
+
|
|
12
|
+
const ASSISTANT_REPLY_TEXT =
|
|
13
|
+
'This is a generic assistant reply for the docs preview. Wire your app to a real model here.';
|
|
14
|
+
|
|
15
|
+
function makeMessage(role: MessageRole, text: string): Message {
|
|
16
|
+
return {
|
|
17
|
+
id: crypto.randomUUID(),
|
|
18
|
+
role,
|
|
19
|
+
text,
|
|
20
|
+
timestamp: Date.now(),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
14
24
|
export default function ChatPage() {
|
|
15
|
-
const [
|
|
25
|
+
const [messages, setMessages] = useState<Message[]>([]);
|
|
26
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
27
|
+
const replyTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
28
|
+
const scrollRef = useRef<ScrollRef>(null);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
return () => {
|
|
32
|
+
if (replyTimeoutRef.current != null) {
|
|
33
|
+
clearTimeout(replyTimeoutRef.current);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}, []);
|
|
37
|
+
|
|
38
|
+
const isEmpty = messages.length === 0 && !isLoading;
|
|
39
|
+
const isLastMessageFromUser =
|
|
40
|
+
messages.length > 0 &&
|
|
41
|
+
messages[messages.length - 1]?.role === MessageRole.USER;
|
|
42
|
+
|
|
43
|
+
const onSubmit = useCallback(
|
|
44
|
+
(raw: string) => {
|
|
45
|
+
const text = raw.trim();
|
|
46
|
+
if (!text || isLoading) return;
|
|
47
|
+
|
|
48
|
+
setMessages(prev => [...prev, makeMessage(MessageRole.USER, text)]);
|
|
49
|
+
setIsLoading(true);
|
|
50
|
+
|
|
51
|
+
if (replyTimeoutRef.current != null) {
|
|
52
|
+
clearTimeout(replyTimeoutRef.current);
|
|
53
|
+
}
|
|
54
|
+
replyTimeoutRef.current = setTimeout(() => {
|
|
55
|
+
replyTimeoutRef.current = null;
|
|
56
|
+
setMessages(prev => [
|
|
57
|
+
...prev,
|
|
58
|
+
makeMessage(MessageRole.ASSISTANT, ASSISTANT_REPLY_TEXT),
|
|
59
|
+
]);
|
|
60
|
+
setIsLoading(false);
|
|
61
|
+
}, 900);
|
|
62
|
+
},
|
|
63
|
+
[isLoading],
|
|
64
|
+
);
|
|
16
65
|
|
|
17
66
|
return (
|
|
18
67
|
<>
|
|
@@ -22,39 +71,38 @@ export default function ChatPage() {
|
|
|
22
71
|
subheader="Chat shell with message list and prompt."
|
|
23
72
|
actions={<DocsHeaderActions />}
|
|
24
73
|
/>
|
|
25
|
-
<PageContentSection
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
{
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
</Chat>
|
|
74
|
+
<PageContentSection>
|
|
75
|
+
<ChatChrome
|
|
76
|
+
showResizeHandle={false}
|
|
77
|
+
resizeHandle={undefined}
|
|
78
|
+
onClose={undefined}
|
|
79
|
+
isEmpty={isEmpty}
|
|
80
|
+
renderPresets={() => null}
|
|
81
|
+
messages={messages}
|
|
82
|
+
onQuickReply={() => {}}
|
|
83
|
+
suppressedQuickReplyKeys={NO_QUICK_REPLY_KEYS}
|
|
84
|
+
isLoading={isLoading}
|
|
85
|
+
scriptContinueLabel={undefined}
|
|
86
|
+
onScriptContinue={undefined}
|
|
87
|
+
showBranchActionsRow={false}
|
|
88
|
+
showSyntheticBranchButtons={false}
|
|
89
|
+
unusedBranchKeys={[]}
|
|
90
|
+
isScriptComplete={false}
|
|
91
|
+
onGenerateDashboard={undefined}
|
|
92
|
+
generatingDashboard={false}
|
|
93
|
+
onGenerateDashboardClick={() => {}}
|
|
94
|
+
showInlinePresets={false}
|
|
95
|
+
isLastMessageFromUser={isLastMessageFromUser}
|
|
96
|
+
scrollRef={scrollRef}
|
|
97
|
+
effectiveScopeId="docs-chat-inline"
|
|
98
|
+
onPromptSubmit={onSubmit}
|
|
99
|
+
onChatDeleted={() => {}}
|
|
100
|
+
emptyState={{
|
|
101
|
+
title: 'Start a conversation',
|
|
102
|
+
description:
|
|
103
|
+
'Send a message below. This demo appends a canned reply after a short delay.',
|
|
104
|
+
}}
|
|
105
|
+
/>
|
|
58
106
|
</PageContentSection>
|
|
59
107
|
</>
|
|
60
108
|
);
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { UserConfig } from 'vite';
|
|
2
|
-
export type SybilionStandaloneViteDevOptions = {
|
|
3
|
-
mode: string;
|
|
4
|
-
/** Directory containing `.env*` files. @default process.cwd() */
|
|
5
|
-
envDir?: string;
|
|
6
|
-
/** Prefix proxied to Sybilion API (SDK `apiPrefix`). @default `/api` */
|
|
7
|
-
apiPrefix?: string;
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Vite `server` + `preview` fragment for standalone Sybilion SPAs: same-origin `/api` in dev,
|
|
11
|
-
* proxied to `VITE_SYBILION_API_BASE_URL`. Uses `PORT` from env (default `3000`).
|
|
12
|
-
*/
|
|
13
|
-
export declare function sybilionStandaloneViteDev(options: SybilionStandaloneViteDevOptions): Pick<UserConfig, 'server' | 'preview'>;
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { loadEnv } from 'vite';
|
|
2
|
-
|
|
3
|
-
const DEFAULT_PORT = 3000;
|
|
4
|
-
const SYBILION_API_ENV = 'VITE_SYBILION_API_BASE_URL';
|
|
5
|
-
let warnedMissingApiUrl = false;
|
|
6
|
-
function parsePort(raw) {
|
|
7
|
-
if (raw == null || raw === '')
|
|
8
|
-
return DEFAULT_PORT;
|
|
9
|
-
const n = Number.parseInt(raw, 10);
|
|
10
|
-
if (!Number.isFinite(n) || n <= 0 || n > 65_535)
|
|
11
|
-
return DEFAULT_PORT;
|
|
12
|
-
return n;
|
|
13
|
-
}
|
|
14
|
-
function normalizeApiPrefix(apiPrefix) {
|
|
15
|
-
return apiPrefix.startsWith('/') ? apiPrefix : `/${apiPrefix}`;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Vite `server` + `preview` fragment for standalone Sybilion SPAs: same-origin `/api` in dev,
|
|
19
|
-
* proxied to `VITE_SYBILION_API_BASE_URL`. Uses `PORT` from env (default `3000`).
|
|
20
|
-
*/
|
|
21
|
-
function sybilionStandaloneViteDev(options) {
|
|
22
|
-
const envDir = options.envDir ?? process.cwd();
|
|
23
|
-
const apiPrefix = normalizeApiPrefix(options.apiPrefix ?? '/api');
|
|
24
|
-
const env = loadEnv(options.mode, envDir, '');
|
|
25
|
-
const port = parsePort(env.PORT);
|
|
26
|
-
const target = (env[SYBILION_API_ENV] ?? '').replace(/\/$/, '');
|
|
27
|
-
const proxy = {};
|
|
28
|
-
if (target) {
|
|
29
|
-
proxy[apiPrefix] = {
|
|
30
|
-
target,
|
|
31
|
-
changeOrigin: true,
|
|
32
|
-
secure: true,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
else if (options.mode === 'development' && !warnedMissingApiUrl) {
|
|
36
|
-
warnedMissingApiUrl = true;
|
|
37
|
-
console.warn(`[@sybilion/uilib] ${SYBILION_API_ENV} is not set; API dev proxy disabled.`);
|
|
38
|
-
}
|
|
39
|
-
const serverPreview = {
|
|
40
|
-
port,
|
|
41
|
-
proxy,
|
|
42
|
-
};
|
|
43
|
-
return {
|
|
44
|
-
server: serverPreview,
|
|
45
|
-
preview: serverPreview,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export { sybilionStandaloneViteDev };
|