@moldable-ai/ui 0.1.1

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 (117) hide show
  1. package/LICENSE +101 -0
  2. package/dist/components/chat/chat-input.d.ts +14 -0
  3. package/dist/components/chat/chat-input.d.ts.map +1 -0
  4. package/dist/components/chat/chat-input.js +36 -0
  5. package/dist/components/chat/chat-message.d.ts +30 -0
  6. package/dist/components/chat/chat-message.d.ts.map +1 -0
  7. package/dist/components/chat/chat-message.js +250 -0
  8. package/dist/components/chat/chat-messages.d.ts +10 -0
  9. package/dist/components/chat/chat-messages.d.ts.map +1 -0
  10. package/dist/components/chat/chat-messages.js +30 -0
  11. package/dist/components/chat/chat-panel.d.ts +60 -0
  12. package/dist/components/chat/chat-panel.d.ts.map +1 -0
  13. package/dist/components/chat/chat-panel.js +115 -0
  14. package/dist/components/chat/conversation-history.d.ts +18 -0
  15. package/dist/components/chat/conversation-history.d.ts.map +1 -0
  16. package/dist/components/chat/conversation-history.js +32 -0
  17. package/dist/components/chat/index.d.ts +10 -0
  18. package/dist/components/chat/index.d.ts.map +1 -0
  19. package/dist/components/chat/index.js +9 -0
  20. package/dist/components/chat/model-selector.d.ts +16 -0
  21. package/dist/components/chat/model-selector.d.ts.map +1 -0
  22. package/dist/components/chat/model-selector.js +9 -0
  23. package/dist/components/chat/reasoning-effort-selector.d.ts +14 -0
  24. package/dist/components/chat/reasoning-effort-selector.d.ts.map +1 -0
  25. package/dist/components/chat/reasoning-effort-selector.js +10 -0
  26. package/dist/components/chat/thinking-timeline.d.ts +21 -0
  27. package/dist/components/chat/thinking-timeline.d.ts.map +1 -0
  28. package/dist/components/chat/thinking-timeline.js +46 -0
  29. package/dist/components/chat/tool-handlers.d.ts +21 -0
  30. package/dist/components/chat/tool-handlers.d.ts.map +1 -0
  31. package/dist/components/chat/tool-handlers.js +434 -0
  32. package/dist/components/code-block.d.ts +9 -0
  33. package/dist/components/code-block.d.ts.map +1 -0
  34. package/dist/components/code-block.js +112 -0
  35. package/dist/components/markdown.d.ts +10 -0
  36. package/dist/components/markdown.d.ts.map +1 -0
  37. package/dist/components/markdown.js +189 -0
  38. package/dist/components/ui/alert-dialog.d.ts +15 -0
  39. package/dist/components/ui/alert-dialog.d.ts.map +1 -0
  40. package/dist/components/ui/alert-dialog.js +38 -0
  41. package/dist/components/ui/badge.d.ts +10 -0
  42. package/dist/components/ui/badge.d.ts.map +1 -0
  43. package/dist/components/ui/badge.js +22 -0
  44. package/dist/components/ui/button.d.ts +12 -0
  45. package/dist/components/ui/button.d.ts.map +1 -0
  46. package/dist/components/ui/button.js +35 -0
  47. package/dist/components/ui/card.d.ts +9 -0
  48. package/dist/components/ui/card.d.ts.map +1 -0
  49. package/dist/components/ui/card.js +16 -0
  50. package/dist/components/ui/checkbox.d.ts +5 -0
  51. package/dist/components/ui/checkbox.d.ts.map +1 -0
  52. package/dist/components/ui/checkbox.js +9 -0
  53. package/dist/components/ui/collapsible.d.ts +6 -0
  54. package/dist/components/ui/collapsible.d.ts.map +1 -0
  55. package/dist/components/ui/collapsible.js +5 -0
  56. package/dist/components/ui/command.d.ts +19 -0
  57. package/dist/components/ui/command.d.ts.map +1 -0
  58. package/dist/components/ui/command.js +29 -0
  59. package/dist/components/ui/dialog.d.ts +20 -0
  60. package/dist/components/ui/dialog.d.ts.map +1 -0
  61. package/dist/components/ui/dialog.js +23 -0
  62. package/dist/components/ui/dropdown-menu.d.ts +26 -0
  63. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  64. package/dist/components/ui/dropdown-menu.js +51 -0
  65. package/dist/components/ui/index.d.ts +19 -0
  66. package/dist/components/ui/index.d.ts.map +1 -0
  67. package/dist/components/ui/index.js +18 -0
  68. package/dist/components/ui/input.d.ts +5 -0
  69. package/dist/components/ui/input.d.ts.map +1 -0
  70. package/dist/components/ui/input.js +8 -0
  71. package/dist/components/ui/label.d.ts +5 -0
  72. package/dist/components/ui/label.d.ts.map +1 -0
  73. package/dist/components/ui/label.js +8 -0
  74. package/dist/components/ui/popover.d.ts +8 -0
  75. package/dist/components/ui/popover.d.ts.map +1 -0
  76. package/dist/components/ui/popover.js +16 -0
  77. package/dist/components/ui/scroll-area.d.ts +6 -0
  78. package/dist/components/ui/scroll-area.d.ts.map +1 -0
  79. package/dist/components/ui/scroll-area.js +11 -0
  80. package/dist/components/ui/select.d.ts +14 -0
  81. package/dist/components/ui/select.d.ts.map +1 -0
  82. package/dist/components/ui/select.js +26 -0
  83. package/dist/components/ui/switch.d.ts +5 -0
  84. package/dist/components/ui/switch.d.ts.map +1 -0
  85. package/dist/components/ui/switch.js +8 -0
  86. package/dist/components/ui/tabs.d.ts +8 -0
  87. package/dist/components/ui/tabs.d.ts.map +1 -0
  88. package/dist/components/ui/tabs.js +16 -0
  89. package/dist/components/ui/textarea.d.ts +5 -0
  90. package/dist/components/ui/textarea.d.ts.map +1 -0
  91. package/dist/components/ui/textarea.js +8 -0
  92. package/dist/components/ui/tooltip.d.ts +8 -0
  93. package/dist/components/ui/tooltip.d.ts.map +1 -0
  94. package/dist/components/ui/tooltip.js +17 -0
  95. package/dist/components/widget-layout.d.ts +10 -0
  96. package/dist/components/widget-layout.d.ts.map +1 -0
  97. package/dist/components/widget-layout.js +30 -0
  98. package/dist/index.d.ts +10 -0
  99. package/dist/index.d.ts.map +1 -0
  100. package/dist/index.js +18 -0
  101. package/dist/lib/commands.d.ts +74 -0
  102. package/dist/lib/commands.d.ts.map +1 -0
  103. package/dist/lib/commands.js +73 -0
  104. package/dist/lib/commands.test.d.ts +2 -0
  105. package/dist/lib/commands.test.d.ts.map +1 -0
  106. package/dist/lib/commands.test.js +111 -0
  107. package/dist/lib/theme.d.ts +25 -0
  108. package/dist/lib/theme.d.ts.map +1 -0
  109. package/dist/lib/theme.js +103 -0
  110. package/dist/lib/utils.d.ts +3 -0
  111. package/dist/lib/utils.d.ts.map +1 -0
  112. package/dist/lib/utils.js +5 -0
  113. package/dist/lib/workspace.d.ts +41 -0
  114. package/dist/lib/workspace.d.ts.map +1 -0
  115. package/dist/lib/workspace.js +82 -0
  116. package/package.json +87 -0
  117. package/src/styles/index.css +199 -0
