@messenger-box/tailwind-ui-inbox 10.0.3-alpha.73 → 10.0.3-alpha.74

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 (145) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/components/AIAgent/AIAgent.d.ts +7 -0
  3. package/lib/components/AIAgent/AIAgent.d.ts.map +1 -1
  4. package/lib/components/AIAgent/AIAgent.js +362 -615
  5. package/lib/components/AIAgent/AIAgent.js.map +1 -1
  6. package/lib/components/InboxMessage/InputComponent.d.ts.map +1 -1
  7. package/lib/components/InboxMessage/InputComponent.js +143 -140
  8. package/lib/components/InboxMessage/InputComponent.js.map +1 -1
  9. package/lib/components/InboxMessage/RightSidebarAi.d.ts +23 -0
  10. package/lib/components/InboxMessage/RightSidebarAi.d.ts.map +1 -0
  11. package/lib/components/InboxMessage/RightSidebarAi.js +9 -0
  12. package/lib/components/InboxMessage/RightSidebarAi.js.map +1 -0
  13. package/lib/components/InboxMessage/index.d.ts +1 -0
  14. package/lib/components/InboxMessage/index.d.ts.map +1 -1
  15. package/lib/components/InboxMessage/message-widgets/ErrorFixCard.d.ts +11 -0
  16. package/lib/components/InboxMessage/message-widgets/ErrorFixCard.d.ts.map +1 -0
  17. package/lib/components/InboxMessage/message-widgets/ErrorFixCard.js +194 -0
  18. package/lib/components/InboxMessage/message-widgets/ErrorFixCard.js.map +1 -0
  19. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts +5 -1
  20. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts.map +1 -1
  21. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js +308 -857
  22. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js.map +1 -1
  23. package/lib/components/ModelConfigPanel.d.ts +12 -0
  24. package/lib/components/ModelConfigPanel.d.ts.map +1 -0
  25. package/lib/components/ModelConfigPanel.js +304 -0
  26. package/lib/components/ModelConfigPanel.js.map +1 -0
  27. package/lib/components/filler-components/RightSiderBar.d.ts +24 -0
  28. package/lib/components/filler-components/RightSiderBar.d.ts.map +1 -0
  29. package/lib/components/filler-components/RightSiderBar.js +335 -0
  30. package/lib/components/filler-components/RightSiderBar.js.map +1 -0
  31. package/lib/components/index.d.ts +4 -2
  32. package/lib/components/index.d.ts.map +1 -1
  33. package/lib/components/live-code-editor/hybrid-live-editor.d.ts +20 -0
  34. package/lib/components/live-code-editor/hybrid-live-editor.d.ts.map +1 -0
  35. package/lib/components/live-code-editor/hybrid-live-editor.js +68 -0
  36. package/lib/components/live-code-editor/hybrid-live-editor.js.map +1 -0
  37. package/lib/components/live-code-editor/index.d.ts +4 -0
  38. package/lib/components/live-code-editor/index.d.ts.map +1 -0
  39. package/lib/components/live-code-editor/live-code-editor.d.ts +14 -0
  40. package/lib/components/live-code-editor/live-code-editor.d.ts.map +1 -0
  41. package/lib/components/live-code-editor/live-code-editor.js +207 -0
  42. package/lib/components/live-code-editor/live-code-editor.js.map +1 -0
  43. package/lib/components/slot-fill/chat-message-filler.js +1 -1
  44. package/lib/components/slot-fill/chat-message-filler.js.map +1 -1
  45. package/lib/components/slot-fill/index.d.ts +1 -0
  46. package/lib/components/slot-fill/index.d.ts.map +1 -1
  47. package/lib/components/slot-fill/right-sidebar-filler.d.ts +4 -0
  48. package/lib/components/slot-fill/right-sidebar-filler.d.ts.map +1 -0
  49. package/lib/components/slot-fill/right-sidebar-filler.js +13 -0
  50. package/lib/components/slot-fill/right-sidebar-filler.js.map +1 -0
  51. package/lib/components/ui/button.d.ts +9 -0
  52. package/lib/components/ui/button.d.ts.map +1 -0
  53. package/lib/compute.js +1 -2
  54. package/lib/container/AiInbox.d.ts.map +1 -1
  55. package/lib/container/AiLandingInput.d.ts.map +1 -1
  56. package/lib/container/AiLandingInput.js +46 -119
  57. package/lib/container/AiLandingInput.js.map +1 -1
  58. package/lib/container/Inbox.js +1 -1
  59. package/lib/container/Inbox.js.map +1 -1
  60. package/lib/container/InboxAiMessagesLoader.d.ts +0 -21
  61. package/lib/container/InboxAiMessagesLoader.d.ts.map +1 -1
  62. package/lib/container/InboxAiMessagesLoader.js +18 -32
  63. package/lib/container/InboxAiMessagesLoader.js.map +1 -1
  64. package/lib/container/ServiceInbox.js +1 -1
  65. package/lib/container/ServiceInbox.js.map +1 -1
  66. package/lib/container/ThreadMessages.js +1 -1
  67. package/lib/container/ThreadMessages.js.map +1 -1
  68. package/lib/container/ThreadMessagesInbox.js +1 -1
  69. package/lib/container/ThreadMessagesInbox.js.map +1 -1
  70. package/lib/container/Threads.js +1 -1
  71. package/lib/container/Threads.js.map +1 -1
  72. package/lib/container/index.d.ts +2 -1
  73. package/lib/container/index.d.ts.map +1 -1
  74. package/lib/enums/messenger-slot-fill-name-enum.d.ts +2 -1
  75. package/lib/enums/messenger-slot-fill-name-enum.d.ts.map +1 -1
  76. package/lib/enums/messenger-slot-fill-name-enum.js +1 -0
  77. package/lib/enums/messenger-slot-fill-name-enum.js.map +1 -1
  78. package/lib/hooks/index.d.ts +3 -0
  79. package/lib/hooks/index.d.ts.map +1 -0
  80. package/lib/hooks/use-file-sync.d.ts +16 -0
  81. package/lib/hooks/use-file-sync.d.ts.map +1 -0
  82. package/lib/hooks/use-file-sync.js +63 -0
  83. package/lib/hooks/use-file-sync.js.map +1 -0
  84. package/lib/hooks/usePersistentModelConfig.d.ts +15 -0
  85. package/lib/hooks/usePersistentModelConfig.d.ts.map +1 -0
  86. package/lib/hooks/usePersistentModelConfig.js +46 -0
  87. package/lib/hooks/usePersistentModelConfig.js.map +1 -0
  88. package/lib/index.d.ts +4 -2
  89. package/lib/index.d.ts.map +1 -1
  90. package/lib/index.js +1 -1
  91. package/lib/machines/aiAgentMachine.d.ts.map +1 -1
  92. package/lib/machines/aiAgentMachine.js +64 -21
  93. package/lib/machines/aiAgentMachine.js.map +1 -1
  94. package/lib/machines/aiAgentMachine.simple.d.ts +3 -0
  95. package/lib/machines/aiAgentMachine.simple.d.ts.map +1 -0
  96. package/lib/machines/aiAgentMachine.simple.js +108 -0
  97. package/lib/machines/aiAgentMachine.simple.js.map +1 -0
  98. package/lib/machines/index.d.ts +3 -0
  99. package/lib/machines/index.d.ts.map +1 -0
  100. package/lib/module.d.ts +2 -1
  101. package/lib/module.d.ts.map +1 -1
  102. package/lib/module.js +11 -3
  103. package/lib/module.js.map +1 -1
  104. package/lib/routes.json +1 -2
  105. package/lib/templates/InboxWithAi.d.ts.map +1 -1
  106. package/lib/templates/InboxWithAi.js +129 -70
  107. package/lib/templates/InboxWithAi.js.map +1 -1
  108. package/lib/templates/InboxWithAi.tsx +151 -90
  109. package/lib/utils/utils.d.ts +2 -0
  110. package/lib/utils/utils.d.ts.map +1 -0
  111. package/lib/utils/utils.js +3 -0
  112. package/lib/utils/utils.js.map +1 -0
  113. package/package.json +8 -5
  114. package/src/components/AIAgent/AIAgent.tsx +469 -731
  115. package/src/components/AIAgent/AIAgent.tsx.bk +1365 -0
  116. package/src/components/InboxMessage/InputComponent.tsx +2 -1
  117. package/src/components/InboxMessage/RightSidebarAi.tsx +37 -0
  118. package/src/components/InboxMessage/index.ts +1 -0
  119. package/src/components/InboxMessage/message-widgets/ErrorFixCard.tsx +240 -0
  120. package/src/components/InboxMessage/message-widgets/ModernMessageGroup.tsx +337 -1116
  121. package/src/components/ModelConfigPanel.tsx +334 -0
  122. package/src/components/filler-components/RightSiderBar.tsx +408 -0
  123. package/src/components/index.ts +4 -1
  124. package/src/components/live-code-editor/hybrid-live-editor.tsx +105 -0
  125. package/src/components/live-code-editor/index.ts +3 -0
  126. package/src/components/live-code-editor/live-code-editor.tsx +257 -0
  127. package/src/components/slot-fill/index.ts +1 -0
  128. package/src/components/slot-fill/right-sidebar-filler.tsx +39 -0
  129. package/src/components/ui/button.tsx +32 -0
  130. package/src/container/AiInbox.tsx +26 -3
  131. package/src/container/AiLandingInput.tsx +48 -22
  132. package/src/container/InboxAiMessagesLoader.tsx +17 -31
  133. package/src/container/index.ts +2 -0
  134. package/src/enums/messenger-slot-fill-name-enum.ts +1 -0
  135. package/src/hooks/index.ts +2 -0
  136. package/src/hooks/use-file-sync.ts +91 -0
  137. package/src/hooks/usePersistentModelConfig.ts +63 -0
  138. package/src/index.ts +7 -0
  139. package/src/machines/aiAgentMachine.simple.ts +89 -0
  140. package/src/machines/aiAgentMachine.ts +67 -19
  141. package/src/machines/aiAgentMachine.ts.bk +1296 -0
  142. package/src/machines/index.ts +2 -0
  143. package/src/module.tsx +10 -1
  144. package/src/templates/InboxWithAi.tsx +151 -90
  145. package/src/utils/utils.ts +3 -0
