@moldable-ai/ui 0.2.3 → 0.2.6
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/chat/chat-input.js +1 -1
- package/dist/components/chat/chat-message.d.ts.map +1 -1
- package/dist/components/chat/chat-message.js +5 -4
- package/dist/components/chat/chat-messages.d.ts +26 -1
- package/dist/components/chat/chat-messages.d.ts.map +1 -1
- package/dist/components/chat/chat-messages.js +33 -22
- package/dist/components/chat/chat-panel.d.ts +15 -2
- package/dist/components/chat/chat-panel.d.ts.map +1 -1
- package/dist/components/chat/chat-panel.js +130 -21
- package/dist/components/chat/checkpoint-badge.d.ts +20 -0
- package/dist/components/chat/checkpoint-badge.d.ts.map +1 -0
- package/dist/components/chat/checkpoint-badge.js +16 -0
- package/dist/components/chat/index.d.ts +3 -1
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +2 -0
- package/dist/components/chat/restore-dialog.d.ts +33 -0
- package/dist/components/chat/restore-dialog.d.ts.map +1 -0
- package/dist/components/chat/restore-dialog.js +23 -0
- package/dist/components/chat/tool-handlers.d.ts.map +1 -1
- package/dist/components/chat/tool-handlers.js +138 -2
- package/dist/components/ui/alert-dialog.d.ts +8 -4
- package/dist/components/ui/alert-dialog.d.ts.map +1 -1
- package/dist/components/ui/alert-dialog.js +14 -11
- package/dist/components/ui/avatar.d.ts +7 -2
- package/dist/components/ui/avatar.d.ts.map +1 -1
- package/dist/components/ui/avatar.js +13 -4
- package/dist/components/ui/badge.d.ts +1 -1
- package/dist/components/ui/badge.d.ts.map +1 -1
- package/dist/components/ui/badge.js +9 -7
- package/dist/components/ui/button.d.ts +3 -4
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/button.js +6 -6
- package/dist/components/ui/card.d.ts +8 -7
- package/dist/components/ui/card.d.ts.map +1 -1
- package/dist/components/ui/card.js +22 -14
- package/dist/components/ui/checkbox.js +1 -1
- package/dist/components/ui/collapsible.d.ts +3 -3
- package/dist/components/ui/collapsible.d.ts.map +1 -1
- package/dist/components/ui/collapsible.js +11 -3
- package/dist/components/ui/combobox.d.ts +25 -0
- package/dist/components/ui/combobox.d.ts.map +1 -0
- package/dist/components/ui/combobox.js +58 -0
- package/dist/components/ui/command.d.ts +15 -15
- package/dist/components/ui/command.d.ts.map +1 -1
- package/dist/components/ui/command.js +29 -24
- package/dist/components/ui/dialog.d.ts +15 -17
- package/dist/components/ui/dialog.d.ts.map +1 -1
- package/dist/components/ui/dialog.js +33 -19
- package/dist/components/ui/dropdown-menu.js +6 -6
- package/dist/components/ui/input-group.d.ts +2 -2
- package/dist/components/ui/input.d.ts +1 -2
- package/dist/components/ui/input.d.ts.map +1 -1
- package/dist/components/ui/input.js +4 -5
- package/dist/components/ui/label.d.ts +1 -1
- package/dist/components/ui/label.d.ts.map +1 -1
- package/dist/components/ui/label.js +3 -3
- package/dist/components/ui/popover.d.ts +4 -1
- package/dist/components/ui/popover.d.ts.map +1 -1
- package/dist/components/ui/popover.js +10 -1
- package/dist/components/ui/scroll-area.d.ts +2 -2
- package/dist/components/ui/scroll-area.d.ts.map +1 -1
- package/dist/components/ui/scroll-area.js +9 -7
- package/dist/components/ui/select.d.ts +13 -11
- package/dist/components/ui/select.d.ts.map +1 -1
- package/dist/components/ui/select.js +35 -23
- package/dist/components/ui/sheet.d.ts +2 -1
- package/dist/components/ui/sheet.d.ts.map +1 -1
- package/dist/components/ui/sheet.js +2 -2
- package/dist/components/ui/switch.d.ts +4 -2
- package/dist/components/ui/switch.d.ts.map +1 -1
- package/dist/components/ui/switch.js +4 -4
- package/dist/components/ui/tabs.d.ts +7 -3
- package/dist/components/ui/tabs.d.ts.map +1 -1
- package/dist/components/ui/tabs.js +18 -6
- package/dist/components/ui/textarea.d.ts +1 -2
- package/dist/components/ui/textarea.d.ts.map +1 -1
- package/dist/components/ui/textarea.js +4 -5
- package/dist/components/ui/tooltip.js +1 -1
- package/dist/lib/commands.d.ts.map +1 -1
- package/package.json +6 -3
- package/src/styles/index.css +7 -0
|
@@ -34,5 +34,5 @@ export function ChatInput({ input, onInputChange, onSubmit, isResponding, placeh
|
|
|
34
34
|
}
|
|
35
35
|
// If type="submit", form handles submission
|
|
36
36
|
};
|
|
37
|
-
return (_jsx("div", { className: cn('isolate w-full p-2'), "data-chat-input-wrapper": true, children: _jsx("div", { className: "mx-auto max-w-5xl", children: _jsx("form", { onSubmit: onSubmit, children: _jsxs("div", { className: cn('bg-background relative flex w-full flex-col rounded-3xl border', compact && 'h-14'), children: [_jsx("div", { className: cn('w-full px-5', compact ? 'py-4' : 'py-4 pr-14'), children: _jsx(Textarea, { ref: inputRef, value: input, placeholder: placeholder, onChange: onInputChange, onKeyDown: handleKeyDown, className: cn('max-h-32 min-h-[24px] resize-none rounded-none border-0 bg-transparent p-0 text-sm shadow-none focus:outline-none focus-visible:ring-0 focus-visible:ring-offset-0', compact && 'max-h-6 min-h-6') }) }), !compact && (_jsx("div", { className: "absolute bottom-3 right-3", children: _jsxs(Button, { type: showStopButton ? 'button' : 'submit', size: "icon", className: "bg-primary text-primary-foreground size-8 cursor-pointer rounded-full hover:opacity-70 disabled:opacity-30", disabled: !hasInput && !isResponding, onClick: handleButtonClick, children: [showStopButton ? (_jsx(Square, { className: "fill-primary-foreground size-4 animate-pulse" })) : (_jsx(ArrowUp, { className: "size-4" })), _jsx("span", { className: "sr-only", children: showStopButton ? 'Stop generating' : 'Send message' })] }) }))] }) }) }) }));
|
|
37
|
+
return (_jsx("div", { className: cn('isolate w-full p-2'), "data-chat-input-wrapper": true, children: _jsx("div", { className: "mx-auto max-w-5xl", children: _jsx("form", { onSubmit: onSubmit, children: _jsxs("div", { className: cn('bg-background relative flex w-full flex-col rounded-3xl border', compact && 'h-14'), children: [_jsx("div", { className: cn('w-full px-5', compact ? 'py-4' : 'py-4 pr-14'), children: _jsx(Textarea, { ref: inputRef, value: input, placeholder: placeholder, onChange: onInputChange, onKeyDown: handleKeyDown, className: cn('max-h-32 min-h-[24px] resize-none rounded-none border-0 !bg-transparent p-0 text-sm shadow-none focus:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 dark:!bg-transparent', compact && 'max-h-6 min-h-6') }) }), !compact && (_jsx("div", { className: "absolute bottom-3 right-3", children: _jsxs(Button, { type: showStopButton ? 'button' : 'submit', size: "icon", className: "bg-primary text-primary-foreground size-8 cursor-pointer rounded-full hover:opacity-70 disabled:opacity-30", disabled: !hasInput && !isResponding, onClick: handleButtonClick, children: [showStopButton ? (_jsx(Square, { className: "fill-primary-foreground size-4 animate-pulse" })) : (_jsx(ArrowUp, { className: "size-4" })), _jsx("span", { className: "sr-only", children: showStopButton ? 'Stop generating' : 'Send message' })] }) }))] }) }) }) }));
|
|
38
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-message.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-message.tsx"],"names":[],"mappings":"AAgCA,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IACE,IAAI,EAAE,iBAAiB,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB,QAAQ,CAAC,EAAE;QACT,EAAE,EAAE,MAAM,CAAA;QACV,QAAQ,CAAC,EAAE,OAAO,CAAA;KACnB,CAAA;CACF,CAAA;AAEL,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAA;IACrC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;CAC1B,CAAA;AAED,KAAK,YAAY,GAAG;IAClB,OAAO,EAAE,WAAW,CAAA;IACpB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAcD,iBAAS,WAAW,CAAC,EACnB,OAAO,EACP,MAAc,EACd,WAAmB,GACpB,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"chat-message.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-message.tsx"],"names":[],"mappings":"AAgCA,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IACE,IAAI,EAAE,iBAAiB,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB,QAAQ,CAAC,EAAE;QACT,EAAE,EAAE,MAAM,CAAA;QACV,QAAQ,CAAC,EAAE,OAAO,CAAA;KACnB,CAAA;CACF,CAAA;AAEL,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAA;IACrC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;CAC1B,CAAA;AAED,KAAK,YAAY,GAAG;IAClB,OAAO,EAAE,WAAW,CAAA;IACpB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAcD,iBAAS,WAAW,CAAC,EACnB,OAAO,EACP,MAAc,EACd,WAAmB,GACpB,EAAE,YAAY,2CA6bd;AAED,eAAO,MAAM,OAAO,yDAAoB,CAAA;AAExC,wBAAgB,eAAe,4CAiB9B"}
|
|
@@ -256,7 +256,7 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
|
256
256
|
lastThinkingGroup,
|
|
257
257
|
shouldKeepLastGroupOpen,
|
|
258
258
|
]);
|
|
259
|
-
return (_jsx(AnimatePresence, { children: _jsx(motion.div, { "data-testid": `message-${message.role}`, className: "group/message w-full min-w-0 px-2", initial: { y: 5, opacity: 0 }, animate: { y: 0, opacity: 1 }, "data-role": message.role, children: _jsx("div", { className:
|
|
259
|
+
return (_jsx(AnimatePresence, { children: _jsx(motion.div, { "data-testid": `message-${message.role}`, className: "group/message w-full min-w-0 px-2", initial: { y: 5, opacity: 0 }, animate: { y: 0, opacity: 1 }, "data-role": message.role, children: _jsx("div", { className: "flex w-full min-w-0 items-start gap-4", children: _jsx("div", { className: "mt-1 flex w-full min-w-0 flex-col gap-2", children: contentItems.map((item) => {
|
|
260
260
|
if (item.type === 'thinking' &&
|
|
261
261
|
item.thinkingGroup &&
|
|
262
262
|
message.role === 'assistant') {
|
|
@@ -289,11 +289,12 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
|
289
289
|
return (_jsx("div", { className: "w-full min-w-0", children: item.content }, item.id));
|
|
290
290
|
}
|
|
291
291
|
if (item.type === 'text' && item.textPart) {
|
|
292
|
+
if (message.role === 'user') {
|
|
293
|
+
return (_jsx("div", { className: "bg-secondary text-secondary-foreground w-full rounded-2xl px-4 py-2.5 pb-4", children: _jsx("div", { className: "whitespace-pre-wrap break-words text-sm leading-relaxed", children: item.textPart.text }) }, item.id));
|
|
294
|
+
}
|
|
292
295
|
return (_jsx("div", { className: cn('flex min-w-0 flex-col gap-4', {
|
|
293
|
-
'bg-secondary text-secondary-foreground w-fit rounded-2xl px-4 py-2.5': message.role === 'user',
|
|
294
296
|
'w-full': message.role === 'assistant',
|
|
295
|
-
}), children: _jsx(Markdown, { markdown: item.textPart.text, proseSize: "sm", className: cn('[&_.prose]:leading-relaxed [&_.prose]:text-current', '[&_.prose_pre]:bg-background/80 [&_.prose_code]:text-current', '[&_.prose_p:last-child]:mb-0 [&_.prose_p]:mb-2',
|
|
296
|
-
'[&_.prose_pre]:bg-secondary-foreground/20') }) }, item.id));
|
|
297
|
+
}), children: _jsx(Markdown, { markdown: item.textPart.text, proseSize: "sm", className: cn('[&_.prose]:leading-relaxed [&_.prose]:text-current', '[&_.prose_pre]:bg-background/80 [&_.prose_code]:text-current', '[&_.prose_p:last-child]:mb-0 [&_.prose_p]:mb-2') }) }, item.id));
|
|
297
298
|
}
|
|
298
299
|
return null;
|
|
299
300
|
}) }) }) }) }));
|
|
@@ -1,10 +1,35 @@
|
|
|
1
1
|
import { type ChatMessage } from './chat-message';
|
|
2
2
|
type ChatStatus = 'ready' | 'submitted' | 'streaming' | 'error';
|
|
3
|
+
/**
|
|
4
|
+
* Checkpoint info for a specific message
|
|
5
|
+
*/
|
|
6
|
+
export interface MessageCheckpoint {
|
|
7
|
+
messageId: string;
|
|
8
|
+
fileCount: number;
|
|
9
|
+
totalBytes: number;
|
|
10
|
+
}
|
|
3
11
|
interface MessagesProps {
|
|
4
12
|
messages: ChatMessage[];
|
|
5
13
|
status: ChatStatus;
|
|
14
|
+
/** Map of message ID to checkpoint info */
|
|
15
|
+
checkpoints?: Map<string, MessageCheckpoint>;
|
|
16
|
+
/** Message ID currently being restored */
|
|
17
|
+
restoringMessageId?: string | null;
|
|
18
|
+
/** Callback when restore is requested for a message */
|
|
19
|
+
onRestoreCheckpoint?: (messageId: string) => void;
|
|
20
|
+
}
|
|
21
|
+
export declare function computeIsThinking(messages: ChatMessage[], status: ChatStatus): boolean;
|
|
22
|
+
interface MessageRowProps {
|
|
23
|
+
message: ChatMessage;
|
|
24
|
+
isLast: boolean;
|
|
25
|
+
isStreaming: boolean;
|
|
26
|
+
checkpoint?: MessageCheckpoint;
|
|
27
|
+
restoringMessageId?: string | null;
|
|
28
|
+
onRestoreCheckpoint?: (messageId: string) => void;
|
|
6
29
|
}
|
|
7
|
-
declare function
|
|
30
|
+
declare function PureMessageRow({ message, isLast, isStreaming, checkpoint, restoringMessageId, onRestoreCheckpoint, }: MessageRowProps): import("react/jsx-runtime").JSX.Element;
|
|
31
|
+
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;
|
|
8
33
|
export declare const Messages: import("react").MemoExoticComponent<typeof PureMessages>;
|
|
9
34
|
export {};
|
|
10
35
|
//# 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;
|
|
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,30 +1,41 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { memo } from 'react';
|
|
3
3
|
import { Message, ThinkingMessage } from './chat-message';
|
|
4
|
-
|
|
4
|
+
import { CheckpointBadge } from './checkpoint-badge';
|
|
5
|
+
export function computeIsThinking(messages, status) {
|
|
5
6
|
const lastMessage = messages[messages.length - 1];
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
if (!lastMessage)
|
|
8
|
+
return false;
|
|
9
|
+
if (status !== 'submitted' && status !== 'streaming') {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
if (lastMessage.role === 'user') {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
if (lastMessage.role === 'assistant') {
|
|
16
|
+
const hasVisibleText = (lastMessage.parts ?? []).some((part) => {
|
|
17
|
+
return part.type === 'text' && part.text.trim().length > 0;
|
|
18
|
+
});
|
|
19
|
+
// Also check content fallback
|
|
20
|
+
if (!hasVisibleText && lastMessage.content?.trim()) {
|
|
10
21
|
return false;
|
|
11
22
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
return false;
|
|
26
|
-
})();
|
|
23
|
+
return !hasVisibleText;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
function PureMessageRow({ message, isLast, isStreaming, checkpoint, restoringMessageId, onRestoreCheckpoint, }) {
|
|
28
|
+
// Only show checkpoint badge on user messages that have checkpoints
|
|
29
|
+
const showCheckpointBadge = checkpoint && message.role === 'user' && onRestoreCheckpoint;
|
|
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
|
+
}
|
|
32
|
+
export const MessageRow = memo(PureMessageRow);
|
|
33
|
+
function PureMessages({ messages, status, checkpoints, restoringMessageId, onRestoreCheckpoint, }) {
|
|
34
|
+
const isThinking = computeIsThinking(messages, status);
|
|
27
35
|
const isStreaming = status === 'streaming' || status === 'submitted';
|
|
28
|
-
return (_jsxs(_Fragment, { children: [messages.map((message, index) =>
|
|
36
|
+
return (_jsxs(_Fragment, { children: [messages.map((message, index) => {
|
|
37
|
+
const checkpoint = checkpoints?.get(message.id);
|
|
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, {})] }));
|
|
29
40
|
}
|
|
30
41
|
export const Messages = memo(PureMessages);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ChangeEvent, type FormEvent, type ReactNode } from 'react';
|
|
2
|
-
import type
|
|
2
|
+
import { type ChatMessage } from './chat-message';
|
|
3
|
+
import { type MessageCheckpoint } from './chat-messages';
|
|
3
4
|
import { type ConversationMeta } from './conversation-history';
|
|
4
5
|
import { type ModelOption } from './model-selector';
|
|
5
6
|
import { type ReasoningEffortOption } from './reasoning-effort-selector';
|
|
@@ -74,10 +75,22 @@ export interface ChatPanelProps {
|
|
|
74
75
|
toolProgress?: Record<string, ToolProgressData>;
|
|
75
76
|
/** Callback for tool approval responses */
|
|
76
77
|
onApprovalResponse?: ApprovalResponseHandler;
|
|
78
|
+
/** Whether the chat is in "edit this app" mode (injects app context into system prompt) */
|
|
79
|
+
isEditingApp?: boolean;
|
|
80
|
+
/** Callback when "edit this app" mode is toggled */
|
|
81
|
+
onEditingAppChange?: (editing: boolean) => void;
|
|
82
|
+
/** Whether to show the editing app toggle (only shown when an app is active) */
|
|
83
|
+
showEditingAppToggle?: boolean;
|
|
84
|
+
/** Map of message ID to checkpoint info */
|
|
85
|
+
checkpoints?: Map<string, MessageCheckpoint>;
|
|
86
|
+
/** Message ID currently being restored */
|
|
87
|
+
restoringMessageId?: string | null;
|
|
88
|
+
/** Callback when restore is requested for a message */
|
|
89
|
+
onRestoreCheckpoint?: (messageId: string) => void;
|
|
77
90
|
}
|
|
78
91
|
/**
|
|
79
92
|
* Floating chat panel with model selector
|
|
80
93
|
*/
|
|
81
|
-
export declare function ChatPanel({ messages, status, 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, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
94
|
+
export declare function ChatPanel({ messages, status, 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, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
82
95
|
export {};
|
|
83
96
|
//# 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,35 +1,51 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { AlertCircle, ChevronDown, MessageCircle, Plus } from 'lucide-react';
|
|
4
|
-
import { useCallback, useEffect, useRef, useState, } from 'react';
|
|
3
|
+
import { AlertCircle, ChevronDown, Code2, MessageCircle, Plus, } from 'lucide-react';
|
|
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 {
|
|
10
|
+
import { ThinkingMessage } from './chat-message';
|
|
11
|
+
import { MessageRow, computeIsThinking, } from './chat-messages';
|
|
11
12
|
import { ConversationHistory, } from './conversation-history';
|
|
12
13
|
import { ModelSelector } from './model-selector';
|
|
13
14
|
import { ReasoningEffortSelector, } from './reasoning-effort-selector';
|
|
14
15
|
import { ToolApprovalProvider, } from './tool-approval-context';
|
|
15
16
|
import { ToolProgressProvider } from './tool-progress-context';
|
|
16
17
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
18
|
+
import { Virtualizer } from 'virtua';
|
|
17
19
|
/**
|
|
18
20
|
* Floating chat panel with model selector
|
|
19
21
|
*/
|
|
20
|
-
export function ChatPanel({ messages, status, 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, }) {
|
|
22
|
+
export function ChatPanel({ messages, status, 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, }) {
|
|
21
23
|
const inputRef = useRef(null);
|
|
22
|
-
const messagesEndRef = useRef(null);
|
|
23
24
|
const scrollAreaRef = useRef(null);
|
|
25
|
+
const viewportRef = useRef(null);
|
|
26
|
+
const virtualizerRef = useRef(null);
|
|
27
|
+
const prevMessageCountRef = useRef(messages.length);
|
|
28
|
+
const scrollRafRef = useRef(null);
|
|
29
|
+
const [viewportVersion, setViewportVersion] = useState(0);
|
|
24
30
|
const [isAtBottom, setIsAtBottom] = useState(true);
|
|
25
31
|
const isResponding = status === 'streaming' || status === 'submitted';
|
|
26
|
-
//
|
|
32
|
+
// Capture the Radix viewport element so virtua can use it as the scroll container
|
|
27
33
|
useEffect(() => {
|
|
28
34
|
const scrollArea = scrollAreaRef.current;
|
|
29
35
|
if (!scrollArea)
|
|
30
36
|
return;
|
|
31
|
-
// Radix ScrollArea puts the viewport as first child element
|
|
32
37
|
const viewport = scrollArea.querySelector('[data-radix-scroll-area-viewport]');
|
|
38
|
+
if (!viewport)
|
|
39
|
+
return;
|
|
40
|
+
viewport.style.overflowAnchor = 'none';
|
|
41
|
+
if (viewportRef.current !== viewport) {
|
|
42
|
+
viewportRef.current = viewport;
|
|
43
|
+
setViewportVersion((version) => version + 1);
|
|
44
|
+
}
|
|
45
|
+
}, [isExpanded]);
|
|
46
|
+
// Track scroll position to detect if user is at bottom
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
const viewport = viewportRef.current;
|
|
33
49
|
if (!viewport)
|
|
34
50
|
return;
|
|
35
51
|
const handleScroll = () => {
|
|
@@ -38,9 +54,10 @@ export function ChatPanel({ messages, status, input, onInputChange, onSubmit, on
|
|
|
38
54
|
const atBottom = scrollHeight - scrollTop - clientHeight < 50;
|
|
39
55
|
setIsAtBottom(atBottom);
|
|
40
56
|
};
|
|
57
|
+
handleScroll();
|
|
41
58
|
viewport.addEventListener('scroll', handleScroll);
|
|
42
59
|
return () => viewport.removeEventListener('scroll', handleScroll);
|
|
43
|
-
}, [
|
|
60
|
+
}, [viewportVersion]);
|
|
44
61
|
// Focus input when expanded
|
|
45
62
|
useEffect(() => {
|
|
46
63
|
if (isExpanded) {
|
|
@@ -50,12 +67,6 @@ export function ChatPanel({ messages, status, input, onInputChange, onSubmit, on
|
|
|
50
67
|
return () => clearTimeout(timer);
|
|
51
68
|
}
|
|
52
69
|
}, [isExpanded]);
|
|
53
|
-
// Scroll to bottom on new messages only if user is already at bottom
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
if (isExpanded && messagesEndRef.current && isAtBottom) {
|
|
56
|
-
messagesEndRef.current.scrollIntoView({ behavior: 'smooth' });
|
|
57
|
-
}
|
|
58
|
-
}, [messages, isExpanded, isAtBottom]);
|
|
59
70
|
const handleSubmit = useCallback((e) => {
|
|
60
71
|
e?.preventDefault();
|
|
61
72
|
if (!input.trim())
|
|
@@ -100,11 +111,109 @@ export function ChatPanel({ messages, status, input, onInputChange, onSubmit, on
|
|
|
100
111
|
}
|
|
101
112
|
onMinimizedChange?.(true);
|
|
102
113
|
}, [isExpanded, onExpandedChange, onMinimizedChange]);
|
|
114
|
+
const isStreaming = status === 'streaming' || status === 'submitted';
|
|
115
|
+
const isThinking = useMemo(() => computeIsThinking(messages, status), [messages, status]);
|
|
116
|
+
const showMissingApiKeyPrompt = missingApiKey ||
|
|
117
|
+
(error && error.message?.includes('API_KEY not configured'));
|
|
118
|
+
const showErrorMessage = Boolean(error) &&
|
|
119
|
+
status === 'error' &&
|
|
120
|
+
!error?.message?.includes('API_KEY not configured');
|
|
121
|
+
const lastMessageId = messages[messages.length - 1]?.id;
|
|
122
|
+
const listItems = useMemo(() => {
|
|
123
|
+
const items = [];
|
|
124
|
+
if (messages.length === 0 && welcomeMessage) {
|
|
125
|
+
items.push({ key: 'welcome', kind: 'welcome' });
|
|
126
|
+
}
|
|
127
|
+
for (const message of messages) {
|
|
128
|
+
items.push({ key: message.id, kind: 'message', message });
|
|
129
|
+
}
|
|
130
|
+
if (isThinking) {
|
|
131
|
+
items.push({ key: 'thinking', kind: 'thinking' });
|
|
132
|
+
}
|
|
133
|
+
if (showMissingApiKeyPrompt) {
|
|
134
|
+
items.push({ key: 'missing-api-key', kind: 'missing-api-key' });
|
|
135
|
+
}
|
|
136
|
+
if (showErrorMessage) {
|
|
137
|
+
items.push({ key: 'error', kind: 'error' });
|
|
138
|
+
}
|
|
139
|
+
return items;
|
|
140
|
+
}, [
|
|
141
|
+
isThinking,
|
|
142
|
+
messages,
|
|
143
|
+
showErrorMessage,
|
|
144
|
+
showMissingApiKeyPrompt,
|
|
145
|
+
welcomeMessage,
|
|
146
|
+
]);
|
|
147
|
+
const renderVirtualItem = useCallback((item) => {
|
|
148
|
+
switch (item.kind) {
|
|
149
|
+
case 'welcome':
|
|
150
|
+
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 })) }));
|
|
151
|
+
case 'message': {
|
|
152
|
+
const checkpoint = checkpoints?.get(item.message.id);
|
|
153
|
+
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 }) }));
|
|
154
|
+
}
|
|
155
|
+
case 'thinking':
|
|
156
|
+
return (_jsx("div", { className: "px-2 pb-4", children: _jsx(ThinkingMessage, {}) }));
|
|
157
|
+
case 'missing-api-key':
|
|
158
|
+
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 start chatting with AI" })] }), onAddApiKey && (_jsx(Button, { onClick: onAddApiKey, size: "sm", className: "cursor-pointer", children: "Add API key" }))] }) }));
|
|
159
|
+
case 'error':
|
|
160
|
+
return (_jsx("div", { className: "px-2 pb-4", children: error && (_jsxs("div", { className: "border-destructive/30 bg-destructive/10 text-destructive mx-2 flex items-start gap-2 rounded-lg border p-3 text-sm", children: [_jsx(AlertCircle, { className: "mt-0.5 size-4 shrink-0" }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "font-medium", children: "Request failed" }), _jsx("p", { className: "text-destructive/80 mt-0.5 break-words", children: error.message || 'An unknown error occurred' })] })] })) }));
|
|
161
|
+
}
|
|
162
|
+
}, [
|
|
163
|
+
checkpoints,
|
|
164
|
+
error,
|
|
165
|
+
isStreaming,
|
|
166
|
+
lastMessageId,
|
|
167
|
+
messages.length,
|
|
168
|
+
onAddApiKey,
|
|
169
|
+
onRestoreCheckpoint,
|
|
170
|
+
restoringMessageId,
|
|
171
|
+
welcomeMessage,
|
|
172
|
+
]);
|
|
173
|
+
// Scroll to bottom on new messages if the user is already at bottom
|
|
174
|
+
useEffect(() => {
|
|
175
|
+
const previousMessageCount = prevMessageCountRef.current;
|
|
176
|
+
const hasNewMessage = messages.length > previousMessageCount;
|
|
177
|
+
prevMessageCountRef.current = messages.length;
|
|
178
|
+
if (!isExpanded || !isAtBottom) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const lastIndex = listItems.length - 1;
|
|
182
|
+
if (lastIndex < 0)
|
|
183
|
+
return;
|
|
184
|
+
if (scrollRafRef.current !== null) {
|
|
185
|
+
cancelAnimationFrame(scrollRafRef.current);
|
|
186
|
+
}
|
|
187
|
+
scrollRafRef.current = requestAnimationFrame(() => {
|
|
188
|
+
virtualizerRef.current?.scrollToIndex(lastIndex, {
|
|
189
|
+
align: 'end',
|
|
190
|
+
smooth: hasNewMessage,
|
|
191
|
+
});
|
|
192
|
+
scrollRafRef.current = null;
|
|
193
|
+
});
|
|
194
|
+
return () => {
|
|
195
|
+
if (scrollRafRef.current !== null) {
|
|
196
|
+
cancelAnimationFrame(scrollRafRef.current);
|
|
197
|
+
scrollRafRef.current = null;
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
}, [isAtBottom, isExpanded, listItems.length, messages.length]);
|
|
103
201
|
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: {
|
|
104
202
|
width: isExpanded ? 'min(90vw, 640px)' : '400px',
|
|
105
203
|
y: isMinimized ? 'calc(100% + 24px)' : 0,
|
|
106
204
|
opacity: isMinimized ? 0 : 1,
|
|
107
|
-
}, transition: { type: 'tween', duration: 0.25, ease: 'easeOut' }, className: cn('pointer-events-none fixed bottom-6 left-[calc(50%+var(--sidebar-width,0px)/2)] z-50 flex -translate-x-1/2 justify-center', isMinimized && 'pointer-events-none', className), children: _jsxs("div", { className: cn('bg-background pointer-events-auto relative w-full
|
|
205
|
+
}, transition: { type: 'tween', duration: 0.25, ease: 'easeOut' }, className: cn('pointer-events-none fixed bottom-6 left-[calc(50%+var(--sidebar-width,0px)/2)] z-50 flex -translate-x-1/2 justify-center', isMinimized && 'pointer-events-none', className), children: _jsxs("div", { className: cn('bg-background/80 pointer-events-auto relative w-full shadow-[0_8px_40px_-12px_rgba(0,0,0,0.15)]', isExpanded
|
|
206
|
+
? 'bg-background'
|
|
207
|
+
: 'bg-background/80 supports-[backdrop-filter]:backdrop-blur-xl', 'rounded-[28px]', isExpanded ? 'overflow-hidden' : 'overflow-visible'), children: [!isExpanded && (_jsx("div", { className: "pointer-events-none absolute inset-0 z-0 rounded-[inherit]", style: {
|
|
208
|
+
background: 'linear-gradient(134deg, rgba(255,255,255,0.08) 0%, rgba(255,255,255,0.02) 50%, transparent 55%)',
|
|
209
|
+
} })), _jsx("div", { className: "pointer-events-none absolute inset-0 z-50 rounded-[inherit]", style: {
|
|
210
|
+
padding: '1px',
|
|
211
|
+
background: 'linear-gradient(135deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.1) 20%, rgba(255,255,255,0.05) 45%, rgba(255,255,255,0.05) 55%, rgba(255,255,255,0.1) 80%, rgba(255,255,255,0.25) 100%)',
|
|
212
|
+
mask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
|
|
213
|
+
WebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
|
|
214
|
+
maskComposite: 'exclude',
|
|
215
|
+
WebkitMaskComposite: 'xor',
|
|
216
|
+
} }), !isExpanded && (_jsx(motion.div, { className: "rounded-4xl pointer-events-none absolute inset-0 z-0", animate: {
|
|
108
217
|
boxShadow: [
|
|
109
218
|
'0 0 0px 0px color-mix(in oklch, var(--primary) 0%, #ff8800 00%)',
|
|
110
219
|
'0 0 20px 0px color-mix(in oklch, var(--primary) 20%, #ff8800 30%)',
|
|
@@ -120,13 +229,13 @@ export function ChatPanel({ messages, status, input, onInputChange, onSubmit, on
|
|
|
120
229
|
opacity: isExpanded ? 1 : 0,
|
|
121
230
|
}, transition: { type: 'tween', duration: 0.2, ease: 'easeOut' }, className: "relative z-10 flex flex-col overflow-hidden", style: { pointerEvents: isExpanded ? 'auto' : 'none' }, children: [_jsxs("div", { className: "flex items-center justify-between border-b px-4 py-2", children: [_jsxs("div", { className: "flex items-center gap-1", children: [_jsx(ModelSelector, { models: models, selectedModel: selectedModel, onModelChange: onModelChange, disabled: isResponding }), reasoningEffortOptions &&
|
|
122
231
|
selectedReasoningEffort &&
|
|
123
|
-
onReasoningEffortChange && (_jsx(ReasoningEffortSelector, { options: reasoningEffortOptions, selectedEffort: selectedReasoningEffort, onEffortChange: onReasoningEffortChange, disabled: isResponding }))
|
|
232
|
+
onReasoningEffortChange && (_jsx(ReasoningEffortSelector, { options: reasoningEffortOptions, selectedEffort: selectedReasoningEffort, onEffortChange: onReasoningEffortChange, disabled: isResponding })), showEditingAppToggle && onEditingAppChange && (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: () => onEditingAppChange(!isEditingApp), className: cn('ml-1', isEditingApp
|
|
233
|
+
? 'text-primary bg-primary/10 hover:bg-primary/20'
|
|
234
|
+
: 'text-muted-foreground hover:text-foreground'), children: _jsx(Code2, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: isEditingApp
|
|
235
|
+
? 'Editing this app (click to disable)'
|
|
236
|
+
: 'Not editing app (click to enable)' }) })] }) }))] }), _jsxs("div", { className: "flex items-center gap-1", children: [conversations &&
|
|
124
237
|
conversations.length > 0 &&
|
|
125
|
-
onSelectConversation && (_jsx(ConversationHistory, { conversations: conversations, currentConversationId: currentConversationId, onSelect: onSelectConversation, onDelete: onDeleteConversation, disabled: isResponding })), _jsxs(TooltipProvider, { children: [messages.length > 0 && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: handleNewChat, disabled: isResponding, className: "text-muted-foreground hover:text-foreground", children: _jsx(Plus, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: "New chat" }) })] })), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: handleMinimize, className: "text-muted-foreground hover:text-foreground", children: _jsx(ChevronDown, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: "Minimize" }) })] })] })] })] }), _jsx(ScrollArea, { ref: scrollAreaRef, className: "min-w-0 flex-1 px-2", children:
|
|
126
|
-
(error &&
|
|
127
|
-
error.message?.includes('API_KEY not configured'))) && (_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 start chatting with AI" })] }), onAddApiKey && (_jsx(Button, { onClick: onAddApiKey, size: "sm", className: "cursor-pointer", children: "Add API key" }))] })), error &&
|
|
128
|
-
status === 'error' &&
|
|
129
|
-
!error.message?.includes('API_KEY not configured') && (_jsxs("div", { className: "border-destructive/30 bg-destructive/10 text-destructive mx-2 flex items-start gap-2 rounded-lg border p-3 text-sm", children: [_jsx(AlertCircle, { className: "mt-0.5 size-4 shrink-0" }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "font-medium", children: "Request failed" }), _jsx("p", { className: "text-destructive/80 mt-0.5 break-words", children: error.message || 'An unknown error occurred' })] })] })), _jsx("div", { ref: messagesEndRef })] }) })] }), _jsx("div", { className: "relative z-10", onClick: () => {
|
|
238
|
+
onSelectConversation && (_jsx(ConversationHistory, { conversations: conversations, currentConversationId: currentConversationId, onSelect: onSelectConversation, onDelete: onDeleteConversation, disabled: isResponding })), _jsxs(TooltipProvider, { children: [messages.length > 0 && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: handleNewChat, disabled: isResponding, className: "text-muted-foreground hover:text-foreground", children: _jsx(Plus, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: "New chat" }) })] })), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: handleMinimize, className: "text-muted-foreground hover:text-foreground", children: _jsx(ChevronDown, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: "Minimize" }) })] })] })] })] }), _jsx(ScrollArea, { ref: scrollAreaRef, className: "min-w-0 flex-1 px-2", children: _jsx("div", { className: "min-w-0 max-w-full overflow-hidden py-4", 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)))) }) }) }) })] }), _jsx("div", { className: "relative z-10", onClick: () => {
|
|
130
239
|
if (!isExpanded) {
|
|
131
240
|
onExpandedChange(true);
|
|
132
241
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface CheckpointBadgeProps {
|
|
2
|
+
/** Message ID this checkpoint belongs to */
|
|
3
|
+
messageId: string;
|
|
4
|
+
/** Number of files in the checkpoint */
|
|
5
|
+
fileCount: number;
|
|
6
|
+
/** Total bytes of the checkpoint */
|
|
7
|
+
totalBytes?: number;
|
|
8
|
+
/** Whether a restore is in progress */
|
|
9
|
+
isRestoring?: boolean;
|
|
10
|
+
/** Callback when restore is requested */
|
|
11
|
+
onRestore: () => void;
|
|
12
|
+
/** Additional class name */
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Badge shown on messages that have a checkpoint restore point.
|
|
17
|
+
* Clicking triggers the restore confirmation dialog.
|
|
18
|
+
*/
|
|
19
|
+
export declare function CheckpointBadge({ isRestoring, onRestore, className, }: CheckpointBadgeProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
//# sourceMappingURL=checkpoint-badge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint-badge.d.ts","sourceRoot":"","sources":["../../../src/components/chat/checkpoint-badge.tsx"],"names":[],"mappings":"AAYA,MAAM,WAAW,oBAAoB;IACnC,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAA;IACjB,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uCAAuC;IACvC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,yCAAyC;IACzC,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAC9B,WAAW,EACX,SAAS,EACT,SAAS,GACV,EAAE,oBAAoB,2CAiCtB"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { History, Loader2 } from 'lucide-react';
|
|
4
|
+
import { cn } from '../../lib/utils';
|
|
5
|
+
import { Button } from '../ui/button';
|
|
6
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '../ui/tooltip';
|
|
7
|
+
/**
|
|
8
|
+
* Badge shown on messages that have a checkpoint restore point.
|
|
9
|
+
* Clicking triggers the restore confirmation dialog.
|
|
10
|
+
*/
|
|
11
|
+
export function CheckpointBadge({ isRestoring, onRestore, className, }) {
|
|
12
|
+
return (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: (e) => {
|
|
13
|
+
e.stopPropagation();
|
|
14
|
+
onRestore();
|
|
15
|
+
}, disabled: isRestoring, className: cn('text-muted-foreground hover:text-foreground hover:bg-muted/50 size-6', !isRestoring && 'cursor-pointer', isRestoring && 'cursor-wait', className), children: isRestoring ? (_jsx(Loader2, { className: "size-3.5 animate-spin" })) : (_jsx(History, { className: "size-3.5" })) }) }), _jsx(TooltipContent, { side: "top", children: _jsx("p", { children: "Undo all changes from this point onwards" }) })] }) }));
|
|
16
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { ChatInput } from './chat-input';
|
|
2
2
|
export { Message, ThinkingMessage, type ChatMessage, type ChatMessagePart, } from './chat-message';
|
|
3
|
-
export { Messages } from './chat-messages';
|
|
3
|
+
export { Messages, type MessageCheckpoint } from './chat-messages';
|
|
4
|
+
export { CheckpointBadge, type CheckpointBadgeProps } from './checkpoint-badge';
|
|
5
|
+
export { RestoreDialog, type CheckpointInfo, type RestoreDialogProps, } from './restore-dialog';
|
|
4
6
|
export { ModelSelector, type ModelOption } from './model-selector';
|
|
5
7
|
export { ReasoningEffortSelector, type ReasoningEffortOption, } from './reasoning-effort-selector';
|
|
6
8
|
export { ConversationHistory, type ConversationMeta, } from './conversation-history';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EACL,OAAO,EACP,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,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,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,6 +1,8 @@
|
|
|
1
1
|
export { ChatInput } from './chat-input';
|
|
2
2
|
export { Message, ThinkingMessage, } from './chat-message';
|
|
3
3
|
export { Messages } from './chat-messages';
|
|
4
|
+
export { CheckpointBadge } from './checkpoint-badge';
|
|
5
|
+
export { RestoreDialog, } from './restore-dialog';
|
|
4
6
|
export { ModelSelector } from './model-selector';
|
|
5
7
|
export { ReasoningEffortSelector, } from './reasoning-effort-selector';
|
|
6
8
|
export { ConversationHistory, } from './conversation-history';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface CheckpointInfo {
|
|
2
|
+
/** The message ID of the checkpoint */
|
|
3
|
+
messageId: string;
|
|
4
|
+
/** Number of files in the checkpoint */
|
|
5
|
+
fileCount: number;
|
|
6
|
+
/** Total bytes of the checkpoint */
|
|
7
|
+
totalBytes: number;
|
|
8
|
+
/** When the checkpoint was created */
|
|
9
|
+
createdAt: string;
|
|
10
|
+
/** List of file paths (for display) */
|
|
11
|
+
files?: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface RestoreDialogProps {
|
|
14
|
+
/** Whether the dialog is open */
|
|
15
|
+
open: boolean;
|
|
16
|
+
/** Callback when dialog state changes */
|
|
17
|
+
onOpenChange: (open: boolean) => void;
|
|
18
|
+
/** The checkpoint to potentially restore */
|
|
19
|
+
checkpoint: CheckpointInfo | null;
|
|
20
|
+
/** Callback when restore is confirmed */
|
|
21
|
+
onConfirm: () => void;
|
|
22
|
+
/** Whether a restore is in progress */
|
|
23
|
+
isRestoring?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Dialog for confirming checkpoint restoration.
|
|
27
|
+
*
|
|
28
|
+
* Note: The restore operation is cascading - it will undo ALL changes from
|
|
29
|
+
* this point forward, including deleting files that were created after this
|
|
30
|
+
* checkpoint.
|
|
31
|
+
*/
|
|
32
|
+
export declare function RestoreDialog({ open, onOpenChange, checkpoint, onConfirm, isRestoring, }: RestoreDialogProps): import("react/jsx-runtime").JSX.Element | null;
|
|
33
|
+
//# sourceMappingURL=restore-dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restore-dialog.d.ts","sourceRoot":"","sources":["../../../src/components/chat/restore-dialog.tsx"],"names":[],"mappings":"AAcA,MAAM,WAAW,cAAc;IAC7B,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAA;IACjB,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,iCAAiC;IACjC,IAAI,EAAE,OAAO,CAAA;IACb,yCAAyC;IACzC,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,4CAA4C;IAC5C,UAAU,EAAE,cAAc,GAAG,IAAI,CAAA;IACjC,yCAAyC;IACzC,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,uCAAuC;IACvC,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,GACZ,EAAE,kBAAkB,kDAwFpB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { AlertTriangle, History } from 'lucide-react';
|
|
4
|
+
import { Button } from '../ui/button';
|
|
5
|
+
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '../ui/dialog';
|
|
6
|
+
import { formatDistanceToNow } from 'date-fns';
|
|
7
|
+
/**
|
|
8
|
+
* Dialog for confirming checkpoint restoration.
|
|
9
|
+
*
|
|
10
|
+
* Note: The restore operation is cascading - it will undo ALL changes from
|
|
11
|
+
* this point forward, including deleting files that were created after this
|
|
12
|
+
* checkpoint.
|
|
13
|
+
*/
|
|
14
|
+
export function RestoreDialog({ open, onOpenChange, checkpoint, onConfirm, isRestoring, }) {
|
|
15
|
+
if (!checkpoint)
|
|
16
|
+
return null;
|
|
17
|
+
const files = checkpoint.files ?? [];
|
|
18
|
+
const displayFiles = files.slice(0, 5);
|
|
19
|
+
const remainingCount = files.length - displayFiles.length;
|
|
20
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-md", children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(History, { className: "text-muted-foreground size-5" }), "Restore to this point?"] }), _jsxs(DialogDescription, { children: ["This will undo all changes made from", ' ', formatDistanceToNow(new Date(checkpoint.createdAt), {
|
|
21
|
+
addSuffix: true,
|
|
22
|
+
}), ' ', "onwards."] })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "bg-destructive/10 flex items-start gap-2 rounded-lg p-3 text-sm", children: [_jsx(AlertTriangle, { className: "text-destructive mt-0.5 size-4 shrink-0" }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-foreground", children: "All changes after this point will be reverted. Files created after this checkpoint will be deleted." }), _jsx("p", { className: "text-muted-foreground", children: "This action cannot be undone." })] })] }), files.length > 0 && (_jsxs("div", { className: "text-muted-foreground text-sm", children: [_jsx("p", { className: "text-foreground mb-2 font-medium", children: "Files in this checkpoint:" }), _jsxs("ul", { className: "bg-muted/50 space-y-1 rounded-md p-2 font-mono text-xs", children: [displayFiles.map((file) => (_jsx("li", { className: "truncate", children: file }, file))), remainingCount > 0 && (_jsxs("li", { className: "text-muted-foreground/70", children: ["...and ", remainingCount, " more"] }))] })] })), _jsxs("p", { className: "text-muted-foreground text-xs", children: [checkpoint.fileCount, " file", checkpoint.fileCount !== 1 ? 's' : '', ' ', "captured"] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: isRestoring, className: "cursor-pointer", children: "Cancel" }), _jsx(Button, { variant: "destructive", onClick: onConfirm, disabled: isRestoring, className: "cursor-pointer", children: isRestoring ? 'Restoring...' : 'Restore' })] })] }) }));
|
|
23
|
+
}
|
|
@@ -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":"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;AAkOD;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CA+0D7D,CAAA;AAoBD;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAmC5D"}
|