@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.
Files changed (44) hide show
  1. package/dist/components/app-error-boundary.d.ts +26 -0
  2. package/dist/components/app-error-boundary.d.ts.map +1 -0
  3. package/dist/components/app-error-boundary.js +87 -0
  4. package/dist/components/chat/chat-input.d.ts +9 -1
  5. package/dist/components/chat/chat-input.d.ts.map +1 -1
  6. package/dist/components/chat/chat-input.js +13 -4
  7. package/dist/components/chat/chat-message.d.ts +6 -0
  8. package/dist/components/chat/chat-message.d.ts.map +1 -1
  9. package/dist/components/chat/chat-message.js +576 -157
  10. package/dist/components/chat/chat-messages.d.ts +2 -1
  11. package/dist/components/chat/chat-messages.d.ts.map +1 -1
  12. package/dist/components/chat/chat-messages.js +3 -3
  13. package/dist/components/chat/chat-panel.d.ts +25 -3
  14. package/dist/components/chat/chat-panel.d.ts.map +1 -1
  15. package/dist/components/chat/chat-panel.js +173 -49
  16. package/dist/components/chat/index.d.ts +3 -3
  17. package/dist/components/chat/index.d.ts.map +1 -1
  18. package/dist/components/chat/index.js +1 -1
  19. package/dist/components/chat/model-selector.d.ts.map +1 -1
  20. package/dist/components/chat/model-selector.js +1 -1
  21. package/dist/components/chat/tool-handlers.d.ts.map +1 -1
  22. package/dist/components/chat/tool-handlers.js +89 -1
  23. package/dist/components/markdown.d.ts.map +1 -1
  24. package/dist/components/markdown.js +18 -4
  25. package/dist/components/rich-media-player.d.ts +8 -0
  26. package/dist/components/rich-media-player.d.ts.map +1 -0
  27. package/dist/components/rich-media-player.js +99 -0
  28. package/dist/components/ui/command.d.ts +2 -1
  29. package/dist/components/ui/command.d.ts.map +1 -1
  30. package/dist/components/ui/command.js +2 -2
  31. package/dist/components/ui/resizable.d.ts +4 -1
  32. package/dist/components/ui/resizable.d.ts.map +1 -1
  33. package/dist/components/ui/resizable.js +1 -1
  34. package/dist/index.d.ts +3 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +4 -1
  37. package/dist/lib/commands.d.ts +8 -0
  38. package/dist/lib/commands.d.ts.map +1 -1
  39. package/dist/lib/commands.js +18 -0
  40. package/package.json +17 -14
  41. package/src/styles/index.css +25 -0
  42. package/dist/lib/commands.test.d.ts +0 -2
  43. package/dist/lib/commands.test.d.ts.map +0 -1
  44. 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,EAAE,KAAK,WAAW,EAA4B,MAAM,gBAAgB,CAAA;AAG3E,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;CAClD;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,GACpB,EAAE,aAAa,2CAyBf;AAED,eAAO,MAAM,QAAQ,0DAAqB,CAAA"}
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":"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"}
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, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { AlertCircle, ChevronDown, Code2, CornerDownRight, Image as ImageIcon, ListPlus, MessageCircle, X, } from 'lucide-react';
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
- onStop,
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
- for (const message of messages) {
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-4", children: _jsx(MessageRow, { message: item.message, isLast: item.message.id === lastMessageId, isStreaming: isStreaming, checkpoint: checkpoint, restoringMessageId: restoringMessageId, onRestoreCheckpoint: onRestoreCheckpoint }) }));
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-4", children: _jsx(ThinkingMessage, {}) }));
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
- // Scroll to bottom on new messages if the user is already at bottom
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: hasNewMessage,
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, listItems.length, messages.length]);
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
- height: isExpanded ? 'min(60vh, 480px)' : '0px',
253
- opacity: isExpanded ? 1 : 0,
254
- }, transition: { type: 'tween', duration: 0.2, ease: 'easeOut' }, className: "relative z-10 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" }) })] }) }) }), _jsx(ScrollArea, { ref: scrollAreaRef, className: "min-h-0 min-w-0 flex-1 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)))) }) }) }) })] }), _jsxs("div", { className: "relative z-10", onClick: () => {
255
- if (!isExpanded) {
256
- onExpandedChange(true);
257
- }
258
- }, 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("div", { className: "space-y-1.5", children: queuedMessages.map((message) => (_jsxs("div", { className: "bg-muted/60 text-foreground flex min-h-9 items-center gap-2 rounded-lg px-2.5 py-1.5 text-xs", children: [_jsxs("span", { className: "min-w-0 flex-1 truncate", children: [message.preview, message.attachmentCount > 0 &&
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,2CAyCpB"}
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.5 rounded-full px-2.5 text-xs font-medium', className), children: [_jsx("span", { children: currentModel?.icon }), _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))) })] }));
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":"AAuBA,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,CAg/D7D,CAAA;AAoBD;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAmC5D"}
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"}