itismyskillmarket 1.3.14 → 1.3.16

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/gui/app.js CHANGED
@@ -12,6 +12,7 @@ const state = {
12
12
  pageSize: 20,
13
13
  searchQuery: '',
14
14
  totalPages: 1,
15
+ previousView: 'skills',
15
16
  };
16
17
 
17
18
  // -----------------------------------------------------------------------------
@@ -21,9 +22,23 @@ const state = {
21
22
  document.addEventListener('DOMContentLoaded', () => {
22
23
  initializeNavigation();
23
24
  initializeControls();
25
+ initializeCollapsibleSections();
24
26
  loadSkills();
25
27
  });
26
28
 
29
+ // -----------------------------------------------------------------------------
30
+ // 可折叠侧边栏分组
31
+ // -----------------------------------------------------------------------------
32
+
33
+ function initializeCollapsibleSections() {
34
+ // 默认全部展开
35
+ }
36
+
37
+ function toggleSection(header) {
38
+ const group = header.parentElement;
39
+ group.classList.toggle('collapsed');
40
+ }
41
+
27
42
  // -----------------------------------------------------------------------------
28
43
  // 导航切换
29
44
  // -----------------------------------------------------------------------------
@@ -46,6 +61,10 @@ function initializeNavigation() {
46
61
  }
47
62
 
48
63
  function switchView(view) {
64
+ // Save previous view (but not when navigating to skill-detail from back)
65
+ if (state.currentView !== 'skill-detail') {
66
+ state.previousView = state.currentView;
67
+ }
49
68
  state.currentView = view;
50
69
 
51
70
  // 隐藏所有视图
@@ -57,23 +76,25 @@ function switchView(view) {
57
76
  targetView.classList.add('active');
58
77
  }
59
78
 
60
- // 加载对应数据
61
- switch(view) {
62
- case 'skills':
63
- loadSkills();
64
- break;
65
- case 'installed':
66
- loadInstalled();
67
- break;
68
- case 'platforms':
69
- loadPlatforms();
70
- break;
71
- case 'help':
72
- loadHelp();
73
- break;
74
- case 'admin':
75
- loadAdminDashboard();
76
- break;
79
+ // 加载对应数据 (skill-detail 视图的数据由 showSkillDetail 加载)
80
+ if (view !== 'skill-detail') {
81
+ switch(view) {
82
+ case 'skills':
83
+ loadSkills();
84
+ break;
85
+ case 'installed':
86
+ loadInstalled();
87
+ break;
88
+ case 'platforms':
89
+ loadPlatforms();
90
+ break;
91
+ case 'help':
92
+ loadHelp();
93
+ break;
94
+ case 'admin':
95
+ loadAdminDashboard();
96
+ break;
97
+ }
77
98
  }
78
99
  }
79
100
 
@@ -108,11 +129,13 @@ function initializeControls() {
108
129
  const refreshAdmin = document.getElementById('refresh-admin');
109
130
  if (refreshAdmin) refreshAdmin.addEventListener('click', () => loadAdminDashboard());
110
131
 
111
- // 模态框关闭
112
- document.querySelector('.modal-close').addEventListener('click', closeModal);
113
- document.getElementById('modal').addEventListener('click', (e) => {
114
- if (e.target.id === 'modal') closeModal();
115
- });
132
+ // Admin 模态框 - 点击外部关闭
133
+ const modalEl = document.getElementById('modal');
134
+ if (modalEl) {
135
+ modalEl.addEventListener('click', (e) => {
136
+ if (e.target === modalEl) closeModal();
137
+ });
138
+ }
116
139
  }
117
140
 
118
141
  // -----------------------------------------------------------------------------
@@ -182,18 +205,18 @@ function createSkillCard(skill, isInstalled) {
182
205
  const platformTags = platforms.map(p => `<span class="platform-tag">${p}</span>`).join('');
183
206
 
184
207
  return `
185
- <div class="skill-card">
208
+ <div class="skill-card" onclick="showSkillDetail('${skill.id}')">
186
209
  <h3>${skill.displayName || skill.id}</h3>
187
210
  <div class="skill-id">${skill.id}@${skill.version || 'latest'}</div>
188
211
  <p>${skill.description || 'No description'}</p>
189
212
  <div class="platforms">${platformTags}</div>
190
213
  <div class="actions">
191
214
  ${isInstalled ? `
192
- <button class="btn btn-danger btn-sm" onclick="uninstallSkill('${skill.id}')">Uninstall</button>
193
- <button class="btn btn-primary btn-sm" onclick="updateSkill('${skill.id}')">Update</button>
215
+ <button class="btn btn-danger btn-sm" onclick="event.stopPropagation(); uninstallSkill('${skill.id}')">Uninstall</button>
216
+ <button class="btn btn-primary btn-sm" onclick="event.stopPropagation(); updateSkill('${skill.id}')">Update</button>
217
+ <button class="btn btn-secondary btn-sm" onclick="event.stopPropagation(); showSkillDetail('${skill.id}')">Info</button>
194
218
  ` : `
195
- <button class="btn btn-success btn-sm" onclick="installSkill('${skill.id}')">Install</button>
196
- <button class="btn btn-secondary btn-sm" onclick="showSkillInfo('${skill.id}')">Info</button>
219
+ <button class="btn btn-success btn-sm" onclick="event.stopPropagation(); installSkill('${skill.id}')">Install</button>
197
220
  `}
198
221
  </div>
199
222
  </div>
@@ -479,37 +502,85 @@ async function updateAllSkills() {
479
502
  }
480
503
  }
481
504
 
482
- async function showSkillInfo(skillId) {
483
- const modal = document.getElementById('modal');
484
- const modalBody = document.getElementById('modal-body');
505
+ // -----------------------------------------------------------------------------
506
+ // Skill 详情视图 (替换旧模态框)
507
+ // -----------------------------------------------------------------------------
508
+
509
+ async function showSkillDetail(skillId) {
510
+ const content = document.getElementById('skill-detail-content');
511
+ content.innerHTML = '<div class="loading">Loading skill details...</div>';
485
512
 
486
- modalBody.innerHTML = '<div class="loading">Loading...</div>';
487
- modal.classList.remove('hidden');
513
+ // 切换到详情视图
514
+ const btn = document.querySelector(`.nav-btn[data-view="skill-detail"]`);
515
+ // skill-detail 没有 nav-btn,直接用 switchView
516
+ state.previousView = state.currentView;
517
+ state.currentView = 'skill-detail';
518
+
519
+ // 隐藏所有视图,显示详情视图
520
+ document.querySelectorAll('.view').forEach(v => v.classList.remove('active'));
521
+ const targetView = document.getElementById('view-skill-detail');
522
+ if (targetView) targetView.classList.add('active');
488
523
 
489
524
  try {
490
- const response = await fetch(`/api/skills?search=${skillId}`);
525
+ const response = await fetch(`/api/skill-info?skill=${encodeURIComponent(skillId)}`);
491
526
  const data = await response.json();
492
- const skills = data.skills || data;
493
- const skill = Array.isArray(skills) ? skills.find(s => s.id === skillId) : skills;
494
527
 
495
- if (!skill) {
496
- modalBody.innerHTML = '<div class="loading">Skill not found</div>';
528
+ if (data.error) {
529
+ content.innerHTML = `<div class="loading">Error: ${data.error}</div>`;
497
530
  return;
498
531
  }
499
532
 
500
- modalBody.innerHTML = `
501
- <h2>${skill.displayName || skill.id}</h2>
502
- <div class="detail-row"><strong>ID:</strong> ${skill.id}</div>
503
- <div class="detail-row"><strong>Version:</strong> ${skill.version || 'latest'}</div>
504
- <div class="detail-row"><strong>Description:</strong> ${skill.description || 'No description'}</div>
505
- <div class="detail-row"><strong>Platforms:</strong> ${(skill.platforms || []).join(', ') || 'None'}</div>
506
- ${skill.link ? `<div class="detail-row"><strong>Link:</strong> <a href="${skill.link}" target="_blank">${skill.link}</a></div>` : ''}
507
- <div class="actions" style="margin-top: 20px;">
508
- <button class="btn btn-success" onclick="installSkill('${skill.id}'); closeModal();">Install</button>
533
+ const platforms = data.platforms || [];
534
+ const versions = data.versions || [];
535
+ const escapedId = data.id.replace(/'/g, "\\'");
536
+
537
+ content.innerHTML = `
538
+ <div class="skill-detail">
539
+ <h2>${data.displayName || data.id}</h2>
540
+ <div class="detail-name">${data.name}@${data.version}</div>
541
+
542
+ <div class="detail-section">
543
+ <h3>Description</h3>
544
+ <div class="description-text">${data.description || 'No description'}</div>
545
+ </div>
546
+
547
+ <div class="detail-section">
548
+ <h3>Details</h3>
549
+ <div class="detail-row"><strong>ID:</strong> ${data.id}</div>
550
+ <div class="detail-row"><strong>Version:</strong> ${data.version}</div>
551
+ ${data.license ? `<div class="detail-row"><strong>License:</strong> ${data.license}</div>` : ''}
552
+ ${data.author ? `<div class="detail-row"><strong>Author:</strong> ${data.author}</div>` : ''}
553
+ ${data.homepage ? `<div class="detail-row"><strong>Homepage:</strong> <a href="${data.homepage}" target="_blank">${data.homepage}</a></div>` : ''}
554
+ ${data.repository ? `<div class="detail-row"><strong>Repository:</strong> <a href="${data.repository}" target="_blank">${data.repository}</a></div>` : ''}
555
+ </div>
556
+
557
+ <div class="detail-section">
558
+ <h3>Platforms</h3>
559
+ <div class="platform-tags">
560
+ ${platforms.length ? platforms.map(p => `<span class="platform-tag">${p}</span>`).join('') : '<span class="platform-tag">N/A</span>'}
561
+ </div>
562
+ </div>
563
+
564
+ ${versions.length ? `
565
+ <div class="detail-section">
566
+ <h3>Versions (last ${versions.length})</h3>
567
+ <div class="version-list">
568
+ ${versions.slice().reverse().map(v => `
569
+ <div class="version-item">
570
+ <span>${v} ${v === data.version ? '<span class="version-latest">latest</span>' : ''}</span>
571
+ </div>
572
+ `).join('')}
573
+ </div>
574
+ </div>
575
+ ` : ''}
576
+
577
+ <div class="detail-actions">
578
+ <button class="btn btn-success" onclick="installSkill('${escapedId}')">Install</button>
579
+ </div>
509
580
  </div>
510
581
  `;
511
582
  } catch (err) {
512
- modalBody.innerHTML = `<div class="loading">Error: ${err.message}</div>`;
583
+ content.innerHTML = `<div class="loading">Error: ${err.message}</div>`;
513
584
  }
514
585
  }
515
586
 
@@ -517,6 +588,15 @@ function closeModal() {
517
588
  document.getElementById('modal').classList.add('hidden');
518
589
  }
519
590
 
591
+ function goBack() {
592
+ const target = state.previousView || 'skills';
593
+ // 更新导航按钮状态
594
+ document.querySelectorAll('.nav-btn').forEach(b => {
595
+ b.classList.toggle('active', b.dataset.view === target);
596
+ });
597
+ switchView(target);
598
+ }
599
+
520
600
  // -----------------------------------------------------------------------------
521
601
  // Toast 通知
522
602
  // -----------------------------------------------------------------------------
package/gui/index.html CHANGED
@@ -8,37 +8,20 @@
8
8
  </head>
9
9
  <body>
10
10
  <div id="app">
11
- <!-- 侧边栏 -->
12
- <aside class="sidebar">
13
- <div class="logo">
14
- <h1>📦 SkillMarket</h1>
11
+ <!-- 顶部导航栏 -->
12
+ <header class="topbar">
13
+ <div class="topbar-left">
14
+ <span class="topbar-logo">📦 SkillMarket</span>
15
+ <span class="topbar-version" id="gui-version">v1.3.16</span>
15
16
  </div>
16
- <nav>
17
- <div class="nav-section">Browse</div>
18
- <button class="nav-btn active" data-view="skills">
19
- <span class="icon">📋</span> Skills
20
- </button>
21
- <button class="nav-btn" data-view="installed">
22
- <span class="icon">✅</span> Installed
23
- </button>
24
- <button class="nav-btn" data-view="platforms">
25
- <span class="icon">💻</span> Platforms
26
- </button>
27
-
28
- <div class="nav-section">Manage</div>
29
- <button class="nav-btn" data-view="admin">
30
- <span class="icon">⚙️</span> Admin
31
- </button>
32
-
33
- <div class="nav-section">Support</div>
34
- <button class="nav-btn" data-view="help">
35
- <span class="icon">📖</span> Help
36
- </button>
17
+ <nav class="topbar-nav">
18
+ <button class="nav-btn active" data-view="skills">📋 Skills</button>
19
+ <button class="nav-btn" data-view="installed">✅ Installed</button>
20
+ <button class="nav-btn" data-view="platforms">💻 Platforms</button>
21
+ <button class="nav-btn" data-view="admin">⚙️ Admin</button>
22
+ <button class="nav-btn" data-view="help">📖 Help</button>
37
23
  </nav>
38
- <div class="sidebar-footer">
39
- <span class="version" id="gui-version">v1.3.14</span>
40
- </div>
41
- </aside>
24
+ </header>
42
25
 
43
26
  <!-- 主内容区 -->
44
27
  <main class="main-content">
@@ -94,12 +77,19 @@
94
77
  <div id="admin-stats" class="admin-stats"></div>
95
78
  <div id="admin-skills-list" class="admin-skills-list"></div>
96
79
  </div>
80
+
81
+ <!-- Skill 详情视图 -->
82
+ <div id="view-skill-detail" class="view">
83
+ <div class="view-header">
84
+ <button class="btn btn-secondary" onclick="goBack()">← Back</button>
85
+ </div>
86
+ <div id="skill-detail-content"></div>
87
+ </div>
97
88
  </main>
98
89
 
99
- <!-- Skill 详情模态框 -->
90
+ <!-- Admin 模态框 -->
100
91
  <div id="modal" class="modal hidden">
101
92
  <div class="modal-content">
102
- <button class="modal-close">&times;</button>
103
93
  <div id="modal-body"></div>
104
94
  </div>
105
95
  </div>
package/gui/style.css CHANGED
@@ -16,6 +16,7 @@
16
16
  --success: #4caf50;
17
17
  --warning: #ff9800;
18
18
  --danger: #f44336;
19
+ --topbar-height: 52px;
19
20
  }
20
21
 
21
22
  * {
@@ -29,58 +30,67 @@ body {
29
30
  background: var(--bg-primary);
30
31
  color: var(--text-secondary);
31
32
  display: flex;
33
+ flex-direction: column;
32
34
  height: 100vh;
33
35
  overflow: hidden;
34
36
  }
35
37
 
36
38
  /* -----------------------------------------------------------------------------
37
- 侧边栏
39
+ 顶部导航栏
38
40
  ----------------------------------------------------------------------------- */
39
41
 
40
- .sidebar {
41
- width: 240px;
42
+ .topbar {
43
+ height: var(--topbar-height);
42
44
  background: var(--bg-secondary);
43
- border-right: 1px solid var(--border-color);
45
+ border-bottom: 1px solid var(--border-color);
44
46
  display: flex;
45
- flex-direction: column;
46
- padding: 20px 0;
47
+ align-items: center;
48
+ padding: 0 16px;
49
+ gap: 16px;
50
+ flex-shrink: 0;
47
51
  }
48
52
 
49
- .logo {
50
- padding: 0 20px 20px;
51
- border-bottom: 1px solid var(--border-color);
52
- margin-bottom: 20px;
53
+ .topbar-left {
54
+ display: flex;
55
+ align-items: center;
56
+ gap: 10px;
57
+ flex-shrink: 0;
53
58
  }
54
59
 
55
- .logo h1 {
56
- font-size: 1.2rem;
60
+ .topbar-logo {
61
+ font-size: 1rem;
62
+ font-weight: 700;
57
63
  color: var(--accent);
64
+ white-space: nowrap;
58
65
  }
59
66
 
60
- nav {
67
+ .topbar-version {
68
+ font-size: 0.75rem;
69
+ color: var(--text-muted);
70
+ white-space: nowrap;
71
+ }
72
+
73
+ .topbar-nav {
74
+ display: flex;
75
+ gap: 2px;
61
76
  flex: 1;
62
- padding: 0 10px;
63
77
  }
64
78
 
65
79
  .nav-btn {
66
- width: 100%;
67
- padding: 12px 16px;
68
- margin-bottom: 8px;
80
+ padding: 8px 14px;
69
81
  background: transparent;
70
82
  border: none;
71
- color: var(--text-secondary);
72
- text-align: left;
83
+ color: var(--text-muted);
73
84
  cursor: pointer;
74
- border-radius: 8px;
75
- font-size: 0.95rem;
85
+ border-radius: 6px;
86
+ font-size: 0.85rem;
76
87
  transition: all 0.2s;
77
- display: flex;
78
- align-items: center;
79
- gap: 10px;
88
+ white-space: nowrap;
80
89
  }
81
90
 
82
91
  .nav-btn:hover {
83
92
  background: var(--bg-hover);
93
+ color: var(--text-secondary);
84
94
  }
85
95
 
86
96
  .nav-btn.active {
@@ -88,29 +98,6 @@ nav {
88
98
  color: white;
89
99
  }
90
100
 
91
- .nav-btn .icon {
92
- font-size: 1.2rem;
93
- }
94
-
95
- .nav-section {
96
- padding: 16px 20px 6px;
97
- font-size: 0.7rem;
98
- font-weight: 600;
99
- color: var(--text-muted);
100
- text-transform: uppercase;
101
- letter-spacing: 1px;
102
- }
103
-
104
- .sidebar-footer {
105
- padding: 20px;
106
- border-top: 1px solid var(--border-color);
107
- }
108
-
109
- .version {
110
- color: var(--text-muted);
111
- font-size: 0.85rem;
112
- }
113
-
114
101
  /* -----------------------------------------------------------------------------
115
102
  主内容区
116
103
  ----------------------------------------------------------------------------- */
@@ -118,8 +105,8 @@ nav {
118
105
  .main-content {
119
106
  flex: 1;
120
107
  overflow-y: auto;
121
- min-height: 0; /* flex 子项必须显式允许收缩 */
122
- padding: 30px;
108
+ min-height: 0;
109
+ padding: 20px 28px;
123
110
  }
124
111
 
125
112
  .view {
@@ -128,40 +115,46 @@ nav {
128
115
 
129
116
  .view.active {
130
117
  display: block;
131
- overflow-y: auto;
132
- min-height: 0;
133
118
  }
134
119
 
135
120
  .view-header {
136
121
  display: flex;
137
122
  justify-content: space-between;
138
123
  align-items: center;
139
- margin-bottom: 24px;
124
+ margin-bottom: 16px;
125
+ gap: 12px;
140
126
  }
141
127
 
142
128
  .view-header h2 {
143
- font-size: 1.8rem;
129
+ font-size: 1.5rem;
144
130
  color: var(--text-secondary);
131
+ flex-shrink: 0;
145
132
  }
146
133
 
147
134
  .controls {
148
135
  display: flex;
149
- gap: 12px;
136
+ gap: 10px;
150
137
  align-items: center;
151
138
  }
152
139
 
153
140
  .controls input,
154
141
  .controls select {
155
- padding: 8px 12px;
142
+ padding: 7px 10px;
156
143
  background: var(--bg-secondary);
157
144
  border: 1px solid var(--border-color);
158
145
  color: var(--text-secondary);
159
146
  border-radius: 6px;
160
- font-size: 0.9rem;
147
+ font-size: 0.85rem;
161
148
  }
162
149
 
163
150
  .controls input {
164
- width: 250px;
151
+ width: 220px;
152
+ }
153
+
154
+ .controls input:focus,
155
+ .controls select:focus {
156
+ outline: none;
157
+ border-color: var(--accent);
165
158
  }
166
159
 
167
160
  /* -----------------------------------------------------------------------------
@@ -169,12 +162,13 @@ nav {
169
162
  ----------------------------------------------------------------------------- */
170
163
 
171
164
  .btn {
172
- padding: 8px 16px;
165
+ padding: 7px 14px;
173
166
  border: none;
174
167
  border-radius: 6px;
175
168
  cursor: pointer;
176
- font-size: 0.9rem;
169
+ font-size: 0.85rem;
177
170
  transition: all 0.2s;
171
+ white-space: nowrap;
178
172
  }
179
173
 
180
174
  .btn-primary {
@@ -207,8 +201,8 @@ nav {
207
201
  }
208
202
 
209
203
  .btn-sm {
210
- padding: 6px 12px;
211
- font-size: 0.85rem;
204
+ padding: 5px 10px;
205
+ font-size: 0.8rem;
212
206
  }
213
207
 
214
208
  /* -----------------------------------------------------------------------------
@@ -218,15 +212,16 @@ nav {
218
212
  .skills-grid {
219
213
  display: grid;
220
214
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
221
- gap: 16px;
215
+ gap: 14px;
222
216
  }
223
217
 
224
218
  .skill-card {
225
219
  background: var(--bg-secondary);
226
220
  border: 1px solid var(--border-color);
227
221
  border-radius: 10px;
228
- padding: 20px;
222
+ padding: 18px;
229
223
  transition: transform 0.2s, border-color 0.2s;
224
+ cursor: pointer;
230
225
  }
231
226
 
232
227
  .skill-card:hover {
@@ -235,20 +230,20 @@ nav {
235
230
  }
236
231
 
237
232
  .skill-card h3 {
238
- font-size: 1.1rem;
239
- margin-bottom: 8px;
233
+ font-size: 1.05rem;
234
+ margin-bottom: 6px;
240
235
  color: var(--accent);
241
236
  }
242
237
 
243
238
  .skill-card .skill-id {
244
239
  color: var(--text-muted);
245
- font-size: 0.85rem;
240
+ font-size: 0.8rem;
246
241
  margin-bottom: 8px;
247
242
  }
248
243
 
249
244
  .skill-card p {
250
245
  color: var(--text-secondary);
251
- font-size: 0.9rem;
246
+ font-size: 0.85rem;
252
247
  line-height: 1.5;
253
248
  margin-bottom: 12px;
254
249
  }
@@ -256,21 +251,28 @@ nav {
256
251
  .skill-card .platforms {
257
252
  display: flex;
258
253
  flex-wrap: wrap;
259
- gap: 6px;
254
+ gap: 5px;
260
255
  margin-bottom: 12px;
261
256
  }
262
257
 
263
258
  .platform-tag {
264
- padding: 4px 8px;
259
+ padding: 3px 7px;
265
260
  background: var(--bg-card);
266
261
  border-radius: 4px;
267
- font-size: 0.8rem;
262
+ font-size: 0.75rem;
268
263
  color: var(--text-muted);
269
264
  }
270
265
 
271
266
  .skill-card .actions {
272
267
  display: flex;
273
- gap: 8px;
268
+ gap: 6px;
269
+ pointer-events: auto;
270
+ }
271
+
272
+ /* Prevent card click when clicking action buttons */
273
+ .skill-card .actions .btn {
274
+ position: relative;
275
+ z-index: 1;
274
276
  }
275
277
 
276
278
  /* -----------------------------------------------------------------------------
@@ -280,21 +282,21 @@ nav {
280
282
  .platforms-list {
281
283
  display: flex;
282
284
  flex-direction: column;
283
- gap: 12px;
285
+ gap: 10px;
284
286
  }
285
287
 
286
288
  .platform-card {
287
289
  background: var(--bg-secondary);
288
290
  border: 1px solid var(--border-color);
289
291
  border-radius: 10px;
290
- padding: 20px;
292
+ padding: 18px;
291
293
  display: flex;
292
294
  justify-content: space-between;
293
295
  align-items: center;
294
296
  }
295
297
 
296
298
  .platform-card h3 {
297
- font-size: 1.1rem;
299
+ font-size: 1.05rem;
298
300
  color: var(--text-secondary);
299
301
  }
300
302
 
@@ -317,7 +319,7 @@ nav {
317
319
  ----------------------------------------------------------------------------- */
318
320
 
319
321
  .pagination {
320
- margin-top: 24px;
322
+ margin-top: 20px;
321
323
  display: flex;
322
324
  justify-content: center;
323
325
  align-items: center;
@@ -325,7 +327,7 @@ nav {
325
327
  }
326
328
 
327
329
  .pagination button {
328
- padding: 8px 12px;
330
+ padding: 7px 12px;
329
331
  background: var(--bg-secondary);
330
332
  border: 1px solid var(--border-color);
331
333
  color: var(--text-secondary);
@@ -344,64 +346,113 @@ nav {
344
346
 
345
347
  .pagination .page-info {
346
348
  color: var(--text-muted);
349
+ font-size: 0.85rem;
347
350
  }
348
351
 
349
352
  /* -----------------------------------------------------------------------------
350
- 模态框
353
+ Skill 详情视图
351
354
  ----------------------------------------------------------------------------- */
352
355
 
353
- .modal {
354
- position: fixed;
355
- top: 0;
356
- left: 0;
357
- width: 100%;
358
- height: 100%;
359
- background: rgba(0, 0, 0, 0.7);
356
+ .skill-detail {
357
+ background: var(--bg-secondary);
358
+ border: 1px solid var(--border-color);
359
+ border-radius: 12px;
360
+ padding: 28px;
361
+ }
362
+
363
+ .skill-detail h2 {
364
+ font-size: 1.6rem;
365
+ color: var(--accent);
366
+ margin-bottom: 4px;
367
+ }
368
+
369
+ .skill-detail .detail-name {
370
+ font-size: 0.9rem;
371
+ color: var(--text-muted);
372
+ margin-bottom: 16px;
373
+ }
374
+
375
+ .skill-detail .detail-section {
376
+ margin-bottom: 20px;
377
+ padding-bottom: 16px;
378
+ border-bottom: 1px solid var(--border-color);
379
+ }
380
+
381
+ .skill-detail .detail-section:last-child {
382
+ border-bottom: none;
383
+ margin-bottom: 0;
384
+ padding-bottom: 0;
385
+ }
386
+
387
+ .skill-detail .detail-section h3 {
388
+ font-size: 1rem;
389
+ color: var(--text-secondary);
390
+ margin-bottom: 10px;
391
+ }
392
+
393
+ .skill-detail .detail-row {
360
394
  display: flex;
361
- justify-content: center;
362
- align-items: center;
363
- z-index: 1000;
395
+ gap: 8px;
396
+ margin-bottom: 8px;
397
+ font-size: 0.9rem;
364
398
  }
365
399
 
366
- .modal.hidden {
367
- display: none;
400
+ .skill-detail .detail-row strong {
401
+ color: var(--text-muted);
402
+ min-width: 100px;
403
+ flex-shrink: 0;
368
404
  }
369
405
 
370
- .modal-content {
371
- background: var(--bg-secondary);
372
- border: 1px solid var(--border-color);
373
- border-radius: 12px;
374
- padding: 30px;
375
- max-width: 600px;
376
- width: 90%;
377
- max-height: 80vh;
406
+ .skill-detail .detail-row a {
407
+ color: var(--accent);
408
+ text-decoration: none;
409
+ }
410
+
411
+ .skill-detail .detail-row a:hover {
412
+ text-decoration: underline;
413
+ }
414
+
415
+ .skill-detail .platform-tags {
416
+ display: flex;
417
+ flex-wrap: wrap;
418
+ gap: 6px;
419
+ }
420
+
421
+ .skill-detail .version-list {
422
+ display: flex;
423
+ flex-direction: column;
424
+ gap: 4px;
425
+ max-height: 200px;
378
426
  overflow-y: auto;
379
- position: relative;
380
427
  }
381
428
 
382
- .modal-close {
383
- position: absolute;
384
- top: 15px;
385
- right: 15px;
386
- background: none;
387
- border: none;
429
+ .skill-detail .version-item {
430
+ display: flex;
431
+ justify-content: space-between;
432
+ padding: 6px 10px;
433
+ background: var(--bg-primary);
434
+ border-radius: 4px;
435
+ font-size: 0.85rem;
388
436
  color: var(--text-secondary);
389
- font-size: 1.5rem;
390
- cursor: pointer;
391
437
  }
392
438
 
393
- .modal-content h2 {
439
+ .skill-detail .version-item .version-latest {
394
440
  color: var(--accent);
395
- margin-bottom: 16px;
441
+ font-size: 0.75rem;
442
+ margin-left: 6px;
396
443
  }
397
444
 
398
- .modal-content .detail-row {
399
- margin-bottom: 12px;
445
+ .skill-detail .detail-actions {
446
+ display: flex;
447
+ gap: 10px;
448
+ margin-top: 16px;
449
+ flex-wrap: wrap;
400
450
  }
401
451
 
402
- .modal-content .detail-row strong {
403
- color: var(--text-muted);
404
- margin-right: 8px;
452
+ .skill-detail .description-text {
453
+ font-size: 0.9rem;
454
+ color: var(--text-secondary);
455
+ line-height: 1.6;
405
456
  }
406
457
 
407
458
  /* -----------------------------------------------------------------------------
@@ -410,9 +461,9 @@ nav {
410
461
 
411
462
  .toast {
412
463
  position: fixed;
413
- bottom: 30px;
414
- right: 30px;
415
- padding: 16px 24px;
464
+ bottom: 24px;
465
+ right: 24px;
466
+ padding: 14px 22px;
416
467
  background: var(--bg-card);
417
468
  border: 1px solid var(--border-color);
418
469
  border-radius: 8px;
@@ -420,6 +471,7 @@ nav {
420
471
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
421
472
  z-index: 2000;
422
473
  transition: all 0.3s;
474
+ font-size: 0.9rem;
423
475
  }
424
476
 
425
477
  .toast.hidden {
@@ -435,13 +487,50 @@ nav {
435
487
  }
436
488
 
437
489
  /* -----------------------------------------------------------------------------
438
- 加载动画
490
+ Admin 模态框
491
+ ----------------------------------------------------------------------------- */
492
+
493
+ .modal {
494
+ position: fixed;
495
+ top: 0; left: 0; right: 0; bottom: 0;
496
+ background: rgba(0, 0, 0, 0.6);
497
+ display: flex;
498
+ align-items: center;
499
+ justify-content: center;
500
+ z-index: 1000;
501
+ }
502
+
503
+ .modal.hidden {
504
+ display: none;
505
+ }
506
+
507
+ .modal-content {
508
+ background: var(--bg-secondary);
509
+ border: 1px solid var(--border-color);
510
+ border-radius: 12px;
511
+ padding: 28px;
512
+ min-width: 420px;
513
+ max-width: 560px;
514
+ max-height: 80vh;
515
+ overflow-y: auto;
516
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
517
+ }
518
+
519
+ .modal-content h2 {
520
+ font-size: 1.2rem;
521
+ color: var(--accent);
522
+ margin-bottom: 16px;
523
+ }
524
+
525
+ /* -----------------------------------------------------------------------------
526
+ 加载 & 空状态
439
527
  ----------------------------------------------------------------------------- */
440
528
 
441
529
  .loading {
442
530
  text-align: center;
443
531
  padding: 40px;
444
532
  color: var(--text-muted);
533
+ font-size: 0.9rem;
445
534
  }
446
535
 
447
536
  /* -----------------------------------------------------------------------------
@@ -452,81 +541,81 @@ nav {
452
541
  background: var(--bg-secondary);
453
542
  border: 1px solid var(--border-color);
454
543
  border-radius: 10px;
455
- padding: 24px;
456
- margin-bottom: 20px;
544
+ padding: 22px;
545
+ margin-bottom: 18px;
457
546
  }
458
547
 
459
548
  .help-section h3 {
460
549
  color: var(--accent);
461
- font-size: 1.15rem;
462
- margin-bottom: 12px;
550
+ font-size: 1.1rem;
551
+ margin-bottom: 10px;
463
552
  }
464
553
 
465
554
  .help-section > p {
466
555
  color: var(--text-secondary);
467
- font-size: 0.9rem;
468
- margin-bottom: 16px;
556
+ font-size: 0.85rem;
557
+ margin-bottom: 14px;
469
558
  }
470
559
 
471
560
  .help-steps {
472
561
  display: flex;
473
562
  flex-direction: column;
474
- gap: 16px;
563
+ gap: 14px;
475
564
  }
476
565
 
477
566
  .help-step {
478
567
  background: var(--bg-card);
479
568
  border-radius: 8px;
480
- padding: 16px;
569
+ padding: 14px;
481
570
  }
482
571
 
483
572
  .help-step > strong {
484
573
  color: var(--text-secondary);
485
- font-size: 0.95rem;
574
+ font-size: 0.9rem;
486
575
  display: block;
487
- margin-bottom: 8px;
576
+ margin-bottom: 6px;
488
577
  }
489
578
 
490
579
  .help-step ol {
491
580
  margin: 0;
492
581
  padding-left: 20px;
493
582
  color: var(--text-secondary);
494
- font-size: 0.9rem;
583
+ font-size: 0.85rem;
495
584
  line-height: 1.8;
496
585
  }
497
586
 
498
587
  .help-step pre {
499
588
  background: var(--bg-primary);
500
589
  color: var(--text-secondary);
501
- padding: 12px;
590
+ padding: 10px;
502
591
  border-radius: 6px;
503
- font-size: 0.85rem;
592
+ font-size: 0.82rem;
504
593
  overflow-x: auto;
505
- margin: 8px 0 0;
594
+ margin: 6px 0 0;
506
595
  }
507
596
 
508
597
  .help-note {
509
598
  color: #ffcc00;
510
- font-size: 0.85rem;
511
- margin-top: 8px;
599
+ font-size: 0.82rem;
600
+ margin-top: 6px;
512
601
  }
513
602
 
514
603
  .help-table {
515
604
  width: 100%;
516
605
  border-collapse: collapse;
517
- font-size: 0.9rem;
606
+ font-size: 0.85rem;
518
607
  }
519
608
 
520
609
  .help-table th {
521
610
  text-align: left;
522
- padding: 10px 12px;
611
+ padding: 8px 10px;
523
612
  background: var(--bg-card);
524
613
  color: var(--text-secondary);
525
614
  border-bottom: 2px solid var(--border-color);
526
615
  }
527
616
 
528
617
  .help-table td {
529
- padding: 10px 12px;
618
+ padding: 8px 10px;
530
619
  color: var(--text-secondary);
531
620
  border-bottom: 1px solid var(--border-color);
532
621
  vertical-align: top;
@@ -534,52 +623,31 @@ nav {
534
623
 
535
624
  .help-table code {
536
625
  background: var(--bg-card);
537
- padding: 2px 6px;
626
+ padding: 2px 5px;
538
627
  border-radius: 3px;
539
- font-size: 0.85rem;
628
+ font-size: 0.82rem;
540
629
  }
541
630
 
542
631
  .help-table tr:hover td {
543
632
  background: var(--bg-hover);
544
633
  }
545
634
 
546
- /* -----------------------------------------------------------------------------
547
- 滚动条
548
- ----------------------------------------------------------------------------- */
549
-
550
- ::-webkit-scrollbar {
551
- width: 8px;
552
- }
553
-
554
- ::-webkit-scrollbar-track {
555
- background: var(--bg-primary);
556
- }
557
-
558
- ::-webkit-scrollbar-thumb {
559
- background: var(--bg-card);
560
- border-radius: 4px;
561
- }
562
-
563
- ::-webkit-scrollbar-thumb:hover {
564
- background: var(--bg-hover);
565
- }
566
-
567
635
  /* -----------------------------------------------------------------------------
568
636
  Admin 视图
569
637
  ----------------------------------------------------------------------------- */
570
638
 
571
639
  .admin-stats {
572
640
  display: grid;
573
- grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
574
- gap: 14px;
575
- margin-bottom: 24px;
641
+ grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
642
+ gap: 12px;
643
+ margin-bottom: 22px;
576
644
  }
577
645
 
578
646
  .admin-stat-card {
579
647
  background: var(--bg-secondary);
580
648
  border: 1px solid var(--border-color);
581
649
  border-radius: 10px;
582
- padding: 20px;
650
+ padding: 18px;
583
651
  text-align: center;
584
652
  transition: border-color 0.2s;
585
653
  }
@@ -589,16 +657,16 @@ nav {
589
657
  }
590
658
 
591
659
  .admin-stat-value {
592
- font-size: 2rem;
660
+ font-size: 1.8rem;
593
661
  font-weight: 700;
594
662
  color: var(--accent);
595
663
  line-height: 1.2;
596
664
  }
597
665
 
598
666
  .admin-stat-label {
599
- font-size: 0.8rem;
667
+ font-size: 0.75rem;
600
668
  color: var(--text-muted);
601
- margin-top: 6px;
669
+ margin-top: 5px;
602
670
  text-transform: uppercase;
603
671
  letter-spacing: 0.5px;
604
672
  }
@@ -606,14 +674,14 @@ nav {
606
674
  .admin-skills-list {
607
675
  display: flex;
608
676
  flex-direction: column;
609
- gap: 8px;
677
+ gap: 6px;
610
678
  }
611
679
 
612
680
  .admin-skill-row {
613
681
  background: var(--bg-secondary);
614
682
  border: 1px solid var(--border-color);
615
683
  border-radius: 8px;
616
- padding: 14px 18px;
684
+ padding: 12px 16px;
617
685
  display: flex;
618
686
  align-items: center;
619
687
  justify-content: space-between;
@@ -631,95 +699,60 @@ nav {
631
699
  }
632
700
 
633
701
  .admin-skill-info h4 {
634
- font-size: 0.95rem;
702
+ font-size: 0.9rem;
635
703
  color: var(--accent);
636
704
  margin-bottom: 2px;
637
705
  }
638
706
 
639
707
  .admin-skill-info .admin-skill-meta {
640
- font-size: 0.8rem;
708
+ font-size: 0.78rem;
641
709
  color: var(--text-muted);
642
710
  }
643
711
 
644
712
  .admin-skill-info .admin-skill-desc {
645
- font-size: 0.82rem;
713
+ font-size: 0.8rem;
646
714
  color: var(--text-secondary);
647
715
  margin-top: 2px;
648
716
  white-space: nowrap;
649
717
  overflow: hidden;
650
718
  text-overflow: ellipsis;
651
- max-width: 300px;
719
+ max-width: 280px;
652
720
  }
653
721
 
654
722
  .admin-actions {
655
723
  display: flex;
656
- gap: 6px;
724
+ gap: 5px;
657
725
  flex-shrink: 0;
658
726
  }
659
727
 
660
728
  .admin-btn {
661
- padding: 5px 10px;
729
+ padding: 4px 9px;
662
730
  border: none;
663
731
  border-radius: 4px;
664
732
  cursor: pointer;
665
- font-size: 0.78rem;
733
+ font-size: 0.75rem;
666
734
  transition: all 0.2s;
667
735
  white-space: nowrap;
668
736
  }
669
737
 
670
- .admin-btn-deprecate {
671
- background: #664400;
672
- color: #ffcc00;
673
- }
674
-
675
- .admin-btn-deprecate:hover {
676
- background: #886600;
677
- }
738
+ .admin-btn-deprecate { background: #664400; color: #ffcc00; }
739
+ .admin-btn-deprecate:hover { background: #886600; }
678
740
 
679
- .admin-btn-unpublish {
680
- background: #661400;
681
- color: #ff6666;
682
- }
683
-
684
- .admin-btn-unpublish:hover {
685
- background: #881800;
686
- }
687
-
688
- .admin-btn-tag {
689
- background: var(--bg-card);
690
- color: var(--text-secondary);
691
- border: 1px solid var(--border-color);
692
- }
741
+ .admin-btn-unpublish { background: #661400; color: #ff6666; }
742
+ .admin-btn-unpublish:hover { background: #881800; }
693
743
 
694
- .admin-btn-tag:hover {
695
- background: var(--bg-hover);
696
- }
744
+ .admin-btn-tag { background: var(--bg-card); color: var(--text-secondary); border: 1px solid var(--border-color); }
745
+ .admin-btn-tag:hover { background: var(--bg-hover); }
697
746
 
698
- .admin-btn-owner {
699
- background: var(--bg-card);
700
- color: var(--text-secondary);
701
- border: 1px solid var(--border-color);
702
- }
747
+ .admin-btn-owner { background: var(--bg-card); color: var(--text-secondary); border: 1px solid var(--border-color); }
748
+ .admin-btn-owner:hover { background: var(--bg-hover); }
703
749
 
704
- .admin-btn-owner:hover {
705
- background: var(--bg-hover);
706
- }
707
-
708
- .admin-btn-access {
709
- background: var(--bg-card);
710
- color: var(--text-secondary);
711
- border: 1px solid var(--border-color);
712
- }
713
-
714
- .admin-btn-access:hover {
715
- background: var(--bg-hover);
716
- }
717
-
718
- /* Admin modal sections */
750
+ .admin-btn-access { background: var(--bg-card); color: var(--text-secondary); border: 1px solid var(--border-color); }
751
+ .admin-btn-access:hover { background: var(--bg-hover); }
719
752
 
720
753
  .admin-modal-section {
721
- margin-bottom: 20px;
722
- padding-bottom: 16px;
754
+ margin-bottom: 18px;
755
+ padding-bottom: 14px;
723
756
  border-bottom: 1px solid var(--border-color);
724
757
  }
725
758
 
@@ -730,33 +763,33 @@ nav {
730
763
  }
731
764
 
732
765
  .admin-modal-section h3 {
733
- font-size: 1rem;
766
+ font-size: 0.95rem;
734
767
  color: var(--text-secondary);
735
- margin-bottom: 12px;
768
+ margin-bottom: 10px;
736
769
  }
737
770
 
738
771
  .admin-input-group {
739
772
  display: flex;
740
773
  gap: 8px;
741
- margin-bottom: 10px;
774
+ margin-bottom: 8px;
742
775
  align-items: center;
743
776
  }
744
777
 
745
778
  .admin-input-group label {
746
- font-size: 0.85rem;
779
+ font-size: 0.82rem;
747
780
  color: var(--text-muted);
748
- min-width: 70px;
781
+ min-width: 65px;
749
782
  }
750
783
 
751
784
  .admin-input-group input,
752
785
  .admin-input-group select {
753
786
  flex: 1;
754
- padding: 7px 10px;
787
+ padding: 6px 9px;
755
788
  background: var(--bg-primary);
756
789
  border: 1px solid var(--border-color);
757
790
  color: var(--text-secondary);
758
791
  border-radius: 5px;
759
- font-size: 0.85rem;
792
+ font-size: 0.82rem;
760
793
  }
761
794
 
762
795
  .admin-input-group input:focus,
@@ -768,59 +801,71 @@ nav {
768
801
  .admin-tag-list {
769
802
  display: flex;
770
803
  flex-wrap: wrap;
771
- gap: 6px;
772
- margin: 8px 0;
804
+ gap: 5px;
805
+ margin: 6px 0;
773
806
  }
774
807
 
775
808
  .admin-tag-item {
776
- padding: 4px 10px;
809
+ padding: 3px 8px;
777
810
  background: var(--bg-card);
778
811
  border-radius: 4px;
779
- font-size: 0.82rem;
812
+ font-size: 0.8rem;
780
813
  color: var(--text-secondary);
781
814
  display: flex;
782
815
  align-items: center;
783
- gap: 6px;
816
+ gap: 5px;
784
817
  }
785
818
 
786
819
  .admin-tag-item .tag-version {
787
820
  color: var(--text-muted);
788
821
  }
789
822
 
790
- .admin-warning-text {
791
- color: #ffcc00;
792
- font-size: 0.82rem;
793
- margin-top: 4px;
794
- }
795
-
796
- .admin-danger-text {
797
- color: #ff6666;
798
- font-size: 0.82rem;
799
- margin-top: 4px;
800
- }
823
+ .admin-warning-text { color: #ffcc00; font-size: 0.8rem; margin-top: 4px; }
824
+ .admin-danger-text { color: #ff6666; font-size: 0.8rem; margin-top: 4px; }
801
825
 
802
826
  .admin-checkbox-group {
803
827
  display: flex;
804
828
  align-items: center;
805
829
  gap: 8px;
806
- margin-bottom: 10px;
830
+ margin-bottom: 8px;
807
831
  }
808
832
 
809
833
  .admin-checkbox-group label {
810
- font-size: 0.85rem;
834
+ font-size: 0.82rem;
811
835
  color: var(--text-secondary);
812
836
  }
813
837
 
814
838
  .admin-radio-group {
815
839
  display: flex;
816
- gap: 16px;
817
- margin-bottom: 10px;
840
+ gap: 14px;
841
+ margin-bottom: 8px;
818
842
  }
819
843
 
820
844
  .admin-radio-group label {
821
- font-size: 0.85rem;
845
+ font-size: 0.82rem;
822
846
  color: var(--text-secondary);
823
847
  display: flex;
824
848
  align-items: center;
825
- gap: 6px;
849
+ gap: 5px;
850
+ }
851
+
852
+ /* -----------------------------------------------------------------------------
853
+ 滚动条
854
+ ----------------------------------------------------------------------------- */
855
+
856
+ ::-webkit-scrollbar {
857
+ width: 8px;
858
+ }
859
+
860
+ ::-webkit-scrollbar-track {
861
+ background: var(--bg-primary);
862
+ }
863
+
864
+ ::-webkit-scrollbar-thumb {
865
+ background: var(--bg-card);
866
+ border-radius: 4px;
867
+ }
868
+
869
+ ::-webkit-scrollbar-thumb:hover {
870
+ background: var(--bg-hover);
826
871
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "itismyskillmarket",
3
- "version": "1.3.14",
3
+ "version": "1.3.16",
4
4
  "description": "Cross-platform skill manager for AI coding tools",
5
5
  "type": "module",
6
6
  "bin": {