@@ -0,0 +1,115 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { AlertCircle, Minimize2, Plus } from 'lucide-react';
4
+ import { useCallback, useEffect, useRef, useState, } from 'react';
5
+ import { cn } from '../../lib/utils';
6
+ import { Button } from '../ui/button';
7
+ import { ScrollArea } from '../ui/scroll-area';
8
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '../ui/tooltip';
9
+ import { ChatInput } from './chat-input';
10
+ import { Messages } from './chat-messages';
11
+ import { ConversationHistory, } from './conversation-history';
12
+ import { ModelSelector } from './model-selector';
13
+ import { ReasoningEffortSelector, } from './reasoning-effort-selector';
14
+ import { AnimatePresence, motion } from 'framer-motion';
15
+ /**
16
+ * Floating chat panel with model selector
17
+ */
18
+ 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, className, error, }) {
19
+ const inputRef = useRef(null);
20
+ const messagesEndRef = useRef(null);
21
+ const scrollAreaRef = useRef(null);
22
+ const [isAtBottom, setIsAtBottom] = useState(true);
23
+ const isResponding = status === 'streaming' || status === 'submitted';
24
+ // Track scroll position to detect if user is at bottom
25
+ useEffect(() => {
26
+ const scrollArea = scrollAreaRef.current;
27
+ if (!scrollArea)
28
+ return;
29
+ // Radix ScrollArea puts the viewport as first child element
30
+ const viewport = scrollArea.querySelector('[data-radix-scroll-area-viewport]');
31
+ if (!viewport)
32
+ return;
33
+ const handleScroll = () => {
34
+ const { scrollTop, scrollHeight, clientHeight } = viewport;
35
+ // Consider "at bottom" if within 50px of the bottom
36
+ const atBottom = scrollHeight - scrollTop - clientHeight < 50;
37
+ setIsAtBottom(atBottom);
38
+ };
39
+ viewport.addEventListener('scroll', handleScroll);
40
+ return () => viewport.removeEventListener('scroll', handleScroll);
41
+ }, [isExpanded]);
42
+ // Focus input when expanded
43
+ useEffect(() => {
44
+ if (isExpanded) {
45
+ const timer = setTimeout(() => {
46
+ inputRef.current?.focus();
47
+ }, 200);
48
+ return () => clearTimeout(timer);
49
+ }
50
+ }, [isExpanded]);
51
+ // Scroll to bottom on new messages only if user is already at bottom
52
+ useEffect(() => {
53
+ if (isExpanded && messagesEndRef.current && isAtBottom) {
54
+ messagesEndRef.current.scrollIntoView({ behavior: 'smooth' });
55
+ }
56
+ }, [messages, isExpanded, isAtBottom]);
57
+ const handleSubmit = useCallback((e) => {
58
+ e?.preventDefault();
59
+ if (!input.trim())
60
+ return;
61
+ // If streaming, stop the current response first
62
+ if (isResponding && onStop) {
63
+ onStop();
64
+ }
65
+ if (!isExpanded) {
66
+ onExpandedChange(true);
67
+ }
68
+ // Reset to bottom when user sends a message
69
+ setIsAtBottom(true);
70
+ onSubmit(e);
71
+ }, [input, isResponding, isExpanded, onExpandedChange, onSubmit, onStop]);
72
+ const handleNewChat = useCallback(() => {
73
+ onNewChat?.();
74
+ inputRef.current?.focus();
75
+ }, [onNewChat]);
76
+ // Keyboard shortcut: Cmd+Shift+O for new chat
77
+ useEffect(() => {
78
+ const handleKeyDown = (e) => {
79
+ if (e.key === 'o' && e.metaKey && e.shiftKey) {
80
+ e.preventDefault();
81
+ // Only create new chat if not currently streaming and panel is expanded
82
+ if (!isResponding && isExpanded) {
83
+ handleNewChat();
84
+ }
85
+ }
86
+ };
87
+ document.addEventListener('keydown', handleKeyDown);
88
+ return () => document.removeEventListener('keydown', handleKeyDown);
89
+ }, [isResponding, isExpanded, handleNewChat]);
90
+ return (_jsxs(_Fragment, { children: [_jsx(AnimatePresence, { children: isExpanded && (_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(motion.div, { initial: false, animate: {
91
+ width: isExpanded ? 'min(90vw, 640px)' : '400px',
92
+ }, transition: { type: 'tween', duration: 0.2, 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', 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: {
93
+ boxShadow: [
94
+ '0 0 0px 0px color-mix(in oklch, var(--primary) 0%, #ff8800 00%)',
95
+ '0 0 20px 0px color-mix(in oklch, var(--primary) 20%, #ff8800 30%)',
96
+ '0 0 30px 2px color-mix(in oklch, var(--primary) 0%, #ff8800 00%)',
97
+ ],
98
+ }, transition: {
99
+ duration: 4,
100
+ repeat: Infinity,
101
+ repeatDelay: 5,
102
+ ease: [0.4, 0, 0.2, 1],
103
+ } })), _jsxs(motion.div, { initial: false, animate: {
104
+ height: isExpanded ? 'min(60vh, 480px)' : '0px',
105
+ opacity: isExpanded ? 1 : 0,
106
+ }, 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 &&
107
+ selectedReasoningEffort &&
108
+ onReasoningEffortChange && (_jsx(ReasoningEffortSelector, { options: reasoningEffortOptions, selectedEffort: selectedReasoningEffort, onEffortChange: onReasoningEffortChange, disabled: isResponding }))] }), _jsxs("div", { className: "flex items-center gap-1", children: [conversations &&
109
+ conversations.length > 0 &&
110
+ 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: () => onExpandedChange(false), className: "text-muted-foreground hover:text-foreground", children: _jsx(Minimize2, { 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 space-y-4 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(Messages, { messages: messages, status: status }), error && status === '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' })] })] })), _jsx("div", { ref: messagesEndRef })] }) })] }), _jsx("div", { className: "relative z-10", onClick: () => {
111
+ if (!isExpanded) {
112
+ onExpandedChange(true);
113
+ }
114
+ }, children: _jsx(ChatInput, { input: input, onInputChange: onInputChange, onSubmit: handleSubmit, isResponding: isResponding, placeholder: placeholder, inputRef: inputRef, onStop: onStop, compact: !isExpanded }) })] }) })] }));
115
+ }
@@ -0,0 +1,18 @@
1
+ export interface ConversationMeta {
2
+ id: string;
3
+ title: string;
4
+ createdAt: string;
5
+ updatedAt: string;
6
+ messageCount: number;
7
+ }
8
+ interface ConversationHistoryProps {
9
+ conversations: ConversationMeta[];
10
+ currentConversationId?: string | null;
11
+ onSelect: (id: string) => void;
12
+ onDelete?: (id: string) => void;
13
+ disabled?: boolean;
14
+ className?: string;
15
+ }
16
+ export declare function ConversationHistory({ conversations, currentConversationId, onSelect, onDelete, disabled, className, }: ConversationHistoryProps): import("react/jsx-runtime").JSX.Element | null;
17
+ export {};
18
+ //# sourceMappingURL=conversation-history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-history.d.ts","sourceRoot":"","sources":["../../../src/components/chat/conversation-history.tsx"],"names":[],"mappings":"AAiBA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,UAAU,wBAAwB;IAChC,aAAa,EAAE,gBAAgB,EAAE,CAAA;IACjC,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAiBD,wBAAgB,mBAAmB,CAAC,EAClC,aAAa,EACb,qBAAqB,EACrB,QAAQ,EACR,QAAQ,EACR,QAAgB,EAChB,SAAS,GACV,EAAE,wBAAwB,kDAsE1B"}
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Clock, Trash2 } from 'lucide-react';
3
+ import { cn } from '../../lib/utils';
4
+ import { Button } from '../ui/button';
5
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '../ui/dropdown-menu';
6
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '../ui/tooltip';
7
+ function formatRelativeTime(dateStr) {
8
+ const date = new Date(dateStr);
9
+ const now = new Date();
10
+ const diffMs = now.getTime() - date.getTime();
11
+ const diffMins = Math.floor(diffMs / 60000);
12
+ const diffHours = Math.floor(diffMs / 3600000);
13
+ const diffDays = Math.floor(diffMs / 86400000);
14
+ if (diffMins < 1)
15
+ return 'Just now';
16
+ if (diffMins < 60)
17
+ return `${diffMins}m ago`;
18
+ if (diffHours < 24)
19
+ return `${diffHours}h ago`;
20
+ if (diffDays < 7)
21
+ return `${diffDays}d ago`;
22
+ return date.toLocaleDateString();
23
+ }
24
+ export function ConversationHistory({ conversations, currentConversationId, onSelect, onDelete, disabled = false, className, }) {
25
+ if (conversations.length === 0) {
26
+ return null;
27
+ }
28
+ return (_jsx(TooltipProvider, { children: _jsxs(DropdownMenu, { children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", disabled: disabled, className: cn('text-muted-foreground hover:text-foreground', className), children: _jsx(Clock, { className: "size-4" }) }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: "Conversation history" }) })] }), _jsxs(DropdownMenuContent, { align: "start", className: "w-64", children: [_jsx("div", { className: "text-muted-foreground px-2 py-1.5 text-xs font-medium", children: "Recent conversations" }), _jsx(DropdownMenuSeparator, {}), _jsx("div", { className: "max-h-64 overflow-y-auto", children: conversations.slice(0, 20).map((conv) => (_jsxs(DropdownMenuItem, { onClick: () => onSelect(conv.id), className: cn('group flex cursor-pointer items-start gap-2 py-2', conv.id === currentConversationId && 'bg-accent'), children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-sm", children: conv.title }), _jsxs("p", { className: "text-muted-foreground text-xs", children: [formatRelativeTime(conv.updatedAt), " \u00B7 ", conv.messageCount, ' ', "messages"] })] }), onDelete && (_jsx(Button, { variant: "ghost", size: "icon-sm", onClick: (e) => {
29
+ e.stopPropagation();
30
+ onDelete(conv.id);
31
+ }, className: "text-muted-foreground hover:text-destructive size-6 shrink-0 opacity-0 group-hover:opacity-100", children: _jsx(Trash2, { className: "size-3" }) }))] }, conv.id))) })] })] }) }));
32
+ }
@@ -0,0 +1,10 @@
1
+ export { ChatInput } from './chat-input';
2
+ export { Message, ThinkingMessage, type ChatMessage, type ChatMessagePart, } from './chat-message';
3
+ export { Messages } from './chat-messages';
4
+ export { ModelSelector, type ModelOption } from './model-selector';
5
+ export { ReasoningEffortSelector, type ReasoningEffortOption, } from './reasoning-effort-selector';
6
+ export { ConversationHistory, type ConversationMeta, } from './conversation-history';
7
+ export { ChatPanel, type ChatPanelProps } from './chat-panel';
8
+ export { ThinkingTimeline, ThinkingTimelineMarker, type ThinkingTimelineItem, type ThinkingTimelineProps, } from './thinking-timeline';
9
+ export { getToolHandler, DEFAULT_TOOL_HANDLERS, type ToolHandler, } from './tool-handlers';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7D,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"}
@@ -0,0 +1,9 @@
1
+ export { ChatInput } from './chat-input';
2
+ export { Message, ThinkingMessage, } from './chat-message';
3
+ export { Messages } from './chat-messages';
4
+ export { ModelSelector } from './model-selector';
5
+ export { ReasoningEffortSelector, } from './reasoning-effort-selector';
6
+ export { ConversationHistory, } from './conversation-history';
7
+ export { ChatPanel } from './chat-panel';
8
+ export { ThinkingTimeline, ThinkingTimelineMarker, } from './thinking-timeline';
9
+ export { getToolHandler, DEFAULT_TOOL_HANDLERS, } from './tool-handlers';
@@ -0,0 +1,16 @@
1
+ import type { ReactNode } from 'react';
2
+ export type ModelOption = {
3
+ id: string;
4
+ name: string;
5
+ icon: ReactNode;
6
+ };
7
+ type ModelSelectorProps = {
8
+ models: ModelOption[];
9
+ selectedModel: string;
10
+ onModelChange: (modelId: string) => void;
11
+ disabled?: boolean;
12
+ className?: string;
13
+ };
14
+ export declare function ModelSelector({ models, selectedModel, onModelChange, disabled, className, }: ModelSelectorProps): import("react/jsx-runtime").JSX.Element;
15
+ export {};
16
+ //# sourceMappingURL=model-selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../../src/components/chat/model-selector.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAUtC,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,SAAS,CAAA;CAChB,CAAA;AAED,KAAK,kBAAkB,GAAG;IACxB,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,aAAa,EACb,aAAa,EACb,QAAgB,EAChB,SAAS,GACV,EAAE,kBAAkB,2CAqCpB"}
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Check, ChevronDown } from 'lucide-react';
3
+ import { cn } from '../../lib/utils';
4
+ import { Button } from '../ui/button';
5
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '../ui/dropdown-menu';
6
+ export function ModelSelector({ models, selectedModel, onModelChange, disabled = false, className, }) {
7
+ const currentModel = models.find((m) => m.id === selectedModel) ?? models[0];
8
+ return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", disabled: disabled, className: cn('text-muted-foreground hover:text-foreground h-7 gap-1.5 rounded-full px-2.5 text-xs font-medium', className), children: [_jsx("span", { children: currentModel?.icon }), _jsx("span", { children: currentModel?.name }), _jsx(ChevronDown, { className: "size-3 opacity-50" })] }) }), _jsx(DropdownMenuContent, { align: "start", className: "min-w-[160px]", children: models.map((model) => (_jsxs(DropdownMenuItem, { onClick: () => onModelChange(model.id), className: "flex items-center gap-2", children: [_jsx("span", { children: model.icon }), _jsx("span", { className: "flex-1", children: model.name }), model.id === selectedModel && (_jsx(Check, { className: "text-primary size-4" }))] }, model.id))) })] }));
9
+ }
@@ -0,0 +1,14 @@
1
+ export type ReasoningEffortOption = {
2
+ value: string;
3
+ label: string;
4
+ };
5
+ type ReasoningEffortSelectorProps = {
6
+ options: ReasoningEffortOption[];
7
+ selectedEffort: string;
8
+ onEffortChange: (effort: string) => void;
9
+ disabled?: boolean;
10
+ className?: string;
11
+ };
12
+ export declare function ReasoningEffortSelector({ options, selectedEffort, onEffortChange, disabled, className, }: ReasoningEffortSelectorProps): import("react/jsx-runtime").JSX.Element;
13
+ export {};
14
+ //# sourceMappingURL=reasoning-effort-selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reasoning-effort-selector.d.ts","sourceRoot":"","sources":["../../../src/components/chat/reasoning-effort-selector.tsx"],"names":[],"mappings":"AAgBA,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,KAAK,4BAA4B,GAAG;IAClC,OAAO,EAAE,qBAAqB,EAAE,CAAA;IAChC,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,wBAAgB,uBAAuB,CAAC,EACtC,OAAO,EACP,cAAc,EACd,cAAc,EACd,QAAgB,EAChB,SAAS,GACV,EAAE,4BAA4B,2CA6C9B"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Brain, Check } from 'lucide-react';
3
+ import { cn } from '../../lib/utils';
4
+ import { Button } from '../ui/button';
5
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '../ui/dropdown-menu';
6
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '../ui/tooltip';
7
+ export function ReasoningEffortSelector({ options, selectedEffort, onEffortChange, disabled = false, className, }) {
8
+ const currentOption = options.find((o) => o.value === selectedEffort) ?? options[1]; // Default to medium
9
+ return (_jsx(TooltipProvider, { children: _jsxs(DropdownMenu, { children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", disabled: disabled, className: cn('text-muted-foreground hover:text-foreground', className), children: _jsx(Brain, { className: "size-4" }) }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsxs("p", { children: ["Reasoning: ", currentOption?.label] }) })] }), _jsx(DropdownMenuContent, { align: "start", className: "min-w-[140px]", children: options.map((option) => (_jsxs(DropdownMenuItem, { onClick: () => onEffortChange(option.value), className: "flex items-center gap-2", children: [_jsx(Brain, { className: "text-muted-foreground size-3.5" }), _jsx("span", { className: "flex-1", children: option.label }), option.value === selectedEffort && (_jsx(Check, { className: "text-primary size-4" }))] }, option.value))) })] }) }));
10
+ }
@@ -0,0 +1,21 @@
1
+ import { type ReactNode } from 'react';
2
+ export declare enum ThinkingTimelineMarker {
3
+ Default = "default",
4
+ None = "none",
5
+ Loading = "loading",
6
+ Search = "search",
7
+ Read = "read",
8
+ File = "file",
9
+ Terminal = "terminal",
10
+ Folder = "folder"
11
+ }
12
+ export type ThinkingTimelineItem = {
13
+ content: ReactNode;
14
+ marker?: ThinkingTimelineMarker;
15
+ };
16
+ export type ThinkingTimelineProps = {
17
+ items: ThinkingTimelineItem[];
18
+ className?: string;
19
+ };
20
+ export declare function ThinkingTimeline({ items, className }: ThinkingTimelineProps): import("react/jsx-runtime").JSX.Element | null;
21
+ //# sourceMappingURL=thinking-timeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thinking-timeline.d.ts","sourceRoot":"","sources":["../../../src/components/chat/thinking-timeline.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,oBAAY,sBAAsB;IAChC,OAAO,YAAY;IACnB,IAAI,SAAS;IACb,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,IAAI,SAAS;IACb,IAAI,SAAS;IACb,QAAQ,aAAa;IACrB,MAAM,WAAW;CAClB;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,SAAS,CAAA;IAClB,MAAM,CAAC,EAAE,sBAAsB,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,oBAAoB,EAAE,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,qBAAqB,kDAwC3E"}
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Eye, FileText, FolderOpen, Globe, Loader2, Terminal, } from 'lucide-react';
4
+ import { cn } from '../../lib/utils';
5
+ export var ThinkingTimelineMarker;
6
+ (function (ThinkingTimelineMarker) {
7
+ ThinkingTimelineMarker["Default"] = "default";
8
+ ThinkingTimelineMarker["None"] = "none";
9
+ ThinkingTimelineMarker["Loading"] = "loading";
10
+ ThinkingTimelineMarker["Search"] = "search";
11
+ ThinkingTimelineMarker["Read"] = "read";
12
+ ThinkingTimelineMarker["File"] = "file";
13
+ ThinkingTimelineMarker["Terminal"] = "terminal";
14
+ ThinkingTimelineMarker["Folder"] = "folder";
15
+ })(ThinkingTimelineMarker || (ThinkingTimelineMarker = {}));
16
+ export function ThinkingTimeline({ items, className }) {
17
+ if (!items?.length) {
18
+ return null;
19
+ }
20
+ return (_jsx("div", { className: cn('relative my-1', className), children: _jsx("ol", { className: "relative flex flex-col", children: items.map((item, index) => {
21
+ const isLast = index === items.length - 1;
22
+ const marker = item.marker ?? ThinkingTimelineMarker.Default;
23
+ return (_jsxs("li", { className: cn('relative flex w-full items-start gap-2', !isLast && 'pb-4'), children: [_jsxs("div", { className: "relative flex w-5 shrink-0 justify-center self-stretch", children: [!isLast && (_jsx("div", { className: "bg-border pointer-events-none absolute bottom-[-24px] left-1/2 top-6 w-px -translate-x-1/2" })), _jsx("span", { className: cn('text-muted-foreground flex h-6 w-6 items-center justify-center', marker === ThinkingTimelineMarker.Default ? 'mt-1' : 'mt-0'), children: getMarkerVisual(marker) })] }), _jsx("div", { className: "min-w-0 flex-1", children: item.content })] }, index));
24
+ }) }) }));
25
+ }
26
+ function getMarkerVisual(marker) {
27
+ switch (marker) {
28
+ case ThinkingTimelineMarker.None:
29
+ return null;
30
+ case ThinkingTimelineMarker.Loading:
31
+ return _jsx(Loader2, { className: "size-4 animate-spin" });
32
+ case ThinkingTimelineMarker.Search:
33
+ return _jsx(Globe, { className: "size-4" });
34
+ case ThinkingTimelineMarker.Read:
35
+ return _jsx(Eye, { className: "size-4" });
36
+ case ThinkingTimelineMarker.File:
37
+ return _jsx(FileText, { className: "size-4" });
38
+ case ThinkingTimelineMarker.Terminal:
39
+ return _jsx(Terminal, { className: "size-4" });
40
+ case ThinkingTimelineMarker.Folder:
41
+ return _jsx(FolderOpen, { className: "size-4" });
42
+ case ThinkingTimelineMarker.Default:
43
+ default:
44
+ return (_jsx("span", { className: "bg-foreground/70 block h-[6px] w-[6px] rounded-full" }));
45
+ }
46
+ }
@@ -0,0 +1,21 @@
1
+ import { type ReactNode } from 'react';
2
+ import { ThinkingTimelineMarker } from './thinking-timeline';
3
+ /**
4
+ * Tool handler definition
5
+ */
6
+ export type ToolHandler = {
7
+ loadingLabel: string;
8
+ marker?: ThinkingTimelineMarker;
9
+ inline?: boolean;
10
+ renderOutput: (output: unknown, toolCallId: string) => ReactNode;
11
+ renderLoading?: (args?: unknown) => ReactNode;
12
+ };
13
+ /**
14
+ * Default tool handlers for Moldable tools
15
+ */
16
+ export declare const DEFAULT_TOOL_HANDLERS: Record<string, ToolHandler>;
17
+ /**
18
+ * Get the handler for a tool
19
+ */
20
+ export declare function getToolHandler(toolName: string): ToolHandler;
21
+ //# sourceMappingURL=tool-handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-handlers.d.ts","sourceRoot":"","sources":["../../../src/components/chat/tool-handlers.tsx"],"names":[],"mappings":"AAqBA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAOhD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAE5D;;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;CAC9C,CAAA;AAmND;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CA+nC7D,CAAA;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAgC5D"}