itismyskillmarket 1.3.9 → 1.3.11
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 +61 -19
- package/gui/app.js +114 -1
- package/gui/index.html +12 -1
- package/gui/style.css +99 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -103,7 +103,37 @@ async function isSkillInstalled(skillId) {
|
|
|
103
103
|
// src/commands/npm.ts
|
|
104
104
|
import https from "https";
|
|
105
105
|
import { URL as URL2 } from "url";
|
|
106
|
-
|
|
106
|
+
|
|
107
|
+
// src/config.ts
|
|
108
|
+
var NPM_SCOPE = process.env.SKM_NPM_SCOPE || "@itismyskillmarket";
|
|
109
|
+
var NPM_SCOPE_FALLBACK = process.env.SKM_NPM_SCOPE_FALLBACK || "@wanxuchen";
|
|
110
|
+
var NPM_REGISTRY = process.env.SKM_NPM_REGISTRY || "https://registry.npmjs.org";
|
|
111
|
+
var DEFAULT_SCOPES = [
|
|
112
|
+
"@itismyskillmarket",
|
|
113
|
+
"@wanxuchen",
|
|
114
|
+
"@thisisskillmarket",
|
|
115
|
+
"@this-is-skillmarket",
|
|
116
|
+
"@skillmarket"
|
|
117
|
+
];
|
|
118
|
+
var SKILL_SCOPES = process.env.SKM_NPM_SCOPES ? process.env.SKM_NPM_SCOPES.split(",").map((s) => s.trim()).filter(Boolean) : DEFAULT_SCOPES;
|
|
119
|
+
var SKM_URL = process.env.SKM_URL || `https://www.npmjs.com/package/${NPM_SCOPE}`;
|
|
120
|
+
|
|
121
|
+
// src/commands/npm.ts
|
|
122
|
+
async function fetchNpmPackage(packageName, retries = 1) {
|
|
123
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
124
|
+
try {
|
|
125
|
+
const result = await fetchNpmPackageOnce(packageName);
|
|
126
|
+
if (result !== null) return result;
|
|
127
|
+
} catch {
|
|
128
|
+
if (attempt === retries) return null;
|
|
129
|
+
}
|
|
130
|
+
if (attempt < retries) {
|
|
131
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
async function fetchNpmPackageOnce(packageName) {
|
|
107
137
|
return new Promise((resolve2, reject) => {
|
|
108
138
|
const isScoped = packageName.startsWith("@");
|
|
109
139
|
let encodedName;
|
|
@@ -123,6 +153,11 @@ async function fetchNpmPackage(packageName) {
|
|
|
123
153
|
const url = new URL2(`https://registry.npmjs.org/${encodedName}`);
|
|
124
154
|
const req = https.get(url.toString(), { timeout: 1e4 }, (res) => {
|
|
125
155
|
let data = "";
|
|
156
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
157
|
+
res.resume();
|
|
158
|
+
resolve2(null);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
126
161
|
res.on("data", (chunk) => {
|
|
127
162
|
data += chunk;
|
|
128
163
|
});
|
|
@@ -146,18 +181,6 @@ async function fetchNpmPackage(packageName) {
|
|
|
146
181
|
});
|
|
147
182
|
});
|
|
148
183
|
}
|
|
149
|
-
var SKILL_SCOPES = [
|
|
150
|
-
"@wanxuchen",
|
|
151
|
-
// 原作者 scope
|
|
152
|
-
"@itismyskillmarket",
|
|
153
|
-
// 当前包名 scope
|
|
154
|
-
"@thisisskillmarket",
|
|
155
|
-
// 曾用 scope
|
|
156
|
-
"@this-is-skillmarket",
|
|
157
|
-
// 曾用 scope (带横线)
|
|
158
|
-
"@skillmarket"
|
|
159
|
-
// 通用 scope
|
|
160
|
-
];
|
|
161
184
|
function getPossiblePackageNames(skillId) {
|
|
162
185
|
if (skillId.startsWith("@")) {
|
|
163
186
|
return [skillId];
|
|
@@ -884,7 +907,7 @@ async function syncSkill(skillId) {
|
|
|
884
907
|
// src/commands/update.ts
|
|
885
908
|
async function updateSkill(skillId) {
|
|
886
909
|
if (skillId) {
|
|
887
|
-
const pkgInfo = await fetchNpmPackage(
|
|
910
|
+
const pkgInfo = await fetchNpmPackage(`${NPM_SCOPE}/${skillId}`);
|
|
888
911
|
if (pkgInfo) {
|
|
889
912
|
const latestVersion = pkgInfo["dist-tags"]?.latest;
|
|
890
913
|
console.log(`Updating ${skillId} to ${latestVersion}...`);
|
|
@@ -901,7 +924,7 @@ async function updateSkill(skillId) {
|
|
|
901
924
|
`);
|
|
902
925
|
let hasUpdates = false;
|
|
903
926
|
for (const skill of installed) {
|
|
904
|
-
const pkgInfo = await fetchNpmPackage(
|
|
927
|
+
const pkgInfo = await fetchNpmPackage(`${NPM_SCOPE_FALLBACK}/${skill.id}`);
|
|
905
928
|
if (pkgInfo) {
|
|
906
929
|
const latestVersion = pkgInfo["dist-tags"]?.latest;
|
|
907
930
|
if (latestVersion && latestVersion !== skill.version) {
|
|
@@ -1424,7 +1447,7 @@ async function publishSkill(skillName, options) {
|
|
|
1424
1447
|
}
|
|
1425
1448
|
console.log(`
|
|
1426
1449
|
\u2705 ${skillName} published successfully!`);
|
|
1427
|
-
console.log(` View at:
|
|
1450
|
+
console.log(` View at: ${SKM_URL}/${skillName}`);
|
|
1428
1451
|
}
|
|
1429
1452
|
|
|
1430
1453
|
// src/commands/verify.ts
|
|
@@ -1607,6 +1630,7 @@ API_ROUTES.GET["/api/skills"] = async (_req, res, url) => {
|
|
|
1607
1630
|
setCache(cacheKey, searchResult, 3e4);
|
|
1608
1631
|
}
|
|
1609
1632
|
const { packages, total } = searchResult;
|
|
1633
|
+
let fetchErrors = 0;
|
|
1610
1634
|
const skillDetails = await throttledMap(packages, async (pkgName) => {
|
|
1611
1635
|
try {
|
|
1612
1636
|
const pkgCacheKey = `pkg:${pkgName}`;
|
|
@@ -1615,7 +1639,10 @@ API_ROUTES.GET["/api/skills"] = async (_req, res, url) => {
|
|
|
1615
1639
|
info = await fetchNpmPackage(pkgName);
|
|
1616
1640
|
if (info) setCache(pkgCacheKey, info, 3e4);
|
|
1617
1641
|
}
|
|
1618
|
-
if (!info)
|
|
1642
|
+
if (!info) {
|
|
1643
|
+
fetchErrors++;
|
|
1644
|
+
return null;
|
|
1645
|
+
}
|
|
1619
1646
|
const latestVersion = info["dist-tags"]?.latest || "unknown";
|
|
1620
1647
|
const pkg = info.versions?.[latestVersion];
|
|
1621
1648
|
const meta = pkg?.skillmarket;
|
|
@@ -1631,12 +1658,13 @@ API_ROUTES.GET["/api/skills"] = async (_req, res, url) => {
|
|
|
1631
1658
|
repository: pkg?.repository?.url || ""
|
|
1632
1659
|
};
|
|
1633
1660
|
} catch {
|
|
1661
|
+
fetchErrors++;
|
|
1634
1662
|
return null;
|
|
1635
1663
|
}
|
|
1636
1664
|
}, 3);
|
|
1637
1665
|
const skills = skillDetails.filter(Boolean);
|
|
1638
1666
|
const totalPages = Math.ceil(total / limit) || 1;
|
|
1639
|
-
jsonResponse(res, 200, { skills, page, totalPages, total });
|
|
1667
|
+
jsonResponse(res, 200, { skills, page, totalPages, total, fetchErrors });
|
|
1640
1668
|
} catch (err) {
|
|
1641
1669
|
jsonResponse(res, 500, {
|
|
1642
1670
|
error: String(err),
|
|
@@ -1722,6 +1750,15 @@ API_ROUTES.GET["/api/skill-info"] = async (_req, res, url) => {
|
|
|
1722
1750
|
jsonResponse(res, 500, { error: String(err) });
|
|
1723
1751
|
}
|
|
1724
1752
|
};
|
|
1753
|
+
API_ROUTES.GET["/api/config"] = async (_req, res, _url) => {
|
|
1754
|
+
jsonResponse(res, 200, {
|
|
1755
|
+
npmScope: NPM_SCOPE,
|
|
1756
|
+
npmScopeFallback: NPM_SCOPE_FALLBACK,
|
|
1757
|
+
npmRegistry: NPM_REGISTRY,
|
|
1758
|
+
skmUrl: SKM_URL,
|
|
1759
|
+
skillScopes: SKILL_SCOPES
|
|
1760
|
+
});
|
|
1761
|
+
};
|
|
1725
1762
|
API_ROUTES.POST["/api/install"] = async (req, res, _url) => {
|
|
1726
1763
|
try {
|
|
1727
1764
|
const body = await parseBody(req);
|
|
@@ -1779,7 +1816,12 @@ function serveStaticFile(res, filePath) {
|
|
|
1779
1816
|
const content = readFileSync(filePath);
|
|
1780
1817
|
const ext = extname(filePath);
|
|
1781
1818
|
const mime = MIME_TYPES[ext] || "application/octet-stream";
|
|
1782
|
-
res.writeHead(200, {
|
|
1819
|
+
res.writeHead(200, {
|
|
1820
|
+
"Content-Type": mime,
|
|
1821
|
+
"Cache-Control": "no-cache, no-store, must-revalidate",
|
|
1822
|
+
"Pragma": "no-cache",
|
|
1823
|
+
"Expires": "0"
|
|
1824
|
+
});
|
|
1783
1825
|
res.end(content);
|
|
1784
1826
|
}
|
|
1785
1827
|
async function handleRequest(req, res) {
|
package/gui/app.js
CHANGED
|
@@ -68,6 +68,9 @@ function switchView(view) {
|
|
|
68
68
|
case 'platforms':
|
|
69
69
|
loadPlatforms();
|
|
70
70
|
break;
|
|
71
|
+
case 'help':
|
|
72
|
+
loadHelp();
|
|
73
|
+
break;
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
|
|
@@ -135,6 +138,7 @@ async function loadSkills() {
|
|
|
135
138
|
|
|
136
139
|
renderSkills(data.skills || data, container);
|
|
137
140
|
renderPagination(data.page, data.totalPages || 1);
|
|
141
|
+
renderFetchWarning(data.fetchErrors);
|
|
138
142
|
} catch (err) {
|
|
139
143
|
container.innerHTML = `<div class="loading">Error: ${err.message}</div>`;
|
|
140
144
|
}
|
|
@@ -191,6 +195,23 @@ function createSkillCard(skill, isInstalled) {
|
|
|
191
195
|
`;
|
|
192
196
|
}
|
|
193
197
|
|
|
198
|
+
// -----------------------------------------------------------------------------
|
|
199
|
+
// 网络错误提示
|
|
200
|
+
// -----------------------------------------------------------------------------
|
|
201
|
+
|
|
202
|
+
function renderFetchWarning(fetchErrors) {
|
|
203
|
+
const existing = document.getElementById('fetch-warning');
|
|
204
|
+
if (existing) existing.remove();
|
|
205
|
+
|
|
206
|
+
if (!fetchErrors || fetchErrors === 0) return;
|
|
207
|
+
|
|
208
|
+
const warning = document.createElement('div');
|
|
209
|
+
warning.id = 'fetch-warning';
|
|
210
|
+
warning.style.cssText = 'background: #664400; color: #ffcc00; padding: 8px 16px; border-radius: 6px; margin-bottom: 16px; font-size: 0.9rem;';
|
|
211
|
+
warning.textContent = `⚠ ${fetchErrors} skill(s) failed to load details from npm registry. Refresh to retry.`;
|
|
212
|
+
document.querySelector('.view-header').after(warning);
|
|
213
|
+
}
|
|
214
|
+
|
|
194
215
|
// -----------------------------------------------------------------------------
|
|
195
216
|
// 分页
|
|
196
217
|
// -----------------------------------------------------------------------------
|
|
@@ -266,9 +287,101 @@ function renderPlatforms(platforms, container) {
|
|
|
266
287
|
}
|
|
267
288
|
|
|
268
289
|
// -----------------------------------------------------------------------------
|
|
269
|
-
//
|
|
290
|
+
// Help 视图
|
|
270
291
|
// -----------------------------------------------------------------------------
|
|
271
292
|
|
|
293
|
+
async function loadHelp() {
|
|
294
|
+
const container = document.getElementById('help-content');
|
|
295
|
+
container.innerHTML = '<div class="loading">Loading...</div>';
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
const response = await fetch('/api/config');
|
|
299
|
+
const config = await response.json();
|
|
300
|
+
renderHelp(config, container);
|
|
301
|
+
} catch (err) {
|
|
302
|
+
container.innerHTML = `<div class="loading">Error: ${err.message}</div>`;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function renderHelp(config, container) {
|
|
307
|
+
const envVars = [
|
|
308
|
+
{ var: 'SKM_NPM_SCOPE', default: config.npmScope, desc: '主要 npm scope,用于发布/查找 skill' },
|
|
309
|
+
{ var: 'SKM_NPM_SCOPE_FALLBACK', default: config.npmScopeFallback, desc: '回退 scope(兼容旧安装)' },
|
|
310
|
+
{ var: 'SKM_NPM_SCOPES', default: config.skillScopes.join(', '), desc: '搜索时尝试的 scope 列表(逗号分隔)' },
|
|
311
|
+
{ var: 'SKM_NPM_REGISTRY', default: config.npmRegistry, desc: 'npm registry 地址' },
|
|
312
|
+
{ var: 'SKM_URL', default: config.skmUrl, desc: '个人链接前缀(publish 输出用)' },
|
|
313
|
+
];
|
|
314
|
+
|
|
315
|
+
container.innerHTML = `
|
|
316
|
+
<div class="help-section">
|
|
317
|
+
<h3>📖 GitHub Actions — Publish Skill</h3>
|
|
318
|
+
<p>在 GitHub 仓库页面操作:</p>
|
|
319
|
+
<div class="help-steps">
|
|
320
|
+
<div class="help-step">
|
|
321
|
+
<strong>发布主包(SkillMarket CLI)</strong>
|
|
322
|
+
<ol>
|
|
323
|
+
<li>GitHub → <strong>Releases</strong> → Create a new release</li>
|
|
324
|
+
<li>输入 Tag (如 v1.3.11),点击 Publish release</li>
|
|
325
|
+
<li>Action <code>Publish to npm</code> 自动触发 → 构建 → npm publish</li>
|
|
326
|
+
</ol>
|
|
327
|
+
</div>
|
|
328
|
+
<div class="help-step">
|
|
329
|
+
<strong>发布独立 Skill</strong>
|
|
330
|
+
<ol>
|
|
331
|
+
<li>GitHub → <strong>Actions</strong> → Publish Skill → Run workflow</li>
|
|
332
|
+
<li>输入 <code>skill_name</code>(skills/ 下的目录名)和可选 <code>version</code></li>
|
|
333
|
+
<li>自动执行:npm install → npm version → npm publish</li>
|
|
334
|
+
</ol>
|
|
335
|
+
<p class="help-note">⚠ 需要 GitHub Secrets 中配置 <code>NPM_TOKEN</code></p>
|
|
336
|
+
</div>
|
|
337
|
+
<div class="help-step">
|
|
338
|
+
<strong>本地发布</strong>
|
|
339
|
+
<pre>skm publish <skill-name>
|
|
340
|
+
skm publish <skill-name> --version 1.0.1</pre>
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
|
|
345
|
+
<div class="help-section">
|
|
346
|
+
<h3>⚙️ 环境变量配置</h3>
|
|
347
|
+
<p>设置以下环境变量可覆盖默认配置:</p>
|
|
348
|
+
<table class="help-table">
|
|
349
|
+
<thead>
|
|
350
|
+
<tr><th>变量</th><th>当前值</th><th>说明</th></tr>
|
|
351
|
+
</thead>
|
|
352
|
+
<tbody>
|
|
353
|
+
${envVars.map(v => `
|
|
354
|
+
<tr>
|
|
355
|
+
<td><code>${v.var}</code></td>
|
|
356
|
+
<td><code>${v.default}</code></td>
|
|
357
|
+
<td>${v.desc}</td>
|
|
358
|
+
</tr>
|
|
359
|
+
`).join('')}
|
|
360
|
+
</tbody>
|
|
361
|
+
</table>
|
|
362
|
+
</div>
|
|
363
|
+
|
|
364
|
+
<div class="help-section">
|
|
365
|
+
<h3>🔧 常用命令</h3>
|
|
366
|
+
<table class="help-table">
|
|
367
|
+
<thead><tr><th>命令</th><th>说明</th></tr></thead>
|
|
368
|
+
<tbody>
|
|
369
|
+
<tr><td><code>skm ls</code></td><td>列出可用 skills</td></tr>
|
|
370
|
+
<tr><td><code>skm ls --installed</code></td><td>列出已安装 skills</td></tr>
|
|
371
|
+
<tr><td><code>skm install <skill></code></td><td>安装 skill 到所有平台</td></tr>
|
|
372
|
+
<tr><td><code>skm install <skill> --platform opencode</code></td><td>安装到指定平台</td></tr>
|
|
373
|
+
<tr><td><code>skm uninstall <skill></code></td><td>卸载 skill</td></tr>
|
|
374
|
+
<tr><td><code>skm update <skill></code></td><td>更新 skill</td></tr>
|
|
375
|
+
<tr><td><code>skm update --all</code></td><td>更新所有 skills</td></tr>
|
|
376
|
+
<tr><td><code>skm platforms</code></td><td>查看可用平台</td></tr>
|
|
377
|
+
<tr><td><code>skm gui</code></td><td>启动图形界面</td></tr>
|
|
378
|
+
<tr><td><code>skm gui 18790</code></td><td>指定端口启动 GUI</td></tr>
|
|
379
|
+
</tbody>
|
|
380
|
+
</table>
|
|
381
|
+
</div>
|
|
382
|
+
`;
|
|
383
|
+
}
|
|
384
|
+
|
|
272
385
|
async function installSkill(skillId) {
|
|
273
386
|
try {
|
|
274
387
|
showToast(`Installing ${skillId}...`, 'info');
|
package/gui/index.html
CHANGED
|
@@ -23,9 +23,12 @@
|
|
|
23
23
|
<button class="nav-btn" data-view="platforms">
|
|
24
24
|
<span class="icon">💻</span> Platforms
|
|
25
25
|
</button>
|
|
26
|
+
<button class="nav-btn" data-view="help">
|
|
27
|
+
<span class="icon">📖</span> Help
|
|
28
|
+
</button>
|
|
26
29
|
</nav>
|
|
27
30
|
<div class="sidebar-footer">
|
|
28
|
-
<span class="version" id="gui-version">v1.3.
|
|
31
|
+
<span class="version" id="gui-version">v1.3.11</span>
|
|
29
32
|
</div>
|
|
30
33
|
</aside>
|
|
31
34
|
|
|
@@ -65,6 +68,14 @@
|
|
|
65
68
|
</div>
|
|
66
69
|
<div id="platforms-list" class="platforms-list"></div>
|
|
67
70
|
</div>
|
|
71
|
+
|
|
72
|
+
<!-- Help 视图 -->
|
|
73
|
+
<div id="view-help" class="view">
|
|
74
|
+
<div class="view-header">
|
|
75
|
+
<h2>Help & Configuration</h2>
|
|
76
|
+
</div>
|
|
77
|
+
<div id="help-content"></div>
|
|
78
|
+
</div>
|
|
68
79
|
</main>
|
|
69
80
|
|
|
70
81
|
<!-- Skill 详情模态框 -->
|
package/gui/style.css
CHANGED
|
@@ -435,6 +435,105 @@ nav {
|
|
|
435
435
|
color: var(--text-muted);
|
|
436
436
|
}
|
|
437
437
|
|
|
438
|
+
/* -----------------------------------------------------------------------------
|
|
439
|
+
Help 视图
|
|
440
|
+
----------------------------------------------------------------------------- */
|
|
441
|
+
|
|
442
|
+
.help-section {
|
|
443
|
+
background: var(--bg-secondary);
|
|
444
|
+
border: 1px solid var(--border-color);
|
|
445
|
+
border-radius: 10px;
|
|
446
|
+
padding: 24px;
|
|
447
|
+
margin-bottom: 20px;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.help-section h3 {
|
|
451
|
+
color: var(--accent);
|
|
452
|
+
font-size: 1.15rem;
|
|
453
|
+
margin-bottom: 12px;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.help-section > p {
|
|
457
|
+
color: var(--text-secondary);
|
|
458
|
+
font-size: 0.9rem;
|
|
459
|
+
margin-bottom: 16px;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.help-steps {
|
|
463
|
+
display: flex;
|
|
464
|
+
flex-direction: column;
|
|
465
|
+
gap: 16px;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.help-step {
|
|
469
|
+
background: var(--bg-card);
|
|
470
|
+
border-radius: 8px;
|
|
471
|
+
padding: 16px;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.help-step > strong {
|
|
475
|
+
color: var(--text-secondary);
|
|
476
|
+
font-size: 0.95rem;
|
|
477
|
+
display: block;
|
|
478
|
+
margin-bottom: 8px;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.help-step ol {
|
|
482
|
+
margin: 0;
|
|
483
|
+
padding-left: 20px;
|
|
484
|
+
color: var(--text-secondary);
|
|
485
|
+
font-size: 0.9rem;
|
|
486
|
+
line-height: 1.8;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.help-step pre {
|
|
490
|
+
background: var(--bg-primary);
|
|
491
|
+
color: var(--text-secondary);
|
|
492
|
+
padding: 12px;
|
|
493
|
+
border-radius: 6px;
|
|
494
|
+
font-size: 0.85rem;
|
|
495
|
+
overflow-x: auto;
|
|
496
|
+
margin: 8px 0 0;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.help-note {
|
|
500
|
+
color: #ffcc00;
|
|
501
|
+
font-size: 0.85rem;
|
|
502
|
+
margin-top: 8px;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.help-table {
|
|
506
|
+
width: 100%;
|
|
507
|
+
border-collapse: collapse;
|
|
508
|
+
font-size: 0.9rem;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.help-table th {
|
|
512
|
+
text-align: left;
|
|
513
|
+
padding: 10px 12px;
|
|
514
|
+
background: var(--bg-card);
|
|
515
|
+
color: var(--text-secondary);
|
|
516
|
+
border-bottom: 2px solid var(--border-color);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
.help-table td {
|
|
520
|
+
padding: 10px 12px;
|
|
521
|
+
color: var(--text-secondary);
|
|
522
|
+
border-bottom: 1px solid var(--border-color);
|
|
523
|
+
vertical-align: top;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.help-table code {
|
|
527
|
+
background: var(--bg-card);
|
|
528
|
+
padding: 2px 6px;
|
|
529
|
+
border-radius: 3px;
|
|
530
|
+
font-size: 0.85rem;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.help-table tr:hover td {
|
|
534
|
+
background: var(--bg-hover);
|
|
535
|
+
}
|
|
536
|
+
|
|
438
537
|
/* -----------------------------------------------------------------------------
|
|
439
538
|
滚动条
|
|
440
539
|
----------------------------------------------------------------------------- */
|