nitrostack 1.0.15 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +2 -1
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/mcp-dev-wrapper.js +30 -17
- package/dist/cli/mcp-dev-wrapper.js.map +1 -1
- package/dist/core/app-decorator.js +2 -2
- package/dist/core/app-decorator.js.map +1 -1
- package/dist/core/builders.js +2 -2
- package/dist/core/builders.js.map +1 -1
- package/dist/core/resource.js +1 -1
- package/dist/core/resource.js.map +1 -1
- package/dist/core/server.js +2 -2
- package/dist/core/server.js.map +1 -1
- package/dist/core/transports/http-server.d.ts.map +1 -1
- package/dist/core/transports/http-server.js +21 -1
- package/dist/core/transports/http-server.js.map +1 -1
- package/dist/core/types.d.ts +1 -1
- package/dist/core/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/studio/app/api/chat/route.ts +155 -28
- package/src/studio/app/api/init/route.ts +28 -4
- package/src/studio/app/auth/page.tsx +13 -9
- package/src/studio/app/chat/page.tsx +599 -133
- package/src/studio/app/health/page.tsx +101 -99
- package/src/studio/app/layout.tsx +24 -4
- package/src/studio/app/page.tsx +61 -56
- package/src/studio/app/ping/page.tsx +13 -8
- package/src/studio/app/prompts/page.tsx +72 -70
- package/src/studio/app/resources/page.tsx +88 -86
- package/src/studio/app/settings/page.tsx +270 -0
- package/src/studio/components/EnlargeModal.tsx +21 -15
- package/src/studio/components/LogMessage.tsx +153 -0
- package/src/studio/components/MarkdownRenderer.tsx +410 -0
- package/src/studio/components/Sidebar.tsx +197 -35
- package/src/studio/components/ToolCard.tsx +27 -9
- package/src/studio/components/WidgetRenderer.tsx +4 -2
- package/src/studio/lib/http-client-transport.ts +222 -0
- package/src/studio/lib/llm-service.ts +119 -0
- package/src/studio/lib/log-manager.ts +76 -0
- package/src/studio/lib/mcp-client.ts +103 -13
- package/src/studio/package-lock.json +3129 -0
- package/src/studio/package.json +1 -0
- package/templates/typescript-auth/README.md +3 -1
- package/templates/typescript-auth/src/db/database.ts +5 -8
- package/templates/typescript-auth/src/index.ts +13 -2
- package/templates/typescript-auth/src/modules/addresses/addresses.tools.ts +49 -6
- package/templates/typescript-auth/src/modules/cart/cart.tools.ts +13 -17
- package/templates/typescript-auth/src/modules/orders/orders.tools.ts +38 -16
- package/templates/typescript-auth/src/modules/products/products.tools.ts +4 -4
- package/templates/typescript-auth/src/widgets/app/order-confirmation/page.tsx +25 -0
- package/templates/typescript-auth/src/widgets/app/products-grid/page.tsx +26 -1
- package/templates/typescript-auth-api-key/README.md +3 -1
- package/templates/typescript-auth-api-key/src/index.ts +11 -3
- package/templates/typescript-starter/README.md +3 -1
|
@@ -111,37 +111,37 @@ export default function ResourcesPage() {
|
|
|
111
111
|
}, [selectedExamples, openEnlargeModal, getSelectedExample]);
|
|
112
112
|
|
|
113
113
|
return (
|
|
114
|
-
<div className="
|
|
115
|
-
{/* Header */}
|
|
116
|
-
<div className="
|
|
117
|
-
<div className="flex items-center
|
|
118
|
-
<div className="flex items-center
|
|
119
|
-
<
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
<
|
|
123
|
-
<h1 className="text-3xl font-bold text-foreground">Resources</h1>
|
|
124
|
-
<p className="text-muted-foreground mt-1">Browse MCP resources and UI widgets</p>
|
|
125
|
-
</div>
|
|
114
|
+
<div className="fixed inset-0 flex flex-col bg-background" style={{ left: 'var(--sidebar-width, 15rem)' }}>
|
|
115
|
+
{/* Sticky Header */}
|
|
116
|
+
<div className="sticky top-0 z-10 border-b border-border/50 px-6 py-3 flex items-center justify-between bg-card/80 backdrop-blur-md shadow-sm">
|
|
117
|
+
<div className="flex items-center gap-3">
|
|
118
|
+
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-purple-500 to-pink-500 flex items-center justify-center shadow-md">
|
|
119
|
+
<Package className="w-5 h-5 text-white" strokeWidth={2.5} />
|
|
120
|
+
</div>
|
|
121
|
+
<div>
|
|
122
|
+
<h1 className="text-lg font-bold text-foreground">Resources</h1>
|
|
126
123
|
</div>
|
|
127
|
-
<button onClick={loadResources} className="btn btn-primary gap-2">
|
|
128
|
-
<RefreshCw className="w-4 h-4" />
|
|
129
|
-
Refresh
|
|
130
|
-
</button>
|
|
131
124
|
</div>
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
value={searchQuery}
|
|
137
|
-
onChange={(e) => setSearchQuery(e.target.value)}
|
|
138
|
-
className="input"
|
|
139
|
-
/>
|
|
125
|
+
<button onClick={loadResources} className="btn btn-primary text-sm px-4 py-2 gap-2">
|
|
126
|
+
<RefreshCw className="w-4 h-4" />
|
|
127
|
+
Refresh
|
|
128
|
+
</button>
|
|
140
129
|
</div>
|
|
141
130
|
|
|
142
|
-
{/*
|
|
143
|
-
|
|
144
|
-
<div className="
|
|
131
|
+
{/* Content - ONLY this scrolls */}
|
|
132
|
+
<div className="flex-1 overflow-y-auto overflow-x-hidden">
|
|
133
|
+
<div className="max-w-7xl mx-auto px-6 py-6">
|
|
134
|
+
<input
|
|
135
|
+
type="text"
|
|
136
|
+
placeholder="Search resources..."
|
|
137
|
+
value={searchQuery}
|
|
138
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
139
|
+
className="input mb-6"
|
|
140
|
+
/>
|
|
141
|
+
|
|
142
|
+
{/* UI Widgets Section */}
|
|
143
|
+
{(loadingWidgets || widgets.length > 0) && (
|
|
144
|
+
<div className="mb-12">
|
|
145
145
|
<h2 className="text-2xl font-bold text-foreground mb-6 flex items-center gap-2">
|
|
146
146
|
<Palette className="w-6 h-6 text-primary" />
|
|
147
147
|
UI Widgets {widgets.length > 0 && `(${filteredWidgets.length})`}
|
|
@@ -240,73 +240,75 @@ export default function ResourcesPage() {
|
|
|
240
240
|
))}
|
|
241
241
|
</div>
|
|
242
242
|
)}
|
|
243
|
-
|
|
244
|
-
|
|
243
|
+
</div>
|
|
244
|
+
)}
|
|
245
245
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
246
|
+
{/* MCP Resources Section */}
|
|
247
|
+
<div>
|
|
248
|
+
<h2 className="text-2xl font-bold text-foreground mb-6 flex items-center gap-2">
|
|
249
|
+
<Package className="w-6 h-6 text-primary" />
|
|
250
|
+
MCP Resources {resources.length > 0 && `(${filteredResources.length})`}
|
|
251
|
+
</h2>
|
|
252
|
+
|
|
253
|
+
{/* Resources Grid */}
|
|
254
|
+
{loading.resources ? (
|
|
255
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
256
|
+
{[1, 2, 3].map((i) => (
|
|
257
|
+
<div key={i} className="card skeleton h-64"></div>
|
|
258
|
+
))}
|
|
259
|
+
</div>
|
|
260
|
+
) : filteredResources.length === 0 ? (
|
|
261
|
+
<div className="empty-state">
|
|
262
|
+
<Package className="empty-state-icon" />
|
|
263
|
+
<p className="empty-state-title">
|
|
264
|
+
{searchQuery ? 'No resources found' : 'No resources available'}
|
|
265
|
+
</p>
|
|
266
|
+
<p className="empty-state-description">
|
|
267
|
+
{searchQuery ? 'Try a different search term' : 'No MCP resources have been registered'}
|
|
268
|
+
</p>
|
|
269
|
+
</div>
|
|
270
|
+
) : (
|
|
271
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
272
|
+
{filteredResources.map((resource) => (
|
|
273
|
+
<div key={resource.uri} className="card card-hover p-6 animate-fade-in">
|
|
274
|
+
<div className="flex items-start justify-between mb-4">
|
|
275
|
+
<div className="flex items-center gap-3">
|
|
276
|
+
<div className="w-10 h-10 rounded-lg bg-blue-500/10 flex items-center justify-center">
|
|
277
|
+
<Package className="w-5 h-5 text-blue-500" />
|
|
278
|
+
</div>
|
|
279
|
+
<div>
|
|
280
|
+
<h3 className="font-semibold text-foreground">{resource.name}</h3>
|
|
281
|
+
<span className="badge badge-primary text-xs mt-1">
|
|
282
|
+
Resource
|
|
283
|
+
</span>
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
284
286
|
</div>
|
|
285
|
-
</div>
|
|
286
|
-
</div>
|
|
287
287
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
288
|
+
<p className="text-sm text-muted-foreground mb-4 line-clamp-2">
|
|
289
|
+
{resource.description || 'No description'}
|
|
290
|
+
</p>
|
|
291
291
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
292
|
+
<div className="bg-muted/30 rounded-lg p-3 border border-border">
|
|
293
|
+
<p className="text-xs text-muted-foreground mb-1 font-semibold uppercase tracking-wide">URI</p>
|
|
294
|
+
<p className="text-xs text-foreground font-mono truncate">
|
|
295
|
+
{resource.uri}
|
|
296
|
+
</p>
|
|
297
|
+
</div>
|
|
298
298
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
299
|
+
{resource.mimeType && (
|
|
300
|
+
<div className="mt-3">
|
|
301
|
+
<span className="badge badge-secondary text-xs">
|
|
302
|
+
{resource.mimeType}
|
|
303
|
+
</span>
|
|
304
|
+
</div>
|
|
305
|
+
)}
|
|
304
306
|
</div>
|
|
305
|
-
)}
|
|
307
|
+
))}
|
|
306
308
|
</div>
|
|
307
|
-
)
|
|
309
|
+
)}
|
|
308
310
|
</div>
|
|
309
|
-
|
|
311
|
+
</div>
|
|
310
312
|
</div>
|
|
311
313
|
</div>
|
|
312
314
|
);
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect } from 'react';
|
|
4
|
+
import { Settings as SettingsIcon, Wifi, CheckCircle, AlertCircle } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
export default function SettingsPage() {
|
|
7
|
+
const [transport, setTransport] = useState<'stdio' | 'http'>('stdio');
|
|
8
|
+
const [httpUrl, setHttpUrl] = useState('http://localhost:3002');
|
|
9
|
+
const [httpBasePath, setHttpBasePath] = useState('/mcp');
|
|
10
|
+
const [connecting, setConnecting] = useState(false);
|
|
11
|
+
const [connectionStatus, setConnectionStatus] = useState<'idle' | 'success' | 'error'>('idle');
|
|
12
|
+
const [errorMessage, setErrorMessage] = useState('');
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
// Load saved settings from localStorage
|
|
16
|
+
const savedTransport = localStorage.getItem('mcp_transport');
|
|
17
|
+
const savedHttpUrl = localStorage.getItem('mcp_http_url');
|
|
18
|
+
const savedHttpBasePath = localStorage.getItem('mcp_http_base_path');
|
|
19
|
+
|
|
20
|
+
if (savedTransport) setTransport(savedTransport as 'stdio' | 'http');
|
|
21
|
+
if (savedHttpUrl) setHttpUrl(savedHttpUrl);
|
|
22
|
+
if (savedHttpBasePath) setHttpBasePath(savedHttpBasePath);
|
|
23
|
+
}, []);
|
|
24
|
+
|
|
25
|
+
const handleSaveSettings = async () => {
|
|
26
|
+
setConnecting(true);
|
|
27
|
+
setConnectionStatus('idle');
|
|
28
|
+
setErrorMessage('');
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
// Save settings to localStorage
|
|
32
|
+
localStorage.setItem('mcp_transport', transport);
|
|
33
|
+
if (transport === 'http') {
|
|
34
|
+
localStorage.setItem('mcp_http_url', httpUrl);
|
|
35
|
+
localStorage.setItem('mcp_http_base_path', httpBasePath);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Try to reconnect with new settings
|
|
39
|
+
const response = await fetch('/api/init', {
|
|
40
|
+
method: 'POST',
|
|
41
|
+
headers: { 'Content-Type': 'application/json' },
|
|
42
|
+
body: JSON.stringify({
|
|
43
|
+
transport,
|
|
44
|
+
baseUrl: httpUrl,
|
|
45
|
+
basePath: httpBasePath,
|
|
46
|
+
}),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
const error = await response.json();
|
|
51
|
+
throw new Error(error.error || 'Failed to connect');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const data = await response.json();
|
|
55
|
+
setConnectionStatus('success');
|
|
56
|
+
console.log('✅ Settings saved and connection established:', data);
|
|
57
|
+
} catch (error: any) {
|
|
58
|
+
console.error('❌ Failed to save settings:', error);
|
|
59
|
+
setConnectionStatus('error');
|
|
60
|
+
setErrorMessage(error.message || 'Failed to connect');
|
|
61
|
+
} finally {
|
|
62
|
+
setConnecting(false);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<div className="fixed inset-0 flex flex-col bg-background" style={{ left: 'var(--sidebar-width, 15rem)' }}>
|
|
68
|
+
{/* Sticky Header */}
|
|
69
|
+
<div className="sticky top-0 z-10 border-b border-border/50 px-6 py-3 flex items-center justify-between bg-card/80 backdrop-blur-md shadow-sm">
|
|
70
|
+
<div className="flex items-center gap-3">
|
|
71
|
+
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-primary to-amber-500 flex items-center justify-center shadow-md">
|
|
72
|
+
<SettingsIcon className="w-5 h-5 text-white" strokeWidth={2.5} />
|
|
73
|
+
</div>
|
|
74
|
+
<div>
|
|
75
|
+
<h1 className="text-lg font-bold text-foreground">Settings</h1>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
{/* Content - ONLY this scrolls */}
|
|
81
|
+
<div className="flex-1 overflow-y-auto overflow-x-hidden">
|
|
82
|
+
<div className="max-w-4xl mx-auto px-6 py-6">
|
|
83
|
+
|
|
84
|
+
{/* Settings Card */}
|
|
85
|
+
<div className="max-w-2xl">
|
|
86
|
+
<div className="card p-6">
|
|
87
|
+
<h2 className="text-xl font-semibold text-foreground mb-6">Transport Configuration</h2>
|
|
88
|
+
|
|
89
|
+
{/* Transport Type Selection */}
|
|
90
|
+
<div className="mb-6">
|
|
91
|
+
<label className="block text-sm font-medium text-foreground mb-3">
|
|
92
|
+
Transport Type
|
|
93
|
+
</label>
|
|
94
|
+
<div className="grid grid-cols-2 gap-4">
|
|
95
|
+
<button
|
|
96
|
+
onClick={() => setTransport('stdio')}
|
|
97
|
+
className={`p-4 rounded-lg border-2 transition-all text-left ${
|
|
98
|
+
transport === 'stdio'
|
|
99
|
+
? 'border-primary bg-primary/10'
|
|
100
|
+
: 'border-border hover:border-primary/50'
|
|
101
|
+
}`}
|
|
102
|
+
>
|
|
103
|
+
<div className="flex items-center gap-3 mb-2">
|
|
104
|
+
<div className={`w-3 h-3 rounded-full ${transport === 'stdio' ? 'bg-primary' : 'bg-muted'}`} />
|
|
105
|
+
<h3 className="font-semibold text-foreground">STDIO</h3>
|
|
106
|
+
</div>
|
|
107
|
+
<p className="text-sm text-muted-foreground">
|
|
108
|
+
Direct process communication (default)
|
|
109
|
+
</p>
|
|
110
|
+
</button>
|
|
111
|
+
|
|
112
|
+
<button
|
|
113
|
+
onClick={() => setTransport('http')}
|
|
114
|
+
className={`p-4 rounded-lg border-2 transition-all text-left ${
|
|
115
|
+
transport === 'http'
|
|
116
|
+
? 'border-primary bg-primary/10'
|
|
117
|
+
: 'border-border hover:border-primary/50'
|
|
118
|
+
}`}
|
|
119
|
+
>
|
|
120
|
+
<div className="flex items-center gap-3 mb-2">
|
|
121
|
+
<div className={`w-3 h-3 rounded-full ${transport === 'http' ? 'bg-primary' : 'bg-muted'}`} />
|
|
122
|
+
<h3 className="font-semibold text-foreground">HTTP</h3>
|
|
123
|
+
</div>
|
|
124
|
+
<p className="text-sm text-muted-foreground">
|
|
125
|
+
HTTP/SSE transport for remote servers
|
|
126
|
+
</p>
|
|
127
|
+
</button>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
{/* HTTP Configuration (shown only when HTTP is selected) */}
|
|
132
|
+
{transport === 'http' && (
|
|
133
|
+
<div className="space-y-4 mb-6 p-4 bg-muted/30 rounded-lg border border-border">
|
|
134
|
+
<div>
|
|
135
|
+
<label className="block text-sm font-medium text-foreground mb-2">
|
|
136
|
+
Server URL
|
|
137
|
+
</label>
|
|
138
|
+
<input
|
|
139
|
+
type="text"
|
|
140
|
+
value={httpUrl}
|
|
141
|
+
onChange={(e) => setHttpUrl(e.target.value)}
|
|
142
|
+
placeholder="http://localhost:3002"
|
|
143
|
+
className="input"
|
|
144
|
+
/>
|
|
145
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
146
|
+
The base URL of your MCP HTTP server (default: port 3002)
|
|
147
|
+
</p>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<div>
|
|
151
|
+
<label className="block text-sm font-medium text-foreground mb-2">
|
|
152
|
+
Base Path
|
|
153
|
+
</label>
|
|
154
|
+
<input
|
|
155
|
+
type="text"
|
|
156
|
+
value={httpBasePath}
|
|
157
|
+
onChange={(e) => setHttpBasePath(e.target.value)}
|
|
158
|
+
placeholder="/mcp"
|
|
159
|
+
className="input"
|
|
160
|
+
/>
|
|
161
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
162
|
+
The base path for MCP endpoints
|
|
163
|
+
</p>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
)}
|
|
167
|
+
|
|
168
|
+
{/* Port Allocation Info */}
|
|
169
|
+
<div className="mb-6 p-4 bg-blue-500/10 rounded-lg border border-blue-500/20">
|
|
170
|
+
<h4 className="font-semibold text-foreground mb-2">Port Allocation</h4>
|
|
171
|
+
<div className="space-y-1 text-sm">
|
|
172
|
+
<div className="flex items-center gap-2">
|
|
173
|
+
<span className="text-muted-foreground">Studio UI:</span>
|
|
174
|
+
<code className="px-2 py-0.5 bg-muted rounded text-foreground">Port 3000</code>
|
|
175
|
+
</div>
|
|
176
|
+
<div className="flex items-center gap-2">
|
|
177
|
+
<span className="text-muted-foreground">Widget Server:</span>
|
|
178
|
+
<code className="px-2 py-0.5 bg-muted rounded text-foreground">Port 3001</code>
|
|
179
|
+
</div>
|
|
180
|
+
<div className="flex items-center gap-2">
|
|
181
|
+
<span className="text-muted-foreground">MCP HTTP Server:</span>
|
|
182
|
+
<code className="px-2 py-0.5 bg-primary/20 rounded text-primary font-semibold">Port 3002</code>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
{/* Info Box */}
|
|
188
|
+
<div className="mb-6 p-4 bg-primary/5 rounded-lg border border-primary/20">
|
|
189
|
+
<div className="flex gap-3">
|
|
190
|
+
<Wifi className="w-5 h-5 text-primary flex-shrink-0 mt-0.5" />
|
|
191
|
+
<div>
|
|
192
|
+
<h4 className="font-semibold text-foreground mb-1">Transport Information</h4>
|
|
193
|
+
<p className="text-sm text-muted-foreground mb-2">
|
|
194
|
+
{transport === 'stdio' ? (
|
|
195
|
+
<>
|
|
196
|
+
<strong>STDIO</strong> (Default): Studio spawns the MCP server as a child process and communicates
|
|
197
|
+
via standard input/output. The MCP server still runs dual transport (STDIO + HTTP on port 3002).
|
|
198
|
+
</>
|
|
199
|
+
) : (
|
|
200
|
+
<>
|
|
201
|
+
<strong>HTTP</strong>: Studio connects to the running MCP HTTP server on port 3002.
|
|
202
|
+
Use this when you want to inspect an already-running server or connect remotely.
|
|
203
|
+
</>
|
|
204
|
+
)}
|
|
205
|
+
</p>
|
|
206
|
+
{transport === 'http' && (
|
|
207
|
+
<p className="text-xs text-amber-600 dark:text-amber-500">
|
|
208
|
+
⚠️ Make sure your MCP server is running with <code>npm run dev</code> or <code>npm start</code>
|
|
209
|
+
</p>
|
|
210
|
+
)}
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
{/* Connection Status */}
|
|
216
|
+
{connectionStatus !== 'idle' && (
|
|
217
|
+
<div className={`mb-6 p-4 rounded-lg border ${
|
|
218
|
+
connectionStatus === 'success'
|
|
219
|
+
? 'bg-emerald-500/10 border-emerald-500/20'
|
|
220
|
+
: 'bg-rose-500/10 border-rose-500/20'
|
|
221
|
+
}`}>
|
|
222
|
+
<div className="flex gap-3">
|
|
223
|
+
{connectionStatus === 'success' ? (
|
|
224
|
+
<>
|
|
225
|
+
<CheckCircle className="w-5 h-5 text-emerald-500 flex-shrink-0 mt-0.5" />
|
|
226
|
+
<div>
|
|
227
|
+
<h4 className="font-semibold text-emerald-500 mb-1">Connected</h4>
|
|
228
|
+
<p className="text-sm text-emerald-600">
|
|
229
|
+
Settings saved and connection established successfully.
|
|
230
|
+
</p>
|
|
231
|
+
</div>
|
|
232
|
+
</>
|
|
233
|
+
) : (
|
|
234
|
+
<>
|
|
235
|
+
<AlertCircle className="w-5 h-5 text-rose-500 flex-shrink-0 mt-0.5" />
|
|
236
|
+
<div>
|
|
237
|
+
<h4 className="font-semibold text-rose-500 mb-1">Connection Failed</h4>
|
|
238
|
+
<p className="text-sm text-rose-600">
|
|
239
|
+
{errorMessage || 'Failed to establish connection with the selected transport.'}
|
|
240
|
+
</p>
|
|
241
|
+
</div>
|
|
242
|
+
</>
|
|
243
|
+
)}
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
)}
|
|
247
|
+
|
|
248
|
+
{/* Save Button */}
|
|
249
|
+
<button
|
|
250
|
+
onClick={handleSaveSettings}
|
|
251
|
+
disabled={connecting}
|
|
252
|
+
className="btn btn-primary w-full"
|
|
253
|
+
>
|
|
254
|
+
{connecting ? (
|
|
255
|
+
<>
|
|
256
|
+
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
|
257
|
+
Connecting...
|
|
258
|
+
</>
|
|
259
|
+
) : (
|
|
260
|
+
'Save & Connect'
|
|
261
|
+
)}
|
|
262
|
+
</button>
|
|
263
|
+
</div>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
@@ -26,13 +26,18 @@ export function EnlargeModal() {
|
|
|
26
26
|
if (type !== 'tool') return;
|
|
27
27
|
|
|
28
28
|
closeEnlargeModal();
|
|
29
|
-
setCurrentTab('chat');
|
|
30
|
-
router.push('/chat');
|
|
31
29
|
|
|
32
|
-
//
|
|
30
|
+
// Build the tool execution message
|
|
31
|
+
const toolMessage = `Use the ${item.name} tool`;
|
|
32
|
+
|
|
33
|
+
// Store both the tool name and the message
|
|
33
34
|
if (typeof window !== 'undefined') {
|
|
34
35
|
window.localStorage.setItem('suggestedTool', item.name);
|
|
36
|
+
window.localStorage.setItem('chatInput', toolMessage);
|
|
35
37
|
}
|
|
38
|
+
|
|
39
|
+
setCurrentTab('chat');
|
|
40
|
+
router.push('/chat');
|
|
36
41
|
};
|
|
37
42
|
|
|
38
43
|
return (
|
|
@@ -42,34 +47,35 @@ export function EnlargeModal() {
|
|
|
42
47
|
onClick={closeEnlargeModal}
|
|
43
48
|
>
|
|
44
49
|
<div
|
|
45
|
-
className="relative w-[
|
|
50
|
+
className="relative w-[95vw] max-w-7xl h-[90vh] bg-card border border-border rounded-xl sm:rounded-2xl shadow-2xl overflow-hidden animate-scale-in"
|
|
46
51
|
onClick={(e) => e.stopPropagation()}
|
|
47
52
|
>
|
|
48
53
|
{/* Header */}
|
|
49
|
-
<div className="flex items-center justify-between p-6 border-b border-border bg-muted/30">
|
|
50
|
-
<div className="flex items-center gap-4">
|
|
51
|
-
<span className="text-3xl">{type === 'tool' ? '⚡' : '🎨'}</span>
|
|
52
|
-
<div>
|
|
53
|
-
<h2 className="text-xl font-bold text-foreground">{item.name}</h2>
|
|
54
|
-
<p className="text-sm text-muted-foreground mt-1">
|
|
54
|
+
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between p-4 sm:p-6 border-b border-border bg-muted/30 gap-3 sm:gap-0">
|
|
55
|
+
<div className="flex items-start sm:items-center gap-3 sm:gap-4 flex-1 min-w-0">
|
|
56
|
+
<span className="text-2xl sm:text-3xl flex-shrink-0">{type === 'tool' ? '⚡' : '🎨'}</span>
|
|
57
|
+
<div className="min-w-0 flex-1">
|
|
58
|
+
<h2 className="text-lg sm:text-xl font-bold text-foreground truncate">{item.name}</h2>
|
|
59
|
+
<p className="text-xs sm:text-sm text-muted-foreground mt-1 line-clamp-2">
|
|
55
60
|
{item.description || 'No description available'}
|
|
56
61
|
</p>
|
|
57
62
|
</div>
|
|
58
63
|
</div>
|
|
59
64
|
|
|
60
|
-
<div className="flex items-center gap-2">
|
|
65
|
+
<div className="flex items-center gap-2 w-full sm:w-auto">
|
|
61
66
|
{type === 'tool' && (
|
|
62
67
|
<button
|
|
63
68
|
onClick={handleUseInChat}
|
|
64
|
-
className="btn btn-primary flex items-center gap-2"
|
|
69
|
+
className="btn btn-primary flex items-center gap-2 flex-1 sm:flex-none text-sm"
|
|
65
70
|
>
|
|
66
71
|
<MessageSquare className="w-4 h-4" />
|
|
67
|
-
Use in Chat
|
|
72
|
+
<span className="hidden sm:inline">Use in Chat</span>
|
|
73
|
+
<span className="sm:hidden">Chat</span>
|
|
68
74
|
</button>
|
|
69
75
|
)}
|
|
70
76
|
<button
|
|
71
77
|
onClick={closeEnlargeModal}
|
|
72
|
-
className="btn btn-ghost w-10 h-10 p-0 flex items-center justify-center"
|
|
78
|
+
className="btn btn-ghost w-10 h-10 p-0 flex items-center justify-center flex-shrink-0"
|
|
73
79
|
aria-label="Close"
|
|
74
80
|
>
|
|
75
81
|
<X className="w-5 h-5" />
|
|
@@ -78,7 +84,7 @@ export function EnlargeModal() {
|
|
|
78
84
|
</div>
|
|
79
85
|
|
|
80
86
|
{/* Widget Content */}
|
|
81
|
-
<div className="h-[calc(100%-
|
|
87
|
+
<div className="h-[calc(100%-120px)] sm:h-[calc(100%-100px)] p-3 sm:p-6 overflow-auto bg-background">
|
|
82
88
|
{componentUri && widgetData ? (
|
|
83
89
|
<div className="w-full h-full rounded-xl overflow-hidden border border-border shadow-inner">
|
|
84
90
|
<WidgetRenderer uri={componentUri} data={widgetData} className="w-full h-full" />
|