@moldable-ai/ui 0.2.7 → 0.2.9
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/components/app-error-boundary.d.ts +26 -0
- package/dist/components/app-error-boundary.d.ts.map +1 -0
- package/dist/components/app-error-boundary.js +87 -0
- package/dist/components/chat/chat-input.d.ts +9 -1
- package/dist/components/chat/chat-input.d.ts.map +1 -1
- package/dist/components/chat/chat-input.js +13 -4
- package/dist/components/chat/chat-message.d.ts +6 -0
- package/dist/components/chat/chat-message.d.ts.map +1 -1
- package/dist/components/chat/chat-message.js +576 -157
- package/dist/components/chat/chat-messages.d.ts +2 -1
- package/dist/components/chat/chat-messages.d.ts.map +1 -1
- package/dist/components/chat/chat-messages.js +3 -3
- package/dist/components/chat/chat-panel.d.ts +25 -3
- package/dist/components/chat/chat-panel.d.ts.map +1 -1
- package/dist/components/chat/chat-panel.js +173 -49
- package/dist/components/chat/index.d.ts +3 -3
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +1 -1
- package/dist/components/chat/model-selector.d.ts.map +1 -1
- package/dist/components/chat/model-selector.js +1 -1
- package/dist/components/chat/tool-handlers.d.ts.map +1 -1
- package/dist/components/chat/tool-handlers.js +89 -1
- package/dist/components/markdown.d.ts.map +1 -1
- package/dist/components/markdown.js +18 -4
- package/dist/components/rich-media-player.d.ts +8 -0
- package/dist/components/rich-media-player.d.ts.map +1 -0
- package/dist/components/rich-media-player.js +99 -0
- package/dist/components/ui/command.d.ts +2 -1
- package/dist/components/ui/command.d.ts.map +1 -1
- package/dist/components/ui/command.js +2 -2
- package/dist/components/ui/resizable.d.ts +4 -1
- package/dist/components/ui/resizable.d.ts.map +1 -1
- package/dist/components/ui/resizable.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/lib/commands.d.ts +8 -0
- package/dist/lib/commands.d.ts.map +1 -1
- package/dist/lib/commands.js +18 -0
- package/package.json +17 -14
- package/src/styles/index.css +25 -0
- package/dist/lib/commands.test.d.ts +0 -2
- package/dist/lib/commands.test.d.ts.map +0 -1
- package/dist/lib/commands.test.js +0 -111
|
@@ -17,6 +17,7 @@ interface MessagesProps {
|
|
|
17
17
|
restoringMessageId?: string | null;
|
|
18
18
|
/** Callback when restore is requested for a message */
|
|
19
19
|
onRestoreCheckpoint?: (messageId: string) => void;
|
|
20
|
+
isCompacting?: boolean;
|
|
20
21
|
}
|
|
21
22
|
export declare function computeIsThinking(messages: ChatMessage[], status: ChatStatus): boolean;
|
|
22
23
|
interface MessageRowProps {
|
|
@@ -29,7 +30,7 @@ interface MessageRowProps {
|
|
|
29
30
|
}
|
|
30
31
|
declare function PureMessageRow({ message, isLast, isStreaming, checkpoint, restoringMessageId, onRestoreCheckpoint, }: MessageRowProps): import("react/jsx-runtime").JSX.Element;
|
|
31
32
|
export declare const MessageRow: import("react").MemoExoticComponent<typeof PureMessageRow>;
|
|
32
|
-
declare function PureMessages({ messages, status, checkpoints, restoringMessageId, onRestoreCheckpoint, }: MessagesProps): import("react/jsx-runtime").JSX.Element;
|
|
33
|
+
declare function PureMessages({ messages, status, checkpoints, restoringMessageId, onRestoreCheckpoint, isCompacting, }: MessagesProps): import("react/jsx-runtime").JSX.Element;
|
|
33
34
|
export declare const Messages: import("react").MemoExoticComponent<typeof PureMessages>;
|
|
34
35
|
export {};
|
|
35
36
|
//# sourceMappingURL=chat-messages.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-messages.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-messages.tsx"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"chat-messages.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-messages.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,WAAW,EAIjB,MAAM,gBAAgB,CAAA;AAGvB,KAAK,UAAU,GAAG,OAAO,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,CAAA;AAE/D;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,UAAU,aAAa;IACrB,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,MAAM,EAAE,UAAU,CAAA;IAClB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;IAC5C,0CAA0C;IAC1C,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,uDAAuD;IACvD,mBAAmB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IACjD,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,UAAU,WAwB5E;AAED,UAAU,eAAe;IACvB,OAAO,EAAE,WAAW,CAAA;IACpB,MAAM,EAAE,OAAO,CAAA;IACf,WAAW,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAC9B,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,mBAAmB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;CAClD;AAED,iBAAS,cAAc,CAAC,EACtB,OAAO,EACP,MAAM,EACN,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,mBAAmB,GACpB,EAAE,eAAe,2CAsBjB;AAED,eAAO,MAAM,UAAU,4DAAuB,CAAA;AAE9C,iBAAS,YAAY,CAAC,EACpB,QAAQ,EACR,MAAM,EACN,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACnB,YAAoB,GACrB,EAAE,aAAa,2CA0Bf;AAED,eAAO,MAAM,QAAQ,0DAAqB,CAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { memo } from 'react';
|
|
3
|
-
import { Message, ThinkingMessage } from './chat-message';
|
|
3
|
+
import { CompactingMessage, Message, ThinkingMessage, } from './chat-message';
|
|
4
4
|
import { CheckpointBadge } from './checkpoint-badge';
|
|
5
5
|
export function computeIsThinking(messages, status) {
|
|
6
6
|
const lastMessage = messages[messages.length - 1];
|
|
@@ -30,12 +30,12 @@ function PureMessageRow({ message, isLast, isStreaming, checkpoint, restoringMes
|
|
|
30
30
|
return (_jsxs("div", { className: "group/message-row relative", children: [_jsx(Message, { message: message, isLast: isLast, isStreaming: isStreaming }), showCheckpointBadge && (_jsx("div", { className: "absolute bottom-1 right-4 opacity-0 transition-opacity group-hover/message-row:opacity-100", children: _jsx(CheckpointBadge, { messageId: message.id, fileCount: checkpoint.fileCount, totalBytes: checkpoint.totalBytes, isRestoring: restoringMessageId === message.id, onRestore: () => onRestoreCheckpoint(message.id) }) }))] }));
|
|
31
31
|
}
|
|
32
32
|
export const MessageRow = memo(PureMessageRow);
|
|
33
|
-
function PureMessages({ messages, status, checkpoints, restoringMessageId, onRestoreCheckpoint, }) {
|
|
33
|
+
function PureMessages({ messages, status, checkpoints, restoringMessageId, onRestoreCheckpoint, isCompacting = false, }) {
|
|
34
34
|
const isThinking = computeIsThinking(messages, status);
|
|
35
35
|
const isStreaming = status === 'streaming' || status === 'submitted';
|
|
36
36
|
return (_jsxs(_Fragment, { children: [messages.map((message, index) => {
|
|
37
37
|
const checkpoint = checkpoints?.get(message.id);
|
|
38
38
|
return (_jsx(MessageRow, { message: message, isLast: index === messages.length - 1, isStreaming: isStreaming, checkpoint: checkpoint, restoringMessageId: restoringMessageId, onRestoreCheckpoint: onRestoreCheckpoint }, message.id));
|
|
39
|
-
}), isThinking && _jsx(ThinkingMessage, {})] }));
|
|
39
|
+
}), isThinking && _jsx(ThinkingMessage, {}), isCompacting && _jsx(CompactingMessage, {})] }));
|
|
40
40
|
}
|
|
41
41
|
export const Messages = memo(PureMessages);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ChangeEvent, type FormEvent, type ReactNode } from 'react';
|
|
2
|
-
import { type ChatImageAttachment, type ChatSubmitMode } from './chat-input';
|
|
2
|
+
import { type ChatImageAttachment, type ChatSlashCommandOption, type ChatSubmitMode } from './chat-input';
|
|
3
3
|
import { type ChatMessage } from './chat-message';
|
|
4
4
|
import { type MessageCheckpoint } from './chat-messages';
|
|
5
5
|
import { type ConversationMeta } from './conversation-history';
|
|
@@ -21,9 +21,15 @@ export interface QueuedChatMessage {
|
|
|
21
21
|
id: string;
|
|
22
22
|
preview: string;
|
|
23
23
|
attachmentCount: number;
|
|
24
|
-
status?: 'queued' | 'failed';
|
|
24
|
+
status?: 'queued' | 'failed' | 'steering';
|
|
25
25
|
error?: string;
|
|
26
26
|
}
|
|
27
|
+
export interface ChatContextUsage {
|
|
28
|
+
usedTokens: number;
|
|
29
|
+
maxTokens: number;
|
|
30
|
+
isEstimate?: boolean;
|
|
31
|
+
compactsAutomatically?: boolean;
|
|
32
|
+
}
|
|
27
33
|
export interface ChatPanelProps {
|
|
28
34
|
/** Chat messages */
|
|
29
35
|
messages: ChatMessage[];
|
|
@@ -57,6 +63,8 @@ export interface ChatPanelProps {
|
|
|
57
63
|
conversations?: ConversationMeta[];
|
|
58
64
|
/** Current conversation ID */
|
|
59
65
|
currentConversationId?: string | null;
|
|
66
|
+
/** Incremented when a conversation is explicitly loaded from history */
|
|
67
|
+
scrollToBottomSignal?: number;
|
|
60
68
|
/** Handle conversation selection */
|
|
61
69
|
onSelectConversation?: (id: string) => void;
|
|
62
70
|
/** Handle conversation deletion */
|
|
@@ -111,10 +119,24 @@ export interface ChatPanelProps {
|
|
|
111
119
|
onRetryQueuedMessage?: (id: string) => void;
|
|
112
120
|
/** Send a queued follow-up into the active response immediately */
|
|
113
121
|
onSteerQueuedMessage?: (id: string) => void;
|
|
122
|
+
/** Reorder queued follow-ups */
|
|
123
|
+
onReorderQueuedMessages?: (activeId: string, overId: string) => void;
|
|
124
|
+
/** Edit a queued follow-up in the composer */
|
|
125
|
+
onEditQueuedMessage?: (id: string) => void;
|
|
126
|
+
/** Slash commands matching the current input */
|
|
127
|
+
slashCommands?: ChatSlashCommandOption[];
|
|
128
|
+
/** Fill the composer with a slash command */
|
|
129
|
+
onSlashCommandSelect?: (command: ChatSlashCommandOption) => void;
|
|
130
|
+
/** Whether a manual compaction command is running */
|
|
131
|
+
isCompacting?: boolean;
|
|
132
|
+
/** Display-message index where the currently compacted context resumes */
|
|
133
|
+
compactedHistoryStartIndex?: number | null;
|
|
134
|
+
/** Estimated chat context usage for the selected model */
|
|
135
|
+
contextUsage?: ChatContextUsage;
|
|
114
136
|
}
|
|
115
137
|
/**
|
|
116
138
|
* Floating chat panel with model selector
|
|
117
139
|
*/
|
|
118
|
-
export declare function ChatPanel({ messages, status, isTurnInFlight, input, onInputChange, onSubmit, onStop, onNewChat, models, selectedModel, onModelChange, reasoningEffortOptions, selectedReasoningEffort, onReasoningEffortChange, conversations, currentConversationId, onSelectConversation, onDeleteConversation, placeholder, welcomeMessage, isExpanded, onExpandedChange, isMinimized, onMinimizedChange, className, error, missingApiKey, onAddApiKey, toolProgress, onApprovalResponse, isEditingApp, onEditingAppChange, showEditingAppToggle, checkpoints, restoringMessageId, onRestoreCheckpoint, imageAttachments, onRemoveImageAttachment, isImageDropActive, queuedMessages, onRemoveQueuedMessage, onRetryQueuedMessage, onSteerQueuedMessage, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
140
|
+
export declare function ChatPanel({ messages, status, isTurnInFlight, input, onInputChange, onSubmit, onStop, onNewChat, models, selectedModel, onModelChange, reasoningEffortOptions, selectedReasoningEffort, onReasoningEffortChange, conversations, currentConversationId, scrollToBottomSignal, onSelectConversation, onDeleteConversation, placeholder, welcomeMessage, isExpanded, onExpandedChange, isMinimized, onMinimizedChange, className, error, missingApiKey, onAddApiKey, toolProgress, onApprovalResponse, isEditingApp, onEditingAppChange, showEditingAppToggle, checkpoints, restoringMessageId, onRestoreCheckpoint, imageAttachments, onRemoveImageAttachment, isImageDropActive, queuedMessages, onRemoveQueuedMessage, onRetryQueuedMessage, onSteerQueuedMessage, onReorderQueuedMessages, onEditQueuedMessage, slashCommands, onSlashCommandSelect, isCompacting, compactedHistoryStartIndex, contextUsage, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
119
141
|
export {};
|
|
120
142
|
//# sourceMappingURL=chat-panel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-panel.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-panel.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"chat-panel.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-panel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,36 +1,102 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx,
|
|
3
|
-
import { AlertCircle, ChevronDown, Code2, CornerDownRight, Image as ImageIcon, ListPlus, MessageCircle,
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { AlertCircle, ArrowDown, ChevronDown, ClipboardList, Code2, CornerDownRight, GripVertical, Image as ImageIcon, ListPlus, MessageCircle, Pencil, Trash2, } from 'lucide-react';
|
|
4
4
|
import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
5
5
|
import { cn } from '../../lib/utils';
|
|
6
6
|
import { Button } from '../ui/button';
|
|
7
7
|
import { ScrollArea } from '../ui/scroll-area';
|
|
8
8
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '../ui/tooltip';
|
|
9
9
|
import { ChatInput, } from './chat-input';
|
|
10
|
-
import { ThinkingMessage } from './chat-message';
|
|
10
|
+
import { CompactingMessage, ThinkingMessage, } from './chat-message';
|
|
11
11
|
import { MessageRow, computeIsThinking, } from './chat-messages';
|
|
12
12
|
import { ConversationHistory, } from './conversation-history';
|
|
13
13
|
import { ModelSelector } from './model-selector';
|
|
14
14
|
import { ReasoningEffortSelector, } from './reasoning-effort-selector';
|
|
15
15
|
import { ToolApprovalProvider, } from './tool-approval-context';
|
|
16
16
|
import { ToolProgressProvider } from './tool-progress-context';
|
|
17
|
+
import { DndContext, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors, } from '@dnd-kit/core';
|
|
18
|
+
import { SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy, } from '@dnd-kit/sortable';
|
|
19
|
+
import { CSS } from '@dnd-kit/utilities';
|
|
17
20
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
18
21
|
import { Virtualizer } from 'virtua';
|
|
22
|
+
function clampPercent(value) {
|
|
23
|
+
if (!Number.isFinite(value))
|
|
24
|
+
return 0;
|
|
25
|
+
return Math.max(0, Math.min(100, value));
|
|
26
|
+
}
|
|
27
|
+
function formatTokenCount(tokens) {
|
|
28
|
+
if (tokens < 1_000)
|
|
29
|
+
return `${tokens}`;
|
|
30
|
+
if (tokens < 1_000_000)
|
|
31
|
+
return `${Math.round(tokens / 1_000)}k`;
|
|
32
|
+
const millions = tokens / 1_000_000;
|
|
33
|
+
return `${Number.isInteger(millions) ? millions.toFixed(0) : millions.toFixed(2)}M`;
|
|
34
|
+
}
|
|
35
|
+
function ContextWindowIndicator({ usage }) {
|
|
36
|
+
if (!usage || usage.maxTokens <= 0)
|
|
37
|
+
return null;
|
|
38
|
+
const usedPercent = clampPercent(Math.round((usage.usedTokens / usage.maxTokens) * 100));
|
|
39
|
+
const ringColor = usedPercent >= 90
|
|
40
|
+
? 'var(--destructive)'
|
|
41
|
+
: usedPercent >= 75
|
|
42
|
+
? 'var(--warning)'
|
|
43
|
+
: 'var(--muted-foreground)';
|
|
44
|
+
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "text-muted-foreground hover:text-foreground focus-visible:ring-ring flex size-8 cursor-pointer items-center justify-center rounded-full bg-transparent p-0 focus-visible:outline-none focus-visible:ring-2", "aria-label": `Context window ${usedPercent}% full`, children: _jsx("span", { className: "relative size-4 rounded-full", style: {
|
|
45
|
+
background: `conic-gradient(${ringColor} ${usedPercent}%, color-mix(in oklch, var(--muted-foreground) 22%, transparent) 0)`,
|
|
46
|
+
}, children: _jsx("span", { className: "bg-background absolute inset-[3px] rounded-full" }) }) }) }), _jsxs(TooltipContent, { align: "end", side: "top", sideOffset: 8, className: "w-56 px-3 py-2.5 text-center shadow-lg", children: [_jsx("div", { className: "opacity-70", children: "Context window:" }), _jsxs("div", { className: "mt-1 text-sm font-medium", children: [usedPercent, "% full"] }), _jsxs("div", { className: "mt-1 text-xs", children: [formatTokenCount(usage.usedTokens), " /", ' ', formatTokenCount(usage.maxTokens), " tokens used"] }), usage.compactsAutomatically && (_jsx("div", { className: "mt-2.5 text-xs font-medium leading-4", children: "Moldable automatically compacts its context" }))] })] }));
|
|
47
|
+
}
|
|
48
|
+
function ContextCompactedDivider() {
|
|
49
|
+
return (_jsx("div", { "data-testid": "context-compacted-divider", className: "text-muted-foreground/80 w-full px-4 py-2", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "bg-border h-px min-w-8 flex-1" }), _jsxs("div", { className: "flex select-none items-center gap-2 whitespace-nowrap text-[13px] font-medium leading-5", children: [_jsx(ClipboardList, { className: "size-4" }), _jsx("span", { children: "Context automatically compacted" })] }), _jsx("div", { className: "bg-border h-px min-w-8 flex-1" })] }) }));
|
|
50
|
+
}
|
|
51
|
+
function QueuedMessageRow({ message, isResponding, onRetryQueuedMessage, onSteerQueuedMessage, onRemoveQueuedMessage, onEditQueuedMessage, }) {
|
|
52
|
+
const isSteering = message.status === 'steering';
|
|
53
|
+
const { attributes, listeners, setNodeRef, transform, transition, isDragging, } = useSortable({ id: message.id, disabled: isSteering });
|
|
54
|
+
const style = {
|
|
55
|
+
transform: CSS.Transform.toString(transform),
|
|
56
|
+
transition,
|
|
57
|
+
};
|
|
58
|
+
return (_jsxs("div", { ref: setNodeRef, style: style, className: cn('bg-muted/60 text-foreground flex min-h-9 items-center gap-2 rounded-lg px-2 py-1.5 text-xs', isDragging && 'relative z-20 opacity-80 shadow-lg'), children: [_jsx("button", { type: "button", className: "text-muted-foreground hover:text-foreground flex size-6 shrink-0 cursor-grab items-center justify-center rounded-md transition-colors active:cursor-grabbing disabled:cursor-not-allowed disabled:opacity-40", "aria-label": "Reorder queued follow-up", disabled: isSteering, ...attributes, ...listeners, children: _jsx(GripVertical, { className: "size-3.5" }) }), _jsxs("span", { className: "min-w-0 flex-1 truncate", children: [message.preview, message.attachmentCount > 0 &&
|
|
59
|
+
` (${message.attachmentCount} image${message.attachmentCount === 1 ? '' : 's'})`] }), message.error && (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: (event) => event.stopPropagation(), className: "text-destructive hover:bg-destructive/10 flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md transition-colors", "aria-label": "View queued follow-up error", children: _jsx(AlertCircle, { className: "size-3.5" }) }) }), _jsx(TooltipContent, { side: "top", className: "max-w-80", children: _jsx("p", { className: "break-words text-xs", children: message.error }) })] }) })), message.status === 'failed' && onRetryQueuedMessage && (_jsxs("button", { type: "button", onClick: (event) => {
|
|
60
|
+
event.stopPropagation();
|
|
61
|
+
onRetryQueuedMessage(message.id);
|
|
62
|
+
}, className: "text-muted-foreground hover:bg-accent hover:text-foreground flex h-7 shrink-0 cursor-pointer items-center gap-1.5 rounded-md px-2 text-xs transition-colors", "aria-label": "Retry queued follow-up", children: [_jsx(CornerDownRight, { className: "size-3.5" }), _jsx("span", { children: "Retry" })] })), isSteering && (_jsx("span", { className: "text-muted-foreground shrink-0 px-2 text-xs", children: "Steering" })), !isSteering && isResponding && onSteerQueuedMessage && (_jsxs("button", { type: "button", onClick: (event) => {
|
|
63
|
+
event.stopPropagation();
|
|
64
|
+
onSteerQueuedMessage(message.id);
|
|
65
|
+
}, className: "text-muted-foreground hover:bg-accent hover:text-foreground flex h-7 shrink-0 cursor-pointer items-center gap-1.5 rounded-md px-2 text-xs transition-colors", "aria-label": "Steer active response with queued follow-up", children: [_jsx(CornerDownRight, { className: "size-3.5" }), _jsx("span", { children: "Steer" })] })), !isSteering && onEditQueuedMessage && (_jsx("button", { type: "button", onClick: (event) => {
|
|
66
|
+
event.stopPropagation();
|
|
67
|
+
onEditQueuedMessage(message.id);
|
|
68
|
+
}, className: "text-muted-foreground hover:bg-accent hover:text-foreground flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md transition-colors", "aria-label": "Edit queued follow-up", children: _jsx(Pencil, { className: "size-3.5" }) })), !isSteering && onRemoveQueuedMessage && (_jsx("button", { type: "button", onClick: (event) => {
|
|
69
|
+
event.stopPropagation();
|
|
70
|
+
onRemoveQueuedMessage(message.id);
|
|
71
|
+
}, className: "text-muted-foreground hover:bg-destructive/10 hover:text-destructive flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md transition-colors", "aria-label": "Remove queued follow-up", children: _jsx(Trash2, { className: "size-3.5" }) }))] }));
|
|
72
|
+
}
|
|
19
73
|
/**
|
|
20
74
|
* Floating chat panel with model selector
|
|
21
75
|
*/
|
|
22
|
-
export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChange, onSubmit, onStop, onNewChat, models, selectedModel, onModelChange, reasoningEffortOptions, selectedReasoningEffort, onReasoningEffortChange, conversations, currentConversationId, onSelectConversation, onDeleteConversation, placeholder = 'Ask anything...', welcomeMessage, isExpanded, onExpandedChange, isMinimized = false, onMinimizedChange, className, error, missingApiKey, onAddApiKey, toolProgress = {}, onApprovalResponse, isEditingApp = true, onEditingAppChange, showEditingAppToggle = false, checkpoints, restoringMessageId, onRestoreCheckpoint, imageAttachments = [], onRemoveImageAttachment, isImageDropActive = false, queuedMessages = [], onRemoveQueuedMessage, onRetryQueuedMessage, onSteerQueuedMessage, }) {
|
|
76
|
+
export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChange, onSubmit, onStop, onNewChat, models, selectedModel, onModelChange, reasoningEffortOptions, selectedReasoningEffort, onReasoningEffortChange, conversations, currentConversationId, scrollToBottomSignal = 0, onSelectConversation, onDeleteConversation, placeholder = 'Ask anything...', welcomeMessage, isExpanded, onExpandedChange, isMinimized = false, onMinimizedChange, className, error, missingApiKey, onAddApiKey, toolProgress = {}, onApprovalResponse, isEditingApp = true, onEditingAppChange, showEditingAppToggle = false, checkpoints, restoringMessageId, onRestoreCheckpoint, imageAttachments = [], onRemoveImageAttachment, isImageDropActive = false, queuedMessages = [], onRemoveQueuedMessage, onRetryQueuedMessage, onSteerQueuedMessage, onReorderQueuedMessages, onEditQueuedMessage, slashCommands, onSlashCommandSelect, isCompacting = false, compactedHistoryStartIndex, contextUsage, }) {
|
|
23
77
|
const inputRef = useRef(null);
|
|
24
78
|
const scrollAreaRef = useRef(null);
|
|
25
79
|
const viewportRef = useRef(null);
|
|
26
80
|
const virtualizerRef = useRef(null);
|
|
27
81
|
const prevMessageCountRef = useRef(messages.length);
|
|
82
|
+
const prevConversationIdRef = useRef(currentConversationId ?? null);
|
|
83
|
+
const prevScrollToBottomSignalRef = useRef(scrollToBottomSignal);
|
|
84
|
+
const pendingConversationScrollIdRef = useRef(null);
|
|
85
|
+
const hasPendingScrollToBottomSignalRef = useRef(false);
|
|
28
86
|
const scrollRafRef = useRef(null);
|
|
29
87
|
const [viewportVersion, setViewportVersion] = useState(0);
|
|
30
88
|
const [isAtBottom, setIsAtBottom] = useState(true);
|
|
89
|
+
const queuedMessageIds = useMemo(() => queuedMessages.map((message) => message.id), [queuedMessages]);
|
|
90
|
+
const queueSensors = useSensors(useSensor(PointerSensor, {
|
|
91
|
+
activationConstraint: {
|
|
92
|
+
distance: 6,
|
|
93
|
+
},
|
|
94
|
+
}), useSensor(KeyboardSensor, {
|
|
95
|
+
coordinateGetter: sortableKeyboardCoordinates,
|
|
96
|
+
}));
|
|
31
97
|
const isResponding = status === 'streaming' || status === 'submitted';
|
|
32
98
|
const controlsDisabled = isTurnInFlight ?? isResponding;
|
|
33
|
-
const composerControls = (_jsxs(_Fragment, { children: [_jsx(ModelSelector, { models: models, selectedModel: selectedModel, onModelChange: onModelChange, disabled: controlsDisabled, dropdownAlign: "end", dropdownSide: "top" }), reasoningEffortOptions &&
|
|
99
|
+
const composerControls = (_jsxs(_Fragment, { children: [_jsx(ContextWindowIndicator, { usage: contextUsage }), _jsx(ModelSelector, { models: models, selectedModel: selectedModel, onModelChange: onModelChange, disabled: controlsDisabled, dropdownAlign: "end", dropdownSide: "top" }), reasoningEffortOptions &&
|
|
34
100
|
selectedReasoningEffort &&
|
|
35
101
|
onReasoningEffortChange && (_jsx(ReasoningEffortSelector, { options: reasoningEffortOptions, selectedEffort: selectedReasoningEffort, onEffortChange: onReasoningEffortChange, disabled: controlsDisabled, showLabel: true, dropdownAlign: "end", dropdownSide: "top" }))] }));
|
|
36
102
|
// Capture the Radix viewport element so virtua can use it as the scroll container
|
|
@@ -71,15 +137,23 @@ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChan
|
|
|
71
137
|
return () => clearTimeout(timer);
|
|
72
138
|
}
|
|
73
139
|
}, [isExpanded]);
|
|
140
|
+
const showMissingApiKeyPrompt = missingApiKey ||
|
|
141
|
+
(error &&
|
|
142
|
+
(error.message?.includes('API_KEY not configured') ||
|
|
143
|
+
error.message?.includes('credential required in Vault') ||
|
|
144
|
+
error.message?.includes('credential not configured in Vault')));
|
|
145
|
+
const submitDisabled = Boolean(showMissingApiKeyPrompt);
|
|
74
146
|
const handleSubmit = useCallback((e, mode) => {
|
|
75
147
|
e?.preventDefault();
|
|
148
|
+
if (submitDisabled) {
|
|
149
|
+
if (!isExpanded) {
|
|
150
|
+
onExpandedChange(true);
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
76
154
|
if (!input.trim() && imageAttachments.length === 0)
|
|
77
155
|
return;
|
|
78
156
|
const submitMode = mode ?? (isResponding ? 'steer' : 'send');
|
|
79
|
-
// Steer interrupts the current response and sends the new instruction now.
|
|
80
|
-
if (submitMode === 'steer' && isResponding && onStop) {
|
|
81
|
-
onStop();
|
|
82
|
-
}
|
|
83
157
|
if (!isExpanded) {
|
|
84
158
|
onExpandedChange(true);
|
|
85
159
|
}
|
|
@@ -93,8 +167,21 @@ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChan
|
|
|
93
167
|
isExpanded,
|
|
94
168
|
onExpandedChange,
|
|
95
169
|
onSubmit,
|
|
96
|
-
|
|
170
|
+
submitDisabled,
|
|
97
171
|
]);
|
|
172
|
+
const handleQueuedDragEnd = useCallback((event) => {
|
|
173
|
+
const { active, over } = event;
|
|
174
|
+
if (!over || active.id === over.id)
|
|
175
|
+
return;
|
|
176
|
+
onReorderQueuedMessages?.(String(active.id), String(over.id));
|
|
177
|
+
}, [onReorderQueuedMessages]);
|
|
178
|
+
const handleEditQueuedMessage = useCallback((id) => {
|
|
179
|
+
onEditQueuedMessage?.(id);
|
|
180
|
+
if (!isExpanded) {
|
|
181
|
+
onExpandedChange(true);
|
|
182
|
+
}
|
|
183
|
+
window.setTimeout(() => inputRef.current?.focus(), 0);
|
|
184
|
+
}, [isExpanded, onEditQueuedMessage, onExpandedChange]);
|
|
98
185
|
const handleNewChat = useCallback(() => {
|
|
99
186
|
onNewChat?.();
|
|
100
187
|
inputRef.current?.focus();
|
|
@@ -136,11 +223,6 @@ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChan
|
|
|
136
223
|
}, [isExpanded, onExpandedChange, onMinimizedChange]);
|
|
137
224
|
const isStreaming = status === 'streaming' || status === 'submitted';
|
|
138
225
|
const isThinking = useMemo(() => computeIsThinking(messages, status), [messages, status]);
|
|
139
|
-
const showMissingApiKeyPrompt = missingApiKey ||
|
|
140
|
-
(error &&
|
|
141
|
-
(error.message?.includes('API_KEY not configured') ||
|
|
142
|
-
error.message?.includes('credential required in Vault') ||
|
|
143
|
-
error.message?.includes('credential not configured in Vault')));
|
|
144
226
|
const showErrorMessage = Boolean(error) && status === 'error' && !showMissingApiKeyPrompt;
|
|
145
227
|
const lastMessageId = messages[messages.length - 1]?.id;
|
|
146
228
|
const listItems = useMemo(() => {
|
|
@@ -148,12 +230,24 @@ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChan
|
|
|
148
230
|
if (messages.length === 0 && welcomeMessage) {
|
|
149
231
|
items.push({ key: 'welcome', kind: 'welcome' });
|
|
150
232
|
}
|
|
151
|
-
|
|
233
|
+
const shouldShowCompactedDivider = typeof compactedHistoryStartIndex === 'number' &&
|
|
234
|
+
compactedHistoryStartIndex >= 0 &&
|
|
235
|
+
compactedHistoryStartIndex < messages.length;
|
|
236
|
+
for (const [index, message] of messages.entries()) {
|
|
237
|
+
if (shouldShowCompactedDivider && index === compactedHistoryStartIndex) {
|
|
238
|
+
items.push({
|
|
239
|
+
key: `context-compacted-${compactedHistoryStartIndex}`,
|
|
240
|
+
kind: 'context-compacted-divider',
|
|
241
|
+
});
|
|
242
|
+
}
|
|
152
243
|
items.push({ key: message.id, kind: 'message', message });
|
|
153
244
|
}
|
|
154
245
|
if (isThinking) {
|
|
155
246
|
items.push({ key: 'thinking', kind: 'thinking' });
|
|
156
247
|
}
|
|
248
|
+
if (isCompacting) {
|
|
249
|
+
items.push({ key: 'compacting', kind: 'compacting' });
|
|
250
|
+
}
|
|
157
251
|
if (showMissingApiKeyPrompt) {
|
|
158
252
|
items.push({ key: 'missing-api-key', kind: 'missing-api-key' });
|
|
159
253
|
}
|
|
@@ -163,6 +257,8 @@ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChan
|
|
|
163
257
|
return items;
|
|
164
258
|
}, [
|
|
165
259
|
isThinking,
|
|
260
|
+
isCompacting,
|
|
261
|
+
compactedHistoryStartIndex,
|
|
166
262
|
messages,
|
|
167
263
|
showErrorMessage,
|
|
168
264
|
showMissingApiKeyPrompt,
|
|
@@ -172,12 +268,16 @@ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChan
|
|
|
172
268
|
switch (item.kind) {
|
|
173
269
|
case 'welcome':
|
|
174
270
|
return (_jsx("div", { className: "px-2 pb-4", children: messages.length === 0 && welcomeMessage && (_jsx("div", { className: "bg-muted/50 text-muted-foreground mx-2 rounded-2xl p-4 text-sm", children: welcomeMessage })) }));
|
|
271
|
+
case 'context-compacted-divider':
|
|
272
|
+
return (_jsx("div", { className: "px-2 pb-3", children: _jsx(ContextCompactedDivider, {}) }));
|
|
175
273
|
case 'message': {
|
|
176
274
|
const checkpoint = checkpoints?.get(item.message.id);
|
|
177
|
-
return (_jsx("div", { className: "px-2 pb-
|
|
275
|
+
return (_jsx("div", { className: "px-2 pb-3", children: _jsx(MessageRow, { message: item.message, isLast: item.message.id === lastMessageId, isStreaming: isStreaming, checkpoint: checkpoint, restoringMessageId: restoringMessageId, onRestoreCheckpoint: onRestoreCheckpoint }) }));
|
|
178
276
|
}
|
|
179
277
|
case 'thinking':
|
|
180
|
-
return (_jsx("div", { className: "px-2 pb-
|
|
278
|
+
return (_jsx("div", { className: "px-2 pb-3", children: _jsx(ThinkingMessage, {}) }));
|
|
279
|
+
case 'compacting':
|
|
280
|
+
return (_jsx("div", { className: "px-2 pb-3", children: _jsx(CompactingMessage, {}) }));
|
|
181
281
|
case 'missing-api-key':
|
|
182
282
|
return (_jsx("div", { className: "px-2 pb-4", children: _jsxs("div", { className: "border-primary/30 bg-primary/5 mx-2 flex flex-col items-center gap-3 rounded-lg border p-4 text-center", children: [_jsx("div", { className: "bg-primary/10 flex size-10 items-center justify-center rounded-full", children: _jsx(AlertCircle, { className: "text-primary size-5" }) }), _jsxs("div", { children: [_jsx("p", { className: "text-foreground font-medium", children: "API key required" }), _jsx("p", { className: "text-muted-foreground mt-1 text-sm", children: "Add an API key to Vault to start chatting with AI" })] }), onAddApiKey && (_jsx(Button, { onClick: onAddApiKey, size: "sm", className: "cursor-pointer", children: "Add API key" }))] }) }));
|
|
183
283
|
case 'error':
|
|
@@ -194,35 +294,71 @@ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChan
|
|
|
194
294
|
restoringMessageId,
|
|
195
295
|
welcomeMessage,
|
|
196
296
|
]);
|
|
197
|
-
|
|
198
|
-
useEffect(() => {
|
|
199
|
-
const previousMessageCount = prevMessageCountRef.current;
|
|
200
|
-
const hasNewMessage = messages.length > previousMessageCount;
|
|
201
|
-
prevMessageCountRef.current = messages.length;
|
|
202
|
-
if (!isExpanded || !isAtBottom) {
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
297
|
+
const scrollToBottom = useCallback((smooth = true) => {
|
|
205
298
|
const lastIndex = listItems.length - 1;
|
|
206
299
|
if (lastIndex < 0)
|
|
207
300
|
return;
|
|
208
301
|
if (scrollRafRef.current !== null) {
|
|
209
302
|
cancelAnimationFrame(scrollRafRef.current);
|
|
210
303
|
}
|
|
304
|
+
setIsAtBottom(true);
|
|
211
305
|
scrollRafRef.current = requestAnimationFrame(() => {
|
|
212
306
|
virtualizerRef.current?.scrollToIndex(lastIndex, {
|
|
213
307
|
align: 'end',
|
|
214
|
-
smooth
|
|
308
|
+
smooth,
|
|
215
309
|
});
|
|
216
310
|
scrollRafRef.current = null;
|
|
217
311
|
});
|
|
312
|
+
}, [listItems.length]);
|
|
313
|
+
// New conversations loaded from history should start at the latest message.
|
|
314
|
+
useEffect(() => {
|
|
315
|
+
const previousConversationId = prevConversationIdRef.current;
|
|
316
|
+
const nextConversationId = currentConversationId ?? null;
|
|
317
|
+
prevConversationIdRef.current = nextConversationId;
|
|
318
|
+
if (previousConversationId !== nextConversationId) {
|
|
319
|
+
pendingConversationScrollIdRef.current = nextConversationId;
|
|
320
|
+
setIsAtBottom(true);
|
|
321
|
+
}
|
|
322
|
+
if (!nextConversationId ||
|
|
323
|
+
pendingConversationScrollIdRef.current !== nextConversationId ||
|
|
324
|
+
!isExpanded ||
|
|
325
|
+
listItems.length === 0) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
scrollToBottom(false);
|
|
329
|
+
pendingConversationScrollIdRef.current = null;
|
|
330
|
+
}, [currentConversationId, isExpanded, listItems.length, scrollToBottom]);
|
|
331
|
+
useEffect(() => {
|
|
332
|
+
if (prevScrollToBottomSignalRef.current !== scrollToBottomSignal) {
|
|
333
|
+
prevScrollToBottomSignalRef.current = scrollToBottomSignal;
|
|
334
|
+
hasPendingScrollToBottomSignalRef.current = true;
|
|
335
|
+
setIsAtBottom(true);
|
|
336
|
+
}
|
|
337
|
+
if (!hasPendingScrollToBottomSignalRef.current ||
|
|
338
|
+
!isExpanded ||
|
|
339
|
+
listItems.length === 0) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
scrollToBottom(false);
|
|
343
|
+
hasPendingScrollToBottomSignalRef.current = false;
|
|
344
|
+
}, [isExpanded, listItems.length, scrollToBottom, scrollToBottomSignal]);
|
|
345
|
+
// Scroll to bottom on new messages if the user is already at bottom
|
|
346
|
+
useEffect(() => {
|
|
347
|
+
const previousMessageCount = prevMessageCountRef.current;
|
|
348
|
+
const hasNewMessage = messages.length > previousMessageCount;
|
|
349
|
+
prevMessageCountRef.current = messages.length;
|
|
350
|
+
if (!isExpanded || !isAtBottom) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
scrollToBottom(hasNewMessage);
|
|
218
354
|
return () => {
|
|
219
355
|
if (scrollRafRef.current !== null) {
|
|
220
356
|
cancelAnimationFrame(scrollRafRef.current);
|
|
221
357
|
scrollRafRef.current = null;
|
|
222
358
|
}
|
|
223
359
|
};
|
|
224
|
-
}, [isAtBottom, isExpanded,
|
|
225
|
-
return (_jsxs(_Fragment, { children: [_jsx(AnimatePresence, { children: isExpanded && !isMinimized && (_jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.2 }, className: "fixed inset-0 z-40 bg-black/20", onClick: () => onExpandedChange(false) })) }), _jsx(AnimatePresence, { children: isMinimized && (_jsx(motion.div, { initial: { y: 20, opacity: 0 }, animate: { y: 0, opacity: 1 }, exit: { y: 20, opacity: 0 }, transition: { type: 'tween', duration: 0.2, ease: 'easeOut' }, className: "fixed bottom-0 left-[calc(50%+var(--sidebar-width,0px)/2)] z-50 -translate-x-1/2", children: _jsxs("button", { onClick: handleFlapClick, className: cn('bg-background/95 hover:bg-background group flex cursor-pointer items-center gap-2 rounded-t-xl border border-b-0 px-4 py-2 shadow-lg backdrop-blur transition-colors', 'text-muted-foreground hover:text-foreground'), children: [_jsx(MessageCircle, { className: "size-4" }), _jsx("span", { className: "text-sm font-medium", children: "Chat" }), _jsx("span", { className: "bg-muted text-muted-foreground group-hover:bg-primary/10 group-hover:text-primary rounded px-1.5 py-0.5 text-xs transition-colors", children: "\u2318M" })] }) })) }), _jsx(motion.div, { initial: false, animate: {
|
|
360
|
+
}, [isAtBottom, isExpanded, messages.length, scrollToBottom]);
|
|
361
|
+
return (_jsxs(_Fragment, { children: [_jsx(AnimatePresence, { children: isExpanded && !isMinimized && (_jsx(motion.div, { "data-moldable-chat-backdrop": true, initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.2 }, className: "fixed inset-0 z-40 bg-black/20", onClick: () => onExpandedChange(false) })) }), _jsx(AnimatePresence, { children: isMinimized && (_jsx(motion.div, { "data-moldable-chat-panel": true, initial: { y: 20, opacity: 0 }, animate: { y: 0, opacity: 1 }, exit: { y: 20, opacity: 0 }, transition: { type: 'tween', duration: 0.2, ease: 'easeOut' }, className: "fixed bottom-0 left-[calc(50%+var(--sidebar-width,0px)/2)] z-50 -translate-x-1/2", children: _jsxs("button", { onClick: handleFlapClick, className: cn('bg-background/95 hover:bg-background group flex cursor-pointer items-center gap-2 rounded-t-xl border border-b-0 px-4 py-2 shadow-lg backdrop-blur transition-colors', 'text-muted-foreground hover:text-foreground'), children: [_jsx(MessageCircle, { className: "size-4" }), _jsx("span", { className: "text-sm font-medium", children: "Chat" }), _jsx("span", { className: "bg-muted text-muted-foreground group-hover:bg-primary/10 group-hover:text-primary rounded px-1.5 py-0.5 text-xs transition-colors", children: "\u2318M" })] }) })) }), _jsx(motion.div, { "data-moldable-chat-panel": true, initial: false, animate: {
|
|
226
362
|
width: isExpanded ? 'min(90vw, 640px)' : '400px',
|
|
227
363
|
y: isMinimized ? 'calc(100% + 24px)' : 0,
|
|
228
364
|
opacity: isMinimized ? 0 : 1,
|
|
@@ -248,24 +384,12 @@ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChan
|
|
|
248
384
|
repeat: Infinity,
|
|
249
385
|
repeatDelay: 5,
|
|
250
386
|
ease: [0.4, 0, 0.2, 1],
|
|
251
|
-
} })), _jsxs(motion.div, { initial: false, animate: {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
` (${message.attachmentCount} image${message.attachmentCount === 1 ? '' : 's'})`] }), message.status === 'failed' &&
|
|
260
|
-
message.error &&
|
|
261
|
-
onRetryQueuedMessage && (_jsxs("button", { type: "button", onClick: (event) => {
|
|
262
|
-
event.stopPropagation();
|
|
263
|
-
onRetryQueuedMessage(message.id);
|
|
264
|
-
}, className: "text-muted-foreground hover:bg-accent hover:text-foreground flex h-7 shrink-0 cursor-pointer items-center gap-1.5 rounded-md px-2 text-xs transition-colors", "aria-label": "Retry queued follow-up", children: [_jsx(CornerDownRight, { className: "size-3.5" }), _jsx("span", { children: "Retry" })] })), isResponding && onSteerQueuedMessage && (_jsxs("button", { type: "button", onClick: (event) => {
|
|
265
|
-
event.stopPropagation();
|
|
266
|
-
onSteerQueuedMessage(message.id);
|
|
267
|
-
}, className: "text-muted-foreground hover:bg-accent hover:text-foreground flex h-7 shrink-0 cursor-pointer items-center gap-1.5 rounded-md px-2 text-xs transition-colors", "aria-label": "Steer active response with queued follow-up", children: [_jsx(CornerDownRight, { className: "size-3.5" }), _jsx("span", { children: "Steer" })] })), onRemoveQueuedMessage && (_jsx("button", { type: "button", onClick: (event) => {
|
|
268
|
-
event.stopPropagation();
|
|
269
|
-
onRemoveQueuedMessage(message.id);
|
|
270
|
-
}, className: "text-muted-foreground hover:bg-accent hover:text-foreground flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md transition-colors", "aria-label": "Remove queued follow-up", children: _jsx(X, { className: "size-3.5" }) }))] }, message.id))) })] })), _jsx(ChatInput, { input: input, onInputChange: onInputChange, onSubmit: handleSubmit, isResponding: isResponding, placeholder: placeholder, inputRef: inputRef, onStop: onStop, compact: !isExpanded, imageAttachments: imageAttachments, onRemoveImageAttachment: onRemoveImageAttachment, footerStartControls: isExpanded ? composerStartControls : null, footerControls: isExpanded ? composerControls : null })] })] }) })] }));
|
|
387
|
+
} })), _jsxs("div", { className: "relative z-10 overflow-hidden rounded-[inherit]", children: [_jsxs(motion.div, { initial: false, animate: {
|
|
388
|
+
height: isExpanded ? 'min(60vh, 480px)' : '0px',
|
|
389
|
+
opacity: isExpanded ? 1 : 0,
|
|
390
|
+
}, transition: { type: 'tween', duration: 0.2, ease: 'easeOut' }, className: "flex flex-col overflow-hidden", style: { pointerEvents: isExpanded ? 'auto' : 'none' }, children: [_jsx("div", { className: "absolute right-3 top-3 z-20", children: _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: handleMinimize, className: "text-muted-foreground hover:text-foreground cursor-pointer", children: _jsx(ChevronDown, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: "Minimize" }) })] }) }) }), _jsxs("div", { className: "relative min-h-0 min-w-0 flex-1", children: [_jsx(ScrollArea, { ref: scrollAreaRef, className: "h-full px-2", children: _jsx("div", { className: "min-w-0 max-w-full overflow-hidden py-4 pr-10", children: _jsx(ToolProgressProvider, { value: toolProgress, children: _jsx(ToolApprovalProvider, { onApprovalResponse: onApprovalResponse ?? null, children: viewportVersion > 0 ? (_jsx(Virtualizer, { ref: virtualizerRef, data: listItems, scrollRef: viewportRef, bufferSize: 300, children: (item) => renderVirtualItem(item) }, viewportVersion)) : (listItems.map((item) => (_jsx("div", { children: renderVirtualItem(item) }, item.key)))) }) }) }) }), !isAtBottom && listItems.length > 0 && (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { type: "button", variant: "secondary", size: "icon-sm", onClick: () => scrollToBottom(true), className: "absolute bottom-3 right-4 z-20 size-8 cursor-pointer rounded-full shadow-lg", children: _jsx(ArrowDown, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "top", children: "Scroll to bottom" })] }) }))] })] }), _jsxs("div", { className: "relative z-10", onClick: () => {
|
|
391
|
+
if (!isExpanded) {
|
|
392
|
+
onExpandedChange(true);
|
|
393
|
+
}
|
|
394
|
+
}, children: [queuedMessages.length > 0 && (_jsxs("div", { className: "border-border/70 bg-background/95 border-t px-4 py-2", children: [_jsxs("div", { className: "text-muted-foreground mb-2 flex items-center gap-2 text-xs font-medium", children: [_jsx(ListPlus, { className: "size-3.5" }), _jsx("span", { children: "Queued follow-ups" })] }), _jsx(DndContext, { sensors: queueSensors, collisionDetection: closestCenter, onDragEnd: handleQueuedDragEnd, children: _jsx(SortableContext, { items: queuedMessageIds, strategy: verticalListSortingStrategy, children: _jsx("div", { className: "space-y-1.5", children: queuedMessages.map((message) => (_jsx(QueuedMessageRow, { message: message, isResponding: isResponding, onRetryQueuedMessage: onRetryQueuedMessage, onSteerQueuedMessage: onSteerQueuedMessage, onRemoveQueuedMessage: onRemoveQueuedMessage, onEditQueuedMessage: handleEditQueuedMessage }, message.id))) }) }) })] })), _jsx(ChatInput, { input: input, onInputChange: onInputChange, onSubmit: handleSubmit, isResponding: isResponding, placeholder: placeholder, inputRef: inputRef, onStop: onStop, compact: !isExpanded, imageAttachments: imageAttachments, onRemoveImageAttachment: onRemoveImageAttachment, footerStartControls: isExpanded ? composerStartControls : null, footerControls: isExpanded ? composerControls : null, submitDisabled: submitDisabled, slashCommands: slashCommands, onSlashCommandSelect: onSlashCommandSelect })] })] })] }) })] }));
|
|
271
395
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
export { ChatInput, type ChatImageAttachment, type ChatSubmitMode, } from './chat-input';
|
|
2
|
-
export { Message, ThinkingMessage, type ChatMessage, type ChatMessagePart, } from './chat-message';
|
|
1
|
+
export { ChatInput, type ChatImageAttachment, type ChatSlashCommandOption, type ChatSubmitMode, } from './chat-input';
|
|
2
|
+
export { CompactingMessage, Message, ThinkingMessage, type ChatMessage, type ChatMessagePart, } from './chat-message';
|
|
3
3
|
export { Messages, type MessageCheckpoint } from './chat-messages';
|
|
4
4
|
export { CheckpointBadge, type CheckpointBadgeProps } from './checkpoint-badge';
|
|
5
5
|
export { RestoreDialog, type CheckpointInfo, type RestoreDialogProps, } from './restore-dialog';
|
|
6
6
|
export { ModelSelector, type ModelOption } from './model-selector';
|
|
7
7
|
export { ReasoningEffortSelector, type ReasoningEffortOption, } from './reasoning-effort-selector';
|
|
8
8
|
export { ConversationHistory, type ConversationMeta, } from './conversation-history';
|
|
9
|
-
export { ChatPanel, type ChatPanelProps, type QueuedChatMessage, type ToolProgressData, } from './chat-panel';
|
|
9
|
+
export { ChatPanel, type ChatContextUsage, type ChatPanelProps, type QueuedChatMessage, type ToolProgressData, } from './chat-panel';
|
|
10
10
|
export { ThinkingTimeline, ThinkingTimelineMarker, type ThinkingTimelineItem, type ThinkingTimelineProps, } from './thinking-timeline';
|
|
11
11
|
export { getToolHandler, DEFAULT_TOOL_HANDLERS, type ToolHandler, } from './tool-handlers';
|
|
12
12
|
export { ToolProgressProvider, useToolProgress, useToolCallProgress, } from './tool-progress-context';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,KAAK,mBAAmB,EACxB,KAAK,cAAc,GACpB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,OAAO,EACP,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC/E,OAAO,EACL,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,kBAAkB,GACxB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAClE,OAAO,EACL,uBAAuB,EACvB,KAAK,qBAAqB,GAC3B,MAAM,6BAA6B,CAAA;AACpC,OAAO,EACL,mBAAmB,EACnB,KAAK,gBAAgB,GACtB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,SAAS,EACT,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,KAAK,WAAW,GACjB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,mBAAmB,GACpB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,KAAK,uBAAuB,GAC7B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,cAAc,GACpB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,iBAAiB,EACjB,OAAO,EACP,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC/E,OAAO,EACL,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,kBAAkB,GACxB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAClE,OAAO,EACL,uBAAuB,EACvB,KAAK,qBAAqB,GAC3B,MAAM,6BAA6B,CAAA;AACpC,OAAO,EACL,mBAAmB,EACnB,KAAK,gBAAgB,GACtB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,KAAK,WAAW,GACjB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,mBAAmB,GACpB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,KAAK,uBAAuB,GAC7B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { ChatInput, } from './chat-input';
|
|
2
|
-
export { Message, ThinkingMessage, } from './chat-message';
|
|
2
|
+
export { CompactingMessage, Message, ThinkingMessage, } from './chat-message';
|
|
3
3
|
export { Messages } from './chat-messages';
|
|
4
4
|
export { CheckpointBadge } from './checkpoint-badge';
|
|
5
5
|
export { RestoreDialog, } from './restore-dialog';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../../src/components/chat/model-selector.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAUtC,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,SAAS,CAAA;CAChB,CAAA;AAED,KAAK,kBAAkB,GAAG;IACxB,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAA;IAC1C,YAAY,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;CACnD,CAAA;AAED,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,aAAa,EACb,aAAa,EACb,QAAgB,EAChB,SAAS,EACT,aAAuB,EACvB,YAAuB,GACxB,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../../src/components/chat/model-selector.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAUtC,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,SAAS,CAAA;CAChB,CAAA;AAED,KAAK,kBAAkB,GAAG;IACxB,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAA;IAC1C,YAAY,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;CACnD,CAAA;AAED,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,aAAa,EACb,aAAa,EACb,QAAgB,EAChB,SAAS,EACT,aAAuB,EACvB,YAAuB,GACxB,EAAE,kBAAkB,2CAwCpB"}
|
|
@@ -5,5 +5,5 @@ import { Button } from '../ui/button';
|
|
|
5
5
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '../ui/dropdown-menu';
|
|
6
6
|
export function ModelSelector({ models, selectedModel, onModelChange, disabled = false, className, dropdownAlign = 'start', dropdownSide = 'bottom', }) {
|
|
7
7
|
const currentModel = models.find((m) => m.id === selectedModel) ?? models[0];
|
|
8
|
-
return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", disabled: disabled, className: cn('text-muted-foreground hover:text-foreground h-8 min-w-0 max-w-[180px] cursor-pointer gap-1
|
|
8
|
+
return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", disabled: disabled, className: cn('text-muted-foreground hover:text-foreground h-8 min-w-0 max-w-[180px] cursor-pointer gap-1 rounded-full px-2.5 text-xs font-medium', className), children: [_jsx("span", { className: "min-w-0 truncate", children: currentModel?.name }), _jsx(ChevronDown, { className: "size-3 opacity-50" })] }) }), _jsx(DropdownMenuContent, { align: dropdownAlign, side: dropdownSide, className: "min-w-[160px]", children: models.map((model) => (_jsxs(DropdownMenuItem, { onClick: () => onModelChange(model.id), className: "flex cursor-pointer items-center gap-2", children: [_jsx("span", { children: model.icon }), _jsx("span", { className: "flex-1", children: model.name }), model.id === selectedModel && (_jsx(Check, { className: "text-primary size-4" }))] }, model.id))) })] }));
|
|
9
9
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-handlers.d.ts","sourceRoot":"","sources":["../../../src/components/chat/tool-handlers.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tool-handlers.d.ts","sourceRoot":"","sources":["../../../src/components/chat/tool-handlers.tsx"],"names":[],"mappings":"AAwBA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAEhD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAW5D,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,yBAAyB,CAAA;AAEtE;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,SAAS,GAAG,UAAU,CAAA;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,OAAO,CAAA;CACd;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IAExB,YAAY,EAAE,MAAM,CAAA;IAEpB,MAAM,CAAC,EAAE,sBAAsB,CAAA;IAE/B,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,SAAS,CAAA;IAGhE,aAAa,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,KAAK,SAAS,CAAA;IAG7C,eAAe,CAAC,EAAE,CAChB,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,qBAAqB,KAC5B,SAAS,CAAA;IAGd,cAAc,CAAC,EAAE,CACf,QAAQ,EAAE,gBAAgB,EAC1B,SAAS,EAAE,uBAAuB,KAC/B,SAAS,CAAA;CACf,CAAA;AAoSD;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAqvE7D,CAAA;AAoBD;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAmC5D"}
|