@@ -0,0 +1,257 @@
1
+ import React, { useState, useCallback, useRef, useEffect } from 'react';
2
+ import { Editor } from '@monaco-editor/react';
3
+ import { BsFileText } from '@react-icons/all-files/bs/BsFileText.js';
4
+ import { BsFolder } from '@react-icons/all-files/bs/BsFolder.js';
5
+ import { BsChevronRight } from '@react-icons/all-files/bs/BsChevronRight.js';
6
+ import { BsChevronDown } from '@react-icons/all-files/bs/BsChevronDown.js';
7
+
8
+ interface LiveCodeEditorProps {
9
+ files?: Record<string, string>;
10
+ className?: string;
11
+ onCopyToClipboard?: (text: string) => void;
12
+ onFileUpdate?: (filePath: string, content: string) => Promise<void>;
13
+ readOnly?: boolean;
14
+ autoSave?: boolean;
15
+ debounceMs?: number;
16
+ showFileExplorer?: boolean;
17
+ }
18
+
19
+ interface FileNode {
20
+ name: string;
21
+ type: 'file' | 'folder';
22
+ children?: FileNode[];
23
+ path: string;
24
+ }
25
+
26
+ // File Explorer Component
27
+ const FileExplorer: React.FC<{
28
+ files: Record<string, string>;
29
+ currentFile: string;
30
+ onFileSelect: (filePath: string) => void;
31
+ }> = ({ files, currentFile, onFileSelect }) => {
32
+ // Simple file list without folder structure
33
+ const fileList = Object.keys(files);
34
+
35
+ return (
36
+ <div className="w-48 bg-gray-50 border-l border-gray-200 flex flex-col">
37
+ <div className="p-3 border-b border-gray-200 bg-white">
38
+ <h3 className="text-sm font-semibold text-gray-700">Files</h3>
39
+ </div>
40
+ <div className="flex-1 overflow-y-auto">
41
+ {fileList.map((filePath) => {
42
+ const fileName = filePath.split('/').pop() || filePath;
43
+ const isSelected = currentFile === filePath;
44
+
45
+ return (
46
+ <div
47
+ key={filePath}
48
+ className={`flex items-center py-2 px-3 cursor-pointer hover:bg-gray-100 ${
49
+ isSelected ? 'bg-blue-50 text-blue-600' : 'text-gray-700'
50
+ }`}
51
+ onClick={() => onFileSelect(filePath)}
52
+ >
53
+ <BsFileText className="w-4 h-4 mr-2 text-gray-500" />
54
+ <span className="text-sm">{fileName}</span>
55
+ </div>
56
+ );
57
+ })}
58
+ </div>
59
+ </div>
60
+ );
61
+ };
62
+
63
+ export const LiveCodeEditor: React.FC<LiveCodeEditorProps> = ({
64
+ files = {},
65
+ className = '',
66
+ onCopyToClipboard,
67
+ onFileUpdate,
68
+ readOnly = false,
69
+ autoSave = true,
70
+ debounceMs = 500,
71
+ showFileExplorer = true,
72
+ }) => {
73
+ const [currentFile, setCurrentFile] = useState<string>(Object.keys(files)[0] || '');
74
+ const [fileContent, setFileContent] = useState<string>(files[currentFile] || '');
75
+ const editorRef = useRef<any>(null);
76
+ const timeoutRef = useRef<NodeJS.Timeout | null>(null);
77
+
78
+ // Update file content when files prop changes
79
+ useEffect(() => {
80
+ if (files && Object.keys(files).length > 0) {
81
+ const firstFile = Object.keys(files)[0];
82
+ setCurrentFile(firstFile);
83
+ setFileContent(files[firstFile] || '');
84
+ }
85
+ }, [files]);
86
+
87
+ const handleEditorDidMount = useCallback(
88
+ (editor: any, monaco: any) => {
89
+ editorRef.current = editor;
90
+
91
+ // Configure editor options
92
+ editor.updateOptions({
93
+ readOnly,
94
+ minimap: { enabled: false },
95
+ scrollBeyondLastLine: false,
96
+ wordWrap: 'on',
97
+ lineNumbers: 'on',
98
+ folding: true,
99
+ lineDecorationsWidth: 0,
100
+ lineNumbersMinChars: 0,
101
+ renderLineHighlight: 'none',
102
+ cursorStyle: 'line',
103
+ cursorWidth: 1,
104
+ fontSize: 14,
105
+ fontFamily: "'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace",
106
+ });
107
+
108
+ // Add keyboard shortcuts
109
+ editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyC, () => {
110
+ const selection = editor.getSelection();
111
+ const selectedText = editor.getModel()?.getValueInRange(selection) || '';
112
+ if (selectedText && onCopyToClipboard) {
113
+ onCopyToClipboard(selectedText);
114
+ }
115
+ });
116
+ },
117
+ [readOnly, onCopyToClipboard],
118
+ );
119
+
120
+ const handleEditorChange = useCallback(
121
+ (value: string | undefined) => {
122
+ if (value !== undefined) {
123
+ setFileContent(value);
124
+
125
+ // Debounced auto-save
126
+ if (autoSave && onFileUpdate && currentFile) {
127
+ if (timeoutRef.current) {
128
+ clearTimeout(timeoutRef.current);
129
+ }
130
+
131
+ timeoutRef.current = setTimeout(() => {
132
+ onFileUpdate(currentFile, value);
133
+ }, debounceMs);
134
+ }
135
+ }
136
+ },
137
+ [autoSave, onFileUpdate, currentFile, debounceMs],
138
+ );
139
+
140
+ const getLanguageFromFileName = (fileName: string): string => {
141
+ const extension = fileName.split('.').pop()?.toLowerCase();
142
+ const languageMap: Record<string, string> = {
143
+ js: 'javascript',
144
+ jsx: 'javascript',
145
+ ts: 'typescript',
146
+ tsx: 'typescript',
147
+ py: 'python',
148
+ java: 'java',
149
+ cpp: 'cpp',
150
+ c: 'c',
151
+ cs: 'csharp',
152
+ php: 'php',
153
+ rb: 'ruby',
154
+ go: 'go',
155
+ rs: 'rust',
156
+ html: 'html',
157
+ css: 'css',
158
+ scss: 'scss',
159
+ sass: 'sass',
160
+ less: 'less',
161
+ json: 'json',
162
+ xml: 'xml',
163
+ yaml: 'yaml',
164
+ yml: 'yaml',
165
+ md: 'markdown',
166
+ sql: 'sql',
167
+ sh: 'shell',
168
+ bash: 'shell',
169
+ dockerfile: 'dockerfile',
170
+ };
171
+ return languageMap[extension || ''] || 'plaintext';
172
+ };
173
+
174
+ // Cleanup timeout on unmount
175
+ useEffect(() => {
176
+ return () => {
177
+ if (timeoutRef.current) {
178
+ clearTimeout(timeoutRef.current);
179
+ }
180
+ };
181
+ }, []);
182
+
183
+ const handleFileSelect = useCallback(
184
+ (filePath: string) => {
185
+ setCurrentFile(filePath);
186
+ setFileContent(files[filePath] || '');
187
+ },
188
+ [files],
189
+ );
190
+
191
+ return (
192
+ <div className={`flex h-full w-full ${className}`}>
193
+ {/* Editor Area - Left Side */}
194
+ <div className="flex-1 flex flex-col">
195
+ {/* File Name Header */}
196
+ <div className="flex items-center justify-between px-4 py-2 bg-gray-100 border-b border-gray-200">
197
+ <div className="flex items-center">
198
+ <BsFileText className="w-4 h-4 mr-2 text-gray-500" />
199
+ <span className="text-sm font-medium text-gray-700">
200
+ {currentFile.split('/').pop() || currentFile}
201
+ </span>
202
+ </div>
203
+ <div className="flex items-center space-x-2">
204
+ <button className="text-gray-400 hover:text-gray-600">
205
+ <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
206
+ <path
207
+ strokeLinecap="round"
208
+ strokeLinejoin="round"
209
+ strokeWidth={2}
210
+ d="M6 18L18 6M6 6l12 12"
211
+ />
212
+ </svg>
213
+ </button>
214
+ </div>
215
+ </div>
216
+
217
+ {/* Monaco Editor */}
218
+ <div className="flex-1" style={{ height: '100%', minHeight: '400px' }}>
219
+ <Editor
220
+ height="100%"
221
+ language={getLanguageFromFileName(currentFile)}
222
+ value={fileContent}
223
+ onMount={handleEditorDidMount}
224
+ onChange={handleEditorChange}
225
+ options={{
226
+ readOnly,
227
+ minimap: { enabled: false },
228
+ scrollBeyondLastLine: true,
229
+ wordWrap: 'on',
230
+ lineNumbers: 'on',
231
+ folding: true,
232
+ lineDecorationsWidth: 0,
233
+ lineNumbersMinChars: 0,
234
+ renderLineHighlight: 'none',
235
+ cursorStyle: 'line',
236
+ cursorWidth: 1,
237
+ fontSize: 14,
238
+ fontFamily:
239
+ "'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace",
240
+ scrollbar: {
241
+ vertical: 'auto',
242
+ horizontal: 'auto',
243
+ verticalScrollbarSize: 8,
244
+ horizontalScrollbarSize: 8,
245
+ },
246
+ }}
247
+ />
248
+ </div>
249
+ </div>
250
+
251
+ {/* File Explorer - Right Side */}
252
+ {showFileExplorer && Object.keys(files).length > 0 && (
253
+ <FileExplorer files={files} currentFile={currentFile} onFileSelect={handleFileSelect} />
254
+ )}
255
+ </div>
256
+ );
257
+ };
@@ -1,2 +1,3 @@
1
1
  export { ChatMessageFiller } from './chat-message-slot';
