@nextclaw/ui 0.6.10 → 0.6.11
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/.eslintrc.cjs +10 -0
- package/CHANGELOG.md +9 -0
- package/dist/assets/{ChannelsList-TyMb5Mgz.js → ChannelsList-C49JQ-Zt.js} +1 -1
- package/dist/assets/ChatPage-DIx05c6s.js +36 -0
- package/dist/assets/{DocBrowser-CNtrA0ps.js → DocBrowser-CpOosDEI.js} +1 -1
- package/dist/assets/{LogoBadge-BLqiOM5D.js → LogoBadge-CL_8ZPXU.js} +1 -1
- package/dist/assets/MarketplacePage-BOzko5s9.js +49 -0
- package/dist/assets/{ModelConfig-CCsQ8KFq.js → ModelConfig-BZ4ZfaQB.js} +1 -1
- package/dist/assets/ProvidersList-fPpJ5gl6.js +1 -0
- package/dist/assets/{RuntimeConfig-BO6s-ls-.js → RuntimeConfig-Dt9pLB9P.js} +1 -1
- package/dist/assets/{SecretsConfig-mayFdxpM.js → SecretsConfig-C1PU0Yy8.js} +2 -2
- package/dist/assets/{SessionsConfig-DAIczdBj.js → SessionsConfig-EskBOofQ.js} +2 -2
- package/dist/assets/{card-BP5YnL-G.js → card-C7Gtw2Vs.js} +1 -1
- package/dist/assets/index-Cn6_2To7.js +8 -0
- package/dist/assets/{index-BUiahmWm.css → index-nEYGCJTC.css} +1 -1
- package/dist/assets/{input-B1D2QX0O.js → input-oBvxsnV9.js} +1 -1
- package/dist/assets/{label-DW0j-fXA.js → label-C7F8lMpQ.js} +1 -1
- package/dist/assets/{page-layout-Ch-H9gD-.js → page-layout-DO8BlScF.js} +1 -1
- package/dist/assets/session-run-status-Kg0FwAPn.js +3 -0
- package/dist/assets/{switch-_cZHlGKB.js → switch-C6a5GyZB.js} +1 -1
- package/dist/assets/{tabs-custom-ARxqYYjG.js → tabs-custom-BatFap5k.js} +1 -1
- package/dist/assets/{useConfirmDialog-BaU7nIat.js → useConfirmDialog-zJzVKMdu.js} +2 -2
- package/dist/assets/{vendor-C--HHaLf.js → vendor-TlME1INH.js} +84 -84
- package/dist/index.html +3 -3
- package/package.json +4 -2
- package/src/App.tsx +1 -2
- package/src/api/config.ts +199 -200
- package/src/api/types.ts +36 -24
- package/src/components/chat/ChatConversationPanel.tsx +102 -121
- package/src/components/chat/ChatPage.tsx +165 -437
- package/src/components/chat/ChatSidebar.tsx +30 -36
- package/src/components/chat/ChatThread.tsx +73 -131
- package/src/components/chat/chat-input/ChatInputBarView.tsx +82 -0
- package/src/components/chat/chat-input/ChatInputBottomToolbar.tsx +71 -0
- package/src/components/chat/chat-input/components/ChatInputModelStateHint.tsx +39 -0
- package/src/components/chat/chat-input/components/ChatInputSelectedSkillsSection.tsx +31 -0
- package/src/components/chat/chat-input/components/ChatInputSlashPanelSection.tsx +112 -0
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputAttachButton.tsx +24 -0
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputModelSelector.tsx +58 -0
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputSendControls.tsx +56 -0
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputSessionTypeSelector.tsx +40 -0
- package/src/components/chat/chat-input/useChatInputBarController.ts +313 -0
- package/src/components/chat/chat-input.types.ts +15 -0
- package/src/components/chat/chat-page-data.ts +121 -0
- package/src/components/chat/chat-page-runtime.ts +221 -0
- package/src/components/chat/chat-session-route.ts +59 -0
- package/src/components/chat/chat-stream/nextbot-parsers.ts +52 -0
- package/src/components/chat/chat-stream/nextbot-runtime-agent.ts +413 -0
- package/src/components/chat/chat-stream/stream-event-adapter.ts +98 -0
- package/src/components/chat/chat-stream/transport.ts +159 -0
- package/src/components/chat/chat-stream/types.ts +76 -0
- package/src/components/chat/managers/chat-input.manager.ts +142 -0
- package/src/components/chat/managers/chat-run-status.manager.ts +32 -0
- package/src/components/chat/managers/chat-session-list.manager.ts +77 -0
- package/src/components/chat/managers/chat-stream-actions.manager.ts +34 -0
- package/src/components/chat/managers/chat-thread.manager.ts +86 -0
- package/src/components/chat/managers/chat-ui.manager.ts +65 -0
- package/src/components/chat/presenter/chat-presenter-context.tsx +25 -0
- package/src/components/chat/presenter/chat.presenter.ts +32 -0
- package/src/components/chat/stores/chat-input.store.ts +62 -0
- package/src/components/chat/stores/chat-run-status.store.ts +30 -0
- package/src/components/chat/stores/chat-session-list.store.ts +34 -0
- package/src/components/chat/stores/chat-thread.store.ts +52 -0
- package/src/components/chat/useChatRuntimeController.ts +134 -0
- package/src/components/chat/useChatSessionTypeState.ts +148 -0
- package/src/components/common/MaskedInput.tsx +1 -1
- package/src/hooks/useConfig.ts +31 -1
- package/src/hooks/useObservable.ts +20 -0
- package/src/lib/chat-message.ts +2 -202
- package/src/lib/chat-runtime-utils.ts +250 -0
- package/src/lib/i18n.ts +9 -0
- package/tsconfig.json +2 -1
- package/vite.config.ts +2 -1
- package/dist/assets/ChatPage-CQerYqvy.js +0 -34
- package/dist/assets/MarketplacePage-CotZxxNe.js +0 -49
- package/dist/assets/ProvidersList-BYYX5K_g.js +0 -1
- package/dist/assets/index-D6_5HaDl.js +0 -7
- package/dist/assets/session-run-status-BUYsQeWs.js +0 -5
- package/src/components/chat/ChatInputBar.tsx +0 -590
- package/src/components/chat/useChatStreamController.ts +0 -591
|
@@ -1,44 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { MarketplaceInstalledRecord, SessionEventView } from '@/api/types';
|
|
1
|
+
import { useCallback, useEffect, useLayoutEffect, useRef } from 'react';
|
|
3
2
|
import { Button } from '@/components/ui/button';
|
|
4
3
|
import { ChatThread } from '@/components/chat/ChatThread';
|
|
5
|
-
import { ChatInputBar, type ChatModelOption } from '@/components/chat/ChatInputBar';
|
|
6
4
|
import { ChatWelcome } from '@/components/chat/ChatWelcome';
|
|
5
|
+
import { ChatInputBarView } from '@/components/chat/chat-input/ChatInputBarView';
|
|
6
|
+
import { usePresenter } from '@/components/chat/presenter/chat-presenter-context';
|
|
7
|
+
import { useChatThreadStore } from '@/components/chat/stores/chat-thread.store';
|
|
7
8
|
import { t } from '@/lib/i18n';
|
|
9
|
+
import { cn } from '@/lib/utils';
|
|
8
10
|
import { Trash2 } from 'lucide-react';
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
isProviderStateResolved: boolean;
|
|
12
|
-
modelOptions: ChatModelOption[];
|
|
13
|
-
selectedModel: string;
|
|
14
|
-
onSelectedModelChange: (value: string) => void;
|
|
15
|
-
onGoToProviders: () => void;
|
|
16
|
-
skillRecords: MarketplaceInstalledRecord[];
|
|
17
|
-
isSkillsLoading?: boolean;
|
|
18
|
-
selectedSkills: string[];
|
|
19
|
-
onSelectedSkillsChange: (next: string[]) => void;
|
|
20
|
-
selectedSessionKey: string | null;
|
|
21
|
-
sessionDisplayName?: string;
|
|
22
|
-
canDeleteSession: boolean;
|
|
23
|
-
isDeletePending: boolean;
|
|
24
|
-
onDeleteSession: () => void;
|
|
25
|
-
onCreateSession: () => void;
|
|
26
|
-
threadRef: MutableRefObject<HTMLDivElement | null>;
|
|
27
|
-
onThreadScroll: () => void;
|
|
28
|
-
isHistoryLoading: boolean;
|
|
29
|
-
mergedEvents: SessionEventView[];
|
|
30
|
-
isSending: boolean;
|
|
31
|
-
isAwaitingAssistantOutput: boolean;
|
|
32
|
-
streamingAssistantText: string;
|
|
33
|
-
draft: string;
|
|
34
|
-
onDraftChange: (value: string) => void;
|
|
35
|
-
onSend: () => Promise<void> | void;
|
|
36
|
-
onStop: () => Promise<void> | void;
|
|
37
|
-
canStopGeneration: boolean;
|
|
38
|
-
stopDisabledReason?: string | null;
|
|
39
|
-
sendError?: string | null;
|
|
40
|
-
queuedCount: number;
|
|
41
|
-
};
|
|
12
|
+
const STICKY_BOTTOM_THRESHOLD_PX = 10;
|
|
42
13
|
|
|
43
14
|
function ChatConversationSkeleton() {
|
|
44
15
|
return (
|
|
@@ -68,80 +39,101 @@ function ChatConversationSkeleton() {
|
|
|
68
39
|
);
|
|
69
40
|
}
|
|
70
41
|
|
|
71
|
-
export function ChatConversationPanel({
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
onCreateSession,
|
|
87
|
-
threadRef,
|
|
88
|
-
onThreadScroll,
|
|
89
|
-
isHistoryLoading,
|
|
90
|
-
mergedEvents,
|
|
91
|
-
isSending,
|
|
92
|
-
isAwaitingAssistantOutput,
|
|
93
|
-
streamingAssistantText,
|
|
94
|
-
draft,
|
|
95
|
-
onDraftChange,
|
|
96
|
-
onSend,
|
|
97
|
-
onStop,
|
|
98
|
-
canStopGeneration,
|
|
99
|
-
stopDisabledReason,
|
|
100
|
-
sendError,
|
|
101
|
-
queuedCount,
|
|
102
|
-
}: ChatConversationPanelProps) {
|
|
103
|
-
const showWelcome = !selectedSessionKey && mergedEvents.length === 0;
|
|
104
|
-
const hasConfiguredModel = modelOptions.length > 0;
|
|
105
|
-
const shouldShowProviderHint = isProviderStateResolved && !hasConfiguredModel;
|
|
42
|
+
export function ChatConversationPanel() {
|
|
43
|
+
const presenter = usePresenter();
|
|
44
|
+
const snapshot = useChatThreadStore((state) => state.snapshot);
|
|
45
|
+
const fallbackThreadRef = useRef<HTMLDivElement | null>(null);
|
|
46
|
+
const threadRef = snapshot.threadRef ?? fallbackThreadRef;
|
|
47
|
+
|
|
48
|
+
// --- Sticky-to-bottom scroll state ---
|
|
49
|
+
const isStickyRef = useRef(true);
|
|
50
|
+
const isProgrammaticScrollRef = useRef(false);
|
|
51
|
+
const previousSessionKeyRef = useRef<string | null>(null);
|
|
52
|
+
const pendingInitialScrollRef = useRef(false);
|
|
53
|
+
|
|
54
|
+
const showWelcome = !snapshot.selectedSessionKey && snapshot.uiMessages.length === 0 && !snapshot.isSending;
|
|
55
|
+
const hasConfiguredModel = snapshot.modelOptions.length > 0;
|
|
56
|
+
const shouldShowProviderHint = snapshot.isProviderStateResolved && !hasConfiguredModel;
|
|
106
57
|
const hideEmptyHint =
|
|
107
|
-
isHistoryLoading &&
|
|
108
|
-
|
|
109
|
-
!isSending &&
|
|
110
|
-
!isAwaitingAssistantOutput
|
|
111
|
-
|
|
58
|
+
snapshot.isHistoryLoading &&
|
|
59
|
+
snapshot.uiMessages.length === 0 &&
|
|
60
|
+
!snapshot.isSending &&
|
|
61
|
+
!snapshot.isAwaitingAssistantOutput;
|
|
62
|
+
|
|
63
|
+
const scrollToBottom = useCallback(() => {
|
|
64
|
+
const el = threadRef.current;
|
|
65
|
+
if (!el) return;
|
|
66
|
+
isProgrammaticScrollRef.current = true;
|
|
67
|
+
el.scrollTop = el.scrollHeight;
|
|
68
|
+
}, [threadRef]);
|
|
69
|
+
|
|
70
|
+
const handleScroll = useCallback(() => {
|
|
71
|
+
// Skip sticky check for programmatic scrolls
|
|
72
|
+
if (isProgrammaticScrollRef.current) {
|
|
73
|
+
isProgrammaticScrollRef.current = false;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const el = threadRef.current;
|
|
77
|
+
if (!el) return;
|
|
78
|
+
const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
79
|
+
isStickyRef.current = distanceFromBottom <= STICKY_BOTTOM_THRESHOLD_PX;
|
|
80
|
+
}, [threadRef]);
|
|
81
|
+
|
|
82
|
+
// Session change → force sticky + schedule initial scroll
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (previousSessionKeyRef.current === snapshot.selectedSessionKey) return;
|
|
85
|
+
previousSessionKeyRef.current = snapshot.selectedSessionKey;
|
|
86
|
+
isStickyRef.current = true;
|
|
87
|
+
pendingInitialScrollRef.current = true;
|
|
88
|
+
}, [snapshot.selectedSessionKey]);
|
|
89
|
+
|
|
90
|
+
// Initial scroll after history loads for a new session
|
|
91
|
+
useLayoutEffect(() => {
|
|
92
|
+
if (!pendingInitialScrollRef.current) return;
|
|
93
|
+
if (snapshot.isHistoryLoading || snapshot.uiMessages.length === 0) return;
|
|
94
|
+
pendingInitialScrollRef.current = false;
|
|
95
|
+
scrollToBottom();
|
|
96
|
+
}, [scrollToBottom, snapshot.isHistoryLoading, snapshot.uiMessages]);
|
|
97
|
+
|
|
98
|
+
// Streaming updates: keep bottom visible while still sticky.
|
|
99
|
+
useLayoutEffect(() => {
|
|
100
|
+
if (!isStickyRef.current) return;
|
|
101
|
+
if (snapshot.uiMessages.length === 0) return;
|
|
102
|
+
scrollToBottom();
|
|
103
|
+
}, [scrollToBottom, snapshot.uiMessages]);
|
|
112
104
|
|
|
113
|
-
if (!isProviderStateResolved) {
|
|
105
|
+
if (!snapshot.isProviderStateResolved) {
|
|
114
106
|
return <ChatConversationSkeleton />;
|
|
115
107
|
}
|
|
116
108
|
|
|
117
109
|
return (
|
|
118
110
|
<section className="flex-1 min-h-0 flex flex-col overflow-hidden bg-gradient-to-b from-gray-50/60 to-white">
|
|
119
|
-
{
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
</
|
|
127
|
-
<Button
|
|
128
|
-
variant="ghost"
|
|
129
|
-
size="icon"
|
|
130
|
-
className="rounded-lg shrink-0 text-gray-400 hover:text-destructive"
|
|
131
|
-
onClick={onDeleteSession}
|
|
132
|
-
disabled={!canDeleteSession || isDeletePending}
|
|
133
|
-
>
|
|
134
|
-
<Trash2 className="h-4 w-4" />
|
|
135
|
-
</Button>
|
|
111
|
+
<div className={cn(
|
|
112
|
+
"px-5 border-b border-gray-200/60 bg-white/80 backdrop-blur-sm flex items-center justify-between shrink-0 overflow-hidden transition-all duration-200",
|
|
113
|
+
snapshot.selectedSessionKey ? "py-3 opacity-100" : "h-0 py-0 opacity-0 border-b-0"
|
|
114
|
+
)}>
|
|
115
|
+
<div className="min-w-0 flex-1">
|
|
116
|
+
<span className="text-sm font-medium text-gray-700 truncate">
|
|
117
|
+
{snapshot.sessionDisplayName || snapshot.selectedSessionKey}
|
|
118
|
+
</span>
|
|
136
119
|
</div>
|
|
137
|
-
|
|
120
|
+
<Button
|
|
121
|
+
variant="ghost"
|
|
122
|
+
size="icon"
|
|
123
|
+
className="rounded-lg shrink-0 text-gray-400 hover:text-destructive"
|
|
124
|
+
onClick={presenter.chatThreadManager.deleteSession}
|
|
125
|
+
disabled={!snapshot.canDeleteSession || snapshot.isDeletePending}
|
|
126
|
+
>
|
|
127
|
+
<Trash2 className="h-4 w-4" />
|
|
128
|
+
</Button>
|
|
129
|
+
</div>
|
|
138
130
|
|
|
139
131
|
{shouldShowProviderHint && (
|
|
140
132
|
<div className="px-5 py-2.5 border-b border-amber-200/70 bg-amber-50/70 flex items-center justify-between gap-3 shrink-0">
|
|
141
133
|
<span className="text-xs text-amber-800">{t('chatModelNoOptions')}</span>
|
|
142
134
|
<button
|
|
143
135
|
type="button"
|
|
144
|
-
onClick={
|
|
136
|
+
onClick={presenter.chatThreadManager.goToProviders}
|
|
145
137
|
className="text-xs font-semibold text-amber-900 underline-offset-2 hover:underline"
|
|
146
138
|
>
|
|
147
139
|
{t('chatGoConfigureProvider')}
|
|
@@ -149,42 +141,31 @@ export function ChatConversationPanel({
|
|
|
149
141
|
</div>
|
|
150
142
|
)}
|
|
151
143
|
|
|
152
|
-
{
|
|
153
|
-
|
|
144
|
+
{snapshot.sessionTypeUnavailable && snapshot.sessionTypeUnavailableMessage?.trim() && (
|
|
145
|
+
<div className="px-5 py-2.5 border-b border-amber-200/70 bg-amber-50/70 shrink-0">
|
|
146
|
+
<span className="text-xs text-amber-800">{snapshot.sessionTypeUnavailableMessage}</span>
|
|
147
|
+
</div>
|
|
148
|
+
)}
|
|
149
|
+
|
|
150
|
+
<div
|
|
151
|
+
ref={threadRef}
|
|
152
|
+
onScroll={handleScroll}
|
|
153
|
+
className="flex-1 min-h-0 overflow-y-auto custom-scrollbar"
|
|
154
|
+
>
|
|
154
155
|
{showWelcome ? (
|
|
155
|
-
<ChatWelcome onCreateSession={
|
|
156
|
+
<ChatWelcome onCreateSession={presenter.chatThreadManager.createSession} />
|
|
156
157
|
) : hideEmptyHint ? (
|
|
157
158
|
<div className="h-full" />
|
|
158
|
-
) :
|
|
159
|
+
) : snapshot.uiMessages.length === 0 ? (
|
|
159
160
|
<div className="px-5 py-5 text-sm text-gray-500">{t('chatNoMessages')}</div>
|
|
160
161
|
) : (
|
|
161
162
|
<div className="mx-auto w-full max-w-[min(1120px,100%)] px-6 py-5">
|
|
162
|
-
<ChatThread
|
|
163
|
+
<ChatThread uiMessages={snapshot.uiMessages} isSending={snapshot.isSending && snapshot.isAwaitingAssistantOutput} />
|
|
163
164
|
</div>
|
|
164
165
|
)}
|
|
165
166
|
</div>
|
|
166
167
|
|
|
167
|
-
|
|
168
|
-
<ChatInputBar
|
|
169
|
-
isProviderStateResolved={isProviderStateResolved}
|
|
170
|
-
draft={draft}
|
|
171
|
-
onDraftChange={onDraftChange}
|
|
172
|
-
onSend={onSend}
|
|
173
|
-
onStop={onStop}
|
|
174
|
-
onGoToProviders={onGoToProviders}
|
|
175
|
-
canStopGeneration={canStopGeneration}
|
|
176
|
-
stopDisabledReason={stopDisabledReason}
|
|
177
|
-
sendError={sendError}
|
|
178
|
-
isSending={isSending}
|
|
179
|
-
queuedCount={queuedCount}
|
|
180
|
-
modelOptions={modelOptions}
|
|
181
|
-
selectedModel={selectedModel}
|
|
182
|
-
onSelectedModelChange={onSelectedModelChange}
|
|
183
|
-
skillRecords={skillRecords}
|
|
184
|
-
isSkillsLoading={isSkillsLoading}
|
|
185
|
-
selectedSkills={selectedSkills}
|
|
186
|
-
onSelectedSkillsChange={onSelectedSkillsChange}
|
|
187
|
-
/>
|
|
168
|
+
<ChatInputBarView />
|
|
188
169
|
</section>
|
|
189
170
|
);
|
|
190
171
|
}
|