Skip to content

Commit 616e4ec

Browse files
committed
chore: add lg-chat code
1 parent 46d9b2c commit 616e4ec

File tree

179 files changed

+7177
-110
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

179 files changed

+7177
-110
lines changed

packages/compass-assistant/package.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,23 @@
5757
"compass-preferences-model": "^2.50.0",
5858
"react": "^17.0.2",
5959
"throttleit": "^2.1.0",
60-
"use-sync-external-store": "^1.5.0"
60+
"use-sync-external-store": "^1.5.0",
61+
"@lg-chat/avatar": "file:src/vendor/@lg-chat/avatar",
62+
"@lg-chat/chat-disclaimer": "file:src/vendor/@lg-chat/chat-disclaimer",
63+
"@lg-chat/chat-window": "file:src/vendor/@lg-chat/chat-window",
64+
"@lg-chat/fixed-chat-window": "file:src/vendor/@lg-chat/fixed-chat-window",
65+
"@lg-chat/input-bar": "file:src/vendor/@lg-chat/input-bar",
66+
"@lg-chat/leafygreen-chat-provider": "file:src/vendor/@lg-chat/leafygreen-chat-provider",
67+
"@lg-chat/lg-markdown": "file:src/vendor/@lg-chat/lg-markdown",
68+
"@lg-chat/message-actions": "file:src/vendor/@lg-chat/message-actions",
69+
"@lg-chat/message-feed": "file:src/vendor/@lg-chat/message-feed",
70+
"@lg-chat/message-feedback": "file:src/vendor/@lg-chat/message-feedback",
71+
"@lg-chat/message-prompts": "file:src/vendor/@lg-chat/message-prompts",
72+
"@lg-chat/message-rating": "file:src/vendor/@lg-chat/message-rating",
73+
"@lg-chat/message": "file:src/vendor/@lg-chat/message",
74+
"@lg-chat/rich-links": "file:src/vendor/@lg-chat/rich-links",
75+
"@lg-chat/suggestions": "file:src/vendor/@lg-chat/suggestions",
76+
"@lg-chat/title-bar": "file:src/vendor/@lg-chat/title-bar"
6177
},
6278
"devDependencies": {
6379
"@mongodb-js/eslint-config-compass": "^1.4.6",
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#!/usr/bin/env node
2+
/* eslint-disable no-console */
3+
4+
const fs = require('fs');
5+
const path = require('path');
6+
7+
const VENDOR_DIR = path.join(
8+
__dirname,
9+
'..',
10+
'packages',
11+
'compass-assistant',
12+
'src',
13+
'vendor',
14+
'@lg-chat'
15+
);
16+
const OUTPUT_FILE = path.join(
17+
__dirname,
18+
'..',
19+
'packages',
20+
'compass-assistant',
21+
'src',
22+
'vendor',
23+
'lg-chat-vendor-package.json'
24+
);
25+
26+
function flattenDependencies() {
27+
console.log('Flattening @lg-chat dependencies...');
28+
29+
const allDependencies = {};
30+
const allPeerDependencies = {};
31+
const packageNames = [];
32+
33+
// Read all @lg-chat package.json files
34+
const lgChatDirs = fs
35+
.readdirSync(VENDOR_DIR, { withFileTypes: true })
36+
.filter((dirent) => dirent.isDirectory())
37+
.map((dirent) => dirent.name);
38+
39+
console.log(`Found ${lgChatDirs.length} @lg-chat packages:`, lgChatDirs);
40+
41+
lgChatDirs.forEach((dirName) => {
42+
const packageJsonPath = path.join(VENDOR_DIR, dirName, 'package.json');
43+
44+
if (fs.existsSync(packageJsonPath)) {
45+
try {
46+
const packageJson = JSON.parse(
47+
fs.readFileSync(packageJsonPath, 'utf8')
48+
);
49+
console.log(`Processing ${packageJson.name || dirName}...`);
50+
51+
packageNames.push(packageJson.name || `@lg-chat/${dirName}`);
52+
53+
// Merge production dependencies
54+
if (packageJson.dependencies) {
55+
Object.entries(packageJson.dependencies).forEach(([dep, version]) => {
56+
// Skip workspace dependencies and internal @lg-chat dependencies
57+
if (
58+
!version.startsWith('workspace:') &&
59+
!dep.startsWith('@lg-chat/')
60+
) {
61+
if (allDependencies[dep] && allDependencies[dep] !== version) {
62+
console.warn(
63+
`Version conflict for ${dep}: ${allDependencies[dep]} vs ${version}`
64+
);
65+
// Use the higher version number or keep existing if unable to determine
66+
allDependencies[dep] = version;
67+
} else {
68+
allDependencies[dep] = version;
69+
}
70+
}
71+
});
72+
}
73+
74+
// Merge peer dependencies
75+
if (packageJson.peerDependencies) {
76+
Object.entries(packageJson.peerDependencies).forEach(
77+
([dep, version]) => {
78+
// Skip workspace dependencies and internal @lg-chat dependencies
79+
if (
80+
!version.startsWith('workspace:') &&
81+
!dep.startsWith('@lg-chat/')
82+
) {
83+
if (
84+
allPeerDependencies[dep] &&
85+
allPeerDependencies[dep] !== version
86+
) {
87+
console.warn(
88+
`Peer dependency version conflict for ${dep}: ${allPeerDependencies[dep]} vs ${version}`
89+
);
90+
allPeerDependencies[dep] = version;
91+
} else {
92+
allPeerDependencies[dep] = version;
93+
}
94+
}
95+
}
96+
);
97+
}
98+
} catch (error) {
99+
console.error(`Error processing ${packageJsonPath}:`, error.message);
100+
}
101+
} else {
102+
console.warn(
103+
`No package.json found in ${path.join(VENDOR_DIR, dirName)}`
104+
);
105+
}
106+
});
107+
108+
// Create the flattened package.json
109+
const flattenedPackage = {
110+
name: '@lg-chat/vendor-flattened',
111+
version: '1.0.0',
112+
description: 'Flattened dependencies from all @lg-chat vendor packages',
113+
private: true,
114+
dependencies: allDependencies,
115+
peerDependencies: allPeerDependencies,
116+
_meta: {
117+
generated: new Date().toISOString(),
118+
sourcePackages: packageNames,
119+
script: 'scripts/flatten-lg-chat-dependencies.js',
120+
},
121+
};
122+
123+
// Write the flattened package.json
124+
fs.writeFileSync(OUTPUT_FILE, JSON.stringify(flattenedPackage, null, 2));
125+
126+
console.log('\n=== Summary ===');
127+
console.log(`Processed ${packageNames.length} packages`);
128+
console.log(
129+
`Found ${Object.keys(allDependencies).length} unique dependencies`
130+
);
131+
console.log(
132+
`Found ${Object.keys(allPeerDependencies).length} unique peer dependencies`
133+
);
134+
console.log(`Output written to: ${OUTPUT_FILE}`);
135+
136+
console.log('\n=== Dependencies ===');
137+
Object.entries(allDependencies).forEach(([dep, version]) => {
138+
console.log(` ${dep}: ${version}`);
139+
});
140+
141+
if (Object.keys(allPeerDependencies).length > 0) {
142+
console.log('\n=== Peer Dependencies ===');
143+
Object.entries(allPeerDependencies).forEach(([dep, version]) => {
144+
console.log(` ${dep}: ${version}`);
145+
});
146+
}
147+
}
148+
149+
if (require.main === module) {
150+
flattenDependencies();
151+
}
152+
153+
module.exports = { flattenDependencies };
Lines changed: 57 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,77 @@
1-
import React, { useCallback, useState } from 'react';
1+
import React, { useCallback } from 'react';
22
import type { AssistantMessage } from './compass-assistant-provider';
3-
import type { Chat } from './@ai-sdk/react/chat-react';
4-
import { useChat } from './@ai-sdk/react/use-chat';
3+
import type { Chat } from './vendor/@ai-sdk/react/chat-react';
4+
import { useChat } from './vendor/@ai-sdk/react/use-chat';
5+
import { ChatWindow } from './vendor/@lg-chat/chat-window/src/ChatWindow';
6+
import {
7+
LeafyGreenChatProvider,
8+
Variant,
9+
} from '@lg-chat/leafygreen-chat-provider';
10+
import { Message } from './vendor/@lg-chat/message/src/Message';
11+
import { MessageFeed } from './vendor/@lg-chat/message-feed/src/MessageFeed';
12+
import { InputBar } from './vendor/@lg-chat/input-bar/src/InputBar';
513

614
interface AssistantChatProps {
715
chat: Chat<AssistantMessage>;
816
}
917

10-
/**
11-
* This component is currently using placeholders as Leafygreen UI updates are not available yet.
12-
* Before release, we will replace this with the actual Leafygreen chat components.
13-
*/
1418
export const AssistantChat: React.FunctionComponent<AssistantChatProps> = ({
1519
chat,
1620
}) => {
17-
const [inputValue, setInputValue] = useState('');
18-
const { messages, sendMessage } = useChat({
21+
const { messages, sendMessage, status } = useChat({
1922
chat,
2023
});
2124

22-
const handleInputSubmit = useCallback(
23-
(e: React.FormEvent) => {
24-
e.preventDefault();
25-
if (inputValue.trim()) {
26-
void sendMessage({ text: inputValue.trim() });
27-
setInputValue('');
28-
}
25+
// Transform AI SDK messages to LeafyGreen chat format
26+
const lgMessages = messages.map((message) => ({
27+
id: message.id,
28+
messageBody:
29+
message.metadata?.displayText ||
30+
message.parts
31+
?.filter((part) => part.type === 'text')
32+
.map((part) => part.text)
33+
.join('') ||
34+
'',
35+
isSender: message.role === 'user',
36+
}));
37+
38+
const handleMessageSend = useCallback(
39+
(messageBody: string) => {
40+
void sendMessage({ text: messageBody });
2941
},
30-
[inputValue, sendMessage]
42+
[sendMessage]
3143
);
3244

3345
return (
34-
<div
35-
style={{
36-
display: 'flex',
37-
flexDirection: 'column',
38-
height: '100%',
39-
width: '100%',
40-
}}
41-
data-testid="assistant-chat"
42-
>
43-
{/* Message Feed */}
44-
<div
45-
data-testid="assistant-chat-messages"
46-
style={{
47-
width: '100%',
48-
flex: 1,
49-
overflowY: 'auto',
50-
display: 'flex',
51-
flexDirection: 'column',
52-
gap: '16px',
53-
minHeight: 0,
54-
}}
55-
>
56-
{messages.map((message) => (
57-
<div
58-
key={message.id}
59-
data-testid={`assistant-message-${message.id}`}
60-
style={{
61-
marginBottom: '12px',
62-
padding: '8px 12px',
63-
borderRadius: '8px',
64-
backgroundColor: message.role === 'user' ? '#207245' : '#e9ecef',
65-
color: message.role === 'user' ? 'white' : '#333',
66-
alignSelf: message.role === 'user' ? 'flex-end' : 'flex-start',
67-
maxWidth: '80%',
68-
wordWrap: 'break-word',
69-
whiteSpace: 'pre-wrap',
46+
<div data-testid="assistant-chat" style={{ height: '100%', width: '100%' }}>
47+
<LeafyGreenChatProvider variant={Variant.Compact}>
48+
<ChatWindow title="MongoDB Assistant">
49+
<MessageFeed data-testid="assistant-chat-messages">
50+
{lgMessages.map((messageFields) => (
51+
<Message
52+
key={messageFields.id}
53+
{...messageFields}
54+
data-testid={`assistant-message-${messageFields.id}`}
55+
/>
56+
))}
57+
{status === 'submitted' && (
58+
<Message
59+
id="loading"
60+
messageBody="Thinking..."
61+
isSender={false}
62+
/>
63+
)}
64+
<InputBarFeedback />
65+
</MessageFeed>
66+
<InputBar
67+
data-testid="assistant-chat-input"
68+
onMessageSend={handleMessageSend}
69+
textareaProps={{
70+
placeholder: 'Ask MongoDB Assistant a question',
7071
}}
71-
>
72-
{message.metadata?.displayText ||
73-
message.parts
74-
?.filter((part) => part.type === 'text')
75-
.map((part) => part.text)
76-
.join('') ||
77-
''}
78-
</div>
79-
))}
80-
</div>
81-
82-
{/* Input Bar */}
83-
<form
84-
data-testid="assistant-chat-form"
85-
onSubmit={handleInputSubmit}
86-
style={{
87-
display: 'flex',
88-
gap: '8px',
89-
flexShrink: 0, // Prevents the input bar from shrinking
90-
position: 'sticky',
91-
bottom: 0,
92-
backgroundColor: 'inherit',
93-
paddingTop: '8px',
94-
}}
95-
>
96-
<input
97-
data-testid="assistant-chat-input"
98-
type="text"
99-
value={inputValue}
100-
onChange={(e) => setInputValue(e.target.value)}
101-
placeholder="Ask MongoDB Assistant a question"
102-
style={{
103-
flex: 1,
104-
padding: '8px 12px',
105-
border: '1px solid #ddd',
106-
borderRadius: '4px',
107-
fontSize: '14px',
108-
}}
109-
/>
110-
<button
111-
data-testid="assistant-chat-send-button"
112-
type="submit"
113-
disabled={!inputValue.trim()}
114-
style={{
115-
padding: '8px 16px',
116-
backgroundColor: '#207245',
117-
color: 'white',
118-
border: 'none',
119-
borderRadius: '4px',
120-
cursor: inputValue.trim() ? 'pointer' : 'not-allowed',
121-
opacity: inputValue.trim() ? 1 : 0.6,
122-
}}
123-
>
124-
Send
125-
</button>
126-
</form>
72+
/>
73+
</ChatWindow>
74+
</LeafyGreenChatProvider>
12775
</div>
12876
);
12977
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"overrides": [
3+
{
4+
"files": ["*"],
5+
"rules": {
6+
"@typescript-eslint/naming-convention": "off",
7+
"filename-rules/match": "off",
8+
"@typescript-eslint/consistent-type-imports": "off"
9+
}
10+
}
11+
]
12+
}

0 commit comments

Comments
 (0)