2
2
  export { ChatMessageFill2 } from './chat-message-filler';
3
+ export { RightSidebarFill as RightSidebarFillDefault } from './right-sidebar-filler';
@@ -0,0 +1,39 @@
1
+ import * as React from 'react';
2
+ import { Fill } from '@common-stack/components-pro';
3
+ import { MessengerSlotFillNameEnum } from '../../enums';
4
+ import { RightSidebarFillComponent } from '../filler-components/RightSiderBar';
5
+
6
+ type RightSidebarProps = {
7
+ channelId: string;
8
+ detailSidebarOptions: {
9
+ isMobileView: boolean;
10
+ isSmallTabletView: boolean;
11
+ isTabletView: boolean;
12
+ isDesktopView: boolean;
13
+ isLargeDesktopView: boolean;
14
+ isSmallScreen: boolean;
15
+ [key: string]: any;
16
+ };
17
+ windowWidth: number;
18
+ windowHeight: number;
19
+ activeTab?: string;
20
+ selectedPost?: any;
21
+ messages?: any[];
22
+ setIsLoading?: (isLoading: boolean) => void;
23
+ isLoading?: boolean;
24
+ [key: string]: any;
25
+ };
26
+
27
+ export const RightSidebarFiller = (props: any) => {
28
+ return <Fill {...props} name={MessengerSlotFillNameEnum.INBOX_WITH_AI_RIGHT_SIDEBAR} />;
29
+ };
30
+
31
+ export const RightSidebarFill = () => {
32
+ return (
33
+ <RightSidebarFiller>
34
+ {(props: RightSidebarProps) => {
35
+ return <RightSidebarFillComponent {...props} />;
36
+ }}
37
+ </RightSidebarFiller>
38
+ );
39
+ };
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import { cn } from '../../utils/utils';
3
+
4
+ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
5
+ variant?: 'default' | 'outline' | 'ghost' | 'link';
6
+ size?: 'default' | 'sm' | 'lg';
7
+ children: React.ReactNode;
8
+ }
9
+
10
+ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
11
+ ({ className, variant = 'default', size = 'default', ...props }, ref) => {
12
+ const baseClasses =
13
+ 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background';
14
+
15
+ const variants = {
16
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
17
+ outline: 'border border-input hover:bg-accent hover:text-accent-foreground',
18
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
19
+ link: 'underline-offset-4 hover:underline text-primary',
20
+ };
21
+
22
+ const sizes = {
23
+ default: 'h-10 py-2 px-4',
24
+ sm: 'h-9 px-3 rounded-md',
25
+ lg: 'h-11 px-8 rounded-md',
26
+ };
27
+
28
+ return <button className={cn(baseClasses, variants[variant], sizes[size], className)} ref={ref} {...props} />;
29
+ },
30
+ );
31
+
32
+ Button.displayName = 'Button';
@@ -845,6 +845,8 @@ const MessagesComponent = React.memo((props: any) => {
845
845
  const [isLoadingOlder, setIsLoadingOlder] = React.useState(false);
846
846
  const isLoadingOlderRef = useRef(false);
847
847
  const scrollTimeoutRef = useRef(null);
848
+ const shouldAutoScrollRef = useRef(true);
849
+ const hasInitializedRef = useRef(false);
848
850
 
849
851
  const auth = useSelector(userSelector);
850
852
  const { startUpload } = useUploadFiles();
@@ -866,9 +868,21 @@ const MessagesComponent = React.memo((props: any) => {
866
868
  }
867
869
  }, []);
868
870
 
869
- // Auto-scroll on new messages (but not when loading older messages)
871
+ // Auto-scroll rules:
872
+ // - Initial load: once messages arrive the first time
873
+ // - When user is at bottom and new messages arrive
874
+ // - When we explicitly set shouldAutoScrollRef (e.g., on send)
870
875
  useEffect(() => {
871
- if (!isLoadingOlderRef.current) {
876
+ if (isLoadingOlderRef.current) return;
877
+
878
+ // Initial load: first time messages appear
879
+ if (!hasInitializedRef.current && messages.length > 0) {
880
+ hasInitializedRef.current = true;
881
+ const timer = setTimeout(() => scrollToBottom(), 100);
882
+ return () => clearTimeout(timer);
883
+ }
884
+
885
+ if (shouldAutoScrollRef.current) {
872
886
  const timer = setTimeout(() => scrollToBottom(), 100);
873
887
  return () => clearTimeout(timer);
874
888
  }
@@ -996,6 +1010,12 @@ const MessagesComponent = React.memo((props: any) => {
996
1010
  const isAtTop = Math.ceil(scrollTop) <= 25;
997
1011
  const hasMoreMessages = totalCount > messages.length;
998
1012
 
1013
+ // Track bottom proximity to decide future auto-scroll behavior
1014
+ const distanceFromBottom = scrollHeight - (scrollTop + clientHeight);
1015
+ const isAtBottom = distanceFromBottom <= 30;
1016
+ // Only auto-scroll for future incoming messages if user is at bottom
1017
+ shouldAutoScrollRef.current = isAtBottom;
1018
+
999
1019
  // Additional Firefox-specific check
1000
1020
  const isFirefox = navigator.userAgent.includes('Firefox');
1001
1021
  const firefoxAdjustedTop = isFirefox ? Math.round(scrollTop) <= 30 : isAtTop;
@@ -1028,6 +1048,9 @@ const MessagesComponent = React.memo((props: any) => {
1028
1048
  if (!channelId) return;
1029
1049
 
1030
1050
  try {
1051
+ // Ensure we auto-scroll when sending a new message
1052
+ shouldAutoScrollRef.current = true;
1053
+ setTimeout(() => scrollToBottom(), 0);
1031
1054
  const postId = objectId();
1032
1055
  const currentDate = new Date();
1033
1056
 
@@ -1178,7 +1201,7 @@ const MessagesComponent = React.memo((props: any) => {
1178
1201
  console.error('Error sending message:', error);
1179
1202
  }
1180
1203
  },
1181
- [channelId, auth, startUpload, sendMsg],
1204
+ [channelId, auth, startUpload, sendMsg, scrollToBottom],
1182
1205
  );
1183
1206
 
1184
1207
  // Show loading spinner for initial load
@@ -3,7 +3,9 @@ import { useNavigate } from '@remix-run/react';
3
3
  import { RoomType } from 'common';
4
4
  import { InputComponent } from '../components/InboxMessage';
5
5
  import { objectId } from '@messenger-box/core';
6
- import { useAddChannelMutation, useSendMessagesMutation } from 'common/graphql';
6
+ import { useAddChannelMutation, useSendMessagesMutation, useCreateChannelWithProjectIdMutation } from 'common/graphql';
7
+ import { ModelConfigPanel } from '../components/ModelConfigPanel';
8
+ import { usePersistentModelConfig } from '../hooks/usePersistentModelConfig';
7
9
 
8
10
  const TailwindOverlaySpinner = React.memo(() => (
9
11
  <div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-30">
@@ -12,10 +14,14 @@ const TailwindOverlaySpinner = React.memo(() => (
12
14
  ));
13
15
 
14
16
  const AiLandingInput: React.FC = () => {
17
+ const [createChannelWithProjectId] = useCreateChannelWithProjectIdMutation();
15
18
  const [addChannel] = useAddChannelMutation();
16
19
  const [sendMessage] = useSendMessagesMutation();
17
20
  const navigate = useNavigate();
18
21
  const [isCreatingChannel, setIsCreatingChannel] = React.useState(false);
22
+ const { modelConfig, updateModelConfig, getValidatedConfig, hasApiKey } = usePersistentModelConfig();
23
+ // const [showModelConfig, setShowModelConfig] = React.useState(!hasApiKey);
24
+ const [showModelConfig, setShowModelConfig] = React.useState(true);
19
25
 
20
26
  return (
21
27
  <div className="flex items-center justify-center min-h-screen bg-gray-50">
@@ -25,38 +31,55 @@ const AiLandingInput: React.FC = () => {
25
31
  <h3 className="text-xl font-semibold text-gray-700 mb-2">What would you like to build?</h3>
26
32
  <p className="text-gray-500">Describe your idea and I'll help you create it step by step.</p>
27
33
  </div>
34
+ <div className="mb-0">
35
+ <div className="p-3 bg-gray-50 rounded-lg border">
36
+ <div className="mb-4">
37
+ <h3 className="text-sm font-medium text-gray-900 mb-2">Configuration</h3>
38
+ <p className="text-xs text-gray-600 mb-3">
39
+ Choose your AI model, template, and provide your API key before creating your
40
+ project.
41
+ </p>
42
+ </div>
43
+ <ModelConfigPanel
44
+ config={modelConfig}
45
+ onConfigChange={updateModelConfig}
46
+ isVisible={showModelConfig}
47
+ onToggleVisibility={() => setShowModelConfig(!showModelConfig)}
48
+ showTemplate={true}
49
+ />
50
+ {!hasApiKey && (
51
+ <div className="mt-2 text-xs text-amber-600">
52
+ ⚠️ Please provide an API key to create a projects
53
+ </div>
54
+ )}
55
+ </div>
56
+ </div>
28
57
  <InputComponent
29
58
  handleSend={async (message: string, files: File[]) => {
59
+ const validated = getValidatedConfig();
60
+ if (!validated && !hasApiKey) {
61
+ setShowModelConfig(true);
62
+ return;
63
+ }
30
64
  console.log('Message sent without channel:', message, files);
31
65
  setIsCreatingChannel(true);
32
66
  const id = objectId();
33
- addChannel({
67
+ createChannelWithProjectId({
34
68
  variables: {
35
- name: `AI-Assistant-${id}`,
36
- type: RoomType.Aiassistant,
37
- description: 'AI Assistant',
69
+ projectId: id,
70
+ channelInput: {
71
+ content: message,
72
+ modelConfig: modelConfig,
73
+ },
38
74
  },
39
75
  onCompleted: (data: any) => {
40
76
  console.log('Channel created:', data);
41
- if (!data.createChannel) {
77
+ if (!data.createChannelWithProjectId) {
42
78
  setIsCreatingChannel(false);
43
79
  return;
44
80
  }
45
- const createdId = data.createChannel.id;
46
- sendMessage({
47
- variables: {
48
- channelId: createdId,
49
- content: message,
50
- },
51
- onCompleted: () => {
52
- navigate(`/ai-messenger/app?id=${createdId}`, { replace: true });
53
- setIsCreatingChannel(false);
54
- },
55
- onError: (error: any) => {
56
- console.error('Error sending message:', error);
57
- setIsCreatingChannel(false);
58
- },
59
- });
81
+ navigate(`/ai-messenger/app?id=${id}`, { replace: true });
82
+ setIsCreatingChannel(false);
60
83
  },
61
84
  onError: (error: any) => {
62
85
  console.error('Error creating channel:', error);
@@ -66,7 +89,7 @@ const AiLandingInput: React.FC = () => {
66
89
  }}
67
90
  placeholder="Type your message here..."
68
91
  />
69
- <div className="mt-2">
92
+ {/* <div className="mt-2">
70
93
  <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
71
94
  <div className="space-y-2">
72
95
  <div className="relative">
@@ -155,6 +178,9 @@ const AiLandingInput: React.FC = () => {
155
178
  </div>
156
179
  </div>
157
180
  </div>
181
+ </div> */}
182
+ <div className="text-center text-sm text-gray-400">
183
+ Each project gets its own workspace where you can iterate and refine your ideas.
158
184
  </div>
159
185
  </div>
160
186
  </div>
@@ -1,37 +1,10 @@
1
1
  import React, { useMemo, useCallback } from 'react';
2
- import { useParams, useLocation } from '@remix-run/react';
3
- import { RoomType } from 'common';
4
- import { config } from '../config';
2
+ import { useParams, useLocation, useOutletContext } from '@remix-run/react';
5
3
  import { AIAgent } from '../components/AIAgent';
6
4
  import { useSelector, shallowEqual } from 'react-redux';
7
5
  import { Store, userSelector } from '@adminide-stack/user-auth0-client';
8
6
  import { IUserState } from '@adminide-stack/core';
9
7
 
10
- // Enhanced query parameters generator with better typing and flexibility
11
- export const queryParamsGenerator = (params: {
12
- role?: string;
13
- criteria?: Record<string, any>;
14
- supportServices?: boolean;
15
- orgName?: string;
16
- }) => ({
17
- variable1: {
18
- role: params.role,
19
- criteria: {
20
- ...params.criteria,
21
- ...(params.orgName && { orgName: params.orgName }),
22
- },
23
- supportServices: params.supportServices ? true : false,
24
- supportServiceCriteria: params.supportServices
25
- ? {
26
- type: RoomType.Service,
27
- }
28
- : undefined,
29
- orderBy: {
30
- lastPostAt: 'desc',
31
- },
32
- },
33
- });
34
-
35
8
  interface InboxWithAiLoaderOutletProps {
36
9
  channelFilters?: Record<string, any>;
37
10
  channelRole?: string;
@@ -42,14 +15,27 @@ interface InboxWithAiLoaderOutletProps {
42
15
  }
43
16
 
44
17
  const InboxWithAiLoaderOutlet = (props: InboxWithAiLoaderOutletProps) => {
45
- const { channelFilters: channelFilterProp, channelRole: channelRoleProp, supportServices, pathPrefix } = props;
46
- const { orgName, channelRole: channelRoleParam } = useParams();
47
18
  const location = useLocation();
19
+ const { messages, setMessages, selectedPost, setSelectedPost, setIsLoading, isLoading } = useOutletContext() as any;
48
20
  const urlParams = location?.search ? new URLSearchParams(location.search) : null;
49
21
  const channelId = urlParams?.get('id');
50
22
  const user: any = useSelector<Store.Auth, IUserState>(userSelector, shallowEqual);
51
23
 
52
- return <AIAgent channelId={channelId} placeholder="Ask me anything..." className="h-full" currentUser={user} />;
24
+ return (
25
+ <AIAgent
26
+ channelId={channelId}
27
+ placeholder="Ask me anything..."
28
+ className="h-full"
29
+ currentUser={user}
30
+ messages={messages}
31
+ setMessages={setMessages}
32
+ selectedPost={selectedPost}
33
+ setSelectedPost={setSelectedPost}
34
+ setIsLoading={setIsLoading}
35
+ isLoading={isLoading}
36
+ {...props}
37
+ />
38
+ );
53
39
  };
54
40
 
55
41
  // Display name for debugging
@@ -11,6 +11,7 @@ import { ThreadMessagesInbox } from './ThreadMessagesInbox';
11
11
  import InboxWithAiLoader from './InboxWithAiLoader';
12
12
  import InboxAiMessagesLoader from './InboxAiMessagesLoader';
13
13
  import InboxContainer from './InboxContainer';
14
+ import AiLandingInput from './AiLandingInput';
14
15
 
15
16
  export {
16
17
  Inbox,
@@ -26,4 +27,5 @@ export {
26
27
  InboxWithAiLoader,
27
28
  InboxAiMessagesLoader,
28
29
  InboxContainer,
30
+ AiLandingInput,
29
31
  };
@@ -1,3 +1,4 @@
1
1
  export enum MessengerSlotFillNameEnum {
2
2
  INBOX_RIGHT_SIDEBAR = 'inbox-right-sidebar',
3
+ INBOX_WITH_AI_RIGHT_SIDEBAR = 'inbox-with-ai-right-sidebar',
3
4
  }
@@ -0,0 +1,2 @@
1
+ export * from './usePersistentModelConfig';
2
+ export * from './use-file-sync';