@parallelworks/ui 0.26.0 → 0.28.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/dist/ai/adapter/types.d.ts +5 -0
- package/dist/ai/ai.css +197 -21
- package/dist/ai/components/AllocationSelector.js +2 -2
- package/dist/ai/components/BranchNavigator.js +6 -6
- package/dist/ai/components/ChatEmptyState.js +39 -36
- package/dist/ai/components/ChatInput.js +67 -60
- package/dist/ai/components/ChatMessage.js +21 -20
- package/dist/ai/components/ChatMessageList.d.ts +7 -2
- package/dist/ai/components/ChatMessageList.js +65 -54
- package/dist/ai/components/ChatSidebar.js +6 -6
- package/dist/ai/components/ChatThread.js +3 -63
- package/dist/ai/components/ModelSelector.js +7 -7
- package/dist/ai/components/PastedText.d.ts +3 -1
- package/dist/ai/components/PastedText.js +21 -6
- package/dist/ai/components/Reasoning.d.ts +17 -0
- package/dist/ai/components/Reasoning.js +29 -0
- package/dist/ai/components/agent/AgentMessageParts.js +32 -22
- package/dist/ai/core/ChatProvider.js +3 -1
- package/dist/ai/core/parts.js +20 -0
- package/dist/ai/core/pasteLanguage.d.ts +6 -0
- package/dist/ai/core/pasteLanguage.js +45 -0
- package/dist/ai/index.d.ts +1 -1
- package/dist/ai/index.js +2 -2
- package/dist/ai/strings.d.ts +9 -0
- package/dist/ai/strings.js +7 -1
- package/dist/ai/ui/Notice.d.ts +8 -0
- package/dist/ai/ui/Notice.js +27 -1
- package/dist/ai/ui/sidebar.js +3 -3
- package/dist/file-explorer/FileExplorer.js +34 -11
- package/dist/file-explorer/FilePreview/previewCache.d.ts +1 -1
- package/dist/file-explorer/FilePreview/previewCache.js +6 -4
- package/dist/file-explorer/lib/types.d.ts +1 -0
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -6,13 +6,13 @@ import { FaUndoAlt } from "../../icons/fa/FaUndoAlt.js";
|
|
|
6
6
|
import { FiCopy } from "../../icons/fi/FiCopy.js";
|
|
7
7
|
import { FiDownload } from "../../icons/fi/FiDownload.js";
|
|
8
8
|
import { FiFile } from "../../icons/fi/FiFile.js";
|
|
9
|
-
import { LuChevronRight } from "../../icons/lu/LuChevronRight.js";
|
|
10
9
|
import { MdRefresh } from "../../icons/md/MdRefresh.js";
|
|
11
10
|
import { durationToAbsHumanDuration } from "../../duration.js";
|
|
12
11
|
import { useChatConfig } from "../core/config.js";
|
|
13
12
|
import { formatFileSize } from "../utils.js";
|
|
14
13
|
import Markdown from "../ui/Markdown.js";
|
|
15
14
|
import { chatProseClasses } from "../ui/prose.js";
|
|
15
|
+
import { ReasoningBody, ReasoningToggle } from "./Reasoning.js";
|
|
16
16
|
import AgentMessageParts from "./agent/AgentMessageParts.js";
|
|
17
17
|
import BranchNavigator from "./BranchNavigator.js";
|
|
18
18
|
import { UserMessageText } from "./PastedText.js";
|
|
@@ -49,6 +49,7 @@ function ChatMessage({ message, allMessages, isStreaming = false, isEditing = fa
|
|
|
49
49
|
const isOtherUser = !!otherAuthor;
|
|
50
50
|
const isRightAligned = isUser && !isOtherUser;
|
|
51
51
|
const hasError = !!message.error;
|
|
52
|
+
const hasPastes = !!message.pastes?.length;
|
|
52
53
|
const hasReasoning = !!message.reasoning;
|
|
53
54
|
const toolCallCount = message.parts?.filter((p) => p.kind === "tool_call").length ?? 0;
|
|
54
55
|
const showReasoningButton = !isUser && !isStreaming && hasReasoning;
|
|
@@ -61,6 +62,7 @@ function ChatMessage({ message, allMessages, isStreaming = false, isEditing = fa
|
|
|
61
62
|
const contentRef = useRef(null);
|
|
62
63
|
const [collapsible, setCollapsible] = useState(false);
|
|
63
64
|
const [expanded, setExpanded] = useState(false);
|
|
65
|
+
const [reasoningOpen, setReasoningOpen] = useState(false);
|
|
64
66
|
useEffect(() => {
|
|
65
67
|
if (isUser || isStreaming || !contentRef.current) return;
|
|
66
68
|
const el = contentRef.current;
|
|
@@ -100,7 +102,7 @@ function ChatMessage({ message, allMessages, isStreaming = false, isEditing = fa
|
|
|
100
102
|
};
|
|
101
103
|
const hasParts = !!message.parts?.length;
|
|
102
104
|
if (hasParts && !message.content) return /* @__PURE__ */ jsx("div", {
|
|
103
|
-
className: cx("group py-
|
|
105
|
+
className: cx("group py-3 w-full", !flush && "px-4"),
|
|
104
106
|
children: /* @__PURE__ */ jsx(AgentMessageParts, {
|
|
105
107
|
message,
|
|
106
108
|
isStreaming,
|
|
@@ -121,7 +123,7 @@ function ChatMessage({ message, allMessages, isStreaming = false, isEditing = fa
|
|
|
121
123
|
};
|
|
122
124
|
return /* @__PURE__ */ jsx("div", {
|
|
123
125
|
role: "none",
|
|
124
|
-
className: cx("group
|
|
126
|
+
className: cx("group w-full", isUser ? "pt-8 pb-5" : "py-3", !flush && "px-4"),
|
|
125
127
|
onMouseEnter: () => setShowActions(true),
|
|
126
128
|
onMouseLeave: () => setShowActions(false),
|
|
127
129
|
onFocus: () => setShowActions(true),
|
|
@@ -133,20 +135,20 @@ function ChatMessage({ message, allMessages, isStreaming = false, isEditing = fa
|
|
|
133
135
|
value: editContent,
|
|
134
136
|
onChange: handleTextareaChange,
|
|
135
137
|
onKeyDown: handleKeyDown,
|
|
136
|
-
className: "w-full min-h-[100px] max-h-[200px] overflow-y-auto
|
|
138
|
+
className: "chat-composer block w-full min-h-[100px] max-h-[200px] overflow-y-auto px-4 py-3.5 rounded-[1.25rem] bg-(--theme-panel-bg) theme-text text-[1rem] leading-relaxed resize-none focus:outline-none",
|
|
137
139
|
placeholder: t.editPlaceholder
|
|
138
140
|
}), /* @__PURE__ */ jsxs("div", {
|
|
139
|
-
className: "flex items-center justify-end gap-2 mt-
|
|
141
|
+
className: "flex items-center justify-end gap-2 mt-2.5",
|
|
140
142
|
children: [/* @__PURE__ */ jsx("button", {
|
|
141
143
|
type: "button",
|
|
142
144
|
onClick: onCancelEdit,
|
|
143
|
-
className: "px-4 py-1.5 text-sm font-medium rounded-full transition-colors
|
|
145
|
+
className: "px-4 py-1.5 text-sm font-medium rounded-full transition-colors chat-tint hover:chat-tint-strong text-(--theme-panel)",
|
|
144
146
|
children: t.cancel
|
|
145
147
|
}), /* @__PURE__ */ jsx("button", {
|
|
146
148
|
type: "button",
|
|
147
149
|
onClick: handleSave,
|
|
148
150
|
disabled: !editContent.trim(),
|
|
149
|
-
className: "px-4 py-1.5 text-sm font-medium rounded-full transition-colors disabled:opacity-50 disabled:cursor-not-allowed bg-(--theme-panel) text-(--theme-panel-bg)",
|
|
151
|
+
className: "px-4 py-1.5 text-sm font-medium rounded-full transition-colors hover:opacity-85 disabled:opacity-50 disabled:cursor-not-allowed bg-(--theme-panel) text-(--theme-panel-bg)",
|
|
150
152
|
children: t.send
|
|
151
153
|
})]
|
|
152
154
|
})]
|
|
@@ -155,14 +157,13 @@ function ChatMessage({ message, allMessages, isStreaming = false, isEditing = fa
|
|
|
155
157
|
children: /* @__PURE__ */ jsxs("div", {
|
|
156
158
|
className: cx("flex-1 min-w-0 space-y-1", isRightAligned && "relative flex flex-col items-end"),
|
|
157
159
|
children: [
|
|
158
|
-
showReasoningButton && /* @__PURE__ */
|
|
160
|
+
showReasoningButton && /* @__PURE__ */ jsxs("div", {
|
|
159
161
|
className: "mb-2",
|
|
160
|
-
children: /* @__PURE__ */
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
})
|
|
162
|
+
children: [/* @__PURE__ */ jsx(ReasoningToggle, {
|
|
163
|
+
open: reasoningOpen,
|
|
164
|
+
onToggle: () => onOpenReasoning ? onOpenReasoning(message.reasoning || "", message.reasoningDuration ?? null) : setReasoningOpen((o) => !o),
|
|
165
|
+
children: message.reasoningDuration ? `Thought for ${formatDuration(message.reasoningDuration)}` : "Thought process"
|
|
166
|
+
}), reasoningOpen && /* @__PURE__ */ jsx(ReasoningBody, { children: /* @__PURE__ */ jsx(Markdown, { children: message.reasoning || "" }) })]
|
|
166
167
|
}),
|
|
167
168
|
showTurnDurationLabel && /* @__PURE__ */ jsx("div", {
|
|
168
169
|
className: "mb-2",
|
|
@@ -190,14 +191,14 @@ function ChatMessage({ message, allMessages, isStreaming = false, isEditing = fa
|
|
|
190
191
|
name: otherAuthor.name || otherAuthor.username,
|
|
191
192
|
className: "flex-shrink-0 mb-0.5"
|
|
192
193
|
}), /* @__PURE__ */ jsx("div", {
|
|
193
|
-
className: "px-4 py-2.5 chat-ink rounded-
|
|
194
|
+
className: cx("px-4 py-2.5 chat-ink rounded-[1.25rem] max-w-[80%] inline-block whitespace-pre-wrap wrap-break-word text-[1rem] leading-6 chat-tint", hasPastes && "chat-bubble-roomy"),
|
|
194
195
|
children: /* @__PURE__ */ jsx(UserMessageText, {
|
|
195
196
|
content: message.content,
|
|
196
197
|
pastes: message.pastes
|
|
197
198
|
})
|
|
198
199
|
})]
|
|
199
200
|
}) : isUser ? /* @__PURE__ */ jsx("div", {
|
|
200
|
-
className: "px-4 py-2.5 chat-ink rounded-
|
|
201
|
+
className: cx("px-4 py-2.5 chat-ink rounded-[1.25rem] max-w-[80%] inline-block whitespace-pre-wrap wrap-break-word text-[1rem] leading-6 chat-bubble-user", hasPastes && "chat-bubble-roomy"),
|
|
201
202
|
children: /* @__PURE__ */ jsx(UserMessageText, {
|
|
202
203
|
content: message.content,
|
|
203
204
|
pastes: message.pastes
|
|
@@ -307,21 +308,21 @@ function ChatMessage({ message, allMessages, isStreaming = false, isEditing = fa
|
|
|
307
308
|
/* @__PURE__ */ jsx("button", {
|
|
308
309
|
type: "button",
|
|
309
310
|
onClick: copyToClipboard,
|
|
310
|
-
className: "p-1.5 rounded-
|
|
311
|
+
className: "p-1.5 rounded-md hover:chat-tint theme-muted-text hover:theme-text transition-colors",
|
|
311
312
|
title: t.copy,
|
|
312
313
|
children: copied ? /* @__PURE__ */ jsx(FaCheck, { className: "w-4 h-4 text-green-500" }) : /* @__PURE__ */ jsx(FiCopy, { className: "w-4 h-4" })
|
|
313
314
|
}),
|
|
314
315
|
isUser && !isOtherUser && onEdit && /* @__PURE__ */ jsx("button", {
|
|
315
316
|
type: "button",
|
|
316
317
|
onClick: () => onEdit(message.id),
|
|
317
|
-
className: "p-1.5 rounded-
|
|
318
|
+
className: "p-1.5 rounded-md hover:chat-tint theme-muted-text hover:theme-text transition-colors",
|
|
318
319
|
title: t.edit,
|
|
319
320
|
children: /* @__PURE__ */ jsx(FaEdit, { className: "w-4 h-4" })
|
|
320
321
|
}),
|
|
321
322
|
!isUser && onRegenerate && /* @__PURE__ */ jsx("button", {
|
|
322
323
|
type: "button",
|
|
323
324
|
onClick: () => onRegenerate(message.id),
|
|
324
|
-
className: "p-1.5 rounded-
|
|
325
|
+
className: "p-1.5 rounded-md hover:chat-tint theme-muted-text hover:theme-text transition-colors",
|
|
325
326
|
title: t.regenerate,
|
|
326
327
|
children: /* @__PURE__ */ jsx(MdRefresh, { className: "w-4 h-4" })
|
|
327
328
|
}),
|
|
@@ -331,7 +332,7 @@ function ChatMessage({ message, allMessages, isStreaming = false, isEditing = fa
|
|
|
331
332
|
onNavigate: onNavigateBranch
|
|
332
333
|
}),
|
|
333
334
|
(timestamp || !isUser && (message.model || totalTokens)) && /* @__PURE__ */ jsxs("span", {
|
|
334
|
-
className: cx("chat-meta flex items-center gap-2 ml-2 text-
|
|
335
|
+
className: cx("chat-meta flex items-center gap-2 ml-2 text-xs tabular-nums theme-muted-text transition-opacity duration-150 select-none", showActions ? "opacity-100" : "opacity-0"),
|
|
335
336
|
children: [
|
|
336
337
|
timestamp && /* @__PURE__ */ jsx("span", {
|
|
337
338
|
title: timestamp.toLocaleString(DateTime.DATETIME_MED_WITH_SECONDS),
|
|
@@ -6,8 +6,13 @@ interface ChatMessageListProps {
|
|
|
6
6
|
streamingMessage?: Message | null;
|
|
7
7
|
queuedMessages?: Message[];
|
|
8
8
|
onRemoveQueued?: (id: string) => void;
|
|
9
|
-
/** Which queued messages can still be taken back; all of them when unset.
|
|
9
|
+
/** Which queued messages can still be taken back; all of them when unset.
|
|
10
|
+
* One that cannot has been handed to the running turn. */
|
|
10
11
|
canRemoveQueued?: (id: string) => boolean;
|
|
12
|
+
/** When a queued message reaches the model: once the running reply
|
|
13
|
+
* finishes (the chat provider's queue), or between the agent's steps for a
|
|
14
|
+
* host that steers the turn in progress. */
|
|
15
|
+
queueDelivery?: 'afterReply' | 'nextStep';
|
|
11
16
|
editingMessageId?: string | null | undefined;
|
|
12
17
|
showAuthor?: boolean;
|
|
13
18
|
currentUsername?: string;
|
|
@@ -33,5 +38,5 @@ interface ChatMessageListProps {
|
|
|
33
38
|
* for the first frame is not announced twice. */
|
|
34
39
|
workingIndicator?: boolean;
|
|
35
40
|
}
|
|
36
|
-
export default function ChatMessageList({ messages, allMessages, streamingMessage, queuedMessages, onRemoveQueued, canRemoveQueued, editingMessageId, showAuthor, currentUsername, onNavigateBranch, onEditMessage, onSaveEdit, onCancelEdit, onRegenerateMessage, onRetryMessage, onOpenReasoning, onApprovalAnswer, inputElement, isStreaming: isStreamingProp, flush, label, footer, workingIndicator, }: ChatMessageListProps): import("react").JSX.Element;
|
|
41
|
+
export default function ChatMessageList({ messages, allMessages, streamingMessage, queuedMessages, onRemoveQueued, canRemoveQueued, queueDelivery, editingMessageId, showAuthor, currentUsername, onNavigateBranch, onEditMessage, onSaveEdit, onCancelEdit, onRegenerateMessage, onRetryMessage, onOpenReasoning, onApprovalAnswer, inputElement, isStreaming: isStreamingProp, flush, label, footer, workingIndicator, }: ChatMessageListProps): import("react").JSX.Element;
|
|
37
42
|
export {};
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { FaCheck } from "../../icons/fa/FaCheck.js";
|
|
2
|
+
import { FiClock } from "../../icons/fi/FiClock.js";
|
|
3
3
|
import { durationToAbsHumanDuration } from "../../duration.js";
|
|
4
4
|
import { useChatConfig } from "../core/config.js";
|
|
5
5
|
import { useChat } from "../core/ChatProvider.js";
|
|
6
6
|
import Markdown from "../ui/Markdown.js";
|
|
7
|
+
import { ReasoningBody, ReasoningToggle } from "./Reasoning.js";
|
|
7
8
|
import AgentMessageParts from "./agent/AgentMessageParts.js";
|
|
8
9
|
import ChatMessage_default from "./ChatMessage.js";
|
|
9
10
|
import cx from "classnames";
|
|
10
11
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
11
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
12
13
|
import { Duration } from "luxon";
|
|
13
14
|
//#region src/ai/components/ChatMessageList.tsx
|
|
14
|
-
function ChatMessageList({ messages, allMessages, streamingMessage, queuedMessages, onRemoveQueued, canRemoveQueued, editingMessageId, showAuthor, currentUsername, onNavigateBranch, onEditMessage, onSaveEdit, onCancelEdit, onRegenerateMessage, onRetryMessage, onOpenReasoning, onApprovalAnswer, inputElement, isStreaming: isStreamingProp, flush, label, footer, workingIndicator = true }) {
|
|
15
|
+
function ChatMessageList({ messages, allMessages, streamingMessage, queuedMessages, onRemoveQueued, canRemoveQueued, queueDelivery = "afterReply", editingMessageId, showAuthor, currentUsername, onNavigateBranch, onEditMessage, onSaveEdit, onCancelEdit, onRegenerateMessage, onRetryMessage, onOpenReasoning, onApprovalAnswer, inputElement, isStreaming: isStreamingProp, flush, label, footer, workingIndicator = true }) {
|
|
15
16
|
const { adapter, isStreaming: providerStreaming, isThinking, streamingReasoning, streamingParts, thinkingStartTime } = useChat();
|
|
16
17
|
const isStreaming = isStreamingProp ?? providerStreaming;
|
|
17
18
|
const { strings } = useChatConfig();
|
|
@@ -39,6 +40,7 @@ function ChatMessageList({ messages, allMessages, streamingMessage, queuedMessag
|
|
|
39
40
|
const streamingMessageParts = streamingMessage?.parts;
|
|
40
41
|
const [elapsedTime, setElapsedTime] = useState(0);
|
|
41
42
|
const [finalThinkingDuration, setFinalThinkingDuration] = useState(null);
|
|
43
|
+
const [reasoningOpen, setReasoningOpen] = useState(false);
|
|
42
44
|
const wasThinkingRef = useRef(false);
|
|
43
45
|
useEffect(() => {
|
|
44
46
|
if (isThinking && !wasThinkingRef.current) {
|
|
@@ -53,6 +55,7 @@ function ChatMessageList({ messages, allMessages, streamingMessage, queuedMessag
|
|
|
53
55
|
if (!isStreaming) {
|
|
54
56
|
wasThinkingRef.current = false;
|
|
55
57
|
setFinalThinkingDuration(null);
|
|
58
|
+
setReasoningOpen(false);
|
|
56
59
|
}
|
|
57
60
|
}, [isStreaming]);
|
|
58
61
|
useEffect(() => {
|
|
@@ -143,7 +146,7 @@ function ChatMessageList({ messages, allMessages, streamingMessage, queuedMessag
|
|
|
143
146
|
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
144
147
|
}, [messageCount, isStreaming]);
|
|
145
148
|
if (messages.length === 0 && !streamingMessage && !hasQueuedMessages) return /* @__PURE__ */ jsxs("div", {
|
|
146
|
-
className: "flex-1 overflow-y-auto flex flex-col bg-(--theme-panel-bg)",
|
|
149
|
+
className: "chat-thread flex-1 overflow-y-auto flex flex-col bg-(--theme-panel-bg)",
|
|
147
150
|
children: [
|
|
148
151
|
/* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
149
152
|
footer && /* @__PURE__ */ jsx("div", {
|
|
@@ -163,10 +166,10 @@ function ChatMessageList({ messages, allMessages, streamingMessage, queuedMessag
|
|
|
163
166
|
"aria-relevant": "additions",
|
|
164
167
|
"aria-label": label
|
|
165
168
|
} : {},
|
|
166
|
-
className: "flex-1 overflow-y-auto theme-scrollbar flex flex-col bg-(--theme-panel-bg)",
|
|
169
|
+
className: "chat-thread flex-1 overflow-y-auto theme-scrollbar flex flex-col bg-(--theme-panel-bg)",
|
|
167
170
|
children: [
|
|
168
171
|
/* @__PURE__ */ jsxs("div", {
|
|
169
|
-
className: "w-full max-w-[50rem] mx-auto pt-
|
|
172
|
+
className: "w-full max-w-[50rem] mx-auto pt-4 pb-6 flex-1 px-4",
|
|
170
173
|
children: [
|
|
171
174
|
messages.map((message) => {
|
|
172
175
|
if (message.pseudo) return null;
|
|
@@ -196,37 +199,31 @@ function ChatMessageList({ messages, allMessages, streamingMessage, queuedMessag
|
|
|
196
199
|
}),
|
|
197
200
|
isStreaming && streamingReasoning && /* @__PURE__ */ jsx("div", {
|
|
198
201
|
className: "animate-chat-enter",
|
|
199
|
-
children: /* @__PURE__ */
|
|
200
|
-
className: "
|
|
201
|
-
children: /* @__PURE__ */
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
202
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
203
|
+
className: cx("pt-3", !flush && "px-4"),
|
|
204
|
+
children: [/* @__PURE__ */ jsx(ReasoningToggle, {
|
|
205
|
+
open: reasoningOpen,
|
|
206
|
+
shimmer: isThinking,
|
|
207
|
+
onToggle: () => onOpenReasoning ? onOpenReasoning(streamingReasoning, finalThinkingDuration) : setReasoningOpen((o) => !o),
|
|
208
|
+
children: isThinking ? elapsedTime > 0 ? `${tThinking.label} (${elapsedTime}s)` : tThinking.ellipsis : tThinking.thoughtFor(finalThinkingDuration ? durationToAbsHumanDuration(Duration.fromMillis(finalThinkingDuration)) : `${elapsedTime}s`)
|
|
209
|
+
}), reasoningOpen ? /* @__PURE__ */ jsx(ReasoningBody, { children: /* @__PURE__ */ jsx(Markdown, {
|
|
210
|
+
isStreaming: isThinking,
|
|
211
|
+
children: streamingReasoning
|
|
212
|
+
}) }) : isThinking && /* @__PURE__ */ jsx(ReasoningBody, {
|
|
213
|
+
className: "chat-reasoning-tail",
|
|
214
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
215
|
+
ref: reasoningScrollRef,
|
|
216
|
+
className: "max-h-20 overflow-y-hidden",
|
|
217
|
+
children: /* @__PURE__ */ jsx(Markdown, {
|
|
218
|
+
isStreaming: true,
|
|
219
|
+
children: streamingReasoning
|
|
213
220
|
})
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
children: [/* @__PURE__ */ jsx("div", { className: "absolute inset-x-0 top-0 h-3 bg-gradient-to-b from-(--theme-app-bg) to-transparent pointer-events-none z-[1]" }), /* @__PURE__ */ jsx("div", {
|
|
217
|
-
ref: reasoningScrollRef,
|
|
218
|
-
className: "text-sm theme-muted-text max-h-20 overflow-y-hidden pt-1",
|
|
219
|
-
children: /* @__PURE__ */ jsx(Markdown, {
|
|
220
|
-
isStreaming: true,
|
|
221
|
-
children: streamingReasoning
|
|
222
|
-
})
|
|
223
|
-
})]
|
|
224
|
-
})]
|
|
225
|
-
})
|
|
221
|
+
})
|
|
222
|
+
})]
|
|
226
223
|
})
|
|
227
224
|
}, "thinking"),
|
|
228
225
|
isStreaming && streamingParts.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
229
|
-
className: "animate-chat-enter py-1 px-4",
|
|
226
|
+
className: cx("animate-chat-enter py-1", !flush && "px-4"),
|
|
230
227
|
children: /* @__PURE__ */ jsx(AgentMessageParts, {
|
|
231
228
|
message: {
|
|
232
229
|
id: "streaming-parts",
|
|
@@ -239,9 +236,9 @@ function ChatMessageList({ messages, allMessages, streamingMessage, queuedMessag
|
|
|
239
236
|
showWorkingIndicator && /* @__PURE__ */ jsx("div", {
|
|
240
237
|
className: "animate-chat-enter",
|
|
241
238
|
children: /* @__PURE__ */ jsx("div", {
|
|
242
|
-
className: "py-3 px-4",
|
|
239
|
+
className: cx("py-3", !flush && "px-4"),
|
|
243
240
|
children: /* @__PURE__ */ jsx("div", {
|
|
244
|
-
className: "animate-shimmer text-shimmer",
|
|
241
|
+
className: "animate-shimmer text-shimmer text-sm",
|
|
245
242
|
children: tThinking.ellipsis
|
|
246
243
|
})
|
|
247
244
|
})
|
|
@@ -258,23 +255,37 @@ function ChatMessageList({ messages, allMessages, streamingMessage, queuedMessag
|
|
|
258
255
|
onApprovalAnswer
|
|
259
256
|
})
|
|
260
257
|
}, "streaming"),
|
|
261
|
-
queuedMessages?.map((msg) =>
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
258
|
+
queuedMessages?.map((msg) => {
|
|
259
|
+
const removable = canRemoveQueued?.(msg.id) ?? true;
|
|
260
|
+
const status = !removable ? strings.queue.handedOver : queueDelivery === "nextStep" ? strings.queue.waitingNextStep : strings.queue.waiting;
|
|
261
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
262
|
+
className: cx("animate-chat-enter", removable && "chat-queued"),
|
|
263
|
+
children: [/* @__PURE__ */ jsx(ChatMessage_default, {
|
|
264
|
+
message: msg,
|
|
265
|
+
allMessages,
|
|
266
|
+
flush,
|
|
267
|
+
attachmentDownloadUrl,
|
|
268
|
+
onOpenAttachment
|
|
269
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
270
|
+
"data-testid": "queued-status",
|
|
271
|
+
className: cx("-mt-4 mb-2 flex items-center justify-end gap-2 text-xs theme-muted-text", !flush && "px-4"),
|
|
272
|
+
children: [
|
|
273
|
+
removable ? /* @__PURE__ */ jsx(FiClock, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(FaCheck, { className: "h-3 w-3" }),
|
|
274
|
+
/* @__PURE__ */ jsx("span", { children: status }),
|
|
275
|
+
onRemoveQueued && removable && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
276
|
+
"aria-hidden": "true",
|
|
277
|
+
children: "·"
|
|
278
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
279
|
+
type: "button",
|
|
280
|
+
"aria-label": strings.queue.remove,
|
|
281
|
+
onClick: () => onRemoveQueued(msg.id),
|
|
282
|
+
className: "rounded font-medium underline-offset-2 hover:theme-text hover:underline",
|
|
283
|
+
children: strings.queue.removeShort
|
|
284
|
+
})] })
|
|
285
|
+
]
|
|
286
|
+
})]
|
|
287
|
+
}, msg.id);
|
|
288
|
+
}),
|
|
278
289
|
footer,
|
|
279
290
|
/* @__PURE__ */ jsx("div", {
|
|
280
291
|
ref: bottomRef,
|
|
@@ -287,7 +298,7 @@ function ChatMessageList({ messages, allMessages, streamingMessage, queuedMessag
|
|
|
287
298
|
children: /* @__PURE__ */ jsx("button", {
|
|
288
299
|
type: "button",
|
|
289
300
|
onClick: jumpToLatest,
|
|
290
|
-
className: "-translate-y-full rounded-full
|
|
301
|
+
className: "chat-composer -translate-y-full rounded-full bg-(--theme-panel-bg) px-3 py-1.5 text-xs font-medium theme-text transition-colors hover:bg-(--theme-muted-panel-bg)",
|
|
291
302
|
children: strings.chrome.jumpToLatest
|
|
292
303
|
})
|
|
293
304
|
}),
|
|
@@ -238,11 +238,11 @@ function ChatSidebar({ actions = true }) {
|
|
|
238
238
|
className: cx("absolute inset-y-0 right-0 z-10 w-1 cursor-col-resize touch-none", resizing ? "bg-(--theme-element)" : "hover:bg-(--theme-border)")
|
|
239
239
|
}),
|
|
240
240
|
actions && /* @__PURE__ */ jsxs("div", {
|
|
241
|
-
className: "flex flex-col gap-
|
|
241
|
+
className: "flex flex-col gap-0.5 px-2 pt-2 pb-1",
|
|
242
242
|
children: [/* @__PURE__ */ jsxs("button", {
|
|
243
243
|
type: "button",
|
|
244
244
|
onClick: handleNewChat,
|
|
245
|
-
className:
|
|
245
|
+
className: "flex items-center h-9 px-2 gap-3 rounded-lg font-medium transition-colors hover:chat-tint",
|
|
246
246
|
title: tSidebar.newChat,
|
|
247
247
|
children: [/* @__PURE__ */ jsx(TbEdit, { className: cx("flex-shrink-0", "w-4 h-4") }), /* @__PURE__ */ jsx("span", {
|
|
248
248
|
className: cx("text-sm transition-opacity duration-200", sidebarCollapsed ? "opacity-0 w-0 overflow-hidden" : "opacity-100"),
|
|
@@ -250,7 +250,7 @@ function ChatSidebar({ actions = true }) {
|
|
|
250
250
|
})]
|
|
251
251
|
}), attachmentsAvailable && /* @__PURE__ */ jsxs(LinkComponent, {
|
|
252
252
|
target: { kind: "attachments" },
|
|
253
|
-
className: "flex items-center h-9 px-2 gap-3 rounded-lg hover:
|
|
253
|
+
className: "flex items-center h-9 px-2 gap-3 rounded-lg transition-colors hover:chat-tint",
|
|
254
254
|
title: tSidebar.attachments,
|
|
255
255
|
children: [/* @__PURE__ */ jsx(FaRegFileImage, { className: "flex-shrink-0 w-4 h-4" }), /* @__PURE__ */ jsx("span", {
|
|
256
256
|
className: cx("text-sm transition-opacity duration-200", sidebarCollapsed ? "opacity-0 w-0 overflow-hidden" : "opacity-100"),
|
|
@@ -259,7 +259,7 @@ function ChatSidebar({ actions = true }) {
|
|
|
259
259
|
})]
|
|
260
260
|
}),
|
|
261
261
|
actions && !sidebarCollapsed && conversations.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
262
|
-
className: "px-2 pb-
|
|
262
|
+
className: "px-2 pb-2",
|
|
263
263
|
children: /* @__PURE__ */ jsxs("div", {
|
|
264
264
|
className: "relative",
|
|
265
265
|
children: [/* @__PURE__ */ jsx(FaSearch, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 theme-muted-text pointer-events-none" }), /* @__PURE__ */ jsx("input", {
|
|
@@ -275,7 +275,7 @@ function ChatSidebar({ actions = true }) {
|
|
|
275
275
|
},
|
|
276
276
|
placeholder: strings.chrome.searchPlaceholder,
|
|
277
277
|
"aria-label": strings.chrome.searchLabel,
|
|
278
|
-
className: "w-full h-
|
|
278
|
+
className: "w-full h-9 pl-8 pr-2 text-sm rounded-lg border border-transparent chat-tint theme-text placeholder:theme-muted-text transition-colors focus:outline-none focus:border-(--theme-border) focus:bg-(--theme-panel-bg)"
|
|
279
279
|
})]
|
|
280
280
|
})
|
|
281
281
|
}),
|
|
@@ -320,7 +320,7 @@ function ChatSidebar({ actions = true }) {
|
|
|
320
320
|
kind: "external",
|
|
321
321
|
href: extraLinks.manageProviders
|
|
322
322
|
},
|
|
323
|
-
className: "flex min-w-0 items-center gap-2 rounded-md px-2 py-1.5 text-xs theme-muted-text hover:theme-
|
|
323
|
+
className: "flex min-w-0 items-center gap-2 rounded-md px-2 py-1.5 text-xs theme-muted-text transition-colors hover:chat-tint hover:theme-text",
|
|
324
324
|
title: tSidebar.manageProviders,
|
|
325
325
|
children: [/* @__PURE__ */ jsx(IoSettingsOutline, { className: "h-3.5 w-3.5 shrink-0" }), /* @__PURE__ */ jsx("span", {
|
|
326
326
|
className: "truncate",
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import { IoMdClose } from "../../icons/io/IoMdClose.js";
|
|
2
|
-
import { TbBrain } from "../../icons/tb/TbBrain.js";
|
|
3
1
|
import { useChatConfig } from "../core/config.js";
|
|
4
2
|
import { useChat } from "../core/ChatProvider.js";
|
|
5
|
-
import Markdown from "../ui/Markdown.js";
|
|
6
3
|
import BlockedGroupBanner from "./BlockedGroupBanner.js";
|
|
7
4
|
import useDragDrop from "../core/useDragDrop.js";
|
|
8
5
|
import { ComposerControls, ConnectToolsLink } from "./ComposerChrome.js";
|
|
@@ -11,25 +8,12 @@ import ChatInput from "./ChatInput.js";
|
|
|
11
8
|
import ProviderIssueBanner from "./ProviderIssueBanner.js";
|
|
12
9
|
import { ShareButton } from "./ShareDialog.js";
|
|
13
10
|
import ChatMessageList from "./ChatMessageList.js";
|
|
14
|
-
import cx from "classnames";
|
|
15
11
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
16
12
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
17
13
|
//#region src/ai/components/ChatThread.tsx
|
|
18
14
|
function ChatThread({ conversationId }) {
|
|
19
15
|
const chatInputRef = useRef(null);
|
|
20
16
|
const [editingState, setEditingState] = useState(null);
|
|
21
|
-
const [reasoningDrawerOpen, setReasoningDrawerOpen] = useState(false);
|
|
22
|
-
const [selectedReasoning, setSelectedReasoning] = useState(null);
|
|
23
|
-
const handleOpenReasoning = useCallback((reasoning, duration) => {
|
|
24
|
-
if (reasoningDrawerOpen && selectedReasoning?.content === reasoning) setReasoningDrawerOpen(false);
|
|
25
|
-
else {
|
|
26
|
-
setSelectedReasoning({
|
|
27
|
-
content: reasoning,
|
|
28
|
-
duration
|
|
29
|
-
});
|
|
30
|
-
setReasoningDrawerOpen(true);
|
|
31
|
-
}
|
|
32
|
-
}, [reasoningDrawerOpen, selectedReasoning?.content]);
|
|
33
17
|
const { adapter, currentUser, currentConversation, loadConversation, loadProviders, sendMessage, retryMessage, isLoadingConversation, isStreaming, streamingContent, navigateToBranch, queuedMessages, removeQueuedMessage } = useChat();
|
|
34
18
|
const { slots, strings } = useChatConfig();
|
|
35
19
|
const requestedConversationIdRef = useRef(null);
|
|
@@ -111,9 +95,9 @@ function ChatThread({ conversationId }) {
|
|
|
111
95
|
settingsRight: /* @__PURE__ */ jsxs(Fragment$1, { children: [slots.composerUsage, /* @__PURE__ */ jsx(ComposerControls, {})] })
|
|
112
96
|
})]
|
|
113
97
|
});
|
|
114
|
-
return /* @__PURE__ */
|
|
98
|
+
return /* @__PURE__ */ jsx("div", {
|
|
115
99
|
className: "flex h-full relative bg-(--theme-panel-bg)",
|
|
116
|
-
children:
|
|
100
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
117
101
|
className: "flex-1 flex flex-col min-w-0",
|
|
118
102
|
...attachmentsAvailable ? dragHandlers : {},
|
|
119
103
|
children: [
|
|
@@ -135,7 +119,6 @@ function ChatThread({ conversationId }) {
|
|
|
135
119
|
onRetryMessage: currentConversation?.canCollaborate !== false ? handleRetryMessage : void 0,
|
|
136
120
|
onSaveEdit: handleSaveEdit,
|
|
137
121
|
onCancelEdit: handleCancelEdit,
|
|
138
|
-
onOpenReasoning: handleOpenReasoning,
|
|
139
122
|
inputElement: /* @__PURE__ */ jsx(ChatInput, {
|
|
140
123
|
ref: chatInputRef,
|
|
141
124
|
onSend: handleSendMessage,
|
|
@@ -148,50 +131,7 @@ function ChatThread({ conversationId }) {
|
|
|
148
131
|
})
|
|
149
132
|
})
|
|
150
133
|
]
|
|
151
|
-
})
|
|
152
|
-
className: cx("flex-shrink-0 border-l theme-border flex flex-col h-full transition-[width] duration-300 ease-in-out overflow-hidden bg-(--theme-panel-bg)", reasoningDrawerOpen ? "w-[400px]" : "w-0 border-l-0"),
|
|
153
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
154
|
-
className: "w-[400px] h-full flex flex-col",
|
|
155
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
156
|
-
className: "flex items-center justify-between p-3 border-b theme-border",
|
|
157
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
158
|
-
className: "flex items-center gap-2",
|
|
159
|
-
children: [
|
|
160
|
-
/* @__PURE__ */ jsx(TbBrain, { className: "w-5 h-5 theme-muted-text" }),
|
|
161
|
-
/* @__PURE__ */ jsx("span", {
|
|
162
|
-
className: "font-medium",
|
|
163
|
-
children: strings.thinking.activity
|
|
164
|
-
}),
|
|
165
|
-
selectedReasoning?.duration && /* @__PURE__ */ jsxs("span", {
|
|
166
|
-
className: "text-sm theme-muted-text",
|
|
167
|
-
children: [
|
|
168
|
-
"· ",
|
|
169
|
-
Math.round(selectedReasoning.duration / 1e3),
|
|
170
|
-
"s"
|
|
171
|
-
]
|
|
172
|
-
})
|
|
173
|
-
]
|
|
174
|
-
}), /* @__PURE__ */ jsx("button", {
|
|
175
|
-
type: "button",
|
|
176
|
-
onClick: () => setReasoningDrawerOpen(false),
|
|
177
|
-
className: "p-1 rounded hover:theme-hover transition-colors",
|
|
178
|
-
children: /* @__PURE__ */ jsx(IoMdClose, { className: "w-5 h-5 theme-muted-text" })
|
|
179
|
-
})]
|
|
180
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
181
|
-
className: "flex-1 overflow-y-auto p-4",
|
|
182
|
-
children: selectedReasoning?.content && /* @__PURE__ */ jsxs("div", {
|
|
183
|
-
className: "space-y-4",
|
|
184
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
185
|
-
className: "flex items-center gap-2 text-sm font-medium theme-text",
|
|
186
|
-
children: [/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-blue-500" }), /* @__PURE__ */ jsx("span", { children: strings.thinking.label })]
|
|
187
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
188
|
-
className: "text-sm theme-muted-text leading-relaxed pl-4 border-l-2 theme-border",
|
|
189
|
-
children: /* @__PURE__ */ jsx(Markdown, { children: selectedReasoning.content })
|
|
190
|
-
})]
|
|
191
|
-
})
|
|
192
|
-
})]
|
|
193
|
-
})
|
|
194
|
-
})]
|
|
134
|
+
})
|
|
195
135
|
});
|
|
196
136
|
}
|
|
197
137
|
//#endregion
|
|
@@ -129,8 +129,8 @@ function ModelSelector({ targetSession, variant = "default" }) {
|
|
|
129
129
|
const targetSessionDisplayName = targetSession?.split(":").pop();
|
|
130
130
|
const targetSessionFound = targetSession && models.length > 0 ? models.some((m) => m.id.startsWith(`session:${targetSession}/`)) : false;
|
|
131
131
|
const targetSessionStatus = targetSession ? isLoadingModels ? "connecting" : hasLoadedModels && !targetSessionFound ? "offline" : null : null;
|
|
132
|
-
const boxClass = bare ? "flex items-center gap-1 rounded-
|
|
133
|
-
const textClass = bare ? "text-[
|
|
132
|
+
const boxClass = bare ? "flex items-center gap-1.5 rounded-full h-8 px-2.5 max-w-[280px]" : "flex items-center gap-2 px-3 h-8 rounded-lg min-w-[180px] max-w-[280px]";
|
|
133
|
+
const textClass = bare ? "text-[13px]" : "text-sm";
|
|
134
134
|
if (isLoadingModels && models.length === 0 && !targetSession) return /* @__PURE__ */ jsx("div", {
|
|
135
135
|
ref: containerRef,
|
|
136
136
|
className: "relative",
|
|
@@ -156,7 +156,7 @@ function ModelSelector({ targetSession, variant = "default" }) {
|
|
|
156
156
|
/* @__PURE__ */ jsxs("button", {
|
|
157
157
|
type: "button",
|
|
158
158
|
onClick: () => refreshModels(),
|
|
159
|
-
className:
|
|
159
|
+
className: "flex items-center gap-1 text-xs text-blue-600 hover:underline ml-1",
|
|
160
160
|
children: [/* @__PURE__ */ jsx(FaUndoAlt, { className: "h-2.5 w-2.5" }), "Retry"]
|
|
161
161
|
})
|
|
162
162
|
]
|
|
@@ -182,7 +182,7 @@ function ModelSelector({ targetSession, variant = "default" }) {
|
|
|
182
182
|
type: "button",
|
|
183
183
|
onClick: () => setIsOpen(!isOpen),
|
|
184
184
|
disabled: models.length === 0 && !targetSessionStatus && unreachableSessions.length === 0,
|
|
185
|
-
className: cx(boxClass, "transition-all cursor-pointer bg-transparent", "focus:outline-none", "disabled:opacity-50 disabled:cursor-not-allowed", bare ? "hover:
|
|
185
|
+
className: cx(boxClass, "transition-all cursor-pointer bg-transparent", "focus:outline-none", "disabled:opacity-50 disabled:cursor-not-allowed", bare ? "hover:chat-tint" : "border theme-border hover:theme-hover"),
|
|
186
186
|
children: [
|
|
187
187
|
!bare && /* @__PURE__ */ jsx(FaRobot, { className: cx("h-4 w-4 flex-shrink-0", targetSessionStatus === "connecting" ? "text-amber-500 animate-pulse" : "text-purple-500") }),
|
|
188
188
|
/* @__PURE__ */ jsx("div", {
|
|
@@ -192,12 +192,12 @@ function ModelSelector({ targetSession, variant = "default" }) {
|
|
|
192
192
|
children: [
|
|
193
193
|
/* @__PURE__ */ jsx("span", {
|
|
194
194
|
title: selectedModelName,
|
|
195
|
-
className: cx("truncate", bare ? "text-[
|
|
195
|
+
className: cx("truncate", bare ? "text-[13px] font-medium theme-text" : "text-sm font-medium text-(--theme-panel)"),
|
|
196
196
|
children: selectedModelName
|
|
197
197
|
}),
|
|
198
198
|
selectedProviderName && /* @__PURE__ */ jsx("span", {
|
|
199
199
|
title: selectedProviderName,
|
|
200
|
-
className:
|
|
200
|
+
className: "theme-muted-text truncate flex-shrink-0 max-w-[45%] text-xs",
|
|
201
201
|
children: selectedProviderName
|
|
202
202
|
}),
|
|
203
203
|
selectedModelIssue && /* @__PURE__ */ jsx(FaExclamationTriangle, {
|
|
@@ -206,7 +206,7 @@ function ModelSelector({ targetSession, variant = "default" }) {
|
|
|
206
206
|
})
|
|
207
207
|
]
|
|
208
208
|
}) : /* @__PURE__ */ jsx("span", {
|
|
209
|
-
className: cx(
|
|
209
|
+
className: cx(textClass, targetSessionStatus === "connecting" ? "text-amber-600" : "theme-muted-text"),
|
|
210
210
|
children: triggerText
|
|
211
211
|
})
|
|
212
212
|
}),
|
|
@@ -8,7 +8,9 @@ export declare function PasteFileCard({ id, lines, bytes, saving, error, onRemov
|
|
|
8
8
|
error?: string | undefined;
|
|
9
9
|
onRemove?: (() => void) | undefined;
|
|
10
10
|
}): import("react").JSX.Element;
|
|
11
|
-
/** A user message as typed: an inline paste as
|
|
11
|
+
/** A user message as typed: an inline paste as a code block in the language
|
|
12
|
+
* it reads as, one sent as its file as a card. The blank lines the composer puts around a
|
|
13
|
+
* paste would stack on the block's own spacing, so they are dropped. */
|
|
12
14
|
export declare function UserMessageText({ content, pastes, }: {
|
|
13
15
|
content: string;
|
|
14
16
|
pastes?: MessagePaste[] | null | undefined;
|