@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.
Files changed (81) hide show
  1. package/dist/components/chat/chat-input.js +1 -1
  2. package/dist/components/chat/chat-message.d.ts.map +1 -1
  3. package/dist/components/chat/chat-message.js +5 -4
  4. package/dist/components/chat/chat-messages.d.ts +26 -1
  5. package/dist/components/chat/chat-messages.d.ts.map +1 -1
  6. package/dist/components/chat/chat-messages.js +33 -22
  7. package/dist/components/chat/chat-panel.d.ts +15 -2
  8. package/dist/components/chat/chat-panel.d.ts.map +1 -1
  9. package/dist/components/chat/chat-panel.js +130 -21
  10. package/dist/components/chat/checkpoint-badge.d.ts +20 -0
  11. package/dist/components/chat/checkpoint-badge.d.ts.map +1 -0
  12. package/dist/components/chat/checkpoint-badge.js +16 -0
  13. package/dist/components/chat/index.d.ts +3 -1
  14. package/dist/components/chat/index.d.ts.map +1 -1
  15. package/dist/components/chat/index.js +2 -0
  16. package/dist/components/chat/restore-dialog.d.ts +33 -0
  17. package/dist/components/chat/restore-dialog.d.ts.map +1 -0
  18. package/dist/components/chat/restore-dialog.js +23 -0
  19. package/dist/components/chat/tool-handlers.d.ts.map +1 -1
  20. package/dist/components/chat/tool-handlers.js +138 -2
  21. package/dist/components/ui/alert-dialog.d.ts +8 -4
  22. package/dist/components/ui/alert-dialog.d.ts.map +1 -1
  23. package/dist/components/ui/alert-dialog.js +14 -11
  24. package/dist/components/ui/avatar.d.ts +7 -2
  25. package/dist/components/ui/avatar.d.ts.map +1 -1
  26. package/dist/components/ui/avatar.js +13 -4
  27. package/dist/components/ui/badge.d.ts +1 -1
  28. package/dist/components/ui/badge.d.ts.map +1 -1
  29. package/dist/components/ui/badge.js +9 -7
  30. package/dist/components/ui/button.d.ts +3 -4
  31. package/dist/components/ui/button.d.ts.map +1 -1
  32. package/dist/components/ui/button.js +6 -6
  33. package/dist/components/ui/card.d.ts +8 -7
  34. package/dist/components/ui/card.d.ts.map +1 -1
  35. package/dist/components/ui/card.js +22 -14
  36. package/dist/components/ui/checkbox.js +1 -1
  37. package/dist/components/ui/collapsible.d.ts +3 -3
  38. package/dist/components/ui/collapsible.d.ts.map +1 -1
  39. package/dist/components/ui/collapsible.js +11 -3
  40. package/dist/components/ui/combobox.d.ts +25 -0
  41. package/dist/components/ui/combobox.d.ts.map +1 -0
  42. package/dist/components/ui/combobox.js +58 -0
  43. package/dist/components/ui/command.d.ts +15 -15
  44. package/dist/components/ui/command.d.ts.map +1 -1
  45. package/dist/components/ui/command.js +29 -24
  46. package/dist/components/ui/dialog.d.ts +15 -17
  47. package/dist/components/ui/dialog.d.ts.map +1 -1
  48. package/dist/components/ui/dialog.js +33 -19
  49. package/dist/components/ui/dropdown-menu.js +6 -6
  50. package/dist/components/ui/input-group.d.ts +2 -2
  51. package/dist/components/ui/input.d.ts +1 -2
  52. package/dist/components/ui/input.d.ts.map +1 -1
  53. package/dist/components/ui/input.js +4 -5
  54. package/dist/components/ui/label.d.ts +1 -1
  55. package/dist/components/ui/label.d.ts.map +1 -1
  56. package/dist/components/ui/label.js +3 -3
  57. package/dist/components/ui/popover.d.ts +4 -1
  58. package/dist/components/ui/popover.d.ts.map +1 -1
  59. package/dist/components/ui/popover.js +10 -1
  60. package/dist/components/ui/scroll-area.d.ts +2 -2
  61. package/dist/components/ui/scroll-area.d.ts.map +1 -1
  62. package/dist/components/ui/scroll-area.js +9 -7
  63. package/dist/components/ui/select.d.ts +13 -11
  64. package/dist/components/ui/select.d.ts.map +1 -1
  65. package/dist/components/ui/select.js +35 -23
  66. package/dist/components/ui/sheet.d.ts +2 -1
  67. package/dist/components/ui/sheet.d.ts.map +1 -1
  68. package/dist/components/ui/sheet.js +2 -2
  69. package/dist/components/ui/switch.d.ts +4 -2
  70. package/dist/components/ui/switch.d.ts.map +1 -1
  71. package/dist/components/ui/switch.js +4 -4
  72. package/dist/components/ui/tabs.d.ts +7 -3
  73. package/dist/components/ui/tabs.d.ts.map +1 -1
  74. package/dist/components/ui/tabs.js +18 -6
  75. package/dist/components/ui/textarea.d.ts +1 -2
  76. package/dist/components/ui/textarea.d.ts.map +1 -1
  77. package/dist/components/ui/textarea.js +4 -5
  78. package/dist/components/ui/tooltip.js +1 -1
  79. package/dist/lib/commands.d.ts.map +1 -1
  80. package/package.json +6 -3
  81. 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,2CA8bd;AAED,eAAO,MAAM,OAAO,yDAAoB,CAAA;AAExC,wBAAgB,eAAe,4CAiB9B"}
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: cn('flex w-full min-w-0 items-start gap-4', message.role === 'user' && 'justify-end'), children: _jsx("div", { className: cn('mt-1 flex min-w-0 flex-col gap-2', message.role === 'user' ? 'max-w-[85%] items-end' : 'w-full'), children: contentItems.map((item) => {
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', message.role === 'user' &&
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 PureMessages({ messages, status }: MessagesProps): import("react/jsx-runtime").JSX.Element;
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;AAE3E,KAAK,UAAU,GAAG,OAAO,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,CAAA;AAE/D,UAAU,aAAa;IACrB,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,MAAM,EAAE,UAAU,CAAA;CACnB;AAED,iBAAS,YAAY,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,aAAa,2CA2CxD;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,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, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
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
- function PureMessages({ messages, status }) {
4
+ import { CheckpointBadge } from './checkpoint-badge';
5
+ export function computeIsThinking(messages, status) {
5
6
  const lastMessage = messages[messages.length - 1];
6
- const isThinking = (() => {
7
- if (!lastMessage)
8
- return false;
9
- if (status !== 'submitted' && status !== 'streaming') {
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
- 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()) {
21
- return false;
22
- }
23
- return !hasVisibleText;
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) => (_jsx(Message, { message: message, isLast: index === messages.length - 1, isStreaming: isStreaming }, message.id))), isThinking && _jsx(ThinkingMessage, {})] }));
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 { ChatMessage } from './chat-message';
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":"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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,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 { Messages } from './chat-messages';
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
- // Track scroll position to detect if user is at bottom
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
- }, [isExpanded]);
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 border shadow-lg', 'supports-[backdrop-filter]:bg-background/95 backdrop-blur', 'rounded-4xl', isExpanded ? 'overflow-hidden' : 'overflow-visible'), children: [!isExpanded && (_jsx(motion.div, { className: "rounded-4xl pointer-events-none absolute inset-0 z-0", animate: {
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 }))] }), _jsxs("div", { className: "flex items-center gap-1", children: [conversations &&
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: _jsxs("div", { className: "min-w-0 max-w-full space-y-4 overflow-hidden py-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 })), _jsx(ToolProgressProvider, { value: toolProgress, children: _jsx(ToolApprovalProvider, { onApprovalResponse: onApprovalResponse ?? null, children: _jsx(Messages, { messages: messages, status: status }) }) }), (missingApiKey ||
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;AAC1C,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
+ {"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":"AAoBA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAEhD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAU5D,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,CAk1C7D,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":"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"}