nitrostack 1.0.0 → 1.0.2
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/CHANGELOG.md +30 -0
- package/dist/cli/index.js +4 -1
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
- package/src/studio/README.md +140 -0
- package/src/studio/app/api/auth/fetch-metadata/route.ts +71 -0
- package/src/studio/app/api/auth/register-client/route.ts +67 -0
- package/src/studio/app/api/chat/route.ts +123 -0
- package/src/studio/app/api/health/checks/route.ts +42 -0
- package/src/studio/app/api/health/route.ts +13 -0
- package/src/studio/app/api/init/route.ts +85 -0
- package/src/studio/app/api/ping/route.ts +13 -0
- package/src/studio/app/api/prompts/[name]/route.ts +21 -0
- package/src/studio/app/api/prompts/route.ts +13 -0
- package/src/studio/app/api/resources/[...uri]/route.ts +18 -0
- package/src/studio/app/api/resources/route.ts +13 -0
- package/src/studio/app/api/roots/route.ts +13 -0
- package/src/studio/app/api/sampling/route.ts +14 -0
- package/src/studio/app/api/tools/[name]/call/route.ts +41 -0
- package/src/studio/app/api/tools/route.ts +23 -0
- package/src/studio/app/api/widget-examples/route.ts +44 -0
- package/src/studio/app/auth/callback/page.tsx +160 -0
- package/src/studio/app/auth/page.tsx +543 -0
- package/src/studio/app/chat/page.tsx +530 -0
- package/src/studio/app/chat/page.tsx.backup +390 -0
- package/src/studio/app/globals.css +410 -0
- package/src/studio/app/health/page.tsx +177 -0
- package/src/studio/app/layout.tsx +48 -0
- package/src/studio/app/page.tsx +337 -0
- package/src/studio/app/page.tsx.backup +346 -0
- package/src/studio/app/ping/page.tsx +204 -0
- package/src/studio/app/prompts/page.tsx +228 -0
- package/src/studio/app/resources/page.tsx +313 -0
- package/src/studio/components/EnlargeModal.tsx +116 -0
- package/src/studio/components/Sidebar.tsx +133 -0
- package/src/studio/components/ToolCard.tsx +108 -0
- package/src/studio/components/WidgetRenderer.tsx +99 -0
- package/src/studio/lib/api.ts +207 -0
- package/src/studio/lib/llm-service.ts +361 -0
- package/src/studio/lib/mcp-client.ts +168 -0
- package/src/studio/lib/store.ts +192 -0
- package/src/studio/lib/theme-provider.tsx +50 -0
- package/src/studio/lib/types.ts +107 -0
- package/src/studio/lib/widget-loader.ts +90 -0
- package/src/studio/middleware.ts +27 -0
- package/src/studio/next.config.js +16 -0
- package/src/studio/package-lock.json +2696 -0
- package/src/studio/package.json +34 -0
- package/src/studio/postcss.config.mjs +10 -0
- package/src/studio/tailwind.config.ts +67 -0
- package/src/studio/tsconfig.json +41 -0
- package/templates/typescript-auth/.env.example +23 -0
- package/templates/typescript-auth/src/app.module.ts +103 -0
- package/templates/typescript-auth/src/db/database.ts +163 -0
- package/templates/typescript-auth/src/db/seed.ts +374 -0
- package/templates/typescript-auth/src/db/setup.ts +87 -0
- package/templates/typescript-auth/src/events/analytics.service.ts +52 -0
- package/templates/typescript-auth/src/events/notification.service.ts +40 -0
- package/templates/typescript-auth/src/filters/global-exception.filter.ts +28 -0
- package/templates/typescript-auth/src/guards/README.md +75 -0
- package/templates/typescript-auth/src/guards/jwt.guard.ts +105 -0
- package/templates/typescript-auth/src/health/database.health.ts +41 -0
- package/templates/typescript-auth/src/index.ts +26 -0
- package/templates/typescript-auth/src/interceptors/transform.interceptor.ts +24 -0
- package/templates/typescript-auth/src/middleware/logging.middleware.ts +42 -0
- package/templates/typescript-auth/src/modules/addresses/addresses.module.ts +16 -0
- package/templates/typescript-auth/src/modules/addresses/addresses.prompts.ts +114 -0
- package/templates/typescript-auth/src/modules/addresses/addresses.resources.ts +40 -0
- package/templates/typescript-auth/src/modules/addresses/addresses.tools.ts +241 -0
- package/templates/typescript-auth/src/modules/auth/auth.module.ts +16 -0
- package/templates/typescript-auth/src/modules/auth/auth.prompts.ts +147 -0
- package/templates/typescript-auth/src/modules/auth/auth.resources.ts +84 -0
- package/templates/typescript-auth/src/modules/auth/auth.tools.ts +139 -0
- package/templates/typescript-auth/src/modules/cart/cart.module.ts +16 -0
- package/templates/typescript-auth/src/modules/cart/cart.prompts.ts +95 -0
- package/templates/typescript-auth/src/modules/cart/cart.resources.ts +44 -0
- package/templates/typescript-auth/src/modules/cart/cart.tools.ts +281 -0
- package/templates/typescript-auth/src/modules/orders/orders.module.ts +16 -0
- package/templates/typescript-auth/src/modules/orders/orders.prompts.ts +88 -0
- package/templates/typescript-auth/src/modules/orders/orders.resources.ts +48 -0
- package/templates/typescript-auth/src/modules/orders/orders.tools.ts +281 -0
- package/templates/typescript-auth/src/modules/products/products.module.ts +16 -0
- package/templates/typescript-auth/src/modules/products/products.prompts.ts +146 -0
- package/templates/typescript-auth/src/modules/products/products.resources.ts +98 -0
- package/templates/typescript-auth/src/modules/products/products.tools.ts +266 -0
- package/templates/typescript-auth/src/pipes/validation.pipe.ts +42 -0
- package/templates/typescript-auth/src/services/database.service.ts +90 -0
- package/templates/typescript-auth/src/widgets/app/add-to-cart/page.tsx +122 -0
- package/templates/typescript-auth/src/widgets/app/address-added/page.tsx +116 -0
- package/templates/typescript-auth/src/widgets/app/address-deleted/page.tsx +105 -0
- package/templates/typescript-auth/src/widgets/app/address-list/page.tsx +139 -0
- package/templates/typescript-auth/src/widgets/app/address-updated/page.tsx +153 -0
- package/templates/typescript-auth/src/widgets/app/cart-cleared/page.tsx +86 -0
- package/templates/typescript-auth/src/widgets/app/cart-updated/page.tsx +116 -0
- package/templates/typescript-auth/src/widgets/app/categories/page.tsx +134 -0
- package/templates/typescript-auth/src/widgets/app/layout.tsx +21 -0
- package/templates/typescript-auth/src/widgets/app/login-result/page.tsx +129 -0
- package/templates/typescript-auth/src/widgets/app/order-confirmation/page.tsx +206 -0
- package/templates/typescript-auth/src/widgets/app/order-details/page.tsx +225 -0
- package/templates/typescript-auth/src/widgets/app/order-history/page.tsx +218 -0
- package/templates/typescript-auth/src/widgets/app/product-card/page.tsx +121 -0
- package/templates/typescript-auth/src/widgets/app/products-grid/page.tsx +173 -0
- package/templates/typescript-auth/src/widgets/app/shopping-cart/page.tsx +187 -0
- package/templates/typescript-auth/src/widgets/app/whoami/page.tsx +165 -0
- package/templates/typescript-auth/src/widgets/next.config.js +38 -0
- package/templates/typescript-auth/src/widgets/package.json +18 -0
- package/templates/typescript-auth/src/widgets/styles/ecommerce.ts +169 -0
- package/templates/typescript-auth/src/widgets/tsconfig.json +28 -0
- package/templates/typescript-auth/src/widgets/types/tool-data.ts +141 -0
- package/templates/typescript-auth/src/widgets/widget-manifest.json +464 -0
- package/templates/typescript-auth/tsconfig.json +27 -0
- package/templates/typescript-auth-api-key/.env +15 -0
- package/templates/typescript-auth-api-key/.env.example +4 -0
- package/templates/typescript-auth-api-key/src/app.module.ts +38 -0
- package/templates/typescript-auth-api-key/src/guards/apikey.guard.ts +47 -0
- package/templates/typescript-auth-api-key/src/guards/multi-auth.guard.ts +157 -0
- package/templates/typescript-auth-api-key/src/health/system.health.ts +55 -0
- package/templates/typescript-auth-api-key/src/index.ts +47 -0
- package/templates/typescript-auth-api-key/src/modules/calculator/calculator.module.ts +12 -0
- package/templates/typescript-auth-api-key/src/modules/calculator/calculator.prompts.ts +73 -0
- package/templates/typescript-auth-api-key/src/modules/calculator/calculator.resources.ts +60 -0
- package/templates/typescript-auth-api-key/src/modules/calculator/calculator.tools.ts +71 -0
- package/templates/typescript-auth-api-key/src/modules/demo/demo.module.ts +18 -0
- package/templates/typescript-auth-api-key/src/modules/demo/demo.tools.ts +155 -0
- package/templates/typescript-auth-api-key/src/modules/demo/multi-auth.tools.ts +123 -0
- package/templates/typescript-auth-api-key/src/widgets/app/calculator-operations/page.tsx +133 -0
- package/templates/typescript-auth-api-key/src/widgets/app/calculator-result/page.tsx +134 -0
- package/templates/typescript-auth-api-key/src/widgets/app/layout.tsx +14 -0
- package/templates/typescript-auth-api-key/src/widgets/next.config.js +37 -0
- package/templates/typescript-auth-api-key/src/widgets/package.json +24 -0
- package/templates/typescript-auth-api-key/src/widgets/tsconfig.json +28 -0
- package/templates/typescript-auth-api-key/src/widgets/widget-manifest.json +48 -0
- package/templates/typescript-auth-api-key/tsconfig.json +23 -0
- package/templates/typescript-oauth/.env.example +91 -0
- package/templates/typescript-oauth/src/app.module.ts +89 -0
- package/templates/typescript-oauth/src/guards/oauth.guard.ts +127 -0
- package/templates/typescript-oauth/src/index.ts +74 -0
- package/templates/typescript-oauth/src/modules/demo/demo.module.ts +16 -0
- package/templates/typescript-oauth/src/modules/demo/demo.tools.ts +190 -0
- package/templates/typescript-oauth/src/widgets/app/calculator-operations/page.tsx +133 -0
- package/templates/typescript-oauth/src/widgets/app/calculator-result/page.tsx +134 -0
- package/templates/typescript-oauth/src/widgets/app/layout.tsx +14 -0
- package/templates/typescript-oauth/src/widgets/next.config.js +37 -0
- package/templates/typescript-oauth/src/widgets/package.json +24 -0
- package/templates/typescript-oauth/src/widgets/tsconfig.json +28 -0
- package/templates/typescript-oauth/src/widgets/widget-manifest.json +48 -0
- package/templates/typescript-oauth/tsconfig.json +23 -0
- package/templates/typescript-starter/.env.example +4 -0
- package/templates/typescript-starter/src/app.module.ts +34 -0
- package/templates/typescript-starter/src/health/system.health.ts +55 -0
- package/templates/typescript-starter/src/index.ts +27 -0
- package/templates/typescript-starter/src/modules/calculator/calculator.module.ts +12 -0
- package/templates/typescript-starter/src/modules/calculator/calculator.prompts.ts +73 -0
- package/templates/typescript-starter/src/modules/calculator/calculator.resources.ts +60 -0
- package/templates/typescript-starter/src/modules/calculator/calculator.tools.ts +71 -0
- package/templates/typescript-starter/src/widgets/app/calculator-operations/page.tsx +133 -0
- package/templates/typescript-starter/src/widgets/app/calculator-result/page.tsx +134 -0
- package/templates/typescript-starter/src/widgets/app/layout.tsx +14 -0
- package/templates/typescript-starter/src/widgets/next.config.js +37 -0
- package/templates/typescript-starter/src/widgets/package.json +24 -0
- package/templates/typescript-starter/src/widgets/tsconfig.json +28 -0
- package/templates/typescript-starter/src/widgets/widget-manifest.json +48 -0
- package/templates/typescript-starter/tsconfig.json +23 -0
- package/LICENSE_URLS_UPDATE_COMPLETE.md +0 -388
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { useStudioStore } from '@/lib/store';
|
|
5
|
+
import { api } from '@/lib/api';
|
|
6
|
+
import { Wifi, Activity, Clock, TrendingUp, Radio } from 'lucide-react';
|
|
7
|
+
|
|
8
|
+
export default function PingPage() {
|
|
9
|
+
const { pingHistory, addPingResult } = useStudioStore();
|
|
10
|
+
const [pinging, setPinging] = useState(false);
|
|
11
|
+
const [lastLatency, setLastLatency] = useState<number | null>(null);
|
|
12
|
+
|
|
13
|
+
const handlePing = async () => {
|
|
14
|
+
setPinging(true);
|
|
15
|
+
const startTime = Date.now();
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
await api.ping();
|
|
19
|
+
const latency = Date.now() - startTime;
|
|
20
|
+
setLastLatency(latency);
|
|
21
|
+
addPingResult({ time: new Date(), latency });
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error('Ping failed:', error);
|
|
24
|
+
} finally {
|
|
25
|
+
setPinging(false);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const averageLatency =
|
|
30
|
+
pingHistory.length > 0
|
|
31
|
+
? Math.round(pingHistory.reduce((sum, p) => sum + p.latency, 0) / pingHistory.length)
|
|
32
|
+
: null;
|
|
33
|
+
|
|
34
|
+
const minLatency =
|
|
35
|
+
pingHistory.length > 0
|
|
36
|
+
? Math.min(...pingHistory.map((p) => p.latency))
|
|
37
|
+
: null;
|
|
38
|
+
|
|
39
|
+
const maxLatency =
|
|
40
|
+
pingHistory.length > 0
|
|
41
|
+
? Math.max(...pingHistory.map((p) => p.latency))
|
|
42
|
+
: null;
|
|
43
|
+
|
|
44
|
+
const getLatencyColor = (latency: number) => {
|
|
45
|
+
if (latency < 100) return 'text-emerald-500';
|
|
46
|
+
if (latency < 500) return 'text-amber-500';
|
|
47
|
+
return 'text-rose-500';
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const getLatencyBg = (latency: number) => {
|
|
51
|
+
if (latency < 100) return 'bg-emerald-500/10 border-emerald-500/20';
|
|
52
|
+
if (latency < 500) return 'bg-amber-500/10 border-amber-500/20';
|
|
53
|
+
return 'bg-rose-500/10 border-rose-500/20';
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const getLatencyDotColor = (latency: number) => {
|
|
57
|
+
if (latency < 100) return 'bg-emerald-500';
|
|
58
|
+
if (latency < 500) return 'bg-amber-500';
|
|
59
|
+
return 'bg-rose-500';
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<div className="min-h-screen bg-background p-8">
|
|
64
|
+
{/* Header */}
|
|
65
|
+
<div className="mb-8">
|
|
66
|
+
<div className="flex items-center gap-3 mb-4">
|
|
67
|
+
<div className="w-12 h-12 rounded-lg bg-gradient-to-br from-cyan-500 to-blue-500 flex items-center justify-center">
|
|
68
|
+
<Wifi className="w-6 h-6 text-white" />
|
|
69
|
+
</div>
|
|
70
|
+
<div>
|
|
71
|
+
<h1 className="text-3xl font-bold text-foreground">Ping</h1>
|
|
72
|
+
<p className="text-muted-foreground mt-1">Test server connectivity and latency</p>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
{/* Main Ping Card */}
|
|
78
|
+
<div className="card card-hover p-8 mb-8 text-center bg-gradient-to-br from-card via-card to-muted/20">
|
|
79
|
+
<button
|
|
80
|
+
onClick={handlePing}
|
|
81
|
+
disabled={pinging}
|
|
82
|
+
className="btn btn-primary btn-lg mx-auto px-12 py-4 text-lg gap-3 shadow-lg hover:shadow-xl transition-all"
|
|
83
|
+
>
|
|
84
|
+
{pinging ? (
|
|
85
|
+
<>
|
|
86
|
+
<Radio className="w-6 h-6 animate-pulse" />
|
|
87
|
+
Pinging...
|
|
88
|
+
</>
|
|
89
|
+
) : (
|
|
90
|
+
<>
|
|
91
|
+
<Wifi className="w-6 h-6" />
|
|
92
|
+
Send Ping
|
|
93
|
+
</>
|
|
94
|
+
)}
|
|
95
|
+
</button>
|
|
96
|
+
|
|
97
|
+
{lastLatency !== null && (
|
|
98
|
+
<div className="mt-8 animate-fade-in">
|
|
99
|
+
<div className={`inline-block px-8 py-4 rounded-2xl border ${getLatencyBg(lastLatency)}`}>
|
|
100
|
+
<div className={`text-6xl font-bold ${getLatencyColor(lastLatency)}`}>
|
|
101
|
+
{lastLatency}ms
|
|
102
|
+
</div>
|
|
103
|
+
<div className="text-muted-foreground mt-2 flex items-center justify-center gap-2">
|
|
104
|
+
<Clock className="w-4 h-4" />
|
|
105
|
+
Last ping latency
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
)}
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
{/* Statistics Grid */}
|
|
113
|
+
{pingHistory.length > 0 && (
|
|
114
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
|
115
|
+
{/* Average */}
|
|
116
|
+
<div className="card card-hover p-6">
|
|
117
|
+
<div className="flex items-center gap-3 mb-3">
|
|
118
|
+
<div className="w-10 h-10 rounded-lg bg-blue-500/10 flex items-center justify-center">
|
|
119
|
+
<TrendingUp className="w-5 h-5 text-blue-500" />
|
|
120
|
+
</div>
|
|
121
|
+
<h3 className="font-semibold text-foreground">Average</h3>
|
|
122
|
+
</div>
|
|
123
|
+
<div className="text-3xl font-bold text-foreground">{averageLatency}ms</div>
|
|
124
|
+
<p className="text-sm text-muted-foreground mt-1">{pingHistory.length} total pings</p>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
{/* Min */}
|
|
128
|
+
<div className="card card-hover p-6">
|
|
129
|
+
<div className="flex items-center gap-3 mb-3">
|
|
130
|
+
<div className="w-10 h-10 rounded-lg bg-emerald-500/10 flex items-center justify-center">
|
|
131
|
+
<Activity className="w-5 h-5 text-emerald-500" />
|
|
132
|
+
</div>
|
|
133
|
+
<h3 className="font-semibold text-foreground">Fastest</h3>
|
|
134
|
+
</div>
|
|
135
|
+
<div className="text-3xl font-bold text-emerald-500">{minLatency}ms</div>
|
|
136
|
+
<p className="text-sm text-muted-foreground mt-1">Best response time</p>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
{/* Max */}
|
|
140
|
+
<div className="card card-hover p-6">
|
|
141
|
+
<div className="flex items-center gap-3 mb-3">
|
|
142
|
+
<div className="w-10 h-10 rounded-lg bg-amber-500/10 flex items-center justify-center">
|
|
143
|
+
<Clock className="w-5 h-5 text-amber-500" />
|
|
144
|
+
</div>
|
|
145
|
+
<h3 className="font-semibold text-foreground">Slowest</h3>
|
|
146
|
+
</div>
|
|
147
|
+
<div className="text-3xl font-bold text-amber-500">{maxLatency}ms</div>
|
|
148
|
+
<p className="text-sm text-muted-foreground mt-1">Worst response time</p>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
)}
|
|
152
|
+
|
|
153
|
+
{/* Ping History */}
|
|
154
|
+
{pingHistory.length > 0 && (
|
|
155
|
+
<div>
|
|
156
|
+
<h2 className="text-2xl font-semibold text-foreground mb-6 flex items-center gap-2">
|
|
157
|
+
<Activity className="w-6 h-6 text-primary" />
|
|
158
|
+
Recent Pings
|
|
159
|
+
</h2>
|
|
160
|
+
<div className="space-y-3">
|
|
161
|
+
{pingHistory.slice().reverse().map((ping, idx) => (
|
|
162
|
+
<div
|
|
163
|
+
key={idx}
|
|
164
|
+
className="card card-hover p-5 flex items-center justify-between animate-fade-in"
|
|
165
|
+
>
|
|
166
|
+
<div className="flex items-center gap-3">
|
|
167
|
+
<div className={`w-3 h-3 rounded-full ${getLatencyDotColor(ping.latency)} shadow-lg`} />
|
|
168
|
+
<span className="text-sm text-muted-foreground font-mono">
|
|
169
|
+
{ping.time.toLocaleTimeString()}
|
|
170
|
+
</span>
|
|
171
|
+
</div>
|
|
172
|
+
<div className="flex items-center gap-3">
|
|
173
|
+
<span className={`text-lg font-bold ${getLatencyColor(ping.latency)}`}>
|
|
174
|
+
{ping.latency}ms
|
|
175
|
+
</span>
|
|
176
|
+
<span className={`badge ${
|
|
177
|
+
ping.latency < 100
|
|
178
|
+
? 'badge-success'
|
|
179
|
+
: ping.latency < 500
|
|
180
|
+
? 'badge-warning'
|
|
181
|
+
: 'badge-error'
|
|
182
|
+
}`}>
|
|
183
|
+
{ping.latency < 100 ? 'Excellent' : ping.latency < 500 ? 'Good' : 'Slow'}
|
|
184
|
+
</span>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
))}
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
)}
|
|
191
|
+
|
|
192
|
+
{/* Empty State */}
|
|
193
|
+
{pingHistory.length === 0 && !pinging && (
|
|
194
|
+
<div className="empty-state">
|
|
195
|
+
<Wifi className="empty-state-icon" />
|
|
196
|
+
<p className="empty-state-title">No ping history yet</p>
|
|
197
|
+
<p className="empty-state-description">
|
|
198
|
+
Click the "Send Ping" button above to test your connection
|
|
199
|
+
</p>
|
|
200
|
+
</div>
|
|
201
|
+
)}
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import { useStudioStore } from '@/lib/store';
|
|
5
|
+
import { api } from '@/lib/api';
|
|
6
|
+
import type { Prompt } from '@/lib/types';
|
|
7
|
+
import { FileText, RefreshCw, X, Play, AlertCircle } from 'lucide-react';
|
|
8
|
+
|
|
9
|
+
export default function PromptsPage() {
|
|
10
|
+
const { prompts, setPrompts, loading, setLoading } = useStudioStore();
|
|
11
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
12
|
+
const [selectedPrompt, setSelectedPrompt] = useState<Prompt | null>(null);
|
|
13
|
+
const [promptArgs, setPromptArgs] = useState<Record<string, string>>({});
|
|
14
|
+
const [promptResult, setPromptResult] = useState<any>(null);
|
|
15
|
+
const [executing, setExecuting] = useState(false);
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
loadPrompts();
|
|
19
|
+
}, []);
|
|
20
|
+
|
|
21
|
+
const loadPrompts = async () => {
|
|
22
|
+
setLoading('prompts', true);
|
|
23
|
+
try {
|
|
24
|
+
const data = await api.getPrompts();
|
|
25
|
+
setPrompts(data.prompts || []);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error('Failed to load prompts:', error);
|
|
28
|
+
} finally {
|
|
29
|
+
setLoading('prompts', false);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const handleExecutePrompt = async (e: React.FormEvent) => {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
if (!selectedPrompt) return;
|
|
36
|
+
|
|
37
|
+
setExecuting(true);
|
|
38
|
+
setPromptResult(null);
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const result = await api.executePrompt(selectedPrompt.name, promptArgs);
|
|
42
|
+
setPromptResult(result);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error('Prompt execution failed:', error);
|
|
45
|
+
setPromptResult({ error: 'Execution failed' });
|
|
46
|
+
} finally {
|
|
47
|
+
setExecuting(false);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const filteredPrompts = prompts.filter((prompt) =>
|
|
52
|
+
prompt.name.toLowerCase().includes(searchQuery.toLowerCase())
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div className="min-h-screen bg-background p-8">
|
|
57
|
+
{/* Header */}
|
|
58
|
+
<div className="mb-8">
|
|
59
|
+
<div className="flex items-center justify-between mb-6">
|
|
60
|
+
<div className="flex items-center gap-3">
|
|
61
|
+
<div className="w-12 h-12 rounded-lg bg-gradient-to-br from-blue-500 to-cyan-500 flex items-center justify-center">
|
|
62
|
+
<FileText className="w-6 h-6 text-white" />
|
|
63
|
+
</div>
|
|
64
|
+
<div>
|
|
65
|
+
<h1 className="text-3xl font-bold text-foreground">Prompts</h1>
|
|
66
|
+
<p className="text-muted-foreground mt-1">Browse and execute MCP prompts</p>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
<button onClick={loadPrompts} className="btn btn-primary gap-2">
|
|
70
|
+
<RefreshCw className="w-4 h-4" />
|
|
71
|
+
Refresh
|
|
72
|
+
</button>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<input
|
|
76
|
+
type="text"
|
|
77
|
+
placeholder="Search prompts..."
|
|
78
|
+
value={searchQuery}
|
|
79
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
80
|
+
className="input"
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
{/* Prompts Grid */}
|
|
85
|
+
{loading.prompts ? (
|
|
86
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
87
|
+
{[1, 2, 3].map((i) => (
|
|
88
|
+
<div key={i} className="card skeleton h-40"></div>
|
|
89
|
+
))}
|
|
90
|
+
</div>
|
|
91
|
+
) : filteredPrompts.length === 0 ? (
|
|
92
|
+
<div className="empty-state">
|
|
93
|
+
<AlertCircle className="empty-state-icon" />
|
|
94
|
+
<p className="empty-state-title">
|
|
95
|
+
{searchQuery ? 'No prompts found' : 'No prompts available'}
|
|
96
|
+
</p>
|
|
97
|
+
<p className="empty-state-description">
|
|
98
|
+
{searchQuery ? 'Try a different search term' : 'No prompts have been registered'}
|
|
99
|
+
</p>
|
|
100
|
+
</div>
|
|
101
|
+
) : (
|
|
102
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
103
|
+
{filteredPrompts.map((prompt) => (
|
|
104
|
+
<div
|
|
105
|
+
key={prompt.name}
|
|
106
|
+
className="card card-hover p-6 cursor-pointer animate-fade-in"
|
|
107
|
+
onClick={() => {
|
|
108
|
+
setSelectedPrompt(prompt);
|
|
109
|
+
setPromptArgs({});
|
|
110
|
+
setPromptResult(null);
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
<div className="flex items-center gap-3 mb-3">
|
|
114
|
+
<div className="w-10 h-10 rounded-lg bg-blue-500/10 flex items-center justify-center">
|
|
115
|
+
<FileText className="w-5 h-5 text-blue-500" />
|
|
116
|
+
</div>
|
|
117
|
+
<h3 className="font-semibold text-foreground">{prompt.name}</h3>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<p className="text-sm text-muted-foreground line-clamp-2 mb-3">
|
|
121
|
+
{prompt.description || 'No description'}
|
|
122
|
+
</p>
|
|
123
|
+
|
|
124
|
+
{prompt.arguments && prompt.arguments.length > 0 && (
|
|
125
|
+
<div className="flex items-center gap-1 text-xs text-muted-foreground">
|
|
126
|
+
<span className="badge badge-secondary">
|
|
127
|
+
{prompt.arguments.length} argument{prompt.arguments.length !== 1 ? 's' : ''}
|
|
128
|
+
</span>
|
|
129
|
+
</div>
|
|
130
|
+
)}
|
|
131
|
+
</div>
|
|
132
|
+
))}
|
|
133
|
+
</div>
|
|
134
|
+
)}
|
|
135
|
+
|
|
136
|
+
{/* Prompt Executor Modal */}
|
|
137
|
+
{selectedPrompt && (
|
|
138
|
+
<div
|
|
139
|
+
className="fixed inset-0 z-50 flex items-center justify-center animate-fade-in"
|
|
140
|
+
style={{ backgroundColor: 'rgba(0, 0, 0, 0.85)' }}
|
|
141
|
+
onClick={() => setSelectedPrompt(null)}
|
|
142
|
+
>
|
|
143
|
+
<div
|
|
144
|
+
className="bg-card rounded-2xl p-6 w-[600px] max-h-[80vh] overflow-auto border border-border shadow-2xl animate-scale-in"
|
|
145
|
+
onClick={(e) => e.stopPropagation()}
|
|
146
|
+
>
|
|
147
|
+
<div className="flex items-center justify-between mb-4">
|
|
148
|
+
<div className="flex items-center gap-3">
|
|
149
|
+
<div className="w-10 h-10 rounded-lg bg-blue-500/10 flex items-center justify-center">
|
|
150
|
+
<FileText className="w-5 h-5 text-blue-500" />
|
|
151
|
+
</div>
|
|
152
|
+
<h2 className="text-xl font-bold text-foreground">{selectedPrompt.name}</h2>
|
|
153
|
+
</div>
|
|
154
|
+
<button
|
|
155
|
+
onClick={() => setSelectedPrompt(null)}
|
|
156
|
+
className="btn btn-ghost w-10 h-10 p-0"
|
|
157
|
+
>
|
|
158
|
+
<X className="w-5 h-5" />
|
|
159
|
+
</button>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<p className="text-sm text-muted-foreground mb-6">
|
|
163
|
+
{selectedPrompt.description || 'No description'}
|
|
164
|
+
</p>
|
|
165
|
+
|
|
166
|
+
<form onSubmit={handleExecutePrompt}>
|
|
167
|
+
{selectedPrompt.arguments && selectedPrompt.arguments.length > 0 ? (
|
|
168
|
+
selectedPrompt.arguments.map((arg) => (
|
|
169
|
+
<div key={arg.name} className="mb-4">
|
|
170
|
+
<label className="block text-sm font-medium text-foreground mb-2">
|
|
171
|
+
{arg.name}
|
|
172
|
+
{arg.required && <span className="text-destructive ml-1">*</span>}
|
|
173
|
+
</label>
|
|
174
|
+
<input
|
|
175
|
+
type="text"
|
|
176
|
+
className="input"
|
|
177
|
+
value={promptArgs[arg.name] || ''}
|
|
178
|
+
onChange={(e) =>
|
|
179
|
+
setPromptArgs({ ...promptArgs, [arg.name]: e.target.value })
|
|
180
|
+
}
|
|
181
|
+
required={arg.required}
|
|
182
|
+
placeholder={arg.description || `Enter ${arg.name}`}
|
|
183
|
+
/>
|
|
184
|
+
{arg.description && (
|
|
185
|
+
<p className="text-xs text-muted-foreground mt-1">{arg.description}</p>
|
|
186
|
+
)}
|
|
187
|
+
</div>
|
|
188
|
+
))
|
|
189
|
+
) : (
|
|
190
|
+
<div className="bg-muted/30 rounded-lg p-4 mb-4">
|
|
191
|
+
<p className="text-sm text-muted-foreground">No arguments required</p>
|
|
192
|
+
</div>
|
|
193
|
+
)}
|
|
194
|
+
|
|
195
|
+
<button type="submit" className="btn btn-primary w-full gap-2" disabled={executing}>
|
|
196
|
+
<Play className="w-4 h-4" />
|
|
197
|
+
{executing ? 'Executing...' : 'Execute Prompt'}
|
|
198
|
+
</button>
|
|
199
|
+
</form>
|
|
200
|
+
|
|
201
|
+
{promptResult && (
|
|
202
|
+
<div className="mt-6">
|
|
203
|
+
<h3 className="font-semibold text-foreground mb-3">Messages:</h3>
|
|
204
|
+
<div className="space-y-3">
|
|
205
|
+
{promptResult.messages?.map((msg: any, idx: number) => {
|
|
206
|
+
// Extract text from content (can be string or object with type/text)
|
|
207
|
+
const contentText = typeof msg.content === 'string'
|
|
208
|
+
? msg.content
|
|
209
|
+
: msg.content?.text || JSON.stringify(msg.content);
|
|
210
|
+
|
|
211
|
+
return (
|
|
212
|
+
<div key={idx} className="bg-muted/30 border border-border p-4 rounded-lg">
|
|
213
|
+
<div className="text-xs text-muted-foreground mb-2 uppercase font-semibold">
|
|
214
|
+
{msg.role}
|
|
215
|
+
</div>
|
|
216
|
+
<div className="text-sm whitespace-pre-wrap text-foreground">{contentText}</div>
|
|
217
|
+
</div>
|
|
218
|
+
);
|
|
219
|
+
})}
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
)}
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
)}
|
|
226
|
+
</div>
|
|
227
|
+
);
|
|
228
|
+
}
|