itismyskillmarket 1.3.15 → 1.3.17

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