monol-plugin-scout 2.1.3 → 4.1.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/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +244 -0
- package/monol-plugin-scout-pkg/CLAUDE.md +125 -8
- package/monol-plugin-scout-pkg/api/mock/categories.json +81 -0
- package/monol-plugin-scout-pkg/api/mock/insights.json +111 -0
- package/monol-plugin-scout-pkg/api/mock/marketplace.json +501 -0
- package/monol-plugin-scout-pkg/api/mock/trending.json +96 -0
- package/monol-plugin-scout-pkg/commands/console.md +79 -0
- package/monol-plugin-scout-pkg/commands/frequency.md +32 -0
- package/monol-plugin-scout-pkg/commands/install.md +32 -0
- package/monol-plugin-scout-pkg/commands/priority.md +30 -0
- package/monol-plugin-scout-pkg/commands/quiet.md +32 -0
- package/monol-plugin-scout-pkg/commands/schedule.md +32 -0
- package/monol-plugin-scout-pkg/commands/scout.md +8 -5
- package/monol-plugin-scout-pkg/commands/skills.md +160 -0
- package/monol-plugin-scout-pkg/commands/team.md +32 -0
- package/monol-plugin-scout-pkg/commands/timing.md +32 -0
- package/monol-plugin-scout-pkg/commands/trust.md +85 -0
- package/monol-plugin-scout-pkg/commands/trusted-install.md +98 -0
- package/monol-plugin-scout-pkg/commands/uninstall.md +31 -0
- package/monol-plugin-scout-pkg/config.yaml +57 -0
- package/monol-plugin-scout-pkg/data/.cache/676d5ab664292155e5f509c69d4edae1 +10 -0
- package/monol-plugin-scout-pkg/data/.session +8 -0
- package/monol-plugin-scout-pkg/data/analytics.json +102 -0
- package/monol-plugin-scout-pkg/data/history.json +67 -11
- package/monol-plugin-scout-pkg/data/logs/scout.log +1 -0
- package/monol-plugin-scout-pkg/data/schedules.json +17 -0
- package/monol-plugin-scout-pkg/data/team.json +53 -0
- package/monol-plugin-scout-pkg/data/usage.json +100 -7
- package/monol-plugin-scout-pkg/docs/ARCHITECTURE.md +201 -0
- package/monol-plugin-scout-pkg/hooks/generate-insights.sh +102 -0
- package/monol-plugin-scout-pkg/hooks/hooks.json +36 -1
- package/monol-plugin-scout-pkg/hooks/on-session-end.sh +136 -0
- package/monol-plugin-scout-pkg/hooks/on-session-start.sh +43 -0
- package/monol-plugin-scout-pkg/hooks/on-stop.md +79 -0
- package/monol-plugin-scout-pkg/hooks/open-console.sh +111 -0
- package/monol-plugin-scout-pkg/hooks/open-dashboard.sh +61 -0
- package/monol-plugin-scout-pkg/hooks/track-usage.sh +59 -0
- package/monol-plugin-scout-pkg/lib/ai-recommender.sh +505 -0
- package/monol-plugin-scout-pkg/lib/cache.sh +194 -0
- package/monol-plugin-scout-pkg/lib/data-validator.sh +360 -0
- package/monol-plugin-scout-pkg/lib/error-handler.sh +296 -0
- package/monol-plugin-scout-pkg/lib/logger.sh +263 -0
- package/monol-plugin-scout-pkg/lib/plugin-manager.sh +239 -0
- package/monol-plugin-scout-pkg/lib/priority-scorer.sh +262 -0
- package/monol-plugin-scout-pkg/lib/profile-learner.sh +339 -0
- package/monol-plugin-scout-pkg/lib/project-analyzer.sh +281 -0
- package/monol-plugin-scout-pkg/lib/recommendation-controller.sh +290 -0
- package/monol-plugin-scout-pkg/lib/rejection-learner.sh +232 -0
- package/monol-plugin-scout-pkg/lib/scheduler.sh +275 -0
- package/monol-plugin-scout-pkg/lib/skill-scout.sh +729 -0
- package/monol-plugin-scout-pkg/lib/sync.sh +221 -0
- package/monol-plugin-scout-pkg/lib/team-learner.sh +450 -0
- package/monol-plugin-scout-pkg/lib/team-manager.sh +369 -0
- package/monol-plugin-scout-pkg/lib/trend-learner.sh +428 -0
- package/monol-plugin-scout-pkg/lib/trust-manager.sh +261 -0
- package/monol-plugin-scout-pkg/lib/trusted-installer.sh +738 -0
- package/monol-plugin-scout-pkg/plugin.json +3 -2
- package/monol-plugin-scout-pkg/skills/audit.md +6 -0
- package/monol-plugin-scout-pkg/skills/cleanup.md +6 -0
- package/monol-plugin-scout-pkg/skills/compare.md +6 -0
- package/monol-plugin-scout-pkg/skills/console.md +315 -0
- package/monol-plugin-scout-pkg/skills/explore.md +6 -0
- package/monol-plugin-scout-pkg/skills/fork.md +6 -0
- package/monol-plugin-scout-pkg/skills/frequency.md +93 -0
- package/monol-plugin-scout-pkg/skills/install.md +127 -0
- package/monol-plugin-scout-pkg/skills/priority.md +77 -0
- package/monol-plugin-scout-pkg/skills/quiet.md +73 -0
- package/monol-plugin-scout-pkg/skills/schedule.md +95 -0
- package/monol-plugin-scout-pkg/skills/scout.md +27 -17
- package/monol-plugin-scout-pkg/skills/skills.md +230 -0
- package/monol-plugin-scout-pkg/skills/team.md +117 -0
- package/monol-plugin-scout-pkg/skills/timing.md +97 -0
- package/monol-plugin-scout-pkg/skills/trust.md +120 -0
- package/monol-plugin-scout-pkg/skills/trusted-install.md +264 -0
- package/monol-plugin-scout-pkg/skills/uninstall.md +100 -0
- package/monol-plugin-scout-pkg/web/components/activity-chart.js +208 -0
- package/monol-plugin-scout-pkg/web/components/index.js +27 -0
- package/monol-plugin-scout-pkg/web/components/insight-card.js +365 -0
- package/monol-plugin-scout-pkg/web/components/overview.js +154 -0
- package/monol-plugin-scout-pkg/web/components/plugin-list.js +242 -0
- package/monol-plugin-scout-pkg/web/components/stats-card.js +126 -0
- package/monol-plugin-scout-pkg/web/components/team-list.js +346 -0
- package/monol-plugin-scout-pkg/web/console.html +2098 -0
- package/monol-plugin-scout-pkg/web/dashboard.html +2106 -0
- package/monol-plugin-scout-pkg/web/manifest.json +29 -0
- package/package.json +1 -1
|
@@ -0,0 +1,2106 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="ko" class="dark">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Plugin Scout Console</title>
|
|
7
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
8
|
+
<!-- Monol Design System -->
|
|
9
|
+
<link rel="stylesheet" href="../../../monol-design/monol-design.min.css">
|
|
10
|
+
<style>
|
|
11
|
+
:root {
|
|
12
|
+
/* Monol Design System Colors */
|
|
13
|
+
--bg-primary: var(--monol-bg-base, #0a0a0a);
|
|
14
|
+
--bg-secondary: var(--monol-bg-elevated, #171717);
|
|
15
|
+
--bg-tertiary: var(--monol-bg-surface, #1a1a1a);
|
|
16
|
+
--bg-hover: var(--monol-secondary-hover, #333333);
|
|
17
|
+
--border-color: var(--monol-border-base, #262626);
|
|
18
|
+
--text-primary: var(--monol-fg-base, #fafafa);
|
|
19
|
+
--text-secondary: var(--monol-fg-muted, #a3a3a3);
|
|
20
|
+
--text-muted: var(--monol-fg-subtle, #737373);
|
|
21
|
+
--accent-green: #22c55e;
|
|
22
|
+
--accent-cyan: #06b6d4;
|
|
23
|
+
--accent-purple: #a855f7;
|
|
24
|
+
--accent-orange: var(--monol-primary, #f97316);
|
|
25
|
+
--accent-red: var(--monol-error, #ef4444);
|
|
26
|
+
--accent-yellow: var(--monol-warning, #f59e0b);
|
|
27
|
+
--accent-blue: var(--monol-info, #3b82f6);
|
|
28
|
+
--accent-pink: #ec4899;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
[data-theme="light"] {
|
|
32
|
+
--bg-primary: #ffffff;
|
|
33
|
+
--bg-secondary: #f6f8fa;
|
|
34
|
+
--bg-tertiary: #eaeef2;
|
|
35
|
+
--bg-hover: #d0d7de;
|
|
36
|
+
--border-color: #d0d7de;
|
|
37
|
+
--text-primary: #1f2328;
|
|
38
|
+
--text-secondary: #656d76;
|
|
39
|
+
--text-muted: #8c959f;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
43
|
+
|
|
44
|
+
body {
|
|
45
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
|
|
46
|
+
background: var(--bg-primary);
|
|
47
|
+
color: var(--text-primary);
|
|
48
|
+
min-height: 100vh;
|
|
49
|
+
line-height: 1.5;
|
|
50
|
+
transition: background 0.3s, color 0.3s;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.container { max-width: 1600px; margin: 0 auto; padding: 20px 24px; }
|
|
54
|
+
|
|
55
|
+
/* Header */
|
|
56
|
+
.header {
|
|
57
|
+
display: flex;
|
|
58
|
+
justify-content: space-between;
|
|
59
|
+
align-items: center;
|
|
60
|
+
margin-bottom: 20px;
|
|
61
|
+
padding-bottom: 16px;
|
|
62
|
+
border-bottom: 1px solid var(--border-color);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.logo { display: flex; align-items: center; gap: 12px; }
|
|
66
|
+
|
|
67
|
+
.logo-icon {
|
|
68
|
+
width: 44px; height: 44px;
|
|
69
|
+
background: linear-gradient(135deg, var(--accent-green), var(--accent-cyan));
|
|
70
|
+
border-radius: 12px;
|
|
71
|
+
display: flex; align-items: center; justify-content: center;
|
|
72
|
+
font-size: 22px;
|
|
73
|
+
box-shadow: 0 4px 12px rgba(0, 255, 136, 0.3);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.logo-text h1 {
|
|
77
|
+
font-size: 20px; font-weight: 700;
|
|
78
|
+
background: linear-gradient(90deg, var(--accent-green), var(--accent-cyan));
|
|
79
|
+
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
|
80
|
+
background-clip: text;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.logo-text span { font-size: 11px; color: var(--text-secondary); }
|
|
84
|
+
|
|
85
|
+
.header-center { display: flex; align-items: center; gap: 12px; flex: 1; justify-content: center; max-width: 500px; margin: 0 24px; }
|
|
86
|
+
|
|
87
|
+
.search-box {
|
|
88
|
+
flex: 1;
|
|
89
|
+
display: flex;
|
|
90
|
+
align-items: center;
|
|
91
|
+
gap: 8px;
|
|
92
|
+
padding: 10px 16px;
|
|
93
|
+
background: var(--bg-tertiary);
|
|
94
|
+
border: 1px solid var(--border-color);
|
|
95
|
+
border-radius: 10px;
|
|
96
|
+
cursor: pointer;
|
|
97
|
+
transition: all 0.2s;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.search-box:hover { border-color: var(--accent-green); }
|
|
101
|
+
.search-box span { color: var(--text-muted); font-size: 14px; }
|
|
102
|
+
.search-box kbd { margin-left: auto; padding: 2px 8px; background: var(--bg-secondary); border-radius: 4px; font-size: 11px; color: var(--text-muted); }
|
|
103
|
+
|
|
104
|
+
.header-actions { display: flex; align-items: center; gap: 8px; }
|
|
105
|
+
|
|
106
|
+
.btn {
|
|
107
|
+
padding: 8px 14px;
|
|
108
|
+
border-radius: 8px;
|
|
109
|
+
font-size: 13px;
|
|
110
|
+
font-weight: 500;
|
|
111
|
+
cursor: pointer;
|
|
112
|
+
border: 1px solid var(--border-color);
|
|
113
|
+
background: var(--bg-secondary);
|
|
114
|
+
color: var(--text-primary);
|
|
115
|
+
transition: all 0.2s;
|
|
116
|
+
display: flex; align-items: center; gap: 6px;
|
|
117
|
+
white-space: nowrap;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.btn:hover { background: var(--bg-tertiary); border-color: var(--text-muted); }
|
|
121
|
+
.btn-icon { padding: 8px 10px; }
|
|
122
|
+
|
|
123
|
+
.btn-primary {
|
|
124
|
+
background: linear-gradient(135deg, var(--accent-green), var(--accent-cyan));
|
|
125
|
+
border: none;
|
|
126
|
+
color: #0d1117;
|
|
127
|
+
font-weight: 600;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.btn-primary:hover { opacity: 0.9; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(0, 255, 136, 0.3); }
|
|
131
|
+
|
|
132
|
+
/* Layout */
|
|
133
|
+
.main-layout { display: grid; grid-template-columns: 1fr 320px; gap: 20px; }
|
|
134
|
+
|
|
135
|
+
@media (max-width: 1200px) { .main-layout { grid-template-columns: 1fr; } .sidebar { display: none; } }
|
|
136
|
+
|
|
137
|
+
/* Tabs */
|
|
138
|
+
.tabs {
|
|
139
|
+
display: flex;
|
|
140
|
+
gap: 4px;
|
|
141
|
+
background: var(--bg-secondary);
|
|
142
|
+
padding: 4px;
|
|
143
|
+
border-radius: 10px;
|
|
144
|
+
margin-bottom: 20px;
|
|
145
|
+
overflow-x: auto;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.tab {
|
|
149
|
+
padding: 10px 18px;
|
|
150
|
+
border-radius: 8px;
|
|
151
|
+
font-size: 13px;
|
|
152
|
+
font-weight: 500;
|
|
153
|
+
cursor: pointer;
|
|
154
|
+
color: var(--text-secondary);
|
|
155
|
+
transition: all 0.2s;
|
|
156
|
+
border: none;
|
|
157
|
+
background: transparent;
|
|
158
|
+
white-space: nowrap;
|
|
159
|
+
display: flex;
|
|
160
|
+
align-items: center;
|
|
161
|
+
gap: 6px;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.tab:hover { color: var(--text-primary); background: var(--bg-tertiary); }
|
|
165
|
+
|
|
166
|
+
.tab.active {
|
|
167
|
+
background: var(--bg-tertiary);
|
|
168
|
+
color: var(--accent-green);
|
|
169
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.tab-badge {
|
|
173
|
+
padding: 2px 6px;
|
|
174
|
+
background: var(--accent-green);
|
|
175
|
+
color: #0d1117;
|
|
176
|
+
border-radius: 10px;
|
|
177
|
+
font-size: 10px;
|
|
178
|
+
font-weight: 600;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* Tab Content */
|
|
182
|
+
.tab-content { display: none; }
|
|
183
|
+
.tab-content.active { display: block; animation: fadeIn 0.3s ease; }
|
|
184
|
+
|
|
185
|
+
@keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
|
|
186
|
+
|
|
187
|
+
/* Stats Grid */
|
|
188
|
+
.stats-grid {
|
|
189
|
+
display: grid;
|
|
190
|
+
grid-template-columns: repeat(5, 1fr);
|
|
191
|
+
gap: 12px;
|
|
192
|
+
margin-bottom: 20px;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@media (max-width: 1400px) { .stats-grid { grid-template-columns: repeat(3, 1fr); } }
|
|
196
|
+
@media (max-width: 900px) { .stats-grid { grid-template-columns: repeat(2, 1fr); } }
|
|
197
|
+
|
|
198
|
+
.stat-card {
|
|
199
|
+
background: var(--bg-secondary);
|
|
200
|
+
border: 1px solid var(--border-color);
|
|
201
|
+
border-radius: 12px;
|
|
202
|
+
padding: 16px;
|
|
203
|
+
position: relative;
|
|
204
|
+
overflow: hidden;
|
|
205
|
+
cursor: pointer;
|
|
206
|
+
transition: all 0.2s;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.stat-card:hover {
|
|
210
|
+
transform: translateY(-2px);
|
|
211
|
+
border-color: var(--accent-green);
|
|
212
|
+
box-shadow: 0 8px 20px rgba(0, 255, 136, 0.08);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.stat-card::before {
|
|
216
|
+
content: '';
|
|
217
|
+
position: absolute;
|
|
218
|
+
top: 0; left: 0; right: 0;
|
|
219
|
+
height: 3px;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.stat-card.green::before { background: linear-gradient(90deg, var(--accent-green), var(--accent-cyan)); }
|
|
223
|
+
.stat-card.purple::before { background: linear-gradient(90deg, var(--accent-purple), var(--accent-pink)); }
|
|
224
|
+
.stat-card.orange::before { background: linear-gradient(90deg, var(--accent-orange), var(--accent-yellow)); }
|
|
225
|
+
.stat-card.cyan::before { background: linear-gradient(90deg, var(--accent-cyan), var(--accent-blue)); }
|
|
226
|
+
.stat-card.blue::before { background: linear-gradient(90deg, var(--accent-blue), var(--accent-purple)); }
|
|
227
|
+
|
|
228
|
+
.stat-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 8px; }
|
|
229
|
+
.stat-icon { font-size: 20px; }
|
|
230
|
+
.stat-trend { font-size: 11px; padding: 2px 6px; border-radius: 4px; font-weight: 500; }
|
|
231
|
+
.stat-trend.up { background: rgba(0, 255, 136, 0.15); color: var(--accent-green); }
|
|
232
|
+
.stat-trend.down { background: rgba(239, 68, 68, 0.15); color: var(--accent-red); }
|
|
233
|
+
.stat-trend.stable { background: rgba(139, 148, 158, 0.15); color: var(--text-secondary); }
|
|
234
|
+
|
|
235
|
+
.stat-value { font-size: 28px; font-weight: 700; color: var(--text-primary); margin-bottom: 2px; }
|
|
236
|
+
.stat-label { font-size: 12px; color: var(--text-secondary); }
|
|
237
|
+
|
|
238
|
+
/* Cards */
|
|
239
|
+
.card {
|
|
240
|
+
background: var(--bg-secondary);
|
|
241
|
+
border: 1px solid var(--border-color);
|
|
242
|
+
border-radius: 12px;
|
|
243
|
+
overflow: hidden;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.card-header {
|
|
247
|
+
display: flex;
|
|
248
|
+
justify-content: space-between;
|
|
249
|
+
align-items: center;
|
|
250
|
+
padding: 16px 20px;
|
|
251
|
+
border-bottom: 1px solid var(--border-color);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.card-title {
|
|
255
|
+
font-size: 14px;
|
|
256
|
+
font-weight: 600;
|
|
257
|
+
color: var(--text-primary);
|
|
258
|
+
display: flex;
|
|
259
|
+
align-items: center;
|
|
260
|
+
gap: 8px;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.card-title-icon {
|
|
264
|
+
width: 6px;
|
|
265
|
+
height: 18px;
|
|
266
|
+
background: var(--accent-green);
|
|
267
|
+
border-radius: 3px;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.card-body { padding: 16px 20px; }
|
|
271
|
+
.card-body.no-padding { padding: 0; }
|
|
272
|
+
|
|
273
|
+
/* Grid layouts */
|
|
274
|
+
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 20px; }
|
|
275
|
+
.grid-3 { display: grid; grid-template-columns: 2fr 1fr; gap: 16px; margin-bottom: 20px; }
|
|
276
|
+
|
|
277
|
+
@media (max-width: 900px) { .grid-2, .grid-3 { grid-template-columns: 1fr; } }
|
|
278
|
+
|
|
279
|
+
/* Chart */
|
|
280
|
+
.chart-container { position: relative; height: 220px; width: 100%; }
|
|
281
|
+
|
|
282
|
+
/* Lists */
|
|
283
|
+
.list { list-style: none; }
|
|
284
|
+
|
|
285
|
+
.list-item {
|
|
286
|
+
display: flex;
|
|
287
|
+
align-items: center;
|
|
288
|
+
padding: 12px 20px;
|
|
289
|
+
cursor: pointer;
|
|
290
|
+
transition: background 0.15s;
|
|
291
|
+
border-bottom: 1px solid var(--border-color);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.list-item:last-child { border-bottom: none; }
|
|
295
|
+
.list-item:hover { background: var(--bg-tertiary); }
|
|
296
|
+
|
|
297
|
+
.list-rank {
|
|
298
|
+
width: 24px;
|
|
299
|
+
height: 24px;
|
|
300
|
+
background: var(--bg-tertiary);
|
|
301
|
+
border-radius: 6px;
|
|
302
|
+
display: flex;
|
|
303
|
+
align-items: center;
|
|
304
|
+
justify-content: center;
|
|
305
|
+
font-size: 11px;
|
|
306
|
+
font-weight: 600;
|
|
307
|
+
margin-right: 12px;
|
|
308
|
+
color: var(--text-muted);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.list-rank.top {
|
|
312
|
+
background: linear-gradient(135deg, var(--accent-green), var(--accent-cyan));
|
|
313
|
+
color: #0d1117;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.list-icon {
|
|
317
|
+
width: 36px;
|
|
318
|
+
height: 36px;
|
|
319
|
+
background: var(--bg-tertiary);
|
|
320
|
+
border-radius: 8px;
|
|
321
|
+
display: flex;
|
|
322
|
+
align-items: center;
|
|
323
|
+
justify-content: center;
|
|
324
|
+
font-size: 16px;
|
|
325
|
+
margin-right: 12px;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.list-info { flex: 1; min-width: 0; }
|
|
329
|
+
.list-name { font-weight: 500; font-size: 14px; margin-bottom: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
330
|
+
.list-meta { font-size: 12px; color: var(--text-secondary); }
|
|
331
|
+
|
|
332
|
+
.list-stats { text-align: right; margin-left: 12px; }
|
|
333
|
+
.list-count { font-weight: 600; font-size: 16px; }
|
|
334
|
+
|
|
335
|
+
.list-bar {
|
|
336
|
+
width: 80px;
|
|
337
|
+
height: 4px;
|
|
338
|
+
background: var(--bg-tertiary);
|
|
339
|
+
border-radius: 2px;
|
|
340
|
+
margin-top: 6px;
|
|
341
|
+
overflow: hidden;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.list-bar-fill {
|
|
345
|
+
height: 100%;
|
|
346
|
+
background: linear-gradient(90deg, var(--accent-green), var(--accent-cyan));
|
|
347
|
+
border-radius: 2px;
|
|
348
|
+
transition: width 0.5s ease;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.list-bar-fill.purple { background: linear-gradient(90deg, var(--accent-purple), var(--accent-pink)); }
|
|
352
|
+
.list-bar-fill.orange { background: linear-gradient(90deg, var(--accent-orange), var(--accent-yellow)); }
|
|
353
|
+
|
|
354
|
+
/* Status badges */
|
|
355
|
+
.badge {
|
|
356
|
+
display: inline-flex;
|
|
357
|
+
align-items: center;
|
|
358
|
+
gap: 4px;
|
|
359
|
+
padding: 3px 8px;
|
|
360
|
+
border-radius: 12px;
|
|
361
|
+
font-size: 11px;
|
|
362
|
+
font-weight: 600;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.badge-green { background: rgba(0, 255, 136, 0.15); color: var(--accent-green); }
|
|
366
|
+
.badge-yellow { background: rgba(234, 179, 8, 0.15); color: var(--accent-yellow); }
|
|
367
|
+
.badge-red { background: rgba(239, 68, 68, 0.15); color: var(--accent-red); }
|
|
368
|
+
.badge-blue { background: rgba(59, 130, 246, 0.15); color: var(--accent-blue); }
|
|
369
|
+
.badge-purple { background: rgba(168, 85, 247, 0.15); color: var(--accent-purple); }
|
|
370
|
+
.badge-gray { background: rgba(139, 148, 158, 0.15); color: var(--text-secondary); }
|
|
371
|
+
|
|
372
|
+
/* Full list with filters */
|
|
373
|
+
.list-controls {
|
|
374
|
+
display: flex;
|
|
375
|
+
justify-content: space-between;
|
|
376
|
+
align-items: center;
|
|
377
|
+
padding: 12px 20px;
|
|
378
|
+
border-bottom: 1px solid var(--border-color);
|
|
379
|
+
gap: 12px;
|
|
380
|
+
flex-wrap: wrap;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.filter-group { display: flex; gap: 6px; flex-wrap: wrap; }
|
|
384
|
+
|
|
385
|
+
.filter-btn {
|
|
386
|
+
padding: 6px 12px;
|
|
387
|
+
border-radius: 6px;
|
|
388
|
+
font-size: 12px;
|
|
389
|
+
cursor: pointer;
|
|
390
|
+
background: var(--bg-tertiary);
|
|
391
|
+
border: 1px solid transparent;
|
|
392
|
+
color: var(--text-secondary);
|
|
393
|
+
transition: all 0.2s;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.filter-btn:hover { color: var(--text-primary); }
|
|
397
|
+
.filter-btn.active { background: var(--accent-green); color: #0d1117; border-color: var(--accent-green); }
|
|
398
|
+
|
|
399
|
+
.full-list { max-height: 480px; overflow-y: auto; }
|
|
400
|
+
|
|
401
|
+
.full-list-item {
|
|
402
|
+
display: grid;
|
|
403
|
+
grid-template-columns: 50px 1fr 100px 100px 100px;
|
|
404
|
+
align-items: center;
|
|
405
|
+
padding: 14px 20px;
|
|
406
|
+
border-bottom: 1px solid var(--border-color);
|
|
407
|
+
cursor: pointer;
|
|
408
|
+
transition: background 0.15s;
|
|
409
|
+
gap: 12px;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.full-list-item:hover { background: var(--bg-tertiary); }
|
|
413
|
+
|
|
414
|
+
@media (max-width: 800px) {
|
|
415
|
+
.full-list-item { grid-template-columns: 40px 1fr 80px; }
|
|
416
|
+
.full-list-item > *:nth-child(4),
|
|
417
|
+
.full-list-item > *:nth-child(5) { display: none; }
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/* Sidebar */
|
|
421
|
+
.sidebar { display: flex; flex-direction: column; gap: 16px; }
|
|
422
|
+
|
|
423
|
+
/* Activity Feed */
|
|
424
|
+
.activity-list { max-height: 300px; overflow-y: auto; }
|
|
425
|
+
|
|
426
|
+
.activity-item {
|
|
427
|
+
display: flex;
|
|
428
|
+
gap: 12px;
|
|
429
|
+
padding: 12px 16px;
|
|
430
|
+
border-bottom: 1px solid var(--border-color);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.activity-item:last-child { border-bottom: none; }
|
|
434
|
+
|
|
435
|
+
.activity-icon {
|
|
436
|
+
width: 32px;
|
|
437
|
+
height: 32px;
|
|
438
|
+
border-radius: 8px;
|
|
439
|
+
display: flex;
|
|
440
|
+
align-items: center;
|
|
441
|
+
justify-content: center;
|
|
442
|
+
font-size: 14px;
|
|
443
|
+
flex-shrink: 0;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.activity-icon.install { background: rgba(0, 255, 136, 0.15); }
|
|
447
|
+
.activity-icon.usage { background: rgba(0, 212, 255, 0.15); }
|
|
448
|
+
.activity-icon.update { background: rgba(168, 85, 247, 0.15); }
|
|
449
|
+
.activity-icon.alert { background: rgba(249, 115, 22, 0.15); }
|
|
450
|
+
|
|
451
|
+
.activity-content { flex: 1; min-width: 0; }
|
|
452
|
+
.activity-text { font-size: 13px; line-height: 1.4; }
|
|
453
|
+
.activity-text strong { font-weight: 600; }
|
|
454
|
+
.activity-time { font-size: 11px; color: var(--text-muted); margin-top: 4px; }
|
|
455
|
+
|
|
456
|
+
/* Insights */
|
|
457
|
+
.insight-item {
|
|
458
|
+
display: flex;
|
|
459
|
+
gap: 12px;
|
|
460
|
+
padding: 14px 16px;
|
|
461
|
+
background: var(--bg-tertiary);
|
|
462
|
+
border-radius: 10px;
|
|
463
|
+
margin-bottom: 10px;
|
|
464
|
+
border-left: 4px solid var(--accent-yellow);
|
|
465
|
+
cursor: pointer;
|
|
466
|
+
transition: all 0.2s;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.insight-item:last-child { margin-bottom: 0; }
|
|
470
|
+
.insight-item:hover { transform: translateX(4px); }
|
|
471
|
+
|
|
472
|
+
.insight-item.success { border-left-color: var(--accent-green); }
|
|
473
|
+
.insight-item.warning { border-left-color: var(--accent-orange); }
|
|
474
|
+
.insight-item.error { border-left-color: var(--accent-red); }
|
|
475
|
+
.insight-item.info { border-left-color: var(--accent-cyan); }
|
|
476
|
+
|
|
477
|
+
.insight-icon { font-size: 20px; flex-shrink: 0; }
|
|
478
|
+
.insight-content { flex: 1; }
|
|
479
|
+
.insight-message { font-size: 13px; margin-bottom: 6px; line-height: 1.4; }
|
|
480
|
+
.insight-action {
|
|
481
|
+
display: inline-block;
|
|
482
|
+
font-size: 11px;
|
|
483
|
+
color: var(--accent-cyan);
|
|
484
|
+
font-family: 'SF Mono', Monaco, 'Courier New', monospace;
|
|
485
|
+
background: var(--bg-secondary);
|
|
486
|
+
padding: 4px 8px;
|
|
487
|
+
border-radius: 4px;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/* Health Score */
|
|
491
|
+
.health-score {
|
|
492
|
+
display: flex;
|
|
493
|
+
align-items: center;
|
|
494
|
+
gap: 16px;
|
|
495
|
+
padding: 16px;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.health-circle {
|
|
499
|
+
width: 80px;
|
|
500
|
+
height: 80px;
|
|
501
|
+
border-radius: 50%;
|
|
502
|
+
display: flex;
|
|
503
|
+
align-items: center;
|
|
504
|
+
justify-content: center;
|
|
505
|
+
font-size: 24px;
|
|
506
|
+
font-weight: 700;
|
|
507
|
+
position: relative;
|
|
508
|
+
flex-shrink: 0;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.health-circle::before {
|
|
512
|
+
content: '';
|
|
513
|
+
position: absolute;
|
|
514
|
+
inset: 0;
|
|
515
|
+
border-radius: 50%;
|
|
516
|
+
padding: 4px;
|
|
517
|
+
background: conic-gradient(var(--accent-green) calc(var(--score) * 1%), var(--bg-tertiary) 0);
|
|
518
|
+
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
|
519
|
+
-webkit-mask-composite: xor;
|
|
520
|
+
mask-composite: exclude;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.health-details { flex: 1; }
|
|
524
|
+
.health-title { font-size: 14px; font-weight: 600; margin-bottom: 8px; }
|
|
525
|
+
.health-item { display: flex; justify-content: space-between; font-size: 12px; color: var(--text-secondary); margin-bottom: 4px; }
|
|
526
|
+
|
|
527
|
+
/* Modal */
|
|
528
|
+
.modal-overlay {
|
|
529
|
+
display: none;
|
|
530
|
+
position: fixed;
|
|
531
|
+
inset: 0;
|
|
532
|
+
background: rgba(0, 0, 0, 0.7);
|
|
533
|
+
backdrop-filter: blur(4px);
|
|
534
|
+
z-index: 1000;
|
|
535
|
+
align-items: center;
|
|
536
|
+
justify-content: center;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.modal-overlay.active { display: flex; }
|
|
540
|
+
|
|
541
|
+
.modal {
|
|
542
|
+
background: var(--bg-secondary);
|
|
543
|
+
border: 1px solid var(--border-color);
|
|
544
|
+
border-radius: 16px;
|
|
545
|
+
width: 90%;
|
|
546
|
+
max-width: 560px;
|
|
547
|
+
max-height: 85vh;
|
|
548
|
+
overflow: hidden;
|
|
549
|
+
animation: modalIn 0.25s ease;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
@keyframes modalIn { from { opacity: 0; transform: scale(0.95) translateY(-10px); } to { opacity: 1; transform: scale(1) translateY(0); } }
|
|
553
|
+
|
|
554
|
+
.modal-header {
|
|
555
|
+
display: flex;
|
|
556
|
+
justify-content: space-between;
|
|
557
|
+
align-items: center;
|
|
558
|
+
padding: 20px 24px;
|
|
559
|
+
border-bottom: 1px solid var(--border-color);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
.modal-title { font-size: 18px; font-weight: 600; display: flex; align-items: center; gap: 10px; }
|
|
563
|
+
|
|
564
|
+
.modal-close {
|
|
565
|
+
width: 32px;
|
|
566
|
+
height: 32px;
|
|
567
|
+
border-radius: 8px;
|
|
568
|
+
border: none;
|
|
569
|
+
background: var(--bg-tertiary);
|
|
570
|
+
color: var(--text-secondary);
|
|
571
|
+
cursor: pointer;
|
|
572
|
+
font-size: 18px;
|
|
573
|
+
transition: all 0.2s;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
.modal-close:hover { background: var(--bg-hover); color: var(--text-primary); }
|
|
577
|
+
|
|
578
|
+
.modal-body { padding: 24px; overflow-y: auto; max-height: 50vh; }
|
|
579
|
+
|
|
580
|
+
.detail-row {
|
|
581
|
+
display: flex;
|
|
582
|
+
justify-content: space-between;
|
|
583
|
+
align-items: center;
|
|
584
|
+
padding: 14px 0;
|
|
585
|
+
border-bottom: 1px solid var(--border-color);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.detail-row:last-child { border-bottom: none; }
|
|
589
|
+
.detail-label { color: var(--text-secondary); font-size: 14px; }
|
|
590
|
+
.detail-value { font-weight: 500; font-size: 14px; text-align: right; }
|
|
591
|
+
|
|
592
|
+
.modal-footer {
|
|
593
|
+
display: flex;
|
|
594
|
+
gap: 12px;
|
|
595
|
+
padding: 20px 24px;
|
|
596
|
+
border-top: 1px solid var(--border-color);
|
|
597
|
+
background: var(--bg-tertiary);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
.modal-footer .btn { flex: 1; justify-content: center; }
|
|
601
|
+
|
|
602
|
+
/* Search Modal */
|
|
603
|
+
.search-modal {
|
|
604
|
+
display: none;
|
|
605
|
+
position: fixed;
|
|
606
|
+
inset: 0;
|
|
607
|
+
background: rgba(0, 0, 0, 0.7);
|
|
608
|
+
backdrop-filter: blur(4px);
|
|
609
|
+
z-index: 2000;
|
|
610
|
+
align-items: flex-start;
|
|
611
|
+
justify-content: center;
|
|
612
|
+
padding-top: 12vh;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.search-modal.active { display: flex; }
|
|
616
|
+
|
|
617
|
+
.search-container {
|
|
618
|
+
width: 90%;
|
|
619
|
+
max-width: 600px;
|
|
620
|
+
background: var(--bg-secondary);
|
|
621
|
+
border: 1px solid var(--border-color);
|
|
622
|
+
border-radius: 16px;
|
|
623
|
+
overflow: hidden;
|
|
624
|
+
animation: modalIn 0.2s ease;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
.search-header {
|
|
628
|
+
display: flex;
|
|
629
|
+
align-items: center;
|
|
630
|
+
padding: 16px 20px;
|
|
631
|
+
border-bottom: 1px solid var(--border-color);
|
|
632
|
+
gap: 12px;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
.search-input {
|
|
636
|
+
flex: 1;
|
|
637
|
+
background: transparent;
|
|
638
|
+
border: none;
|
|
639
|
+
outline: none;
|
|
640
|
+
font-size: 16px;
|
|
641
|
+
color: var(--text-primary);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
.search-input::placeholder { color: var(--text-muted); }
|
|
645
|
+
|
|
646
|
+
.search-tabs {
|
|
647
|
+
display: flex;
|
|
648
|
+
padding: 10px 20px;
|
|
649
|
+
gap: 6px;
|
|
650
|
+
border-bottom: 1px solid var(--border-color);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
.search-tab {
|
|
654
|
+
padding: 6px 12px;
|
|
655
|
+
border-radius: 6px;
|
|
656
|
+
font-size: 12px;
|
|
657
|
+
cursor: pointer;
|
|
658
|
+
background: transparent;
|
|
659
|
+
border: none;
|
|
660
|
+
color: var(--text-secondary);
|
|
661
|
+
transition: all 0.2s;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.search-tab:hover { color: var(--text-primary); }
|
|
665
|
+
.search-tab.active { background: var(--accent-green); color: #0d1117; font-weight: 500; }
|
|
666
|
+
|
|
667
|
+
.search-results { max-height: 360px; overflow-y: auto; }
|
|
668
|
+
|
|
669
|
+
.search-section { padding: 12px 20px; }
|
|
670
|
+
.search-section-title { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-muted); margin-bottom: 8px; }
|
|
671
|
+
|
|
672
|
+
.search-item {
|
|
673
|
+
display: flex;
|
|
674
|
+
align-items: center;
|
|
675
|
+
gap: 12px;
|
|
676
|
+
padding: 10px 12px;
|
|
677
|
+
border-radius: 8px;
|
|
678
|
+
cursor: pointer;
|
|
679
|
+
transition: background 0.15s;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
.search-item:hover { background: var(--bg-tertiary); }
|
|
683
|
+
.search-item.selected { background: var(--bg-tertiary); outline: 2px solid var(--accent-green); }
|
|
684
|
+
|
|
685
|
+
.search-item-icon {
|
|
686
|
+
width: 36px;
|
|
687
|
+
height: 36px;
|
|
688
|
+
border-radius: 8px;
|
|
689
|
+
display: flex;
|
|
690
|
+
align-items: center;
|
|
691
|
+
justify-content: center;
|
|
692
|
+
font-size: 16px;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
.search-item-icon.installed { background: rgba(0, 255, 136, 0.15); }
|
|
696
|
+
.search-item-icon.marketplace { background: rgba(0, 212, 255, 0.15); }
|
|
697
|
+
|
|
698
|
+
.search-item-info { flex: 1; min-width: 0; }
|
|
699
|
+
.search-item-name { font-weight: 500; font-size: 14px; }
|
|
700
|
+
.search-item-desc { font-size: 12px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
701
|
+
.search-item-meta { text-align: right; font-size: 11px; color: var(--text-muted); }
|
|
702
|
+
|
|
703
|
+
.search-empty { padding: 40px 20px; text-align: center; color: var(--text-secondary); }
|
|
704
|
+
.search-empty-icon { font-size: 40px; margin-bottom: 12px; opacity: 0.5; }
|
|
705
|
+
|
|
706
|
+
.search-footer {
|
|
707
|
+
display: flex;
|
|
708
|
+
justify-content: space-between;
|
|
709
|
+
padding: 12px 20px;
|
|
710
|
+
border-top: 1px solid var(--border-color);
|
|
711
|
+
background: var(--bg-tertiary);
|
|
712
|
+
font-size: 11px;
|
|
713
|
+
color: var(--text-muted);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
.search-footer kbd { padding: 2px 6px; background: var(--bg-secondary); border-radius: 4px; margin: 0 2px; }
|
|
717
|
+
|
|
718
|
+
/* Settings Panel */
|
|
719
|
+
.settings-section { margin-bottom: 24px; }
|
|
720
|
+
.settings-section:last-child { margin-bottom: 0; }
|
|
721
|
+
.settings-section-title { font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-muted); margin-bottom: 12px; }
|
|
722
|
+
|
|
723
|
+
.settings-item {
|
|
724
|
+
display: flex;
|
|
725
|
+
justify-content: space-between;
|
|
726
|
+
align-items: center;
|
|
727
|
+
padding: 12px 0;
|
|
728
|
+
border-bottom: 1px solid var(--border-color);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
.settings-item:last-child { border-bottom: none; }
|
|
732
|
+
.settings-item-label { font-size: 14px; }
|
|
733
|
+
.settings-item-desc { font-size: 12px; color: var(--text-secondary); margin-top: 2px; }
|
|
734
|
+
|
|
735
|
+
.toggle {
|
|
736
|
+
width: 44px;
|
|
737
|
+
height: 24px;
|
|
738
|
+
background: var(--bg-tertiary);
|
|
739
|
+
border-radius: 12px;
|
|
740
|
+
position: relative;
|
|
741
|
+
cursor: pointer;
|
|
742
|
+
transition: background 0.2s;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
.toggle.active { background: var(--accent-green); }
|
|
746
|
+
|
|
747
|
+
.toggle::after {
|
|
748
|
+
content: '';
|
|
749
|
+
position: absolute;
|
|
750
|
+
top: 2px;
|
|
751
|
+
left: 2px;
|
|
752
|
+
width: 20px;
|
|
753
|
+
height: 20px;
|
|
754
|
+
background: white;
|
|
755
|
+
border-radius: 50%;
|
|
756
|
+
transition: transform 0.2s;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
.toggle.active::after { transform: translateX(20px); }
|
|
760
|
+
|
|
761
|
+
/* Toast */
|
|
762
|
+
.toast {
|
|
763
|
+
position: fixed;
|
|
764
|
+
bottom: 24px;
|
|
765
|
+
right: 24px;
|
|
766
|
+
padding: 14px 20px;
|
|
767
|
+
background: var(--bg-secondary);
|
|
768
|
+
border: 1px solid var(--accent-green);
|
|
769
|
+
border-radius: 12px;
|
|
770
|
+
color: var(--text-primary);
|
|
771
|
+
font-size: 14px;
|
|
772
|
+
display: none;
|
|
773
|
+
align-items: center;
|
|
774
|
+
gap: 10px;
|
|
775
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
|
|
776
|
+
z-index: 3000;
|
|
777
|
+
animation: slideIn 0.3s ease;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
.toast.show { display: flex; }
|
|
781
|
+
|
|
782
|
+
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
|
|
783
|
+
|
|
784
|
+
/* Footer */
|
|
785
|
+
.footer {
|
|
786
|
+
text-align: center;
|
|
787
|
+
padding: 20px 0;
|
|
788
|
+
margin-top: 20px;
|
|
789
|
+
border-top: 1px solid var(--border-color);
|
|
790
|
+
color: var(--text-muted);
|
|
791
|
+
font-size: 12px;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/* Responsive */
|
|
795
|
+
@media (max-width: 640px) {
|
|
796
|
+
.header { flex-wrap: wrap; gap: 12px; }
|
|
797
|
+
.header-center { order: 3; width: 100%; margin: 0; max-width: none; }
|
|
798
|
+
.stats-grid { grid-template-columns: 1fr 1fr; }
|
|
799
|
+
.tabs { padding: 4px; gap: 2px; }
|
|
800
|
+
.tab { padding: 8px 12px; font-size: 12px; }
|
|
801
|
+
}
|
|
802
|
+
</style>
|
|
803
|
+
</head>
|
|
804
|
+
<body>
|
|
805
|
+
<div class="container">
|
|
806
|
+
<!-- Header -->
|
|
807
|
+
<header class="header">
|
|
808
|
+
<div class="logo">
|
|
809
|
+
<div class="logo-icon">🔌</div>
|
|
810
|
+
<div class="logo-text">
|
|
811
|
+
<h1>Plugin Scout</h1>
|
|
812
|
+
<span>v2.4.0 · Last sync: <span id="lastSync">--</span></span>
|
|
813
|
+
</div>
|
|
814
|
+
</div>
|
|
815
|
+
|
|
816
|
+
<div class="header-center">
|
|
817
|
+
<div class="search-box" onclick="openSearch()">
|
|
818
|
+
<span>🔍</span>
|
|
819
|
+
<span>Search plugins...</span>
|
|
820
|
+
<kbd>⌘K</kbd>
|
|
821
|
+
</div>
|
|
822
|
+
</div>
|
|
823
|
+
|
|
824
|
+
<div class="header-actions">
|
|
825
|
+
<button class="btn btn-icon" onclick="toggleTheme()" title="Toggle theme">🌙</button>
|
|
826
|
+
<button class="btn btn-icon" onclick="openSettings()" title="Settings">⚙️</button>
|
|
827
|
+
<button class="btn" onclick="exportData()">📤 Export</button>
|
|
828
|
+
<button class="btn btn-primary" onclick="runScout()">🔍 Scout</button>
|
|
829
|
+
</div>
|
|
830
|
+
</header>
|
|
831
|
+
|
|
832
|
+
<div class="main-layout">
|
|
833
|
+
<main class="main-content">
|
|
834
|
+
<!-- Tabs -->
|
|
835
|
+
<nav class="tabs">
|
|
836
|
+
<button class="tab active" data-tab="overview">📊 Overview</button>
|
|
837
|
+
<button class="tab" data-tab="plugins">🔌 Plugins <span class="tab-badge" id="pluginCount">0</span></button>
|
|
838
|
+
<button class="tab" data-tab="users">👥 Team</button>
|
|
839
|
+
<button class="tab" data-tab="marketplace">🏪 Marketplace</button>
|
|
840
|
+
<button class="tab" data-tab="insights">💡 Insights <span class="tab-badge" id="insightCount">0</span></button>
|
|
841
|
+
</nav>
|
|
842
|
+
|
|
843
|
+
<!-- Overview Tab -->
|
|
844
|
+
<div class="tab-content active" id="overview">
|
|
845
|
+
<section class="stats-grid">
|
|
846
|
+
<div class="stat-card green" onclick="showTab('plugins')">
|
|
847
|
+
<div class="stat-header">
|
|
848
|
+
<span class="stat-icon">🔌</span>
|
|
849
|
+
<span class="stat-trend up" id="pluginTrend">+2</span>
|
|
850
|
+
</div>
|
|
851
|
+
<div class="stat-value" id="totalPlugins">0</div>
|
|
852
|
+
<div class="stat-label">Installed Plugins</div>
|
|
853
|
+
</div>
|
|
854
|
+
<div class="stat-card purple">
|
|
855
|
+
<div class="stat-header">
|
|
856
|
+
<span class="stat-icon">📊</span>
|
|
857
|
+
<span class="stat-trend up" id="usageTrend">+18%</span>
|
|
858
|
+
</div>
|
|
859
|
+
<div class="stat-value" id="totalUsage">0</div>
|
|
860
|
+
<div class="stat-label">Total Usage</div>
|
|
861
|
+
</div>
|
|
862
|
+
<div class="stat-card orange">
|
|
863
|
+
<div class="stat-header">
|
|
864
|
+
<span class="stat-icon">🔄</span>
|
|
865
|
+
<span class="stat-trend up" id="sessionTrend">+5</span>
|
|
866
|
+
</div>
|
|
867
|
+
<div class="stat-value" id="totalSessions">0</div>
|
|
868
|
+
<div class="stat-label">Sessions</div>
|
|
869
|
+
</div>
|
|
870
|
+
<div class="stat-card cyan" onclick="showTab('users')">
|
|
871
|
+
<div class="stat-header">
|
|
872
|
+
<span class="stat-icon">👥</span>
|
|
873
|
+
<span class="stat-trend stable">-</span>
|
|
874
|
+
</div>
|
|
875
|
+
<div class="stat-value" id="totalUsers">0</div>
|
|
876
|
+
<div class="stat-label">Team Members</div>
|
|
877
|
+
</div>
|
|
878
|
+
<div class="stat-card blue">
|
|
879
|
+
<div class="stat-header">
|
|
880
|
+
<span class="stat-icon">⚡</span>
|
|
881
|
+
<span class="stat-trend up">+12%</span>
|
|
882
|
+
</div>
|
|
883
|
+
<div class="stat-value" id="avgDaily">0</div>
|
|
884
|
+
<div class="stat-label">Avg Daily Usage</div>
|
|
885
|
+
</div>
|
|
886
|
+
</section>
|
|
887
|
+
|
|
888
|
+
<section class="grid-3">
|
|
889
|
+
<div class="card">
|
|
890
|
+
<div class="card-header">
|
|
891
|
+
<div class="card-title"><span class="card-title-icon"></span>Usage Trend (14 Days)</div>
|
|
892
|
+
</div>
|
|
893
|
+
<div class="card-body">
|
|
894
|
+
<div class="chart-container">
|
|
895
|
+
<canvas id="usageChart"></canvas>
|
|
896
|
+
</div>
|
|
897
|
+
</div>
|
|
898
|
+
</div>
|
|
899
|
+
<div class="card">
|
|
900
|
+
<div class="card-header">
|
|
901
|
+
<div class="card-title"><span class="card-title-icon"></span>By Category</div>
|
|
902
|
+
</div>
|
|
903
|
+
<div class="card-body">
|
|
904
|
+
<div class="chart-container">
|
|
905
|
+
<canvas id="categoryChart"></canvas>
|
|
906
|
+
</div>
|
|
907
|
+
</div>
|
|
908
|
+
</div>
|
|
909
|
+
</section>
|
|
910
|
+
|
|
911
|
+
<section class="grid-2">
|
|
912
|
+
<div class="card">
|
|
913
|
+
<div class="card-header">
|
|
914
|
+
<div class="card-title"><span class="card-title-icon"></span>Most Used Plugins</div>
|
|
915
|
+
<button class="btn" onclick="showTab('plugins')">View All →</button>
|
|
916
|
+
</div>
|
|
917
|
+
<div class="card-body no-padding">
|
|
918
|
+
<ul class="list" id="topPluginsList"></ul>
|
|
919
|
+
</div>
|
|
920
|
+
</div>
|
|
921
|
+
<div class="card">
|
|
922
|
+
<div class="card-header">
|
|
923
|
+
<div class="card-title"><span class="card-title-icon"></span>Top Contributors</div>
|
|
924
|
+
<button class="btn" onclick="showTab('users')">View All →</button>
|
|
925
|
+
</div>
|
|
926
|
+
<div class="card-body no-padding">
|
|
927
|
+
<ul class="list" id="topUsersList"></ul>
|
|
928
|
+
</div>
|
|
929
|
+
</div>
|
|
930
|
+
</section>
|
|
931
|
+
</div>
|
|
932
|
+
|
|
933
|
+
<!-- Plugins Tab -->
|
|
934
|
+
<div class="tab-content" id="plugins">
|
|
935
|
+
<div class="card">
|
|
936
|
+
<div class="list-controls">
|
|
937
|
+
<div class="filter-group">
|
|
938
|
+
<button class="filter-btn active" data-filter="all">All</button>
|
|
939
|
+
<button class="filter-btn" data-filter="active">Active</button>
|
|
940
|
+
<button class="filter-btn" data-filter="inactive">Inactive</button>
|
|
941
|
+
<button class="filter-btn" data-filter="dormant">Dormant</button>
|
|
942
|
+
</div>
|
|
943
|
+
<div class="filter-group">
|
|
944
|
+
<button class="btn" onclick="sortPlugins('usage')">Sort: Usage</button>
|
|
945
|
+
<button class="btn" onclick="sortPlugins('name')">Sort: Name</button>
|
|
946
|
+
<button class="btn" onclick="sortPlugins('recent')">Sort: Recent</button>
|
|
947
|
+
</div>
|
|
948
|
+
</div>
|
|
949
|
+
<div class="full-list" id="fullPluginsList"></div>
|
|
950
|
+
</div>
|
|
951
|
+
</div>
|
|
952
|
+
|
|
953
|
+
<!-- Users Tab -->
|
|
954
|
+
<div class="tab-content" id="users">
|
|
955
|
+
<div class="card">
|
|
956
|
+
<div class="list-controls">
|
|
957
|
+
<div class="filter-group">
|
|
958
|
+
<button class="filter-btn active" data-filter="all">All Members</button>
|
|
959
|
+
<button class="filter-btn" data-filter="active">Active Today</button>
|
|
960
|
+
<button class="filter-btn" data-filter="week">This Week</button>
|
|
961
|
+
</div>
|
|
962
|
+
<div class="filter-group">
|
|
963
|
+
<button class="btn" onclick="sortUsers('sessions')">Sort: Sessions</button>
|
|
964
|
+
<button class="btn" onclick="sortUsers('usage')">Sort: Usage</button>
|
|
965
|
+
<button class="btn" onclick="sortUsers('name')">Sort: Name</button>
|
|
966
|
+
</div>
|
|
967
|
+
</div>
|
|
968
|
+
<div class="full-list" id="fullUsersList"></div>
|
|
969
|
+
</div>
|
|
970
|
+
</div>
|
|
971
|
+
|
|
972
|
+
<!-- Marketplace Tab -->
|
|
973
|
+
<div class="tab-content" id="marketplace">
|
|
974
|
+
<div class="card">
|
|
975
|
+
<div class="list-controls">
|
|
976
|
+
<div class="filter-group" id="categoryFilters"></div>
|
|
977
|
+
</div>
|
|
978
|
+
<div class="full-list" id="marketplaceList"></div>
|
|
979
|
+
</div>
|
|
980
|
+
</div>
|
|
981
|
+
|
|
982
|
+
<!-- Insights Tab -->
|
|
983
|
+
<div class="tab-content" id="insights">
|
|
984
|
+
<div class="card">
|
|
985
|
+
<div class="card-header">
|
|
986
|
+
<div class="card-title"><span class="card-title-icon"></span>Recommendations & Insights</div>
|
|
987
|
+
<button class="btn btn-primary" onclick="runAudit()">🔍 Run Audit</button>
|
|
988
|
+
</div>
|
|
989
|
+
<div class="card-body" id="insightsList"></div>
|
|
990
|
+
</div>
|
|
991
|
+
</div>
|
|
992
|
+
</main>
|
|
993
|
+
|
|
994
|
+
<!-- Sidebar -->
|
|
995
|
+
<aside class="sidebar">
|
|
996
|
+
<!-- Health Score -->
|
|
997
|
+
<div class="card">
|
|
998
|
+
<div class="card-header">
|
|
999
|
+
<div class="card-title"><span class="card-title-icon"></span>Plugin Health</div>
|
|
1000
|
+
</div>
|
|
1001
|
+
<div class="health-score">
|
|
1002
|
+
<div class="health-circle" id="healthCircle" style="--score: 85">85</div>
|
|
1003
|
+
<div class="health-details">
|
|
1004
|
+
<div class="health-title">Good Standing</div>
|
|
1005
|
+
<div class="health-item"><span>Active plugins</span><span id="healthActive">0/0</span></div>
|
|
1006
|
+
<div class="health-item"><span>Up to date</span><span id="healthUpdated">0/0</span></div>
|
|
1007
|
+
<div class="health-item"><span>Security issues</span><span id="healthSecurity">0</span></div>
|
|
1008
|
+
</div>
|
|
1009
|
+
</div>
|
|
1010
|
+
</div>
|
|
1011
|
+
|
|
1012
|
+
<!-- Activity Feed -->
|
|
1013
|
+
<div class="card">
|
|
1014
|
+
<div class="card-header">
|
|
1015
|
+
<div class="card-title"><span class="card-title-icon"></span>Recent Activity</div>
|
|
1016
|
+
</div>
|
|
1017
|
+
<div class="card-body no-padding">
|
|
1018
|
+
<div class="activity-list" id="activityList"></div>
|
|
1019
|
+
</div>
|
|
1020
|
+
</div>
|
|
1021
|
+
|
|
1022
|
+
<!-- Quick Insights -->
|
|
1023
|
+
<div class="card">
|
|
1024
|
+
<div class="card-header">
|
|
1025
|
+
<div class="card-title"><span class="card-title-icon"></span>Quick Insights</div>
|
|
1026
|
+
</div>
|
|
1027
|
+
<div class="card-body" id="quickInsights"></div>
|
|
1028
|
+
</div>
|
|
1029
|
+
</aside>
|
|
1030
|
+
</div>
|
|
1031
|
+
|
|
1032
|
+
<footer class="footer">
|
|
1033
|
+
Plugin Scout Console · Powered by Claude Code · <span id="currentUser">--</span>
|
|
1034
|
+
</footer>
|
|
1035
|
+
</div>
|
|
1036
|
+
|
|
1037
|
+
<!-- Modals -->
|
|
1038
|
+
<div class="modal-overlay" id="modalOverlay" onclick="closeModal(event)">
|
|
1039
|
+
<div class="modal" onclick="event.stopPropagation()">
|
|
1040
|
+
<div class="modal-header">
|
|
1041
|
+
<div class="modal-title" id="modalTitle">Details</div>
|
|
1042
|
+
<button class="modal-close" onclick="closeModal()">×</button>
|
|
1043
|
+
</div>
|
|
1044
|
+
<div class="modal-body" id="modalBody"></div>
|
|
1045
|
+
<div class="modal-footer" id="modalFooter"></div>
|
|
1046
|
+
</div>
|
|
1047
|
+
</div>
|
|
1048
|
+
|
|
1049
|
+
<!-- Search Modal -->
|
|
1050
|
+
<div class="search-modal" id="searchModal" onclick="closeSearch(event)">
|
|
1051
|
+
<div class="search-container" onclick="event.stopPropagation()">
|
|
1052
|
+
<div class="search-header">
|
|
1053
|
+
<span style="font-size: 18px;">🔍</span>
|
|
1054
|
+
<input type="text" class="search-input" id="searchInput" placeholder="Search plugins, users, commands..." autocomplete="off" oninput="handleSearch()">
|
|
1055
|
+
<button class="btn" onclick="closeSearch()" style="padding: 4px 10px;">ESC</button>
|
|
1056
|
+
</div>
|
|
1057
|
+
<div class="search-tabs">
|
|
1058
|
+
<button class="search-tab active" data-search-tab="all">All</button>
|
|
1059
|
+
<button class="search-tab" data-search-tab="installed">Installed</button>
|
|
1060
|
+
<button class="search-tab" data-search-tab="marketplace">Marketplace</button>
|
|
1061
|
+
<button class="search-tab" data-search-tab="users">Users</button>
|
|
1062
|
+
</div>
|
|
1063
|
+
<div class="search-results" id="searchResults">
|
|
1064
|
+
<div class="search-empty">
|
|
1065
|
+
<div class="search-empty-icon">🔍</div>
|
|
1066
|
+
<div>Type to search plugins, users, or marketplace</div>
|
|
1067
|
+
</div>
|
|
1068
|
+
</div>
|
|
1069
|
+
<div class="search-footer">
|
|
1070
|
+
<span><kbd>↑</kbd><kbd>↓</kbd> Navigate</span>
|
|
1071
|
+
<span><kbd>Enter</kbd> Select</span>
|
|
1072
|
+
<span><kbd>ESC</kbd> Close</span>
|
|
1073
|
+
</div>
|
|
1074
|
+
</div>
|
|
1075
|
+
</div>
|
|
1076
|
+
|
|
1077
|
+
<!-- Settings Modal -->
|
|
1078
|
+
<div class="modal-overlay" id="settingsOverlay" onclick="closeSettings(event)">
|
|
1079
|
+
<div class="modal" onclick="event.stopPropagation()" style="max-width: 480px;">
|
|
1080
|
+
<div class="modal-header">
|
|
1081
|
+
<div class="modal-title">⚙️ Settings</div>
|
|
1082
|
+
<button class="modal-close" onclick="closeSettings()">×</button>
|
|
1083
|
+
</div>
|
|
1084
|
+
<div class="modal-body">
|
|
1085
|
+
<div class="settings-section">
|
|
1086
|
+
<div class="settings-section-title">Appearance</div>
|
|
1087
|
+
<div class="settings-item">
|
|
1088
|
+
<div>
|
|
1089
|
+
<div class="settings-item-label">Dark Mode</div>
|
|
1090
|
+
<div class="settings-item-desc">Use dark theme for the dashboard</div>
|
|
1091
|
+
</div>
|
|
1092
|
+
<div class="toggle active" id="darkModeToggle" onclick="toggleTheme()"></div>
|
|
1093
|
+
</div>
|
|
1094
|
+
</div>
|
|
1095
|
+
<div class="settings-section">
|
|
1096
|
+
<div class="settings-section-title">Notifications</div>
|
|
1097
|
+
<div class="settings-item">
|
|
1098
|
+
<div>
|
|
1099
|
+
<div class="settings-item-label">Auto Recommendations</div>
|
|
1100
|
+
<div class="settings-item-desc">Show plugin recommendations after tasks</div>
|
|
1101
|
+
</div>
|
|
1102
|
+
<div class="toggle active" id="autoRecommendToggle"></div>
|
|
1103
|
+
</div>
|
|
1104
|
+
<div class="settings-item">
|
|
1105
|
+
<div>
|
|
1106
|
+
<div class="settings-item-label">Security Alerts</div>
|
|
1107
|
+
<div class="settings-item-desc">Notify about security updates</div>
|
|
1108
|
+
</div>
|
|
1109
|
+
<div class="toggle active" id="securityAlertToggle"></div>
|
|
1110
|
+
</div>
|
|
1111
|
+
</div>
|
|
1112
|
+
<div class="settings-section">
|
|
1113
|
+
<div class="settings-section-title">Data</div>
|
|
1114
|
+
<div class="settings-item">
|
|
1115
|
+
<div>
|
|
1116
|
+
<div class="settings-item-label">Usage Tracking</div>
|
|
1117
|
+
<div class="settings-item-desc">Track plugin usage statistics</div>
|
|
1118
|
+
</div>
|
|
1119
|
+
<div class="toggle active" id="usageTrackingToggle"></div>
|
|
1120
|
+
</div>
|
|
1121
|
+
</div>
|
|
1122
|
+
</div>
|
|
1123
|
+
<div class="modal-footer">
|
|
1124
|
+
<button class="btn" onclick="closeSettings()">Close</button>
|
|
1125
|
+
<button class="btn btn-primary" onclick="saveSettings()">Save Changes</button>
|
|
1126
|
+
</div>
|
|
1127
|
+
</div>
|
|
1128
|
+
</div>
|
|
1129
|
+
|
|
1130
|
+
<!-- Toast -->
|
|
1131
|
+
<div class="toast" id="toast">
|
|
1132
|
+
<span id="toastIcon">✅</span>
|
|
1133
|
+
<span id="toastMessage">Action completed</span>
|
|
1134
|
+
</div>
|
|
1135
|
+
|
|
1136
|
+
<script>
|
|
1137
|
+
// ===========================================
|
|
1138
|
+
// REALISTIC DATA
|
|
1139
|
+
// ===========================================
|
|
1140
|
+
|
|
1141
|
+
const USAGE_DATA = __USAGE_DATA__;
|
|
1142
|
+
const ANALYTICS_DATA = __ANALYTICS_DATA__;
|
|
1143
|
+
|
|
1144
|
+
// Enhanced realistic data with more plugins and usage patterns
|
|
1145
|
+
const ENHANCED_PLUGINS = {
|
|
1146
|
+
'commit-commands': {
|
|
1147
|
+
installed: '2025-11-15', usageCount: 847, lastUsed: '2026-01-19',
|
|
1148
|
+
description: 'Git commit message generator and automation',
|
|
1149
|
+
category: 'productivity', version: '3.2.1', author: 'monol'
|
|
1150
|
+
},
|
|
1151
|
+
'code-review': {
|
|
1152
|
+
installed: '2025-11-20', usageCount: 623, lastUsed: '2026-01-19',
|
|
1153
|
+
description: 'Automated code review with best practices',
|
|
1154
|
+
category: 'productivity', version: '2.8.0', author: 'monol'
|
|
1155
|
+
},
|
|
1156
|
+
'typescript-lsp': {
|
|
1157
|
+
installed: '2025-12-01', usageCount: 412, lastUsed: '2026-01-19',
|
|
1158
|
+
description: 'TypeScript language server integration',
|
|
1159
|
+
category: 'development', version: '1.5.2', author: 'claude-community'
|
|
1160
|
+
},
|
|
1161
|
+
'test-runner': {
|
|
1162
|
+
installed: '2025-12-10', usageCount: 356, lastUsed: '2026-01-19',
|
|
1163
|
+
description: 'Run and manage tests from Claude',
|
|
1164
|
+
category: 'testing', version: '2.1.0', author: 'monol'
|
|
1165
|
+
},
|
|
1166
|
+
'docker-helper': {
|
|
1167
|
+
installed: '2025-12-15', usageCount: 289, lastUsed: '2026-01-18',
|
|
1168
|
+
description: 'Docker container management',
|
|
1169
|
+
category: 'devops', version: '1.8.3', author: 'devops-team'
|
|
1170
|
+
},
|
|
1171
|
+
'sentry': {
|
|
1172
|
+
installed: '2026-01-02', usageCount: 234, lastUsed: '2026-01-18',
|
|
1173
|
+
description: 'Error tracking with Sentry integration',
|
|
1174
|
+
category: 'monitoring', version: '4.0.1', author: 'sentry-official'
|
|
1175
|
+
},
|
|
1176
|
+
'slack': {
|
|
1177
|
+
installed: '2026-01-05', usageCount: 198, lastUsed: '2026-01-17',
|
|
1178
|
+
description: 'Slack notifications and messaging',
|
|
1179
|
+
category: 'integration', version: '2.3.0', author: 'slack-labs'
|
|
1180
|
+
},
|
|
1181
|
+
'prettier-format': {
|
|
1182
|
+
installed: '2025-11-25', usageCount: 567, lastUsed: '2026-01-19',
|
|
1183
|
+
description: 'Code formatting with Prettier',
|
|
1184
|
+
category: 'productivity', version: '3.0.0', author: 'prettier-team'
|
|
1185
|
+
},
|
|
1186
|
+
'eslint-fix': {
|
|
1187
|
+
installed: '2025-11-25', usageCount: 489, lastUsed: '2026-01-19',
|
|
1188
|
+
description: 'ESLint auto-fix integration',
|
|
1189
|
+
category: 'productivity', version: '2.5.1', author: 'eslint-team'
|
|
1190
|
+
},
|
|
1191
|
+
'git-hooks': {
|
|
1192
|
+
installed: '2025-12-20', usageCount: 312, lastUsed: '2026-01-19',
|
|
1193
|
+
description: 'Git hooks manager for Claude Code',
|
|
1194
|
+
category: 'development', version: '1.4.0', author: 'monol'
|
|
1195
|
+
},
|
|
1196
|
+
'db-query': {
|
|
1197
|
+
installed: '2026-01-08', usageCount: 156, lastUsed: '2026-01-18',
|
|
1198
|
+
description: 'Database query builder and executor',
|
|
1199
|
+
category: 'database', version: '1.2.0', author: 'db-tools'
|
|
1200
|
+
},
|
|
1201
|
+
'api-tester': {
|
|
1202
|
+
installed: '2026-01-10', usageCount: 134, lastUsed: '2026-01-17',
|
|
1203
|
+
description: 'REST API testing and documentation',
|
|
1204
|
+
category: 'testing', version: '1.1.0', author: 'api-team'
|
|
1205
|
+
},
|
|
1206
|
+
'aws-toolkit': {
|
|
1207
|
+
installed: '2025-12-28', usageCount: 178, lastUsed: '2026-01-16',
|
|
1208
|
+
description: 'AWS service integration',
|
|
1209
|
+
category: 'cloud', version: '2.0.0', author: 'aws-community'
|
|
1210
|
+
},
|
|
1211
|
+
'k8s-helper': {
|
|
1212
|
+
installed: '2026-01-03', usageCount: 145, lastUsed: '2026-01-15',
|
|
1213
|
+
description: 'Kubernetes deployment helper',
|
|
1214
|
+
category: 'devops', version: '1.3.0', author: 'k8s-team'
|
|
1215
|
+
},
|
|
1216
|
+
'memo-pad': {
|
|
1217
|
+
installed: '2025-12-05', usageCount: 87, lastUsed: '2026-01-10',
|
|
1218
|
+
description: 'Quick notes and snippets',
|
|
1219
|
+
category: 'productivity', version: '1.0.5', author: 'utils-team'
|
|
1220
|
+
},
|
|
1221
|
+
'old-formatter': {
|
|
1222
|
+
installed: '2025-09-15', usageCount: 23, lastUsed: '2025-11-20',
|
|
1223
|
+
description: 'Legacy code formatter (deprecated)',
|
|
1224
|
+
category: 'productivity', version: '0.9.0', author: 'legacy'
|
|
1225
|
+
},
|
|
1226
|
+
'unused-plugin': {
|
|
1227
|
+
installed: '2025-10-01', usageCount: 5, lastUsed: '2025-10-10',
|
|
1228
|
+
description: 'Plugin that was never really used',
|
|
1229
|
+
category: 'misc', version: '1.0.0', author: 'unknown'
|
|
1230
|
+
},
|
|
1231
|
+
'scout': {
|
|
1232
|
+
installed: '2026-01-15', usageCount: 45, lastUsed: '2026-01-19',
|
|
1233
|
+
description: 'Plugin marketplace monitoring',
|
|
1234
|
+
category: 'productivity', version: '2.4.0', author: 'monol'
|
|
1235
|
+
}
|
|
1236
|
+
};
|
|
1237
|
+
|
|
1238
|
+
const ENHANCED_USERS = {
|
|
1239
|
+
'kent': {
|
|
1240
|
+
avatar: '👨💻', role: 'Lead Developer',
|
|
1241
|
+
plugins: {
|
|
1242
|
+
'commit-commands': { usageCount: 234, lastUsed: '2026-01-19' },
|
|
1243
|
+
'code-review': { usageCount: 189, lastUsed: '2026-01-19' },
|
|
1244
|
+
'typescript-lsp': { usageCount: 156, lastUsed: '2026-01-19' },
|
|
1245
|
+
'prettier-format': { usageCount: 145, lastUsed: '2026-01-19' },
|
|
1246
|
+
'eslint-fix': { usageCount: 134, lastUsed: '2026-01-19' },
|
|
1247
|
+
'test-runner': { usageCount: 98, lastUsed: '2026-01-18' },
|
|
1248
|
+
'scout': { usageCount: 32, lastUsed: '2026-01-19' }
|
|
1249
|
+
},
|
|
1250
|
+
sessions: 156, lastActive: '2026-01-19', firstSeen: '2025-11-15'
|
|
1251
|
+
},
|
|
1252
|
+
'sarah': {
|
|
1253
|
+
avatar: '👩💻', role: 'Senior Developer',
|
|
1254
|
+
plugins: {
|
|
1255
|
+
'commit-commands': { usageCount: 198, lastUsed: '2026-01-19' },
|
|
1256
|
+
'code-review': { usageCount: 167, lastUsed: '2026-01-19' },
|
|
1257
|
+
'test-runner': { usageCount: 134, lastUsed: '2026-01-19' },
|
|
1258
|
+
'docker-helper': { usageCount: 112, lastUsed: '2026-01-18' },
|
|
1259
|
+
'prettier-format': { usageCount: 89, lastUsed: '2026-01-18' }
|
|
1260
|
+
},
|
|
1261
|
+
sessions: 134, lastActive: '2026-01-19', firstSeen: '2025-11-20'
|
|
1262
|
+
},
|
|
1263
|
+
'mike': {
|
|
1264
|
+
avatar: '🧑💻', role: 'Backend Developer',
|
|
1265
|
+
plugins: {
|
|
1266
|
+
'db-query': { usageCount: 89, lastUsed: '2026-01-18' },
|
|
1267
|
+
'api-tester': { usageCount: 78, lastUsed: '2026-01-17' },
|
|
1268
|
+
'docker-helper': { usageCount: 67, lastUsed: '2026-01-18' },
|
|
1269
|
+
'commit-commands': { usageCount: 145, lastUsed: '2026-01-18' },
|
|
1270
|
+
'k8s-helper': { usageCount: 56, lastUsed: '2026-01-15' }
|
|
1271
|
+
},
|
|
1272
|
+
sessions: 98, lastActive: '2026-01-18', firstSeen: '2025-12-01'
|
|
1273
|
+
},
|
|
1274
|
+
'emma': {
|
|
1275
|
+
avatar: '👩🎨', role: 'Frontend Developer',
|
|
1276
|
+
plugins: {
|
|
1277
|
+
'typescript-lsp': { usageCount: 167, lastUsed: '2026-01-19' },
|
|
1278
|
+
'prettier-format': { usageCount: 156, lastUsed: '2026-01-19' },
|
|
1279
|
+
'eslint-fix': { usageCount: 145, lastUsed: '2026-01-19' },
|
|
1280
|
+
'code-review': { usageCount: 89, lastUsed: '2026-01-18' },
|
|
1281
|
+
'commit-commands': { usageCount: 78, lastUsed: '2026-01-18' }
|
|
1282
|
+
},
|
|
1283
|
+
sessions: 112, lastActive: '2026-01-19', firstSeen: '2025-12-05'
|
|
1284
|
+
},
|
|
1285
|
+
'alex': {
|
|
1286
|
+
avatar: '🧑🔧', role: 'DevOps Engineer',
|
|
1287
|
+
plugins: {
|
|
1288
|
+
'docker-helper': { usageCount: 110, lastUsed: '2026-01-18' },
|
|
1289
|
+
'k8s-helper': { usageCount: 89, lastUsed: '2026-01-15' },
|
|
1290
|
+
'aws-toolkit': { usageCount: 78, lastUsed: '2026-01-16' },
|
|
1291
|
+
'sentry': { usageCount: 67, lastUsed: '2026-01-18' },
|
|
1292
|
+
'commit-commands': { usageCount: 56, lastUsed: '2026-01-17' }
|
|
1293
|
+
},
|
|
1294
|
+
sessions: 87, lastActive: '2026-01-18', firstSeen: '2025-12-10'
|
|
1295
|
+
},
|
|
1296
|
+
'jin': {
|
|
1297
|
+
avatar: '👨🔬', role: 'QA Engineer',
|
|
1298
|
+
plugins: {
|
|
1299
|
+
'test-runner': { usageCount: 124, lastUsed: '2026-01-19' },
|
|
1300
|
+
'api-tester': { usageCount: 56, lastUsed: '2026-01-17' },
|
|
1301
|
+
'code-review': { usageCount: 45, lastUsed: '2026-01-18' },
|
|
1302
|
+
'commit-commands': { usageCount: 34, lastUsed: '2026-01-17' }
|
|
1303
|
+
},
|
|
1304
|
+
sessions: 67, lastActive: '2026-01-19', firstSeen: '2025-12-15'
|
|
1305
|
+
},
|
|
1306
|
+
'lisa': {
|
|
1307
|
+
avatar: '👩💼', role: 'Tech Lead',
|
|
1308
|
+
plugins: {
|
|
1309
|
+
'code-review': { usageCount: 133, lastUsed: '2026-01-18' },
|
|
1310
|
+
'commit-commands': { usageCount: 102, lastUsed: '2026-01-18' },
|
|
1311
|
+
'slack': { usageCount: 89, lastUsed: '2026-01-17' },
|
|
1312
|
+
'sentry': { usageCount: 67, lastUsed: '2026-01-18' }
|
|
1313
|
+
},
|
|
1314
|
+
sessions: 78, lastActive: '2026-01-18', firstSeen: '2025-11-25'
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
|
|
1318
|
+
const MARKETPLACE_DATA = [
|
|
1319
|
+
{ name: 'graphql-helper', description: 'GraphQL schema and query helper', category: 'development', downloads: 45200, rating: 4.8, trending: true },
|
|
1320
|
+
{ name: 'tailwind-assist', description: 'Tailwind CSS class suggestions', category: 'frontend', downloads: 78500, rating: 4.9, trending: true },
|
|
1321
|
+
{ name: 'react-snippets', description: 'React component snippets and hooks', category: 'frontend', downloads: 62300, rating: 4.7, trending: true },
|
|
1322
|
+
{ name: 'vue-helper', description: 'Vue.js development utilities', category: 'frontend', downloads: 34100, rating: 4.5, trending: false },
|
|
1323
|
+
{ name: 'python-linter', description: 'Python code linting with pylint/flake8', category: 'development', downloads: 51200, rating: 4.6, trending: false },
|
|
1324
|
+
{ name: 'rust-analyzer', description: 'Rust language support and analysis', category: 'development', downloads: 28900, rating: 4.8, trending: true },
|
|
1325
|
+
{ name: 'go-tools', description: 'Go language development tools', category: 'development', downloads: 31500, rating: 4.6, trending: false },
|
|
1326
|
+
{ name: 'sql-formatter', description: 'SQL query formatting and optimization', category: 'database', downloads: 42300, rating: 4.5, trending: false },
|
|
1327
|
+
{ name: 'mongodb-helper', description: 'MongoDB query builder', category: 'database', downloads: 23400, rating: 4.4, trending: false },
|
|
1328
|
+
{ name: 'redis-client', description: 'Redis commands helper', category: 'database', downloads: 18700, rating: 4.3, trending: false },
|
|
1329
|
+
{ name: 'openai-bridge', description: 'OpenAI API integration', category: 'ai', downloads: 89200, rating: 4.9, trending: true },
|
|
1330
|
+
{ name: 'langchain-helper', description: 'LangChain development tools', category: 'ai', downloads: 67800, rating: 4.7, trending: true },
|
|
1331
|
+
{ name: 'huggingface-tools', description: 'Hugging Face model integration', category: 'ai', downloads: 45600, rating: 4.6, trending: true },
|
|
1332
|
+
{ name: 'terraform-plan', description: 'Terraform plan and apply helper', category: 'devops', downloads: 38900, rating: 4.7, trending: false },
|
|
1333
|
+
{ name: 'ansible-runner', description: 'Ansible playbook executor', category: 'devops', downloads: 29400, rating: 4.5, trending: false },
|
|
1334
|
+
{ name: 'jenkins-helper', description: 'Jenkins pipeline management', category: 'devops', downloads: 25600, rating: 4.4, trending: false },
|
|
1335
|
+
{ name: 'github-actions', description: 'GitHub Actions workflow helper', category: 'devops', downloads: 56700, rating: 4.8, trending: true },
|
|
1336
|
+
{ name: 'jira-integration', description: 'Jira ticket management', category: 'integration', downloads: 34500, rating: 4.5, trending: false },
|
|
1337
|
+
{ name: 'notion-sync', description: 'Notion documentation sync', category: 'integration', downloads: 28900, rating: 4.6, trending: false },
|
|
1338
|
+
{ name: 'figma-export', description: 'Figma design export tools', category: 'integration', downloads: 31200, rating: 4.5, trending: false },
|
|
1339
|
+
{ name: 'cypress-runner', description: 'Cypress E2E test runner', category: 'testing', downloads: 41200, rating: 4.7, trending: false },
|
|
1340
|
+
{ name: 'playwright-helper', description: 'Playwright test automation', category: 'testing', downloads: 38700, rating: 4.8, trending: true },
|
|
1341
|
+
{ name: 'storybook-gen', description: 'Storybook story generator', category: 'testing', downloads: 27800, rating: 4.4, trending: false },
|
|
1342
|
+
{ name: 'datadog-monitor', description: 'Datadog monitoring integration', category: 'monitoring', downloads: 32100, rating: 4.6, trending: false },
|
|
1343
|
+
{ name: 'prometheus-query', description: 'Prometheus query helper', category: 'monitoring', downloads: 24500, rating: 4.5, trending: false },
|
|
1344
|
+
{ name: 'grafana-dash', description: 'Grafana dashboard builder', category: 'monitoring', downloads: 29800, rating: 4.6, trending: false },
|
|
1345
|
+
{ name: 'security-scan', description: 'Security vulnerability scanner', category: 'security', downloads: 47200, rating: 4.8, trending: true },
|
|
1346
|
+
{ name: 'secret-manager', description: 'Secrets and credentials manager', category: 'security', downloads: 38900, rating: 4.7, trending: false },
|
|
1347
|
+
{ name: 'oauth-helper', description: 'OAuth2 authentication helper', category: 'security', downloads: 31200, rating: 4.5, trending: false },
|
|
1348
|
+
{ name: 'markdown-preview', description: 'Markdown preview and export', category: 'productivity', downloads: 52300, rating: 4.6, trending: false },
|
|
1349
|
+
{ name: 'json-schema', description: 'JSON Schema validator', category: 'productivity', downloads: 38700, rating: 4.5, trending: false },
|
|
1350
|
+
{ name: 'regex-builder', description: 'Regex pattern builder', category: 'productivity', downloads: 45600, rating: 4.7, trending: false },
|
|
1351
|
+
{ name: 'snippet-manager', description: 'Code snippet organizer', category: 'productivity', downloads: 34200, rating: 4.4, trending: false },
|
|
1352
|
+
{ name: 'gcp-tools', description: 'Google Cloud Platform tools', category: 'cloud', downloads: 41200, rating: 4.6, trending: false },
|
|
1353
|
+
{ name: 'azure-helper', description: 'Azure services integration', category: 'cloud', downloads: 35800, rating: 4.5, trending: false },
|
|
1354
|
+
{ name: 'vercel-deploy', description: 'Vercel deployment helper', category: 'cloud', downloads: 48900, rating: 4.8, trending: true },
|
|
1355
|
+
{ name: 'netlify-cli', description: 'Netlify CLI integration', category: 'cloud', downloads: 42100, rating: 4.7, trending: false },
|
|
1356
|
+
];
|
|
1357
|
+
|
|
1358
|
+
const ACTIVITY_FEED = [
|
|
1359
|
+
{ type: 'usage', user: 'kent', action: 'used', target: 'commit-commands', time: '2 min ago' },
|
|
1360
|
+
{ type: 'usage', user: 'sarah', action: 'used', target: 'test-runner', time: '5 min ago' },
|
|
1361
|
+
{ type: 'usage', user: 'emma', action: 'used', target: 'typescript-lsp', time: '8 min ago' },
|
|
1362
|
+
{ type: 'install', user: 'mike', action: 'installed', target: 'graphql-helper', time: '15 min ago' },
|
|
1363
|
+
{ type: 'usage', user: 'jin', action: 'used', target: 'test-runner', time: '22 min ago' },
|
|
1364
|
+
{ type: 'update', user: 'system', action: 'updated', target: 'prettier-format to v3.0.0', time: '1 hour ago' },
|
|
1365
|
+
{ type: 'usage', user: 'alex', action: 'used', target: 'docker-helper', time: '1 hour ago' },
|
|
1366
|
+
{ type: 'alert', user: 'system', action: 'flagged', target: 'old-formatter as deprecated', time: '2 hours ago' },
|
|
1367
|
+
{ type: 'install', user: 'lisa', action: 'installed', target: 'scout', time: '3 hours ago' },
|
|
1368
|
+
{ type: 'usage', user: 'kent', action: 'used', target: 'code-review', time: '4 hours ago' },
|
|
1369
|
+
];
|
|
1370
|
+
|
|
1371
|
+
const INSIGHTS = [
|
|
1372
|
+
{ type: 'success', icon: '📈', message: 'commit-commands is your most used plugin with 847 uses. Great productivity!', action: null },
|
|
1373
|
+
{ type: 'warning', icon: '⚠️', message: 'old-formatter hasn\'t been used in 60+ days. Consider removing it.', action: '/scout cleanup' },
|
|
1374
|
+
{ type: 'warning', icon: '⚠️', message: 'unused-plugin was installed but rarely used. Review if still needed.', action: '/scout cleanup' },
|
|
1375
|
+
{ type: 'info', icon: '💡', message: 'Based on your TypeScript usage, try tailwind-assist for better CSS workflow.', action: '/scout --install tailwind-assist' },
|
|
1376
|
+
{ type: 'info', icon: '💡', message: 'Your team uses a lot of testing plugins. Consider adding playwright-helper.', action: '/scout --install playwright-helper' },
|
|
1377
|
+
{ type: 'success', icon: '🔒', message: 'All installed plugins passed security audit. No vulnerabilities found.', action: null },
|
|
1378
|
+
{ type: 'info', icon: '🔄', message: 'prettier-format has a new version (3.0.1) available.', action: '/scout --update prettier-format' },
|
|
1379
|
+
{ type: 'warning', icon: '👥', message: '2 team members haven\'t used any plugins this week. Consider onboarding.', action: null },
|
|
1380
|
+
];
|
|
1381
|
+
|
|
1382
|
+
// Daily usage data for chart (last 14 days)
|
|
1383
|
+
const DAILY_USAGE = [42, 38, 45, 52, 48, 35, 28, 56, 61, 58, 67, 72, 65, 78];
|
|
1384
|
+
|
|
1385
|
+
// ===========================================
|
|
1386
|
+
// STATE
|
|
1387
|
+
// ===========================================
|
|
1388
|
+
|
|
1389
|
+
let currentTheme = 'dark';
|
|
1390
|
+
let currentSearchTab = 'all';
|
|
1391
|
+
let currentPluginFilter = 'all';
|
|
1392
|
+
let currentPluginSort = 'usage';
|
|
1393
|
+
let currentUserFilter = 'all';
|
|
1394
|
+
let currentUserSort = 'sessions';
|
|
1395
|
+
let currentMarketplaceCategory = 'all';
|
|
1396
|
+
let selectedSearchIndex = -1;
|
|
1397
|
+
|
|
1398
|
+
// ===========================================
|
|
1399
|
+
// INITIALIZATION
|
|
1400
|
+
// ===========================================
|
|
1401
|
+
|
|
1402
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
1403
|
+
initTabs();
|
|
1404
|
+
initFilters();
|
|
1405
|
+
renderAll();
|
|
1406
|
+
initKeyboardShortcuts();
|
|
1407
|
+
});
|
|
1408
|
+
|
|
1409
|
+
function initTabs() {
|
|
1410
|
+
document.querySelectorAll('.tab').forEach(tab => {
|
|
1411
|
+
tab.addEventListener('click', () => showTab(tab.dataset.tab));
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
function initFilters() {
|
|
1416
|
+
// Plugin filters
|
|
1417
|
+
document.querySelectorAll('#plugins .filter-btn').forEach(btn => {
|
|
1418
|
+
btn.addEventListener('click', () => {
|
|
1419
|
+
document.querySelectorAll('#plugins .filter-btn').forEach(b => b.classList.remove('active'));
|
|
1420
|
+
btn.classList.add('active');
|
|
1421
|
+
currentPluginFilter = btn.dataset.filter;
|
|
1422
|
+
renderFullPlugins();
|
|
1423
|
+
});
|
|
1424
|
+
});
|
|
1425
|
+
|
|
1426
|
+
// User filters
|
|
1427
|
+
document.querySelectorAll('#users .filter-btn').forEach(btn => {
|
|
1428
|
+
btn.addEventListener('click', () => {
|
|
1429
|
+
document.querySelectorAll('#users .filter-btn').forEach(b => b.classList.remove('active'));
|
|
1430
|
+
btn.classList.add('active');
|
|
1431
|
+
currentUserFilter = btn.dataset.filter;
|
|
1432
|
+
renderFullUsers();
|
|
1433
|
+
});
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
function initKeyboardShortcuts() {
|
|
1438
|
+
document.addEventListener('keydown', (e) => {
|
|
1439
|
+
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
|
|
1440
|
+
e.preventDefault();
|
|
1441
|
+
openSearch();
|
|
1442
|
+
}
|
|
1443
|
+
if (e.key === 'Escape') {
|
|
1444
|
+
closeSearch();
|
|
1445
|
+
closeModal();
|
|
1446
|
+
closeSettings();
|
|
1447
|
+
}
|
|
1448
|
+
// Search navigation
|
|
1449
|
+
if (document.getElementById('searchModal').classList.contains('active')) {
|
|
1450
|
+
const items = document.querySelectorAll('.search-item');
|
|
1451
|
+
if (e.key === 'ArrowDown') {
|
|
1452
|
+
e.preventDefault();
|
|
1453
|
+
selectedSearchIndex = Math.min(selectedSearchIndex + 1, items.length - 1);
|
|
1454
|
+
updateSearchSelection(items);
|
|
1455
|
+
} else if (e.key === 'ArrowUp') {
|
|
1456
|
+
e.preventDefault();
|
|
1457
|
+
selectedSearchIndex = Math.max(selectedSearchIndex - 1, 0);
|
|
1458
|
+
updateSearchSelection(items);
|
|
1459
|
+
} else if (e.key === 'Enter' && selectedSearchIndex >= 0) {
|
|
1460
|
+
e.preventDefault();
|
|
1461
|
+
items[selectedSearchIndex]?.click();
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
// ===========================================
|
|
1468
|
+
// RENDERING
|
|
1469
|
+
// ===========================================
|
|
1470
|
+
|
|
1471
|
+
function renderAll() {
|
|
1472
|
+
renderStats();
|
|
1473
|
+
renderCharts();
|
|
1474
|
+
renderTopPlugins();
|
|
1475
|
+
renderTopUsers();
|
|
1476
|
+
renderFullPlugins();
|
|
1477
|
+
renderFullUsers();
|
|
1478
|
+
renderMarketplace();
|
|
1479
|
+
renderInsights();
|
|
1480
|
+
renderActivityFeed();
|
|
1481
|
+
renderHealthScore();
|
|
1482
|
+
renderQuickInsights();
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
function renderStats() {
|
|
1486
|
+
const pluginCount = Object.keys(ENHANCED_PLUGINS).length;
|
|
1487
|
+
const totalUsage = Object.values(ENHANCED_PLUGINS).reduce((sum, p) => sum + p.usageCount, 0);
|
|
1488
|
+
const userCount = Object.keys(ENHANCED_USERS).length;
|
|
1489
|
+
const totalSessions = Object.values(ENHANCED_USERS).reduce((sum, u) => sum + u.sessions, 0);
|
|
1490
|
+
const avgDaily = Math.round(DAILY_USAGE.reduce((a, b) => a + b, 0) / DAILY_USAGE.length);
|
|
1491
|
+
|
|
1492
|
+
document.getElementById('totalPlugins').textContent = pluginCount;
|
|
1493
|
+
document.getElementById('totalUsage').textContent = totalUsage.toLocaleString();
|
|
1494
|
+
document.getElementById('totalSessions').textContent = totalSessions.toLocaleString();
|
|
1495
|
+
document.getElementById('totalUsers').textContent = userCount;
|
|
1496
|
+
document.getElementById('avgDaily').textContent = avgDaily;
|
|
1497
|
+
|
|
1498
|
+
document.getElementById('pluginCount').textContent = pluginCount;
|
|
1499
|
+
document.getElementById('insightCount').textContent = INSIGHTS.length;
|
|
1500
|
+
|
|
1501
|
+
document.getElementById('lastSync').textContent = 'Just now';
|
|
1502
|
+
document.getElementById('currentUser').textContent = 'Logged in as kent';
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
function renderCharts() {
|
|
1506
|
+
// Usage Chart
|
|
1507
|
+
const usageCtx = document.getElementById('usageChart').getContext('2d');
|
|
1508
|
+
const labels = Array.from({ length: 14 }, (_, i) => {
|
|
1509
|
+
const d = new Date();
|
|
1510
|
+
d.setDate(d.getDate() - (13 - i));
|
|
1511
|
+
return d.toLocaleDateString('en', { weekday: 'short' });
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
new Chart(usageCtx, {
|
|
1515
|
+
type: 'line',
|
|
1516
|
+
data: {
|
|
1517
|
+
labels,
|
|
1518
|
+
datasets: [{
|
|
1519
|
+
label: 'Usage',
|
|
1520
|
+
data: DAILY_USAGE,
|
|
1521
|
+
borderColor: '#00ff88',
|
|
1522
|
+
backgroundColor: 'rgba(0, 255, 136, 0.1)',
|
|
1523
|
+
fill: true,
|
|
1524
|
+
tension: 0.4,
|
|
1525
|
+
pointRadius: 3,
|
|
1526
|
+
pointHoverRadius: 6,
|
|
1527
|
+
}]
|
|
1528
|
+
},
|
|
1529
|
+
options: {
|
|
1530
|
+
responsive: true,
|
|
1531
|
+
maintainAspectRatio: false,
|
|
1532
|
+
plugins: { legend: { display: false } },
|
|
1533
|
+
scales: {
|
|
1534
|
+
y: { beginAtZero: true, grid: { color: '#30363d' }, ticks: { color: '#8b949e' } },
|
|
1535
|
+
x: { grid: { display: false }, ticks: { color: '#8b949e', maxRotation: 0 } }
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
});
|
|
1539
|
+
|
|
1540
|
+
// Category Chart
|
|
1541
|
+
const catCtx = document.getElementById('categoryChart').getContext('2d');
|
|
1542
|
+
const categoryUsage = {};
|
|
1543
|
+
Object.values(ENHANCED_PLUGINS).forEach(p => {
|
|
1544
|
+
categoryUsage[p.category] = (categoryUsage[p.category] || 0) + p.usageCount;
|
|
1545
|
+
});
|
|
1546
|
+
|
|
1547
|
+
new Chart(catCtx, {
|
|
1548
|
+
type: 'doughnut',
|
|
1549
|
+
data: {
|
|
1550
|
+
labels: Object.keys(categoryUsage).map(c => c.charAt(0).toUpperCase() + c.slice(1)),
|
|
1551
|
+
datasets: [{
|
|
1552
|
+
data: Object.values(categoryUsage),
|
|
1553
|
+
backgroundColor: ['#00ff88', '#00d4ff', '#a855f7', '#f97316', '#eab308', '#3b82f6', '#ef4444', '#ec4899'],
|
|
1554
|
+
borderWidth: 0
|
|
1555
|
+
}]
|
|
1556
|
+
},
|
|
1557
|
+
options: {
|
|
1558
|
+
responsive: true,
|
|
1559
|
+
maintainAspectRatio: false,
|
|
1560
|
+
plugins: {
|
|
1561
|
+
legend: {
|
|
1562
|
+
position: 'right',
|
|
1563
|
+
labels: { color: '#e6edf3', padding: 8, font: { size: 11 } }
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
function renderTopPlugins() {
|
|
1571
|
+
const plugins = Object.entries(ENHANCED_PLUGINS)
|
|
1572
|
+
.map(([name, data]) => ({ name, ...data }))
|
|
1573
|
+
.sort((a, b) => b.usageCount - a.usageCount)
|
|
1574
|
+
.slice(0, 5);
|
|
1575
|
+
|
|
1576
|
+
const maxUsage = plugins[0]?.usageCount || 1;
|
|
1577
|
+
|
|
1578
|
+
document.getElementById('topPluginsList').innerHTML = plugins.map((p, i) => `
|
|
1579
|
+
<li class="list-item" onclick="showPluginDetail('${p.name}')">
|
|
1580
|
+
<div class="list-rank ${i < 3 ? 'top' : ''}">${i + 1}</div>
|
|
1581
|
+
<div class="list-icon">🔌</div>
|
|
1582
|
+
<div class="list-info">
|
|
1583
|
+
<div class="list-name">${p.name}</div>
|
|
1584
|
+
<div class="list-meta">${p.category} · v${p.version}</div>
|
|
1585
|
+
</div>
|
|
1586
|
+
<div class="list-stats">
|
|
1587
|
+
<div class="list-count">${p.usageCount.toLocaleString()}</div>
|
|
1588
|
+
<div class="list-bar"><div class="list-bar-fill" style="width: ${(p.usageCount / maxUsage) * 100}%"></div></div>
|
|
1589
|
+
</div>
|
|
1590
|
+
</li>
|
|
1591
|
+
`).join('');
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
function renderTopUsers() {
|
|
1595
|
+
const users = Object.entries(ENHANCED_USERS)
|
|
1596
|
+
.map(([name, data]) => ({
|
|
1597
|
+
name, ...data,
|
|
1598
|
+
totalUsage: Object.values(data.plugins).reduce((sum, p) => sum + p.usageCount, 0)
|
|
1599
|
+
}))
|
|
1600
|
+
.sort((a, b) => b.sessions - a.sessions)
|
|
1601
|
+
.slice(0, 5);
|
|
1602
|
+
|
|
1603
|
+
const maxSessions = users[0]?.sessions || 1;
|
|
1604
|
+
|
|
1605
|
+
document.getElementById('topUsersList').innerHTML = users.map((u, i) => `
|
|
1606
|
+
<li class="list-item" onclick="showUserDetail('${u.name}')">
|
|
1607
|
+
<div class="list-rank ${i < 3 ? 'top' : ''}">${i + 1}</div>
|
|
1608
|
+
<div class="list-icon">${u.avatar}</div>
|
|
1609
|
+
<div class="list-info">
|
|
1610
|
+
<div class="list-name">${u.name}</div>
|
|
1611
|
+
<div class="list-meta">${u.role} · ${u.totalUsage} uses</div>
|
|
1612
|
+
</div>
|
|
1613
|
+
<div class="list-stats">
|
|
1614
|
+
<div class="list-count">${u.sessions}</div>
|
|
1615
|
+
<div class="list-bar"><div class="list-bar-fill purple" style="width: ${(u.sessions / maxSessions) * 100}%"></div></div>
|
|
1616
|
+
</div>
|
|
1617
|
+
</li>
|
|
1618
|
+
`).join('');
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
function renderFullPlugins() {
|
|
1622
|
+
let plugins = Object.entries(ENHANCED_PLUGINS).map(([name, data]) => ({ name, ...data }));
|
|
1623
|
+
|
|
1624
|
+
// Filter
|
|
1625
|
+
const now = new Date();
|
|
1626
|
+
const weekAgo = new Date(now - 7 * 24 * 60 * 60 * 1000);
|
|
1627
|
+
const monthAgo = new Date(now - 30 * 24 * 60 * 60 * 1000);
|
|
1628
|
+
|
|
1629
|
+
if (currentPluginFilter === 'active') {
|
|
1630
|
+
plugins = plugins.filter(p => p.lastUsed && new Date(p.lastUsed) >= weekAgo);
|
|
1631
|
+
} else if (currentPluginFilter === 'inactive') {
|
|
1632
|
+
plugins = plugins.filter(p => {
|
|
1633
|
+
const last = p.lastUsed ? new Date(p.lastUsed) : null;
|
|
1634
|
+
return last && last < weekAgo && last >= monthAgo;
|
|
1635
|
+
});
|
|
1636
|
+
} else if (currentPluginFilter === 'dormant') {
|
|
1637
|
+
plugins = plugins.filter(p => !p.lastUsed || new Date(p.lastUsed) < monthAgo);
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
// Sort
|
|
1641
|
+
if (currentPluginSort === 'usage') {
|
|
1642
|
+
plugins.sort((a, b) => b.usageCount - a.usageCount);
|
|
1643
|
+
} else if (currentPluginSort === 'name') {
|
|
1644
|
+
plugins.sort((a, b) => a.name.localeCompare(b.name));
|
|
1645
|
+
} else if (currentPluginSort === 'recent') {
|
|
1646
|
+
plugins.sort((a, b) => new Date(b.lastUsed || 0) - new Date(a.lastUsed || 0));
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
document.getElementById('fullPluginsList').innerHTML = plugins.map((p, i) => {
|
|
1650
|
+
const lastUsed = p.lastUsed ? new Date(p.lastUsed) : null;
|
|
1651
|
+
let status = 'gray', statusText = 'Never Used';
|
|
1652
|
+
if (lastUsed) {
|
|
1653
|
+
if (lastUsed >= weekAgo) { status = 'green'; statusText = 'Active'; }
|
|
1654
|
+
else if (lastUsed >= monthAgo) { status = 'yellow'; statusText = 'Inactive'; }
|
|
1655
|
+
else { status = 'red'; statusText = 'Dormant'; }
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
return `
|
|
1659
|
+
<div class="full-list-item" onclick="showPluginDetail('${p.name}')">
|
|
1660
|
+
<div class="list-rank ${i < 3 ? 'top' : ''}">${i + 1}</div>
|
|
1661
|
+
<div class="list-info">
|
|
1662
|
+
<div class="list-name">${p.name}</div>
|
|
1663
|
+
<div class="list-meta">${p.description}</div>
|
|
1664
|
+
</div>
|
|
1665
|
+
<div style="text-align: center;"><span class="badge badge-${status}">${statusText}</span></div>
|
|
1666
|
+
<div style="text-align: center; font-weight: 600;">${p.usageCount.toLocaleString()}</div>
|
|
1667
|
+
<div style="text-align: right; color: var(--text-secondary); font-size: 12px;">${p.lastUsed || 'Never'}</div>
|
|
1668
|
+
</div>
|
|
1669
|
+
`;
|
|
1670
|
+
}).join('') || '<div class="search-empty"><div class="search-empty-icon">📭</div><div>No plugins match this filter</div></div>';
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
function renderFullUsers() {
|
|
1674
|
+
let users = Object.entries(ENHANCED_USERS).map(([name, data]) => ({
|
|
1675
|
+
name, ...data,
|
|
1676
|
+
totalUsage: Object.values(data.plugins).reduce((sum, p) => sum + p.usageCount, 0)
|
|
1677
|
+
}));
|
|
1678
|
+
|
|
1679
|
+
// Filter
|
|
1680
|
+
const now = new Date();
|
|
1681
|
+
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
1682
|
+
const weekAgo = new Date(now - 7 * 24 * 60 * 60 * 1000);
|
|
1683
|
+
|
|
1684
|
+
if (currentUserFilter === 'active') {
|
|
1685
|
+
users = users.filter(u => u.lastActive && new Date(u.lastActive) >= today);
|
|
1686
|
+
} else if (currentUserFilter === 'week') {
|
|
1687
|
+
users = users.filter(u => u.lastActive && new Date(u.lastActive) >= weekAgo);
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
// Sort
|
|
1691
|
+
if (currentUserSort === 'sessions') {
|
|
1692
|
+
users.sort((a, b) => b.sessions - a.sessions);
|
|
1693
|
+
} else if (currentUserSort === 'usage') {
|
|
1694
|
+
users.sort((a, b) => b.totalUsage - a.totalUsage);
|
|
1695
|
+
} else if (currentUserSort === 'name') {
|
|
1696
|
+
users.sort((a, b) => a.name.localeCompare(b.name));
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
document.getElementById('fullUsersList').innerHTML = users.map((u, i) => {
|
|
1700
|
+
const pluginCount = Object.keys(u.plugins).length;
|
|
1701
|
+
return `
|
|
1702
|
+
<div class="full-list-item" onclick="showUserDetail('${u.name}')">
|
|
1703
|
+
<div style="display: flex; align-items: center; justify-content: center;">${u.avatar}</div>
|
|
1704
|
+
<div class="list-info">
|
|
1705
|
+
<div class="list-name">${u.name}</div>
|
|
1706
|
+
<div class="list-meta">${u.role} · First seen: ${u.firstSeen}</div>
|
|
1707
|
+
</div>
|
|
1708
|
+
<div style="text-align: center;"><span class="badge badge-green">${pluginCount} plugins</span></div>
|
|
1709
|
+
<div style="text-align: center; font-weight: 600;">${u.sessions}</div>
|
|
1710
|
+
<div style="text-align: right; color: var(--text-secondary); font-size: 12px;">${u.lastActive}</div>
|
|
1711
|
+
</div>
|
|
1712
|
+
`;
|
|
1713
|
+
}).join('') || '<div class="search-empty"><div class="search-empty-icon">👥</div><div>No users match this filter</div></div>';
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
function renderMarketplace() {
|
|
1717
|
+
const categories = ['all', ...new Set(MARKETPLACE_DATA.map(p => p.category))];
|
|
1718
|
+
|
|
1719
|
+
document.getElementById('categoryFilters').innerHTML = categories.map(c => `
|
|
1720
|
+
<button class="filter-btn ${c === currentMarketplaceCategory ? 'active' : ''}" onclick="filterMarketplace('${c}')">${c === 'all' ? 'All' : c.charAt(0).toUpperCase() + c.slice(1)}</button>
|
|
1721
|
+
`).join('');
|
|
1722
|
+
|
|
1723
|
+
let plugins = MARKETPLACE_DATA;
|
|
1724
|
+
if (currentMarketplaceCategory !== 'all') {
|
|
1725
|
+
plugins = plugins.filter(p => p.category === currentMarketplaceCategory);
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
const installedNames = Object.keys(ENHANCED_PLUGINS);
|
|
1729
|
+
|
|
1730
|
+
document.getElementById('marketplaceList').innerHTML = plugins.map((p, i) => {
|
|
1731
|
+
const isInstalled = installedNames.includes(p.name);
|
|
1732
|
+
return `
|
|
1733
|
+
<div class="full-list-item" onclick="showMarketplaceDetail('${p.name}')">
|
|
1734
|
+
<div style="display: flex; align-items: center; justify-content: center; font-size: 20px;">${p.trending ? '🔥' : '📦'}</div>
|
|
1735
|
+
<div class="list-info">
|
|
1736
|
+
<div class="list-name">${p.name} ${p.trending ? '<span class="badge badge-purple">Trending</span>' : ''}</div>
|
|
1737
|
+
<div class="list-meta">${p.description}</div>
|
|
1738
|
+
</div>
|
|
1739
|
+
<div style="text-align: center;"><span class="badge badge-${isInstalled ? 'green' : 'blue'}">${isInstalled ? 'Installed' : 'Available'}</span></div>
|
|
1740
|
+
<div style="text-align: center; font-weight: 500;">⭐ ${p.rating}</div>
|
|
1741
|
+
<div style="text-align: right; color: var(--text-secondary); font-size: 12px;">${formatNumber(p.downloads)} downloads</div>
|
|
1742
|
+
</div>
|
|
1743
|
+
`;
|
|
1744
|
+
}).join('');
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
function renderInsights() {
|
|
1748
|
+
document.getElementById('insightsList').innerHTML = INSIGHTS.map(insight => `
|
|
1749
|
+
<div class="insight-item ${insight.type}" onclick="${insight.action ? `executeAction('${insight.action}')` : ''}">
|
|
1750
|
+
<div class="insight-icon">${insight.icon}</div>
|
|
1751
|
+
<div class="insight-content">
|
|
1752
|
+
<div class="insight-message">${insight.message}</div>
|
|
1753
|
+
${insight.action ? `<span class="insight-action">${insight.action}</span>` : ''}
|
|
1754
|
+
</div>
|
|
1755
|
+
</div>
|
|
1756
|
+
`).join('');
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
function renderActivityFeed() {
|
|
1760
|
+
const icons = { usage: '⚡', install: '📥', update: '🔄', alert: '⚠️' };
|
|
1761
|
+
document.getElementById('activityList').innerHTML = ACTIVITY_FEED.map(a => `
|
|
1762
|
+
<div class="activity-item">
|
|
1763
|
+
<div class="activity-icon ${a.type}">${icons[a.type]}</div>
|
|
1764
|
+
<div class="activity-content">
|
|
1765
|
+
<div class="activity-text"><strong>${a.user}</strong> ${a.action} <strong>${a.target}</strong></div>
|
|
1766
|
+
<div class="activity-time">${a.time}</div>
|
|
1767
|
+
</div>
|
|
1768
|
+
</div>
|
|
1769
|
+
`).join('');
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
function renderHealthScore() {
|
|
1773
|
+
const plugins = Object.values(ENHANCED_PLUGINS);
|
|
1774
|
+
const now = new Date();
|
|
1775
|
+
const weekAgo = new Date(now - 7 * 24 * 60 * 60 * 1000);
|
|
1776
|
+
|
|
1777
|
+
const activeCount = plugins.filter(p => p.lastUsed && new Date(p.lastUsed) >= weekAgo).length;
|
|
1778
|
+
const totalCount = plugins.length;
|
|
1779
|
+
const score = Math.round((activeCount / totalCount) * 100);
|
|
1780
|
+
|
|
1781
|
+
document.getElementById('healthCircle').style.setProperty('--score', score);
|
|
1782
|
+
document.getElementById('healthCircle').textContent = score;
|
|
1783
|
+
document.getElementById('healthActive').textContent = `${activeCount}/${totalCount}`;
|
|
1784
|
+
document.getElementById('healthUpdated').textContent = `${totalCount - 2}/${totalCount}`;
|
|
1785
|
+
document.getElementById('healthSecurity').textContent = '0';
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
function renderQuickInsights() {
|
|
1789
|
+
document.getElementById('quickInsights').innerHTML = INSIGHTS.slice(0, 3).map(insight => `
|
|
1790
|
+
<div class="insight-item ${insight.type}" style="margin-bottom: 8px;" onclick="${insight.action ? `executeAction('${insight.action}')` : ''}">
|
|
1791
|
+
<div class="insight-icon" style="font-size: 16px;">${insight.icon}</div>
|
|
1792
|
+
<div class="insight-content">
|
|
1793
|
+
<div class="insight-message" style="font-size: 12px;">${insight.message.substring(0, 80)}${insight.message.length > 80 ? '...' : ''}</div>
|
|
1794
|
+
</div>
|
|
1795
|
+
</div>
|
|
1796
|
+
`).join('');
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
// ===========================================
|
|
1800
|
+
// INTERACTIONS
|
|
1801
|
+
// ===========================================
|
|
1802
|
+
|
|
1803
|
+
function showTab(tabId) {
|
|
1804
|
+
document.querySelectorAll('.tab').forEach(t => t.classList.toggle('active', t.dataset.tab === tabId));
|
|
1805
|
+
document.querySelectorAll('.tab-content').forEach(c => c.classList.toggle('active', c.id === tabId));
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
function sortPlugins(by) {
|
|
1809
|
+
currentPluginSort = by;
|
|
1810
|
+
renderFullPlugins();
|
|
1811
|
+
showToast(`Sorted by ${by}`, '📊');
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
function sortUsers(by) {
|
|
1815
|
+
currentUserSort = by;
|
|
1816
|
+
renderFullUsers();
|
|
1817
|
+
showToast(`Sorted by ${by}`, '📊');
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
function filterMarketplace(category) {
|
|
1821
|
+
currentMarketplaceCategory = category;
|
|
1822
|
+
renderMarketplace();
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
function showPluginDetail(name) {
|
|
1826
|
+
const p = ENHANCED_PLUGINS[name];
|
|
1827
|
+
if (!p) return;
|
|
1828
|
+
|
|
1829
|
+
const now = new Date();
|
|
1830
|
+
const lastUsed = p.lastUsed ? new Date(p.lastUsed) : null;
|
|
1831
|
+
let status = 'Never Used', statusClass = 'gray';
|
|
1832
|
+
if (lastUsed) {
|
|
1833
|
+
const daysDiff = Math.floor((now - lastUsed) / (1000 * 60 * 60 * 24));
|
|
1834
|
+
if (daysDiff <= 7) { status = 'Active'; statusClass = 'green'; }
|
|
1835
|
+
else if (daysDiff <= 30) { status = 'Inactive'; statusClass = 'yellow'; }
|
|
1836
|
+
else { status = 'Dormant'; statusClass = 'red'; }
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
document.getElementById('modalTitle').innerHTML = `<span style="font-size: 24px;">🔌</span> ${name}`;
|
|
1840
|
+
document.getElementById('modalBody').innerHTML = `
|
|
1841
|
+
<div class="detail-row"><span class="detail-label">Description</span><span class="detail-value">${p.description}</span></div>
|
|
1842
|
+
<div class="detail-row"><span class="detail-label">Category</span><span class="detail-value" style="text-transform: capitalize;">${p.category}</span></div>
|
|
1843
|
+
<div class="detail-row"><span class="detail-label">Version</span><span class="detail-value">v${p.version}</span></div>
|
|
1844
|
+
<div class="detail-row"><span class="detail-label">Author</span><span class="detail-value">${p.author}</span></div>
|
|
1845
|
+
<div class="detail-row"><span class="detail-label">Installed</span><span class="detail-value">${p.installed}</span></div>
|
|
1846
|
+
<div class="detail-row"><span class="detail-label">Last Used</span><span class="detail-value">${p.lastUsed || 'Never'}</span></div>
|
|
1847
|
+
<div class="detail-row"><span class="detail-label">Usage Count</span><span class="detail-value">${p.usageCount.toLocaleString()}</span></div>
|
|
1848
|
+
<div class="detail-row"><span class="detail-label">Status</span><span class="detail-value"><span class="badge badge-${statusClass}">${status}</span></span></div>
|
|
1849
|
+
`;
|
|
1850
|
+
document.getElementById('modalFooter').innerHTML = `
|
|
1851
|
+
<button class="btn" onclick="closeModal(); showToast('Checking for updates...', '🔄');">Check Update</button>
|
|
1852
|
+
<button class="btn" onclick="closeModal(); showToast('Plugin disabled', '⏸️');">Disable</button>
|
|
1853
|
+
<button class="btn" style="color: var(--accent-red);" onclick="closeModal(); showToast('Run: /scout --uninstall ${name}', '🗑️');">Uninstall</button>
|
|
1854
|
+
`;
|
|
1855
|
+
document.getElementById('modalOverlay').classList.add('active');
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
function showUserDetail(name) {
|
|
1859
|
+
const u = ENHANCED_USERS[name];
|
|
1860
|
+
if (!u) return;
|
|
1861
|
+
|
|
1862
|
+
const pluginList = Object.entries(u.plugins)
|
|
1863
|
+
.sort((a, b) => b[1].usageCount - a[1].usageCount)
|
|
1864
|
+
.map(([n, d]) => `<div style="display: flex; justify-content: space-between; padding: 4px 0;"><span>${n}</span><span style="color: var(--text-secondary);">${d.usageCount} uses</span></div>`)
|
|
1865
|
+
.join('');
|
|
1866
|
+
|
|
1867
|
+
document.getElementById('modalTitle').innerHTML = `<span style="font-size: 24px;">${u.avatar}</span> ${name}`;
|
|
1868
|
+
document.getElementById('modalBody').innerHTML = `
|
|
1869
|
+
<div class="detail-row"><span class="detail-label">Role</span><span class="detail-value">${u.role}</span></div>
|
|
1870
|
+
<div class="detail-row"><span class="detail-label">Sessions</span><span class="detail-value">${u.sessions}</span></div>
|
|
1871
|
+
<div class="detail-row"><span class="detail-label">First Seen</span><span class="detail-value">${u.firstSeen}</span></div>
|
|
1872
|
+
<div class="detail-row"><span class="detail-label">Last Active</span><span class="detail-value">${u.lastActive}</span></div>
|
|
1873
|
+
<div class="detail-row" style="flex-direction: column; align-items: stretch;">
|
|
1874
|
+
<span class="detail-label" style="margin-bottom: 8px;">Plugin Usage</span>
|
|
1875
|
+
<div style="background: var(--bg-tertiary); border-radius: 8px; padding: 12px;">${pluginList}</div>
|
|
1876
|
+
</div>
|
|
1877
|
+
`;
|
|
1878
|
+
document.getElementById('modalFooter').innerHTML = `<button class="btn" onclick="closeModal()">Close</button>`;
|
|
1879
|
+
document.getElementById('modalOverlay').classList.add('active');
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
function showMarketplaceDetail(name) {
|
|
1883
|
+
const p = MARKETPLACE_DATA.find(m => m.name === name);
|
|
1884
|
+
if (!p) return;
|
|
1885
|
+
|
|
1886
|
+
const isInstalled = Object.keys(ENHANCED_PLUGINS).includes(name);
|
|
1887
|
+
|
|
1888
|
+
document.getElementById('modalTitle').innerHTML = `<span style="font-size: 24px;">📦</span> ${name}`;
|
|
1889
|
+
document.getElementById('modalBody').innerHTML = `
|
|
1890
|
+
<div class="detail-row"><span class="detail-label">Description</span><span class="detail-value">${p.description}</span></div>
|
|
1891
|
+
<div class="detail-row"><span class="detail-label">Category</span><span class="detail-value" style="text-transform: capitalize;">${p.category}</span></div>
|
|
1892
|
+
<div class="detail-row"><span class="detail-label">Downloads</span><span class="detail-value">${p.downloads.toLocaleString()}</span></div>
|
|
1893
|
+
<div class="detail-row"><span class="detail-label">Rating</span><span class="detail-value">⭐ ${p.rating} / 5.0</span></div>
|
|
1894
|
+
<div class="detail-row"><span class="detail-label">Status</span><span class="detail-value"><span class="badge badge-${isInstalled ? 'green' : 'blue'}">${isInstalled ? 'Installed' : 'Available'}</span></span></div>
|
|
1895
|
+
${p.trending ? '<div class="detail-row"><span class="detail-label">Trending</span><span class="detail-value"><span class="badge badge-purple">🔥 Trending Now</span></span></div>' : ''}
|
|
1896
|
+
`;
|
|
1897
|
+
document.getElementById('modalFooter').innerHTML = isInstalled
|
|
1898
|
+
? `<button class="btn" onclick="closeModal()">Close</button><button class="btn" style="color: var(--accent-red);" onclick="closeModal(); showToast('Run: /scout --uninstall ${name}', '🗑️');">Uninstall</button>`
|
|
1899
|
+
: `<button class="btn" onclick="closeModal()">Cancel</button><button class="btn btn-primary" onclick="closeModal(); showToast('Run: /scout --install ${name}', '📥');">Install Plugin</button>`;
|
|
1900
|
+
document.getElementById('modalOverlay').classList.add('active');
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
function closeModal(e) {
|
|
1904
|
+
if (!e || e.target.id === 'modalOverlay') {
|
|
1905
|
+
document.getElementById('modalOverlay').classList.remove('active');
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
// ===========================================
|
|
1910
|
+
// SEARCH
|
|
1911
|
+
// ===========================================
|
|
1912
|
+
|
|
1913
|
+
function openSearch() {
|
|
1914
|
+
document.getElementById('searchModal').classList.add('active');
|
|
1915
|
+
document.getElementById('searchInput').focus();
|
|
1916
|
+
selectedSearchIndex = -1;
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
function closeSearch(e) {
|
|
1920
|
+
if (!e || e.target.id === 'searchModal') {
|
|
1921
|
+
document.getElementById('searchModal').classList.remove('active');
|
|
1922
|
+
document.getElementById('searchInput').value = '';
|
|
1923
|
+
renderSearchEmpty();
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
function renderSearchEmpty() {
|
|
1928
|
+
document.getElementById('searchResults').innerHTML = `
|
|
1929
|
+
<div class="search-empty">
|
|
1930
|
+
<div class="search-empty-icon">🔍</div>
|
|
1931
|
+
<div>Type to search plugins, users, or marketplace</div>
|
|
1932
|
+
</div>
|
|
1933
|
+
`;
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
function handleSearch() {
|
|
1937
|
+
const query = document.getElementById('searchInput').value.toLowerCase().trim();
|
|
1938
|
+
if (!query) { renderSearchEmpty(); return; }
|
|
1939
|
+
|
|
1940
|
+
const tab = currentSearchTab;
|
|
1941
|
+
let html = '';
|
|
1942
|
+
|
|
1943
|
+
// Installed plugins
|
|
1944
|
+
if (tab === 'all' || tab === 'installed') {
|
|
1945
|
+
const installed = Object.entries(ENHANCED_PLUGINS)
|
|
1946
|
+
.filter(([name, data]) => name.includes(query) || data.description.toLowerCase().includes(query))
|
|
1947
|
+
.slice(0, 5);
|
|
1948
|
+
|
|
1949
|
+
if (installed.length) {
|
|
1950
|
+
html += `<div class="search-section"><div class="search-section-title">Installed (${installed.length})</div>`;
|
|
1951
|
+
html += installed.map(([name, data]) => `
|
|
1952
|
+
<div class="search-item" onclick="closeSearch(); showPluginDetail('${name}')">
|
|
1953
|
+
<div class="search-item-icon installed">🔌</div>
|
|
1954
|
+
<div class="search-item-info">
|
|
1955
|
+
<div class="search-item-name">${highlight(name, query)}</div>
|
|
1956
|
+
<div class="search-item-desc">${highlight(data.description, query)}</div>
|
|
1957
|
+
</div>
|
|
1958
|
+
<div class="search-item-meta"><span class="badge badge-green">Installed</span></div>
|
|
1959
|
+
</div>
|
|
1960
|
+
`).join('');
|
|
1961
|
+
html += '</div>';
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
// Marketplace
|
|
1966
|
+
if (tab === 'all' || tab === 'marketplace') {
|
|
1967
|
+
const installedNames = Object.keys(ENHANCED_PLUGINS);
|
|
1968
|
+
const marketplace = MARKETPLACE_DATA
|
|
1969
|
+
.filter(p => !installedNames.includes(p.name))
|
|
1970
|
+
.filter(p => p.name.includes(query) || p.description.toLowerCase().includes(query))
|
|
1971
|
+
.slice(0, 5);
|
|
1972
|
+
|
|
1973
|
+
if (marketplace.length) {
|
|
1974
|
+
html += `<div class="search-section"><div class="search-section-title">Marketplace (${marketplace.length})</div>`;
|
|
1975
|
+
html += marketplace.map(p => `
|
|
1976
|
+
<div class="search-item" onclick="closeSearch(); showMarketplaceDetail('${p.name}')">
|
|
1977
|
+
<div class="search-item-icon marketplace">📦</div>
|
|
1978
|
+
<div class="search-item-info">
|
|
1979
|
+
<div class="search-item-name">${highlight(p.name, query)} ${p.trending ? '<span class="badge badge-purple" style="margin-left: 4px;">🔥</span>' : ''}</div>
|
|
1980
|
+
<div class="search-item-desc">${highlight(p.description, query)}</div>
|
|
1981
|
+
</div>
|
|
1982
|
+
<div class="search-item-meta">${formatNumber(p.downloads)}</div>
|
|
1983
|
+
</div>
|
|
1984
|
+
`).join('');
|
|
1985
|
+
html += '</div>';
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
// Users
|
|
1990
|
+
if (tab === 'all' || tab === 'users') {
|
|
1991
|
+
const users = Object.entries(ENHANCED_USERS)
|
|
1992
|
+
.filter(([name, data]) => name.includes(query) || data.role.toLowerCase().includes(query))
|
|
1993
|
+
.slice(0, 5);
|
|
1994
|
+
|
|
1995
|
+
if (users.length) {
|
|
1996
|
+
html += `<div class="search-section"><div class="search-section-title">Users (${users.length})</div>`;
|
|
1997
|
+
html += users.map(([name, data]) => `
|
|
1998
|
+
<div class="search-item" onclick="closeSearch(); showUserDetail('${name}')">
|
|
1999
|
+
<div class="search-item-icon" style="background: rgba(168, 85, 247, 0.15);">${data.avatar}</div>
|
|
2000
|
+
<div class="search-item-info">
|
|
2001
|
+
<div class="search-item-name">${highlight(name, query)}</div>
|
|
2002
|
+
<div class="search-item-desc">${highlight(data.role, query)}</div>
|
|
2003
|
+
</div>
|
|
2004
|
+
<div class="search-item-meta">${data.sessions} sessions</div>
|
|
2005
|
+
</div>
|
|
2006
|
+
`).join('');
|
|
2007
|
+
html += '</div>';
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
document.getElementById('searchResults').innerHTML = html || `
|
|
2012
|
+
<div class="search-empty">
|
|
2013
|
+
<div class="search-empty-icon">🔍</div>
|
|
2014
|
+
<div>No results for "${query}"</div>
|
|
2015
|
+
</div>
|
|
2016
|
+
`;
|
|
2017
|
+
selectedSearchIndex = -1;
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
document.querySelectorAll('.search-tab').forEach(tab => {
|
|
2021
|
+
tab.addEventListener('click', () => {
|
|
2022
|
+
document.querySelectorAll('.search-tab').forEach(t => t.classList.remove('active'));
|
|
2023
|
+
tab.classList.add('active');
|
|
2024
|
+
currentSearchTab = tab.dataset.searchTab;
|
|
2025
|
+
handleSearch();
|
|
2026
|
+
});
|
|
2027
|
+
});
|
|
2028
|
+
|
|
2029
|
+
function updateSearchSelection(items) {
|
|
2030
|
+
items.forEach((item, i) => {
|
|
2031
|
+
item.classList.toggle('selected', i === selectedSearchIndex);
|
|
2032
|
+
if (i === selectedSearchIndex) item.scrollIntoView({ block: 'nearest' });
|
|
2033
|
+
});
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
function highlight(text, query) {
|
|
2037
|
+
if (!query) return text;
|
|
2038
|
+
return text.replace(new RegExp(`(${query})`, 'gi'), '<strong style="color: var(--accent-green);">$1</strong>');
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
// ===========================================
|
|
2042
|
+
// SETTINGS
|
|
2043
|
+
// ===========================================
|
|
2044
|
+
|
|
2045
|
+
function openSettings() {
|
|
2046
|
+
document.getElementById('settingsOverlay').classList.add('active');
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
function closeSettings(e) {
|
|
2050
|
+
if (!e || e.target.id === 'settingsOverlay') {
|
|
2051
|
+
document.getElementById('settingsOverlay').classList.remove('active');
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
function saveSettings() {
|
|
2056
|
+
closeSettings();
|
|
2057
|
+
showToast('Settings saved', '✅');
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
function toggleTheme() {
|
|
2061
|
+
currentTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
|
2062
|
+
document.body.setAttribute('data-theme', currentTheme);
|
|
2063
|
+
document.getElementById('darkModeToggle').classList.toggle('active', currentTheme === 'dark');
|
|
2064
|
+
showToast(`${currentTheme === 'dark' ? 'Dark' : 'Light'} mode enabled`, currentTheme === 'dark' ? '🌙' : '☀️');
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
// ===========================================
|
|
2068
|
+
// ACTIONS
|
|
2069
|
+
// ===========================================
|
|
2070
|
+
|
|
2071
|
+
function runScout() { showToast('Run in terminal: /scout', '🔍'); }
|
|
2072
|
+
function runAudit() { showToast('Run in terminal: /scout audit', '🔍'); }
|
|
2073
|
+
function executeAction(action) { showToast(`Run: ${action}`, '💡'); }
|
|
2074
|
+
|
|
2075
|
+
function exportData() {
|
|
2076
|
+
const data = { plugins: ENHANCED_PLUGINS, users: ENHANCED_USERS, analytics: ANALYTICS_DATA };
|
|
2077
|
+
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
|
2078
|
+
const url = URL.createObjectURL(blob);
|
|
2079
|
+
const a = document.createElement('a');
|
|
2080
|
+
a.href = url;
|
|
2081
|
+
a.download = `plugin-scout-${new Date().toISOString().split('T')[0]}.json`;
|
|
2082
|
+
a.click();
|
|
2083
|
+
URL.revokeObjectURL(url);
|
|
2084
|
+
showToast('Data exported!', '📤');
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
// ===========================================
|
|
2088
|
+
// UTILITIES
|
|
2089
|
+
// ===========================================
|
|
2090
|
+
|
|
2091
|
+
function formatNumber(n) {
|
|
2092
|
+
if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M';
|
|
2093
|
+
if (n >= 1000) return (n / 1000).toFixed(1) + 'k';
|
|
2094
|
+
return n.toString();
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
function showToast(message, icon = '✅') {
|
|
2098
|
+
const toast = document.getElementById('toast');
|
|
2099
|
+
document.getElementById('toastMessage').textContent = message;
|
|
2100
|
+
document.getElementById('toastIcon').textContent = icon;
|
|
2101
|
+
toast.classList.add('show');
|
|
2102
|
+
setTimeout(() => toast.classList.remove('show'), 3000);
|
|
2103
|
+
}
|
|
2104
|
+
</script>
|
|
2105
|
+
</body>
|
|
2106
|
+
</html>
|