@goldensheepai/toknxr-cli 0.2.0
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/.env +21 -0
- package/.env.example +21 -0
- package/README.md +238 -0
- package/interactions.log +8 -0
- package/lib/ai-analytics.js +296 -0
- package/lib/auth.js +73 -0
- package/lib/cli.js +382 -0
- package/lib/code-analysis.js +304 -0
- package/lib/code-review.js +319 -0
- package/lib/config.js +7 -0
- package/lib/dashboard.js +363 -0
- package/lib/hallucination-detector.js +272 -0
- package/lib/policy.js +49 -0
- package/lib/pricing.js +20 -0
- package/lib/proxy.js +359 -0
- package/lib/sync.js +95 -0
- package/package.json +38 -0
- package/src/ai-analytics.ts +418 -0
- package/src/auth.ts +80 -0
- package/src/cli.ts +447 -0
- package/src/code-analysis.ts +365 -0
- package/src/config.ts +10 -0
- package/src/dashboard.tsx +391 -0
- package/src/hallucination-detector.ts +368 -0
- package/src/policy.ts +55 -0
- package/src/pricing.ts +21 -0
- package/src/proxy.ts +438 -0
- package/src/sync.ts +129 -0
- package/start.sh +56 -0
- package/test-analysis.mjs +77 -0
- package/test-coding.mjs +27 -0
- package/test-generate-sample-data.js +118 -0
- package/test-proxy.mjs +25 -0
- package/toknxr.config.json +63 -0
- package/toknxr.policy.json +18 -0
- package/tsconfig.json +19 -0
@@ -0,0 +1,391 @@
|
|
1
|
+
// Simple vanilla JavaScript dashboard for browser compatibility
|
2
|
+
// This creates a pure HTML/CSS/JS dashboard that works without React
|
3
|
+
|
4
|
+
function createDashboard() {
|
5
|
+
const COLORS = {
|
6
|
+
primary: '#6B5BED',
|
7
|
+
secondary: '#9B5BED',
|
8
|
+
accent: '#ED5B9B',
|
9
|
+
success: '#10B981',
|
10
|
+
warning: '#F59E0B',
|
11
|
+
error: '#EF4444',
|
12
|
+
background: '#0F172A',
|
13
|
+
surface: '#1E293B',
|
14
|
+
text: '#F8FAFC',
|
15
|
+
textMuted: '#94A3B8'
|
16
|
+
};
|
17
|
+
|
18
|
+
let stats = null;
|
19
|
+
let lastUpdated = new Date();
|
20
|
+
|
21
|
+
// Create stat card HTML
|
22
|
+
function createStatCard(title: string, value: string | number, icon: string, color: string = COLORS.primary) {
|
23
|
+
return `
|
24
|
+
<div style="background: ${COLORS.surface}; border-radius: 12px; padding: 24px; border: 1px solid ${color}20; box-shadow: 0 4px 6px -1px ${color}10;">
|
25
|
+
<div style="display: flex; align-items: center; justify-content: space-between;">
|
26
|
+
<div>
|
27
|
+
<p style="color: ${COLORS.textMuted}; font-size: 14px; margin: 0 0 8px 0;">${title}</p>
|
28
|
+
<p style="font-size: 32px; font-weight: bold; margin: 0; color: ${COLORS.text};">${value}</p>
|
29
|
+
</div>
|
30
|
+
<div style="font-size: 24px; color: ${color}; opacity: 0.8;">${icon}</div>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
`;
|
34
|
+
}
|
35
|
+
|
36
|
+
// Create provider card HTML
|
37
|
+
function createProviderChart(name: string, data: any) {
|
38
|
+
return `
|
39
|
+
<div style="background: ${COLORS.surface}; border-radius: 8px; padding: 16px; border: 1px solid ${COLORS.primary}20;">
|
40
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
|
41
|
+
<h3 style="margin: 0; color: ${COLORS.text}; font-size: 16px;">${name}</h3>
|
42
|
+
<div style="font-size: 12px; color: ${COLORS.textMuted};">${(data.cost || 0).toFixed(2)}</div>
|
43
|
+
</div>
|
44
|
+
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; font-size: 12px;">
|
45
|
+
<div style="color: ${COLORS.textMuted};">Interactions: <span style="color: ${COLORS.text};">${data.interactions || 0}</span></div>
|
46
|
+
${data.avgQuality ? `<div style="color: ${COLORS.textMuted};">Quality: <span style="color: ${COLORS.accent};">${data.avgQuality}/100</span></div>` : ''}
|
47
|
+
${data.avgEffectiveness ? `<div style="color: ${COLORS.textMuted};">Effectiveness: <span style="color: ${COLORS.secondary};">${data.avgEffectiveness}/100</span></div>` : ''}
|
48
|
+
</div>
|
49
|
+
</div>
|
50
|
+
`;
|
51
|
+
}
|
52
|
+
|
53
|
+
// Create recent activity HTML with prompt previews
|
54
|
+
function createRecentActivity(interactions: any[]) {
|
55
|
+
const recentHTML = interactions.slice(-10).map((interaction: any, i: number) => {
|
56
|
+
const qualityColor = !interaction.qualityScore ? COLORS.textMuted :
|
57
|
+
interaction.qualityScore >= 90 ? COLORS.success :
|
58
|
+
interaction.qualityScore >= 70 ? COLORS.warning : COLORS.error;
|
59
|
+
|
60
|
+
const effectivenessColor = !interaction.effectivenessScore ? COLORS.textMuted :
|
61
|
+
interaction.effectivenessScore >= 90 ? COLORS.success :
|
62
|
+
interaction.effectivenessScore >= 70 ? COLORS.warning : COLORS.error;
|
63
|
+
|
64
|
+
const promptPreview = interaction.userPrompt ?
|
65
|
+
(interaction.userPrompt.length > 60 ?
|
66
|
+
interaction.userPrompt.substring(0, 60) + '...' :
|
67
|
+
interaction.userPrompt) : 'No prompt captured';
|
68
|
+
|
69
|
+
return `
|
70
|
+
<div style="padding: 16px 0; border-bottom: ${i < interactions.length - 1 ? `1px solid ${COLORS.primary}10` : 'none'}; cursor: pointer; transition: background-color 0.2s;"
|
71
|
+
onmouseover="this.style.background='${COLORS.primary}08'"
|
72
|
+
onmouseout="this.style.background='transparent'"
|
73
|
+
onclick="showPromptDetails('${interaction.provider}', '${interaction.model}', \`${(interaction.userPrompt || 'N/A').replace(/'/g, "\\'")}\`, \`${(interaction.aiResponse || 'N/A').substring(0, 300).replace(/'/g, "\\'")}\`, ${interaction.qualityScore || 0}, ${interaction.effectivenessScore || 0}, '${interaction.taskType || 'unknown'}', ${interaction.cost || 0})">
|
74
|
+
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 8px;">
|
75
|
+
<div style="flex: 1;">
|
76
|
+
<div style="color: ${COLORS.text}; font-size: 14px; font-weight: 500; margin-bottom: 4px;">
|
77
|
+
${interaction.provider} • ${interaction.model}
|
78
|
+
</div>
|
79
|
+
<div style="color: ${COLORS.textMuted}; font-size: 12px; margin-bottom: 6px;">
|
80
|
+
${new Date(interaction.timestamp).toLocaleTimeString()}
|
81
|
+
${interaction.taskType ? ` • <span style="color: ${COLORS.accent};">${interaction.taskType}</span>` : ''}
|
82
|
+
</div>
|
83
|
+
<div style="color: ${COLORS.textMuted}; font-size: 11px; font-style: italic; line-height: 1.3; margin-bottom: 8px;">
|
84
|
+
💬 ${promptPreview}
|
85
|
+
</div>
|
86
|
+
</div>
|
87
|
+
<div style="text-align: right; margin-left: 16px;">
|
88
|
+
<div style="color: ${COLORS.success}; font-size: 14px; font-weight: 500; margin-bottom: 4px;">
|
89
|
+
${interaction.cost.toFixed(4)}
|
90
|
+
</div>
|
91
|
+
<div style="display: flex; gap: 8px; font-size: 12px;">
|
92
|
+
${interaction.qualityScore ? `<span style="color: ${qualityColor};">Q:${interaction.qualityScore}</span>` : ''}
|
93
|
+
${interaction.effectivenessScore ? `<span style="color: ${effectivenessColor};">E:${interaction.effectivenessScore}</span>` : ''}
|
94
|
+
</div>
|
95
|
+
</div>
|
96
|
+
</div>
|
97
|
+
</div>
|
98
|
+
`;
|
99
|
+
}).join('');
|
100
|
+
|
101
|
+
return `
|
102
|
+
<div style="background: ${COLORS.surface}; border-radius: 12px; padding: 24px; border: 1px solid ${COLORS.primary}20;">
|
103
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
|
104
|
+
<h2 style="margin: 0; color: ${COLORS.text};">Recent Activity</h2>
|
105
|
+
<div style="display: flex; gap: 8px;">
|
106
|
+
<input type="text" id="prompt-search" placeholder="Search prompts..." style="background: ${COLORS.background}; border: 1px solid ${COLORS.primary}20; color: ${COLORS.text}; padding: 4px 8px; border-radius: 4px; font-size: 12px;" onkeyup="filterPrompts()">
|
107
|
+
<select id="provider-filter" style="background: ${COLORS.background}; border: 1px solid ${COLORS.primary}20; color: ${COLORS.text}; padding: 4px 8px; border-radius: 4px; font-size: 12px;" onchange="filterPrompts()">
|
108
|
+
<option value="">All Providers</option>
|
109
|
+
<option value="Gemini-Pro">Gemini-Pro</option>
|
110
|
+
<option value="OpenAI-GPT4">OpenAI-GPT4</option>
|
111
|
+
<option value="Anthropic-Claude">Anthropic-Claude</option>
|
112
|
+
<option value="Ollama-Llama3">Ollama-Llama3</option>
|
113
|
+
</select>
|
114
|
+
</div>
|
115
|
+
</div>
|
116
|
+
<div id="activity-list" style="max-height: 400px; overflow-y: auto;">
|
117
|
+
${recentHTML}
|
118
|
+
</div>
|
119
|
+
</div>
|
120
|
+
`;
|
121
|
+
}
|
122
|
+
|
123
|
+
// Fetch and update stats
|
124
|
+
async function fetchStats() {
|
125
|
+
try {
|
126
|
+
const response = await fetch('/api/stats');
|
127
|
+
if (!response.ok) throw new Error('Failed to fetch stats');
|
128
|
+
const data = await response.json();
|
129
|
+
|
130
|
+
// Transform the data for display
|
131
|
+
const totalCost = data.totals.total || 0;
|
132
|
+
const totalInteractions = data.totals.requestCount || 0;
|
133
|
+
const avgCostPerTask = totalInteractions ? (totalCost / totalInteractions) : 0;
|
134
|
+
|
135
|
+
// Update stats cards
|
136
|
+
document.getElementById('total-cost')!.innerHTML = `${totalCost.toFixed(4)}`;
|
137
|
+
document.getElementById('total-interactions')!.innerHTML = totalInteractions.toString();
|
138
|
+
document.getElementById('avg-cost')!.innerHTML = `${avgCostPerTask.toFixed(4)}`;
|
139
|
+
document.getElementById('waste-rate')!.innerHTML = '0.0%';
|
140
|
+
|
141
|
+
// Update provider cards
|
142
|
+
const providerContainer = document.getElementById('provider-container');
|
143
|
+
if (providerContainer && data.totals.byProvider) {
|
144
|
+
const providerHTML = Object.entries(data.totals.byProvider).map(([name, providerData]) =>
|
145
|
+
createProviderChart(name, {
|
146
|
+
cost: (providerData as any).costUSD || 0,
|
147
|
+
interactions: (providerData as any).requestCount || 0,
|
148
|
+
avgQuality: (providerData as any).avgQualityScore,
|
149
|
+
avgEffectiveness: (providerData as any).avgEffectivenessScore
|
150
|
+
})
|
151
|
+
).join('');
|
152
|
+
providerContainer.innerHTML = providerHTML;
|
153
|
+
}
|
154
|
+
|
155
|
+
// Update recent activity
|
156
|
+
const activityContainer = document.getElementById('recent-activity');
|
157
|
+
if (activityContainer) {
|
158
|
+
activityContainer.innerHTML = createRecentActivity(data.recentInteractions || []);
|
159
|
+
}
|
160
|
+
|
161
|
+
// Update timestamp
|
162
|
+
lastUpdated = new Date();
|
163
|
+
document.getElementById('last-updated')!.textContent = lastUpdated.toLocaleTimeString();
|
164
|
+
|
165
|
+
} catch (error) {
|
166
|
+
console.error('Dashboard error:', error);
|
167
|
+
document.getElementById('error-message')!.textContent = (error as Error).message;
|
168
|
+
document.getElementById('error-container')!.style.display = 'block';
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
// Create the complete dashboard HTML
|
173
|
+
const dashboardHTML = `
|
174
|
+
<div style="background: ${COLORS.background}; min-height: 100vh; color: ${COLORS.text}; font-family: system-ui, -apple-system, sans-serif;">
|
175
|
+
<!-- Header -->
|
176
|
+
<header style="background: ${COLORS.surface}; border-bottom: 1px solid ${COLORS.primary}20; padding: 16px 24px;">
|
177
|
+
<div style="display: flex; justify-content: space-between; align-items: center; max-width: 1200px; margin: 0 auto;">
|
178
|
+
<div>
|
179
|
+
<h1 style="margin: 0; font-size: 24px; color: ${COLORS.text};">🚀 TokNXR Dashboard</h1>
|
180
|
+
<p style="color: ${COLORS.textMuted}; margin: 4px 0 0 0; font-size: 14px;">
|
181
|
+
Real-time AI Effectiveness & Code Quality Analytics
|
182
|
+
</p>
|
183
|
+
</div>
|
184
|
+
<div style="text-align: right;">
|
185
|
+
<div id="last-updated" style="color: ${COLORS.textMuted}; font-size: 12px;">
|
186
|
+
Last updated: ${lastUpdated.toLocaleTimeString()}
|
187
|
+
</div>
|
188
|
+
<button
|
189
|
+
onclick="fetchStats()"
|
190
|
+
style="background: ${COLORS.primary}; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; margin-top: 4px;"
|
191
|
+
>
|
192
|
+
Refresh
|
193
|
+
</button>
|
194
|
+
</div>
|
195
|
+
</div>
|
196
|
+
</header>
|
197
|
+
|
198
|
+
<!-- Error Message -->
|
199
|
+
<div id="error-container" style="display: none; background: ${COLORS.error}; color: white; padding: 12px 16px; margin: 16px; border-radius: 8px;">
|
200
|
+
Error: <span id="error-message"></span>
|
201
|
+
</div>
|
202
|
+
|
203
|
+
<!-- Main Content -->
|
204
|
+
<main style="padding: 24px; max-width: 1200px; margin: 0 auto;">
|
205
|
+
<!-- Stats Overview -->
|
206
|
+
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 24px; margin-bottom: 32px;">
|
207
|
+
<div style="background: ${COLORS.surface}; border-radius: 12px; padding: 24px; border: 1px solid ${COLORS.success}20;">
|
208
|
+
<div style="display: flex; align-items: center; justify-content: space-between;">
|
209
|
+
<div>
|
210
|
+
<p style="color: ${COLORS.textMuted}; font-size: 14px; margin: 0 0 8px 0;">Total Cost</p>
|
211
|
+
<p id="total-cost" style="font-size: 32px; font-weight: bold; margin: 0; color: ${COLORS.text};">$0.0000</p>
|
212
|
+
</div>
|
213
|
+
<div style="font-size: 24px; color: ${COLORS.success}; opacity: 0.8;">💰</div>
|
214
|
+
</div>
|
215
|
+
</div>
|
216
|
+
|
217
|
+
<div style="background: ${COLORS.surface}; border-radius: 12px; padding: 24px; border: 1px solid ${COLORS.primary}20;">
|
218
|
+
<div style="display: flex; align-items: center; justify-content: space-between;">
|
219
|
+
<div>
|
220
|
+
<p style="color: ${COLORS.textMuted}; font-size: 14px; margin: 0 0 8px 0;">Total Interactions</p>
|
221
|
+
<p id="total-interactions" style="font-size: 32px; font-weight: bold; margin: 0; color: ${COLORS.text};">0</p>
|
222
|
+
</div>
|
223
|
+
<div style="font-size: 24px; color: ${COLORS.primary}; opacity: 0.8;">📊</div>
|
224
|
+
</div>
|
225
|
+
</div>
|
226
|
+
|
227
|
+
<div style="background: ${COLORS.surface}; border-radius: 12px; padding: 24px; border: 1px solid ${COLORS.secondary}20;">
|
228
|
+
<div style="display: flex; align-items: center; justify-content: space-between;">
|
229
|
+
<div>
|
230
|
+
<p style="color: ${COLORS.textMuted}; font-size: 14px; margin: 0 0 8px 0;">Avg Cost per Task</p>
|
231
|
+
<p id="avg-cost" style="font-size: 32px; font-weight: bold; margin: 0; color: ${COLORS.text};">$0.0000</p>
|
232
|
+
</div>
|
233
|
+
<div style="font-size: 24px; color: ${COLORS.secondary}; opacity: 0.8;">🎯</div>
|
234
|
+
</div>
|
235
|
+
</div>
|
236
|
+
|
237
|
+
<div style="background: ${COLORS.surface}; border-radius: 12px; padding: 24px; border: 1px solid ${COLORS.warning}20;">
|
238
|
+
<div style="display: flex; align-items: center; justify-content: space-between;">
|
239
|
+
<div>
|
240
|
+
<p style="color: ${COLORS.textMuted}; font-size: 14px; margin: 0 0 8px 0;">Waste Rate</p>
|
241
|
+
<p id="waste-rate" style="font-size: 32px; font-weight: bold; margin: 0; color: ${COLORS.text};">0.0%</p>
|
242
|
+
</div>
|
243
|
+
<div style="font-size: 24px; color: ${COLORS.warning}; opacity: 0.8;">⚠️</div>
|
244
|
+
</div>
|
245
|
+
</div>
|
246
|
+
</div>
|
247
|
+
|
248
|
+
<!-- Provider Breakdown -->
|
249
|
+
<div id="provider-section" style="margin-bottom: 32px;">
|
250
|
+
<h2 style="color: ${COLORS.text}; margin: 0 0 16px 0;">Provider Breakdown</h2>
|
251
|
+
<div id="provider-container" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px;">
|
252
|
+
<!-- Provider cards will be inserted here -->
|
253
|
+
</div>
|
254
|
+
</div>
|
255
|
+
|
256
|
+
<!-- Recent Activity -->
|
257
|
+
<div id="recent-activity">
|
258
|
+
<!-- Recent activity will be inserted here -->
|
259
|
+
</div>
|
260
|
+
</main>
|
261
|
+
|
262
|
+
<!-- Footer -->
|
263
|
+
<footer style="background: ${COLORS.surface}; border-top: 1px solid ${COLORS.primary}20; padding: 16px 24px; text-align: center; color: ${COLORS.textMuted}; font-size: 12px;">
|
264
|
+
<div style="max-width: 1200px; margin: 0 auto;">
|
265
|
+
TokNXR Dashboard • Real-time AI Analytics • ${new Date().getFullYear()}
|
266
|
+
<br>
|
267
|
+
<span style="color: #FFD700; font-size: 11px; margin-top: 4px; display: block;">
|
268
|
+
🐑 Powered by Golden Sheep AI
|
269
|
+
</span>
|
270
|
+
</div>
|
271
|
+
</footer>
|
272
|
+
</div>
|
273
|
+
`;
|
274
|
+
|
275
|
+
// Set the dashboard content
|
276
|
+
const container = document.getElementById('dashboard-root');
|
277
|
+
if (container) {
|
278
|
+
container.innerHTML = dashboardHTML;
|
279
|
+
|
280
|
+
// Start fetching stats
|
281
|
+
fetchStats();
|
282
|
+
|
283
|
+
// Auto-refresh every 5 seconds
|
284
|
+
setInterval(fetchStats, 5000);
|
285
|
+
}
|
286
|
+
}
|
287
|
+
|
288
|
+
// Modal for showing prompt details
|
289
|
+
function showPromptDetails(provider: string, model: string, userPrompt: string, aiResponse: string, qualityScore: number, effectivenessScore: number, taskType: string, cost: number) {
|
290
|
+
const modal = document.createElement('div');
|
291
|
+
modal.id = 'prompt-modal';
|
292
|
+
modal.style.cssText = `
|
293
|
+
position: fixed;
|
294
|
+
top: 0;
|
295
|
+
left: 0;
|
296
|
+
width: 100%;
|
297
|
+
height: 100%;
|
298
|
+
background: rgba(0, 0, 0, 0.8);
|
299
|
+
display: flex;
|
300
|
+
align-items: center;
|
301
|
+
justify-content: center;
|
302
|
+
z-index: 1000;
|
303
|
+
padding: 20px;
|
304
|
+
box-sizing: border-box;
|
305
|
+
`;
|
306
|
+
|
307
|
+
const modalContent = document.createElement('div');
|
308
|
+
modalContent.style.cssText = `
|
309
|
+
background: #1E293B;
|
310
|
+
border-radius: 12px;
|
311
|
+
padding: 24px;
|
312
|
+
max-width: 800px;
|
313
|
+
max-height: 80vh;
|
314
|
+
overflow-y: auto;
|
315
|
+
color: #F8FAFC;
|
316
|
+
border: 1px solid #6B5BED20;
|
317
|
+
`;
|
318
|
+
|
319
|
+
modalContent.innerHTML = `
|
320
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
321
|
+
<h3 style="margin: 0; color: #F8FAFC;">Interaction Details</h3>
|
322
|
+
<button onclick="closePromptModal()" style="background: #6B5BED; color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer;">✕ Close</button>
|
323
|
+
</div>
|
324
|
+
|
325
|
+
<div style="margin-bottom: 16px;">
|
326
|
+
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 20px;">
|
327
|
+
<div>
|
328
|
+
<strong>Provider:</strong> ${provider}<br>
|
329
|
+
<strong>Model:</strong> ${model}<br>
|
330
|
+
<strong>Task Type:</strong> ${taskType}<br>
|
331
|
+
<strong>Cost:</strong> $${cost.toFixed(4)}
|
332
|
+
</div>
|
333
|
+
<div>
|
334
|
+
<strong>Quality Score:</strong> ${qualityScore}/100<br>
|
335
|
+
<strong>Effectiveness:</strong> ${effectivenessScore}/100<br>
|
336
|
+
<strong>Status:</strong> ${qualityScore >= 90 ? '🟢 Excellent' : qualityScore >= 70 ? '🟡 Good' : '🔴 Needs Improvement'}
|
337
|
+
</div>
|
338
|
+
</div>
|
339
|
+
|
340
|
+
<div style="margin-bottom: 16px;">
|
341
|
+
<h4 style="margin: 0 0 8px 0; color: #F8FAFC;">📝 User Prompt:</h4>
|
342
|
+
<div style="background: #0F172A; padding: 12px; border-radius: 6px; border: 1px solid #6B5BED20; font-family: monospace; white-space: pre-wrap;">${userPrompt}</div>
|
343
|
+
</div>
|
344
|
+
|
345
|
+
<div>
|
346
|
+
<h4 style="margin: 0 0 8px 0; color: #F8FAFC;">🤖 AI Response:</h4>
|
347
|
+
<div style="background: #0F172A; padding: 12px; border-radius: 6px; border: 1px solid #6B5BED20; font-family: monospace; white-space: pre-wrap; max-height: 200px; overflow-y: auto;">${aiResponse}${aiResponse.length > 300 ? '...' : ''}</div>
|
348
|
+
</div>
|
349
|
+
</div>
|
350
|
+
`;
|
351
|
+
|
352
|
+
modal.appendChild(modalContent);
|
353
|
+
document.body.appendChild(modal);
|
354
|
+
|
355
|
+
// Close modal on outside click
|
356
|
+
modal.addEventListener('click', function(e) {
|
357
|
+
if (e.target === modal) {
|
358
|
+
closePromptModal();
|
359
|
+
}
|
360
|
+
});
|
361
|
+
}
|
362
|
+
|
363
|
+
function closePromptModal() {
|
364
|
+
const modal = document.getElementById('prompt-modal');
|
365
|
+
if (modal) {
|
366
|
+
modal.remove();
|
367
|
+
}
|
368
|
+
}
|
369
|
+
|
370
|
+
// Filter prompts based on search and provider filter
|
371
|
+
function filterPrompts() {
|
372
|
+
const searchTermValue = (document.getElementById('prompt-search') as HTMLInputElement)?.value.toLowerCase() || '';
|
373
|
+
const providerFilterValue = (document.getElementById('provider-filter') as HTMLSelectElement)?.value || '';
|
374
|
+
const activityItems = document.querySelectorAll('#activity-list > div');
|
375
|
+
|
376
|
+
activityItems.forEach(item => {
|
377
|
+
const text = item.textContent?.toLowerCase() || '';
|
378
|
+
const provider = item.textContent?.split(' • ')[0] || '';
|
379
|
+
|
380
|
+
const shouldShow = text.includes(searchTermValue) &&
|
381
|
+
(providerFilterValue === '' || text.includes(providerFilterValue.toLowerCase()));
|
382
|
+
(item as HTMLElement).style.display = shouldShow ? 'block' : 'none';
|
383
|
+
});
|
384
|
+
}
|
385
|
+
|
386
|
+
// Initialize dashboard when DOM is loaded
|
387
|
+
if (document.readyState === 'loading') {
|
388
|
+
document.addEventListener('DOMContentLoaded', createDashboard);
|
389
|
+
} else {
|
390
|
+
createDashboard();
|
391
|
+
}
|