@kitnai/chat 0.4.0 → 0.5.0
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/README.md +24 -5
- package/dist/custom-elements.json +475 -0
- package/dist/kitn-chat.es.js +18 -18
- package/dist/llms/llms-full.txt +55 -4
- package/dist/llms/llms.txt +3 -3
- package/dist/theme.tokens.css +6 -2
- package/frameworks/react/index.tsx +54 -0
- package/llms-full.txt +55 -4
- package/llms.txt +3 -3
- package/package.json +20 -2
- package/src/components/chat-thread.tsx +217 -0
- package/src/components/prompt-input.tsx +5 -0
- package/src/elements/chat-workspace.tsx +122 -0
- package/src/elements/chat.tsx +30 -271
- package/src/elements/compiled.css +1 -1
- package/src/elements/define.tsx +1 -1
- package/src/elements/element-types.d.ts +40 -0
- package/src/elements/kitn-chat-workspace.stories.tsx +195 -0
- package/src/elements/register.ts +1 -0
- package/src/elements/styles.css +14 -0
- package/src/primitives/chat-config.tsx +1 -1
- package/src/stories/docs/Installation.mdx +27 -0
- package/src/stories/docs/Integrations.mdx +2 -0
- package/src/stories/docs/Introduction.mdx +12 -3
- package/src/stories/pattern-centered-conversation.stories.tsx +93 -0
- package/src/stories/pattern-docked-widget.stories.tsx +93 -0
- package/src/stories/pattern-empty-state.stories.tsx +76 -0
- package/src/ui/collapsible.stories.tsx +70 -0
- package/src/ui/dropdown.stories.tsx +60 -0
- package/src/ui/hover-card.stories.tsx +78 -0
- package/src/ui/overlay.stories.tsx +115 -0
- package/src/ui/overlay.tsx +1 -1
- package/src/ui/scroll-area.stories.tsx +51 -0
- package/src/ui/textarea.stories.tsx +77 -0
- package/theme.css +6 -2
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { createSignal, Show } from 'solid-js';
|
|
2
|
+
import { defineKitnElement } from './define';
|
|
3
|
+
import { ChatThread, type ChatThreadContextUsage } from '../components/chat-thread';
|
|
4
|
+
import { ConversationList } from '../components/conversation-list';
|
|
5
|
+
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '../ui/resizable';
|
|
6
|
+
import { Button } from '../ui/button';
|
|
7
|
+
import { PanelLeftOpen } from 'lucide-solid';
|
|
8
|
+
import type { SlashCommandItem } from '../components/slash-command';
|
|
9
|
+
import type { ChatMessage } from './chat-types';
|
|
10
|
+
import type { ProseSize } from '../primitives/chat-config';
|
|
11
|
+
import type { ModelOption, ConversationGroup, ConversationSummary } from '../types';
|
|
12
|
+
|
|
13
|
+
interface Props extends Record<string, unknown> {
|
|
14
|
+
/** Pre-bucketed conversation groups for the sidebar. Set as a JS property. */
|
|
15
|
+
groups: ConversationGroup[];
|
|
16
|
+
/** Flat conversation list (auto-bucketed if `groups` is empty). Set as a JS property. */
|
|
17
|
+
conversations: ConversationSummary[];
|
|
18
|
+
/** Id of the open conversation, highlighted in the sidebar. */
|
|
19
|
+
activeId?: string;
|
|
20
|
+
/** The active conversation's message thread, newest last. Set as a JS property. */
|
|
21
|
+
messages: ChatMessage[];
|
|
22
|
+
value?: string;
|
|
23
|
+
placeholder?: string;
|
|
24
|
+
loading?: boolean;
|
|
25
|
+
suggestions?: string[];
|
|
26
|
+
suggestionMode?: 'submit' | 'fill';
|
|
27
|
+
proseSize?: ProseSize;
|
|
28
|
+
codeTheme?: string;
|
|
29
|
+
codeHighlight?: boolean;
|
|
30
|
+
chatTitle?: string;
|
|
31
|
+
models?: ModelOption[];
|
|
32
|
+
currentModel?: string;
|
|
33
|
+
context?: ChatThreadContextUsage;
|
|
34
|
+
scrollButton?: boolean;
|
|
35
|
+
search?: boolean;
|
|
36
|
+
voice?: boolean;
|
|
37
|
+
slashCommands?: SlashCommandItem[];
|
|
38
|
+
slashActiveIds?: string[];
|
|
39
|
+
slashCompact?: boolean;
|
|
40
|
+
/** Sidebar default width as a percent of the workspace (default 22). */
|
|
41
|
+
sidebarWidth?: number;
|
|
42
|
+
/** Sidebar min width in px (default 200). */
|
|
43
|
+
sidebarMinWidth?: number;
|
|
44
|
+
/** Sidebar max width in px (default 420). */
|
|
45
|
+
sidebarMaxWidth?: number;
|
|
46
|
+
/** Initial collapsed state of the sidebar (default false). */
|
|
47
|
+
sidebarCollapsed?: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
defineKitnElement<Props>('kitn-chat-workspace', {
|
|
51
|
+
groups: [], conversations: [], activeId: undefined, messages: [],
|
|
52
|
+
value: undefined, placeholder: 'Send a message...', loading: false,
|
|
53
|
+
suggestions: undefined, suggestionMode: 'submit', proseSize: 'sm',
|
|
54
|
+
codeTheme: 'github-dark-dimmed', codeHighlight: true, chatTitle: undefined,
|
|
55
|
+
models: undefined, currentModel: undefined, context: undefined, scrollButton: true,
|
|
56
|
+
search: false, voice: false, slashCommands: undefined, slashActiveIds: undefined, slashCompact: false,
|
|
57
|
+
sidebarWidth: 22, sidebarMinWidth: 200, sidebarMaxWidth: 420, sidebarCollapsed: false,
|
|
58
|
+
}, (props, { dispatch, flag }) => {
|
|
59
|
+
// Collapse is internal UI state; `sidebarCollapsed` only sets the initial value
|
|
60
|
+
// (not a controlled binding).
|
|
61
|
+
const [collapsed, setCollapsed] = createSignal(props.sidebarCollapsed === true);
|
|
62
|
+
const toggle = () => { const next = !collapsed(); setCollapsed(next); dispatch('sidebartoggle', { collapsed: next }); };
|
|
63
|
+
|
|
64
|
+
// Create the thread ONCE and reference the same node in both <Show> branches.
|
|
65
|
+
// It's owned by this component root (not by a Show branch), so toggling the
|
|
66
|
+
// sidebar moves the node between branches without disposing it — the thread's
|
|
67
|
+
// own state (e.g. an uncontrolled draft) survives the collapse/expand.
|
|
68
|
+
const threadEl = (
|
|
69
|
+
<ChatThread
|
|
70
|
+
messages={props.messages} value={props.value as string | undefined} placeholder={props.placeholder as string}
|
|
71
|
+
loading={flag('loading')} suggestions={props.suggestions as string[] | undefined}
|
|
72
|
+
suggestionMode={props.suggestionMode as 'submit' | 'fill'} proseSize={props.proseSize as ProseSize}
|
|
73
|
+
codeTheme={props.codeTheme as string} codeHighlight={flag('codeHighlight')}
|
|
74
|
+
chatTitle={props.chatTitle as string | undefined} models={props.models as ModelOption[] | undefined}
|
|
75
|
+
currentModel={props.currentModel as string | undefined} context={props.context as ChatThreadContextUsage | undefined}
|
|
76
|
+
scrollButton={props.scrollButton !== false} search={flag('search')} voice={flag('voice')}
|
|
77
|
+
slashCommands={props.slashCommands as SlashCommandItem[] | undefined}
|
|
78
|
+
slashActiveIds={props.slashActiveIds as string[] | undefined} slashCompact={flag('slashCompact')}
|
|
79
|
+
onValueChange={(value) => dispatch('valuechange', { value })}
|
|
80
|
+
onSubmit={(detail) => dispatch('submit', detail)}
|
|
81
|
+
onSuggestionClick={(value) => dispatch('suggestionclick', { value })}
|
|
82
|
+
onModelChange={(modelId) => dispatch('modelchange', { modelId })}
|
|
83
|
+
onMessageAction={(detail) => dispatch('messageaction', detail)}
|
|
84
|
+
onSearch={() => dispatch('search', {})}
|
|
85
|
+
onVoice={() => dispatch('voice', {})}
|
|
86
|
+
onSlashSelect={(command) => dispatch('slashselect', { command })}
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<div class="h-full w-full overflow-hidden bg-background">
|
|
92
|
+
<Show
|
|
93
|
+
when={!collapsed()}
|
|
94
|
+
fallback={
|
|
95
|
+
<div class="relative h-full">
|
|
96
|
+
<Button
|
|
97
|
+
variant="ghost" size="icon-sm" aria-label="Open sidebar"
|
|
98
|
+
class="absolute left-2 top-2 z-10 rounded-full bg-card/80 shadow-sm backdrop-blur"
|
|
99
|
+
onClick={toggle}
|
|
100
|
+
>
|
|
101
|
+
<PanelLeftOpen class="size-4" />
|
|
102
|
+
</Button>
|
|
103
|
+
{threadEl}
|
|
104
|
+
</div>
|
|
105
|
+
}
|
|
106
|
+
>
|
|
107
|
+
<ResizablePanelGroup orientation="horizontal">
|
|
108
|
+
<ResizablePanel defaultSize={props.sidebarWidth as number} data-min-size={String(props.sidebarMinWidth)} data-max-size={String(props.sidebarMaxWidth)}>
|
|
109
|
+
<ConversationList
|
|
110
|
+
groups={props.groups} conversations={props.conversations} activeId={props.activeId as string | undefined}
|
|
111
|
+
onSelect={(id) => dispatch('conversationselect', { id })}
|
|
112
|
+
onNewChat={() => dispatch('newchat', {})}
|
|
113
|
+
onToggleSidebar={toggle}
|
|
114
|
+
/>
|
|
115
|
+
</ResizablePanel>
|
|
116
|
+
<ResizableHandle withHandle />
|
|
117
|
+
<ResizablePanel>{threadEl}</ResizablePanel>
|
|
118
|
+
</ResizablePanelGroup>
|
|
119
|
+
</Show>
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
});
|
package/src/elements/chat.tsx
CHANGED
|
@@ -1,278 +1,37 @@
|
|
|
1
|
-
import { createSignal, For, Show } from 'solid-js';
|
|
2
1
|
import { defineKitnElement } from './define';
|
|
3
|
-
import {
|
|
4
|
-
import { ChatContainer, ChatContainerContent, ChatContainerScrollAnchor } from '../components/chat-container';
|
|
5
|
-
import { Message, MessageContent, MessageActions } from '../components/message';
|
|
6
|
-
import { Reasoning, ReasoningTrigger, ReasoningContent } from '../components/reasoning';
|
|
7
|
-
import { Tool } from '../components/tool';
|
|
8
|
-
import { Attachments, Attachment, AttachmentPreview, AttachmentInfo, type AttachmentData } from '../components/attachments';
|
|
9
|
-
import { ModelSwitcher } from '../components/model-switcher';
|
|
10
|
-
import { ScrollButton } from '../components/scroll-button';
|
|
11
|
-
import {
|
|
12
|
-
Context,
|
|
13
|
-
ContextTrigger,
|
|
14
|
-
ContextContent,
|
|
15
|
-
ContextContentHeader,
|
|
16
|
-
ContextContentBody,
|
|
17
|
-
ContextContentFooter,
|
|
18
|
-
ContextInputUsage,
|
|
19
|
-
ContextOutputUsage,
|
|
20
|
-
} from '../components/context';
|
|
21
|
-
import { Button } from '../ui/button';
|
|
22
|
-
import { Copy, ThumbsUp, ThumbsDown, RefreshCw, Pencil } from 'lucide-solid';
|
|
23
|
-
import type { Component } from 'solid-js';
|
|
24
|
-
import { DefaultPromptInput } from './default-input';
|
|
2
|
+
import { ChatThread, type ChatThreadProps, type ChatThreadContextUsage } from '../components/chat-thread';
|
|
25
3
|
import type { SlashCommandItem } from '../components/slash-command';
|
|
26
|
-
import type { ChatMessage, ChatMessageAction } from './chat-types';
|
|
27
4
|
import type { ProseSize } from '../primitives/chat-config';
|
|
28
5
|
import type { ModelOption } from '../types';
|
|
29
6
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
inputTokens?: number;
|
|
34
|
-
outputTokens?: number;
|
|
35
|
-
estimatedCost?: number;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface Props extends Record<string, unknown> {
|
|
39
|
-
/** The full message thread to render, newest last. Each entry carries its role,
|
|
40
|
-
* content, and optional reasoning/tools/attachments/actions. Set as a JS
|
|
41
|
-
* property (`el.messages = [...]`). */
|
|
42
|
-
messages: ChatMessage[];
|
|
43
|
-
/** Controlled value of the input. When set, the host owns the input text and
|
|
44
|
-
* must update it on `valuechange`; leave unset for uncontrolled behavior. */
|
|
45
|
-
value?: string;
|
|
46
|
-
/** Placeholder text shown in the empty input. */
|
|
47
|
-
placeholder?: string;
|
|
48
|
-
/** When true, shows the loading/streaming state and disables submit (use while
|
|
49
|
-
* awaiting the assistant's reply). */
|
|
50
|
-
loading?: boolean;
|
|
51
|
-
/** Starter prompts shown above the input when the thread is empty. Clicking one
|
|
52
|
-
* follows `suggestionMode`. Set as a JS property. */
|
|
53
|
-
suggestions?: string[];
|
|
54
|
-
/** What clicking a suggestion does: `'submit'` (default) sends it immediately
|
|
55
|
-
* as if typed and submitted; `'fill'` just places it in the input. */
|
|
56
|
-
suggestionMode?: 'submit' | 'fill';
|
|
57
|
-
/** Body/prose font scale for rendered markdown (`'xs' | 'sm' | 'base' | 'lg'`).
|
|
58
|
-
* Defaults to `'sm'`. */
|
|
59
|
-
proseSize?: ProseSize;
|
|
60
|
-
/** Shiki theme name for syntax-highlighted code blocks (e.g.
|
|
61
|
-
* `'github-dark-dimmed'`). */
|
|
62
|
-
codeTheme?: string;
|
|
63
|
-
/** Enable Shiki syntax highlighting in code blocks. Turn off to render plain
|
|
64
|
-
* `<pre>` blocks (lighter, no highlighter load). Default true. */
|
|
65
|
-
codeHighlight?: boolean;
|
|
66
|
-
/** Optional header title shown on the left of the header. */
|
|
67
|
-
chatTitle?: string;
|
|
68
|
-
/** Optional model list. When set (>1 model) a ModelSwitcher is shown in the
|
|
69
|
-
* header and a `modelchange` event fires on selection. */
|
|
70
|
-
models?: ModelOption[];
|
|
71
|
-
/** The currently selected model id (pairs with `models`). */
|
|
72
|
-
currentModel?: string;
|
|
73
|
-
/** Optional context-window token usage. When set, a Context token meter is
|
|
74
|
-
* shown in the header. */
|
|
75
|
-
context?: ContextUsage;
|
|
76
|
-
/** Show the scroll-to-bottom button inside the scroll area. Default true. */
|
|
77
|
-
scrollButton?: boolean;
|
|
78
|
-
/** Show a Search (Globe) button in the input toolbar; fires a `search` event. */
|
|
79
|
-
search?: boolean;
|
|
80
|
-
/** Show a Voice (Mic) button in the input toolbar; fires a `voice` event. */
|
|
81
|
-
voice?: boolean;
|
|
82
|
-
/** Slash commands — when set, typing `/` in the input opens the command
|
|
83
|
-
* palette and fires `slashselect`. Set as a JS property. */
|
|
84
|
-
slashCommands?: SlashCommandItem[];
|
|
85
|
-
/** Command ids to highlight as active in the palette. */
|
|
86
|
-
slashActiveIds?: string[];
|
|
87
|
-
/** Single-line palette rows. */
|
|
88
|
-
slashCompact?: boolean;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const ACTION_LABEL: Record<ChatMessageAction, string> = {
|
|
92
|
-
copy: 'Copy', like: 'Like', dislike: 'Dislike', regenerate: 'Regenerate', edit: 'Edit',
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const ACTION_ICON: Record<ChatMessageAction, Component<{ class?: string }>> = {
|
|
96
|
-
copy: Copy, like: ThumbsUp, dislike: ThumbsDown, regenerate: RefreshCw, edit: Pencil,
|
|
97
|
-
};
|
|
7
|
+
type Props = Omit<ChatThreadProps,
|
|
8
|
+
'class' | 'onValueChange' | 'onSubmit' | 'onSuggestionClick' | 'onModelChange'
|
|
9
|
+
| 'onMessageAction' | 'onSearch' | 'onVoice' | 'onSlashSelect'> & Record<string, unknown>;
|
|
98
10
|
|
|
99
11
|
defineKitnElement<Props>('kitn-chat', {
|
|
100
|
-
messages: [],
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const current = () => props.value ?? internal();
|
|
127
|
-
const handleChange = (v: string) => { setInternal(v); dispatch('valuechange', { value: v }); };
|
|
128
|
-
const handleSubmit = () => {
|
|
129
|
-
dispatch('submit', { value: current(), attachments: attachments() });
|
|
130
|
-
setAttachments([]);
|
|
131
|
-
};
|
|
132
|
-
const handleSuggestionClick = (v: string) => {
|
|
133
|
-
if ((props.suggestionMode ?? 'submit') === 'fill') {
|
|
134
|
-
handleChange(v);
|
|
135
|
-
dispatch('suggestionclick', { value: v });
|
|
136
|
-
} else {
|
|
137
|
-
// Default: behave as if the user typed the suggestion and pressed submit.
|
|
138
|
-
dispatch('submit', { value: v, attachments: attachments() });
|
|
139
|
-
setAttachments([]);
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const showHeader = () => !!(props.chatTitle || props.models || props.context);
|
|
144
|
-
const showScrollButton = () => props.scrollButton !== false;
|
|
145
|
-
|
|
146
|
-
return (
|
|
147
|
-
<ChatConfig proseSize={props.proseSize} codeTheme={props.codeTheme} codeHighlight={flag('codeHighlight')} portalMount={outer.portalMount()}>
|
|
148
|
-
<div class="flex h-full flex-col bg-background">
|
|
149
|
-
<Show when={showHeader()}>
|
|
150
|
-
<header class="flex h-14 shrink-0 items-center justify-between border-b border-border px-5">
|
|
151
|
-
<div class="text-sm font-semibold text-foreground">
|
|
152
|
-
{props.chatTitle}
|
|
153
|
-
</div>
|
|
154
|
-
<div class="flex items-center gap-2">
|
|
155
|
-
<Show when={props.models}>
|
|
156
|
-
<ModelSwitcher
|
|
157
|
-
models={props.models!}
|
|
158
|
-
currentModelId={props.currentModel ?? props.models![0]?.id ?? ''}
|
|
159
|
-
onModelChange={(modelId) => dispatch('modelchange', { modelId })}
|
|
160
|
-
/>
|
|
161
|
-
</Show>
|
|
162
|
-
<Show when={props.context}>
|
|
163
|
-
<Context
|
|
164
|
-
usedTokens={props.context!.usedTokens}
|
|
165
|
-
maxTokens={props.context!.maxTokens}
|
|
166
|
-
inputTokens={props.context!.inputTokens}
|
|
167
|
-
outputTokens={props.context!.outputTokens}
|
|
168
|
-
estimatedCost={props.context!.estimatedCost}
|
|
169
|
-
>
|
|
170
|
-
<ContextTrigger />
|
|
171
|
-
<ContextContent>
|
|
172
|
-
<ContextContentHeader />
|
|
173
|
-
<ContextContentBody>
|
|
174
|
-
<div class="space-y-1.5">
|
|
175
|
-
<ContextInputUsage />
|
|
176
|
-
<ContextOutputUsage />
|
|
177
|
-
</div>
|
|
178
|
-
</ContextContentBody>
|
|
179
|
-
<ContextContentFooter />
|
|
180
|
-
</ContextContent>
|
|
181
|
-
</Context>
|
|
182
|
-
</Show>
|
|
183
|
-
</div>
|
|
184
|
-
</header>
|
|
185
|
-
</Show>
|
|
186
|
-
<div class="relative flex-1 overflow-hidden">
|
|
187
|
-
<ChatContainer class="h-full px-4 py-3">
|
|
188
|
-
<ChatContainerContent class="mx-auto w-full max-w-3xl space-y-4">
|
|
189
|
-
<For each={props.messages}>
|
|
190
|
-
{(m) => (
|
|
191
|
-
<Message class={m.role === 'user' ? 'flex-col items-end' : 'flex-col items-start'}>
|
|
192
|
-
<Show when={m.reasoning}>
|
|
193
|
-
<Reasoning class="mb-2 w-full">
|
|
194
|
-
<ReasoningTrigger>{m.reasoning!.label ?? 'Reasoning'}</ReasoningTrigger>
|
|
195
|
-
<ReasoningContent markdown>{m.reasoning!.text}</ReasoningContent>
|
|
196
|
-
</Reasoning>
|
|
197
|
-
</Show>
|
|
198
|
-
<For each={m.tools ?? []}>
|
|
199
|
-
{(tp) => <Tool toolPart={tp} class="mb-2 w-full" />}
|
|
200
|
-
</For>
|
|
201
|
-
<Show when={m.attachments?.length}>
|
|
202
|
-
<Attachments variant="inline" class={m.role === 'user' ? 'mb-2 justify-end' : 'mb-2'}>
|
|
203
|
-
<For each={m.attachments!}>
|
|
204
|
-
{(att) => (
|
|
205
|
-
<Attachment data={att}>
|
|
206
|
-
<AttachmentPreview />
|
|
207
|
-
<AttachmentInfo />
|
|
208
|
-
</Attachment>
|
|
209
|
-
)}
|
|
210
|
-
</For>
|
|
211
|
-
</Attachments>
|
|
212
|
-
</Show>
|
|
213
|
-
<MessageContent
|
|
214
|
-
markdown={m.role === 'assistant'}
|
|
215
|
-
class={m.role === 'user'
|
|
216
|
-
? 'bg-muted text-primary max-w-[85%] rounded-2xl px-4 py-2'
|
|
217
|
-
: 'bg-transparent p-0'}
|
|
218
|
-
>
|
|
219
|
-
{m.content}
|
|
220
|
-
</MessageContent>
|
|
221
|
-
<Show when={m.actions?.length}>
|
|
222
|
-
<MessageActions class="mt-1 flex gap-0">
|
|
223
|
-
<For each={m.actions!}>
|
|
224
|
-
{(a) => (
|
|
225
|
-
<Button
|
|
226
|
-
variant="ghost" size="icon-sm" class="rounded-full"
|
|
227
|
-
data-action={a}
|
|
228
|
-
aria-label={ACTION_LABEL[a]}
|
|
229
|
-
onClick={() => dispatch('messageaction', { messageId: m.id, action: a })}
|
|
230
|
-
>
|
|
231
|
-
{(() => {
|
|
232
|
-
const Icon = ACTION_ICON[a];
|
|
233
|
-
return <Icon class="size-3.5" />;
|
|
234
|
-
})()}
|
|
235
|
-
</Button>
|
|
236
|
-
)}
|
|
237
|
-
</For>
|
|
238
|
-
</MessageActions>
|
|
239
|
-
</Show>
|
|
240
|
-
</Message>
|
|
241
|
-
)}
|
|
242
|
-
</For>
|
|
243
|
-
<ChatContainerScrollAnchor />
|
|
244
|
-
</ChatContainerContent>
|
|
245
|
-
<Show when={showScrollButton()}>
|
|
246
|
-
<div class="absolute bottom-4 left-1/2 flex w-full max-w-3xl -translate-x-1/2 justify-center px-5">
|
|
247
|
-
<ScrollButton class="shadow-sm" />
|
|
248
|
-
</div>
|
|
249
|
-
</Show>
|
|
250
|
-
</ChatContainer>
|
|
251
|
-
</div>
|
|
252
|
-
<div class="shrink-0 px-4 pb-4">
|
|
253
|
-
<div class="mx-auto max-w-3xl">
|
|
254
|
-
<DefaultPromptInput
|
|
255
|
-
value={current()}
|
|
256
|
-
placeholder={props.placeholder}
|
|
257
|
-
loading={flag('loading')}
|
|
258
|
-
suggestions={props.suggestions}
|
|
259
|
-
attachments={attachments()}
|
|
260
|
-
search={flag('search')}
|
|
261
|
-
voice={flag('voice')}
|
|
262
|
-
slashCommands={props.slashCommands}
|
|
263
|
-
slashActiveIds={props.slashActiveIds}
|
|
264
|
-
slashCompact={flag('slashCompact')}
|
|
265
|
-
onValueChange={handleChange}
|
|
266
|
-
onSubmit={handleSubmit}
|
|
267
|
-
onSuggestionClick={handleSuggestionClick}
|
|
268
|
-
onAttachmentsChange={setAttachments}
|
|
269
|
-
onSearch={() => dispatch('search', {})}
|
|
270
|
-
onVoice={() => dispatch('voice', {})}
|
|
271
|
-
onSlashSelect={(command) => dispatch('slashselect', { command })}
|
|
272
|
-
/>
|
|
273
|
-
</div>
|
|
274
|
-
</div>
|
|
275
|
-
</div>
|
|
276
|
-
</ChatConfig>
|
|
277
|
-
);
|
|
278
|
-
});
|
|
12
|
+
messages: [], value: undefined, placeholder: 'Send a message...', loading: false,
|
|
13
|
+
suggestions: undefined, suggestionMode: 'submit', proseSize: 'sm',
|
|
14
|
+
codeTheme: 'github-dark-dimmed', codeHighlight: true, chatTitle: undefined,
|
|
15
|
+
models: undefined, currentModel: undefined, context: undefined, scrollButton: true,
|
|
16
|
+
search: false, voice: false, slashCommands: undefined, slashActiveIds: undefined, slashCompact: false,
|
|
17
|
+
}, (props, { dispatch, flag }) => (
|
|
18
|
+
<ChatThread
|
|
19
|
+
messages={props.messages} value={props.value as string | undefined} placeholder={props.placeholder as string}
|
|
20
|
+
loading={flag('loading')} suggestions={props.suggestions as string[] | undefined}
|
|
21
|
+
suggestionMode={props.suggestionMode as 'submit' | 'fill'} proseSize={props.proseSize as ProseSize}
|
|
22
|
+
codeTheme={props.codeTheme as string} codeHighlight={flag('codeHighlight')}
|
|
23
|
+
chatTitle={props.chatTitle as string | undefined} models={props.models as ModelOption[] | undefined}
|
|
24
|
+
currentModel={props.currentModel as string | undefined} context={props.context as ChatThreadContextUsage | undefined}
|
|
25
|
+
scrollButton={props.scrollButton !== false} search={flag('search')} voice={flag('voice')}
|
|
26
|
+
slashCommands={props.slashCommands as SlashCommandItem[] | undefined}
|
|
27
|
+
slashActiveIds={props.slashActiveIds as string[] | undefined} slashCompact={flag('slashCompact')}
|
|
28
|
+
onValueChange={(value) => dispatch('valuechange', { value })}
|
|
29
|
+
onSubmit={(detail) => dispatch('submit', detail)}
|
|
30
|
+
onSuggestionClick={(value) => dispatch('suggestionclick', { value })}
|
|
31
|
+
onModelChange={(modelId) => dispatch('modelchange', { modelId })}
|
|
32
|
+
onMessageAction={(detail) => dispatch('messageaction', detail)}
|
|
33
|
+
onSearch={() => dispatch('search', {})}
|
|
34
|
+
onVoice={() => dispatch('voice', {})}
|
|
35
|
+
onSlashSelect={(command) => dispatch('slashselect', { command })}
|
|
36
|
+
/>
|
|
37
|
+
));
|