Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aebdfaf926 |
@ -4,13 +4,13 @@
|
||||
"summary": "技能目录站:把对外发布的技能生成一个静态站(能干什么 / 先装什么 / 要配什么 / 依赖谁 / 一条安装命令),生成物不手改页面。",
|
||||
"tier": 1,
|
||||
"status": "stable",
|
||||
"version": "f7a6763",
|
||||
"version": "5067fdf",
|
||||
"source": {
|
||||
"path": "skills/skill-catalog",
|
||||
"commit": "f7a6763",
|
||||
"describe": "f7a6763",
|
||||
"commit": "5067fdf",
|
||||
"describe": "5067fdf",
|
||||
"dirty": false,
|
||||
"packed_at": "2026-10-02T01:30:47.222Z"
|
||||
"packed_at": "2026-10-02T05:54:51.818Z"
|
||||
},
|
||||
"platforms": [
|
||||
"darwin",
|
||||
@ -163,8 +163,8 @@
|
||||
"files": [
|
||||
{
|
||||
"path": "REFERENCE.md",
|
||||
"bytes": 7578,
|
||||
"sha256": "759ac4d91bed48ae"
|
||||
"bytes": 8622,
|
||||
"sha256": "1f02f50d78756cb2"
|
||||
},
|
||||
{
|
||||
"path": "SKILL.md",
|
||||
@ -174,12 +174,12 @@
|
||||
{
|
||||
"path": "VERSION",
|
||||
"bytes": 6,
|
||||
"sha256": "80447316529fc620"
|
||||
"sha256": "297b50f08386d352"
|
||||
},
|
||||
{
|
||||
"path": "assets/app.js",
|
||||
"bytes": 4601,
|
||||
"sha256": "78a507c30e6c9cf9"
|
||||
"bytes": 5134,
|
||||
"sha256": "585048deddd24fa1"
|
||||
},
|
||||
{
|
||||
"path": "assets/favicon.svg",
|
||||
@ -188,8 +188,8 @@
|
||||
},
|
||||
{
|
||||
"path": "assets/style.css",
|
||||
"bytes": 16997,
|
||||
"sha256": "dfe35d419987cc81"
|
||||
"bytes": 17877,
|
||||
"sha256": "d2a02fd09ea7ffa7"
|
||||
},
|
||||
{
|
||||
"path": "contract.lock.json",
|
||||
@ -198,8 +198,8 @@
|
||||
},
|
||||
{
|
||||
"path": "interface.json",
|
||||
"bytes": 2780,
|
||||
"sha256": "1417b2ba9c473218"
|
||||
"bytes": 2813,
|
||||
"sha256": "85140b7a2154ada6"
|
||||
},
|
||||
{
|
||||
"path": "scripts/setup-hosting.sh",
|
||||
@ -208,8 +208,8 @@
|
||||
},
|
||||
{
|
||||
"path": "scripts/skill-catalog.mjs",
|
||||
"bytes": 37551,
|
||||
"sha256": "7bdd12c1aeea89c5"
|
||||
"bytes": 38433,
|
||||
"sha256": "6faf465b64cc520a"
|
||||
}
|
||||
],
|
||||
"leak_scan": {
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
## 安装
|
||||
|
||||
```sh
|
||||
pi install git:gitea.vhkd.top/pi-hub/skill-catalog.git@v0.1.1
|
||||
pi install git:gitea.vhkd.top/pi-hub/skill-catalog.git@v0.1.2
|
||||
```
|
||||
|
||||
装完 `pi-skill list` 能看到 `skill-catalog`,`pi-skill skill-catalog check` 会告诉还缺什么。
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "skill-catalog",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"description": "技能目录站:把对外发布的技能生成一个静态站(能干什么 / 先装什么 / 要配什么 / 依赖谁 / 一条安装命令),生成物不手改页面。"
|
||||
}
|
||||
|
||||
@ -109,6 +109,19 @@ DNS 加一条 `pi.vhkd.top A 47.243.111.1`(或先不加 DNS,用 IP/路径访
|
||||
- **双主题**:明暗各一套变量;`<head>` 里的引导脚本按「上次选择 → 系统偏好」在首帧前设好 `data-theme`,
|
||||
导航右侧按钮切换并记住(`localStorage: pi-hub-theme`)。新组件必须两套主题下都看过。
|
||||
|
||||
## 4.3 产品级检查单(每次改页面前过一遍)
|
||||
|
||||
- **没有点不动的按钮**:静态信息(依赖名、平台、层级说明)用 `.meta-chip`(无边框、不悬停),
|
||||
可点的才用边框/底色;不确定能不能点的,就别做成按钮的形状。
|
||||
- **首屏动作要有意义**:安装示例固定取底座里最基础的一个(不是数组第一条),并明确标注「安装示例」。
|
||||
- **键盘与小屏**:`/` 聚焦搜索、`Esc` 清空;筛选写进 URL(`#core,tool`)可分享可回退;
|
||||
移动端只收起文字链接,**主题开关与搜索必须留**。
|
||||
- **站点基础设施**:favicon、`theme-color`(明/暗两版)、OG/Twitter、`robots.txt`、`sitemap.xml`、canonical
|
||||
—— 都由 `build` 生成,别手写。
|
||||
- **生成物不进版本库**:`dist/` 已 .gitignore;要发布就跑 `build` + `deploy`。
|
||||
- **无障碍最低线**:交互元素有 `aria-label`/`role`,状态用 `aria-pressed`;`focus-visible` 可见;
|
||||
`prefers-reduced-motion` 关动效。
|
||||
|
||||
## 5. 想加东西往哪加
|
||||
|
||||
- **新页面/新字段**:数据来自技能声明,先想清楚它是不是「声明里已经有的事实」——是就渲染,
|
||||
|
||||
@ -1 +1 @@
|
||||
0.1.1
|
||||
0.1.2
|
||||
|
||||
@ -28,6 +28,8 @@
|
||||
}
|
||||
if (count) count.textContent = String(shown);
|
||||
if (empty) empty.hidden = shown !== 0;
|
||||
const clear = document.getElementById('clear-filters');
|
||||
if (clear) clear.hidden = activeLayers.size + activeRequires.size === 0;
|
||||
};
|
||||
|
||||
search?.addEventListener('input', apply);
|
||||
@ -118,5 +120,17 @@
|
||||
readHash();
|
||||
if (activeLayers.size) apply();
|
||||
|
||||
document.getElementById('clear-filters')?.addEventListener('click', () => {
|
||||
activeLayers.clear();
|
||||
activeRequires.clear();
|
||||
for (const chip of [...layerFilters, ...reqFilters]) {
|
||||
chip.classList.remove('active');
|
||||
chip.setAttribute('aria-pressed', 'false');
|
||||
}
|
||||
if (search) search.value = '';
|
||||
history.replaceState(null, '', location.pathname);
|
||||
apply();
|
||||
});
|
||||
|
||||
apply();
|
||||
})();
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
--ink: #1b1e24;
|
||||
--ink-2: #454b56;
|
||||
--muted: #767c88;
|
||||
--faint: #a3a8b2;
|
||||
--faint: #6f7683; /* 正文小字也要过 AA 对比度 */
|
||||
--line: #e6e6e2;
|
||||
--line-2: #d2d2cd;
|
||||
--accent: #2f5bd7;
|
||||
@ -57,7 +57,7 @@
|
||||
--ink: #eceef2;
|
||||
--ink-2: #c2c7d0;
|
||||
--muted: #8b929e;
|
||||
--faint: #6b7280;
|
||||
--faint: #8a919e;
|
||||
--line: #26292e;
|
||||
--line-2: #383c43;
|
||||
--accent: #93aaff;
|
||||
@ -98,13 +98,21 @@ h1 { font-size: clamp(26px, 3.2vw, var(--t-2xl)); line-height: 1.1; letter-spaci
|
||||
|
||||
/* 标签 / 导航 / 元信息:等宽小字,字距拉开,全大写。 */
|
||||
.label, .nav a, .nav-brand, .ver, .stat, .count, .panel-head, .badge, .rel, .eyebrow,
|
||||
.card-foot, .foot h4, .foot-line, .cmd-step {
|
||||
.foot h4, .foot-line, .cmd-step {
|
||||
font-family: var(--mono);
|
||||
font-size: 11.5px;
|
||||
font-size: var(--t-xs);
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* 卡片脚注:等宽小字,但保持原样大小写(命令名不该被大写化) */
|
||||
.card-foot, .used {
|
||||
font-family: var(--mono);
|
||||
font-size: var(--t-xs);
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
a { color: inherit; text-decoration: none; }
|
||||
a:hover { color: var(--accent); }
|
||||
code { font-family: var(--mono); font-size: 0.86em; letter-spacing: 0; }
|
||||
@ -117,7 +125,7 @@ code { font-family: var(--mono); font-size: 0.86em; letter-spacing: 0; }
|
||||
.nav {
|
||||
position: sticky; top: 0; z-index: 10;
|
||||
display: flex; align-items: center; justify-content: space-between; gap: var(--s-5);
|
||||
padding: var(--s-4) 0; margin-bottom: var(--s-8);
|
||||
padding: var(--s-3) 0; margin-bottom: var(--s-6);
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: color-mix(in srgb, var(--bg) 88%, transparent);
|
||||
backdrop-filter: blur(10px);
|
||||
@ -145,8 +153,8 @@ code { font-family: var(--mono); font-size: 0.86em; letter-spacing: 0; }
|
||||
[data-theme="dark"] .icon-sun { display: none; }
|
||||
|
||||
/* ── 主视觉 ───────────────────────────────────────── */
|
||||
.masthead { margin-bottom: var(--s-7); }
|
||||
.eyebrow { color: var(--faint); margin: 0 0 20px; }
|
||||
.masthead { margin-bottom: var(--s-6); }
|
||||
.eyebrow { color: var(--faint); margin: 0 0 var(--s-4); }
|
||||
.lede { color: var(--ink-2); font-size: var(--t-md); margin: 0 0 var(--s-5); max-width: var(--measure-tight); }
|
||||
|
||||
.cta {
|
||||
@ -210,33 +218,41 @@ button.chip:hover { color: var(--ink); border-color: var(--line-2); }
|
||||
button.chip.active { color: var(--accent); border-color: currentColor; }
|
||||
.chip-warn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
|
||||
|
||||
/* ── 分区面板 ─────────────────────────────────────── */
|
||||
.panel { border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); margin-bottom: var(--s-5); overflow: hidden; }
|
||||
.panel-head {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 11px 16px; border-bottom: 1px solid var(--line);
|
||||
color: var(--ink-2); background: var(--surface-2);
|
||||
/* ── 分区与卡片 ───────────────────────────────────── */
|
||||
.layer-section { margin: 0 0 var(--s-7); }
|
||||
.layer-head {
|
||||
display: flex; align-items: baseline; gap: var(--s-2);
|
||||
padding-bottom: var(--s-2); margin-bottom: var(--s-4);
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
.panel-count { color: var(--faint); }
|
||||
.panel-hint { margin-left: auto; color: var(--faint); letter-spacing: .04em; text-transform: none; font-family: var(--kai); font-size: 12.5px; }
|
||||
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(292px, 1fr)); }
|
||||
.layer-section .label { color: var(--ink-2); }
|
||||
.layer-count { color: var(--faint); }
|
||||
.layer-why {
|
||||
margin-left: auto; color: var(--faint); font-size: var(--t-sm);
|
||||
font-family: var(--kai); letter-spacing: 0; text-transform: none;
|
||||
}
|
||||
|
||||
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: var(--s-4); }
|
||||
.card {
|
||||
display: flex; flex-direction: column; gap: var(--s-2);
|
||||
padding: var(--s-4) var(--s-5) var(--s-4);
|
||||
border-right: 1px solid var(--line); border-bottom: 1px solid var(--line);
|
||||
transition: background .15s;
|
||||
border: 1px solid var(--line); border-radius: var(--radius);
|
||||
background: var(--surface);
|
||||
transition: border-color .15s, background .15s, transform .15s;
|
||||
}
|
||||
.card:hover { background: var(--surface-2); }
|
||||
.card-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
|
||||
.card:hover { border-color: var(--line-2); background: var(--surface-2); transform: translateY(-1px); }
|
||||
.card-top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-2); }
|
||||
.card h3 { margin: 0; font-family: var(--mono); font-size: 15px; letter-spacing: 0; }
|
||||
.card .ver { color: var(--faint); }
|
||||
.card .summary {
|
||||
margin: 0; color: var(--ink-2); font-size: var(--t-sm); line-height: 1.8;
|
||||
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
|
||||
}
|
||||
.card-foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; color: var(--faint); margin-top: auto; padding-top: 4px; }
|
||||
.card-foot {
|
||||
display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap;
|
||||
color: var(--faint); margin-top: auto; padding-top: var(--s-2);
|
||||
}
|
||||
.card-layer { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); letter-spacing: .08em; }
|
||||
.card-foot .chip { padding: 1px 8px; font-size: 11px; letter-spacing: .04em; text-transform: none; }
|
||||
.used { margin-left: auto; }
|
||||
|
||||
/* ── 详情页 ───────────────────────────────────────── */
|
||||
@ -356,3 +372,16 @@ button.chip.active { color: var(--accent); border-color: currentColor; }
|
||||
.nav-links { gap: 0; }
|
||||
.nav-links a { display: none; }
|
||||
}
|
||||
|
||||
/* 筛选行:先说清是什么,再给「清除」出口 */
|
||||
.chipbar { align-items: center; }
|
||||
.chip-label {
|
||||
font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .12em;
|
||||
text-transform: uppercase; color: var(--faint); margin-right: var(--s-1);
|
||||
}
|
||||
.chip.clear { color: var(--muted); border-style: dashed; }
|
||||
.chip.clear:hover { color: var(--ink); border-style: solid; }
|
||||
|
||||
/* 详情页:什么时候用(收集了就必须展示) */
|
||||
.use-when { margin: 0 0 var(--s-4); color: var(--muted); font-size: var(--t-sm); max-width: var(--measure); }
|
||||
.use-when .label { color: var(--faint); margin-right: var(--s-2); }
|
||||
|
||||
@ -102,5 +102,6 @@
|
||||
],
|
||||
"publish": {
|
||||
"package_repo": "https://gitea.vhkd.top/pi-hub/skill-catalog.git"
|
||||
}
|
||||
},
|
||||
"license": "GPL-3.0-or-later"
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ import { resolveConfig, agentDirOf } from '../../skill-interface/scripts/skill-c
|
||||
import { scanLeaks } from '../../skill-interface/scripts/skill-pack.mjs';
|
||||
|
||||
const SKILL = 'skill-catalog';
|
||||
const BRAND = { name: 'Pi Hub', tagline: '为 Pi 装上你要的能力', scope: '技能 · 扩展 · 主题 · 提示模板' };
|
||||
const BRAND = { name: 'Pi Hub', headline: 'Pi 能力目录', scope: '技能 · 扩展 · 主题 · 提示模板' };
|
||||
const SCOPE_HTML = '<span class="nowrap">技能 · 扩展 · 主题 · 提示模板</span>';
|
||||
// 字体三套(都 OFL,可自托管):正文楷(读者喜欢的)+ 标题衬线(中文用思源宋)+
|
||||
// 拉丁/等宽 PragmataPro(本机 local(),商用字体不随站分发)。
|
||||
@ -122,6 +122,7 @@ export function collectCatalog(repo = repoRoot()) {
|
||||
return {
|
||||
name: skill.name,
|
||||
status: declared.status ?? 'stable',
|
||||
license: declared.license ?? '',
|
||||
declaredLayer: declared.catalog?.layer ?? null,
|
||||
summary: declared.summary ?? '',
|
||||
useWhen: declared.useWhen ?? '',
|
||||
@ -231,7 +232,7 @@ function siteFoot(depth = 0) {
|
||||
<li>拉丁 Pragmata Pro(读者本机)</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
<p class="foot-line">${BRAND.name} · 由 skill-catalog 生成 · 条目与技能声明一致</p>
|
||||
<p class="foot-line">${BRAND.name} · 更新于 ${escapeHtml(new Date().toISOString().slice(0, 10))} · 由 skill-catalog 从技能声明生成</p>
|
||||
</footer>`;
|
||||
}
|
||||
|
||||
@ -290,7 +291,9 @@ function cardOf(entry) {
|
||||
].join('');
|
||||
const used = entry.usedByCount ? `<span class="used">被 ${entry.usedByCount} 个技能使用</span>` : '';
|
||||
return `<a class="card" href="skills/${escapeHtml(entry.name)}.html" data-layer="${entry.layer}"
|
||||
data-search="${escapeHtml(`${entry.name} ${entry.summary} ${entry.useWhen} ${entry.layer} ${entry.commands.map(c => c.name).join(' ')}`)}">
|
||||
data-search="${escapeHtml(
|
||||
`${entry.name} ${entry.summary} ${entry.useWhen} ${entry.layer} ${entry.commands.map(c => c.name).join(' ')} ${entry.requires.map(r => r.name).join(' ')}`,
|
||||
)}">
|
||||
<div class="card-top"><h3>${escapeHtml(entry.name)}</h3><span class="ver">v${escapeHtml(entry.version)}</span></div>
|
||||
<p class="summary">${escapeHtml(entry.summary)}</p>
|
||||
<div class="card-foot">
|
||||
@ -385,9 +388,10 @@ ${entry.usesPublic.length || entry.usedBy.length ? '' : '<li class="muted">独
|
||||
<a class="back" href="../index.html">← 全部技能</a>
|
||||
<div class="title-row">
|
||||
<h1>${escapeHtml(entry.name)}</h1>
|
||||
<span class="ver">v${escapeHtml(entry.version)}</span>
|
||||
${entry.version ? `<span class="ver">v${escapeHtml(entry.version)}</span>` : '<span class="ver">未标版本</span>'}
|
||||
</div>
|
||||
<p class="lede">${escapeHtml(entry.summary)}</p>
|
||||
${entry.useWhen ? `<p class="use-when"><span class="label">什么时候用</span>${escapeHtml(entry.useWhen)}</p>` : ''}
|
||||
<div class="chips">
|
||||
<span class="chip layer-${entry.layer}">${LAYER_LABEL[entry.layer]}</span>
|
||||
<span class="meta-chip">${LAYER_HINT[entry.layer]}</span>
|
||||
@ -404,10 +408,13 @@ ${entry.usesPublic.length || entry.usedBy.length ? '' : '<li class="muted">独
|
||||
</article>
|
||||
<aside>
|
||||
<div class="panel side"><div class="panel-head"><span class="label">关系</span></div><div class="panel-body">${relations}</div></div>
|
||||
<div class="panel side"><div class="panel-head"><span class="label">许可</span></div><div class="panel-body">
|
||||
<p class="muted small">${entry.license ? escapeHtml(entry.license) : '未声明许可(以仓库为准)'}</p>
|
||||
</div></div>
|
||||
<div class="panel side"><div class="panel-head"><span class="label">仓库</span></div><div class="panel-body">
|
||||
<ul class="repos">
|
||||
${entry.repos.package ? `<li><span class="rel">Pi 包</span><a href="${escapeHtml(entry.repos.package)}">${escapeHtml(entry.repos.package.replace(/^https?:\/\//, ''))}</a></li>` : ''}
|
||||
${entry.repos.cli ? `<li><span class="rel">CLI</span><a href="${escapeHtml(entry.repos.cli)}">${escapeHtml(entry.repos.cli.replace(/^https?:\/\//, ''))}</a></li>` : ''}
|
||||
${entry.repos.package ? `<li><span class="rel">Pi 包</span><a target="_blank" rel="noreferrer" href="${escapeHtml(entry.repos.package)}">${escapeHtml(entry.repos.package.replace(/^https?:\/\//, ''))}</a></li>` : ''}
|
||||
${entry.repos.cli ? `<li><span class="rel">CLI</span><a target="_blank" rel="noreferrer" href="${escapeHtml(entry.repos.cli)}">${escapeHtml(entry.repos.cli.replace(/^https?:\/\//, ''))}</a></li>` : ''}
|
||||
</ul>
|
||||
</div></div>
|
||||
</aside>
|
||||
@ -430,11 +437,11 @@ function indexPage(entries, siteUrl, fontsCss) {
|
||||
.map(layer => {
|
||||
const items = entries.filter(entry => entry.layer === layer);
|
||||
if (!items.length) return '';
|
||||
return `<section class="panel layer-section" data-layer="${layer}">
|
||||
<div class="panel-head">
|
||||
return `<section class="layer-section" data-layer="${layer}">
|
||||
<div class="layer-head">
|
||||
<span class="dot"></span><span class="label">${LAYER_LABEL[layer]}</span>
|
||||
<span class="panel-count">${items.length}</span>
|
||||
<span class="panel-hint">${LAYER_HINT[layer]}</span>
|
||||
<span class="layer-count">${items.length}</span>
|
||||
<span class="layer-why">${LAYER_HINT[layer]}</span>
|
||||
</div>
|
||||
<div class="grid">${items.map(cardOf).join('\n')}</div>
|
||||
</section>`;
|
||||
@ -444,8 +451,8 @@ function indexPage(entries, siteUrl, fontsCss) {
|
||||
const sample = entries.find(entry => entry.layer === 'core') ?? entries[0];
|
||||
const body = `<header class="masthead">
|
||||
<p class="eyebrow">${BRAND.name} · 自托管目录</p>
|
||||
<h1>${BRAND.tagline}</h1>
|
||||
<p class="lede">${SCOPE_HTML}的集中目录。每条都写清依赖、前置条件与安装命令,装完就能用。</p>
|
||||
<h1>${BRAND.headline}</h1>
|
||||
<p class="lede">集中收录${SCOPE_HTML}。每条都写清依赖、前置条件与安装方式,装完即用。</p>
|
||||
${
|
||||
sample
|
||||
? `<p class="cta-label">安装示例</p>
|
||||
@ -476,14 +483,22 @@ function indexPage(entries, siteUrl, fontsCss) {
|
||||
</div>
|
||||
<span class="count" id="count">${entries.length}</span>
|
||||
</div>
|
||||
${requires.length ? `<div class="chipbar" id="requires">${requires.map(name => `<button class="chip filter" data-requires="${escapeHtml(name)}">${escapeHtml(name)}</button>`).join('')}</div>` : ''}
|
||||
${
|
||||
requires.length
|
||||
? `<div class="chipbar" id="requires">
|
||||
<span class="chip-label">按依赖筛选</span>
|
||||
${requires.map(name => `<button class="chip filter" data-requires="${escapeHtml(name)}" aria-pressed="false">${escapeHtml(name)}</button>`).join('')}
|
||||
<button class="chip clear" id="clear-filters" type="button" hidden>清除筛选</button>
|
||||
</div>`
|
||||
: ''
|
||||
}
|
||||
<main id="sections">
|
||||
${sections}
|
||||
</main>
|
||||
<p class="empty muted" id="empty" hidden>没有匹配的技能。</p>`;
|
||||
return pageShell({
|
||||
title: BRAND.name,
|
||||
description: 'Pi 能力目录:技能与扩展,写清依赖与前置条件,一条命令装好就能用。',
|
||||
title: `${BRAND.headline} · ${BRAND.name}`,
|
||||
description: '集中收录 Pi 的技能、扩展、主题与提示模板:写清依赖、前置条件与安装方式,一条命令装好即用。',
|
||||
body,
|
||||
fontsCss,
|
||||
siteUrl: siteUrl ?? '',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user