diff --git a/MANIFEST.json b/MANIFEST.json index ee2c761..abb9f2e 100644 --- a/MANIFEST.json +++ b/MANIFEST.json @@ -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": { diff --git a/README.md b/README.md index 79feb9b..0f92972 100644 --- a/README.md +++ b/README.md @@ -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` 会告诉还缺什么。 diff --git a/package.json b/package.json index 79e812f..a3832e6 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name": "skill-catalog", - "version": "0.1.1", + "version": "0.1.2", "description": "技能目录站:把对外发布的技能生成一个静态站(能干什么 / 先装什么 / 要配什么 / 依赖谁 / 一条安装命令),生成物不手改页面。" } diff --git a/skills/skill-catalog/REFERENCE.md b/skills/skill-catalog/REFERENCE.md index a090389..0bc82b1 100644 --- a/skills/skill-catalog/REFERENCE.md +++ b/skills/skill-catalog/REFERENCE.md @@ -109,6 +109,19 @@ DNS 加一条 `pi.vhkd.top A 47.243.111.1`(或先不加 DNS,用 IP/路径访 - **双主题**:明暗各一套变量;`` 里的引导脚本按「上次选择 → 系统偏好」在首帧前设好 `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. 想加东西往哪加 - **新页面/新字段**:数据来自技能声明,先想清楚它是不是「声明里已经有的事实」——是就渲染, diff --git a/skills/skill-catalog/VERSION b/skills/skill-catalog/VERSION index 17e51c3..d917d3e 100644 --- a/skills/skill-catalog/VERSION +++ b/skills/skill-catalog/VERSION @@ -1 +1 @@ -0.1.1 +0.1.2 diff --git a/skills/skill-catalog/assets/app.js b/skills/skill-catalog/assets/app.js index 28e8be7..d6dfd4e 100644 --- a/skills/skill-catalog/assets/app.js +++ b/skills/skill-catalog/assets/app.js @@ -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(); })(); diff --git a/skills/skill-catalog/assets/style.css b/skills/skill-catalog/assets/style.css index e607ec0..040546a 100644 --- a/skills/skill-catalog/assets/style.css +++ b/skills/skill-catalog/assets/style.css @@ -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); } diff --git a/skills/skill-catalog/interface.json b/skills/skill-catalog/interface.json index ed1c8d4..3482e4c 100644 --- a/skills/skill-catalog/interface.json +++ b/skills/skill-catalog/interface.json @@ -102,5 +102,6 @@ ], "publish": { "package_repo": "https://gitea.vhkd.top/pi-hub/skill-catalog.git" - } + }, + "license": "GPL-3.0-or-later" } diff --git a/skills/skill-catalog/scripts/skill-catalog.mjs b/skills/skill-catalog/scripts/skill-catalog.mjs index 4a369f2..19e4eac 100644 --- a/skills/skill-catalog/scripts/skill-catalog.mjs +++ b/skills/skill-catalog/scripts/skill-catalog.mjs @@ -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 = '技能 · 扩展 · 主题 · 提示模板'; // 字体三套(都 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) {
  • 拉丁 Pragmata Pro(读者本机)
  • -

    ${BRAND.name} · 由 skill-catalog 生成 · 条目与技能声明一致

    +

    ${BRAND.name} · 更新于 ${escapeHtml(new Date().toISOString().slice(0, 10))} · 由 skill-catalog 从技能声明生成

    `; } @@ -290,7 +291,9 @@ function cardOf(entry) { ].join(''); const used = entry.usedByCount ? `被 ${entry.usedByCount} 个技能使用` : ''; return ` + 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(' ')}`, + )}">

    ${escapeHtml(entry.name)}

    v${escapeHtml(entry.version)}

    ${escapeHtml(entry.summary)}

    @@ -385,9 +388,10 @@ ${entry.usesPublic.length || entry.usedBy.length ? '' : '
  • 独 ← 全部技能

    ${escapeHtml(entry.name)}

    - v${escapeHtml(entry.version)} + ${entry.version ? `v${escapeHtml(entry.version)}` : '未标版本'}

    ${escapeHtml(entry.summary)}

    + ${entry.useWhen ? `

    什么时候用${escapeHtml(entry.useWhen)}

    ` : ''}
    ${LAYER_LABEL[entry.layer]} ${LAYER_HINT[entry.layer]} @@ -404,10 +408,13 @@ ${entry.usesPublic.length || entry.usedBy.length ? '' : '
  • 独 @@ -430,11 +437,11 @@ function indexPage(entries, siteUrl, fontsCss) { .map(layer => { const items = entries.filter(entry => entry.layer === layer); if (!items.length) return ''; - return `
    -
    + return `
    +
    ${LAYER_LABEL[layer]} - ${items.length} - ${LAYER_HINT[layer]} + ${items.length} + ${LAYER_HINT[layer]}
    ${items.map(cardOf).join('\n')}
    `; @@ -444,8 +451,8 @@ function indexPage(entries, siteUrl, fontsCss) { const sample = entries.find(entry => entry.layer === 'core') ?? entries[0]; const body = `

    ${BRAND.name} · 自托管目录

    -

    ${BRAND.tagline}

    -

    ${SCOPE_HTML}的集中目录。每条都写清依赖、前置条件与安装命令,装完就能用。

    +

    ${BRAND.headline}

    +

    集中收录${SCOPE_HTML}。每条都写清依赖、前置条件与安装方式,装完即用。

    ${ sample ? `

    安装示例

    @@ -476,14 +483,22 @@ function indexPage(entries, siteUrl, fontsCss) {
    ${entries.length}
  • -${requires.length ? `
    ${requires.map(name => ``).join('')}
    ` : ''} +${ + requires.length + ? `
    + 按依赖筛选 + ${requires.map(name => ``).join('')} + +
    ` + : '' + }
    ${sections}
    `; return pageShell({ - title: BRAND.name, - description: 'Pi 能力目录:技能与扩展,写清依赖与前置条件,一条命令装好就能用。', + title: `${BRAND.headline} · ${BRAND.name}`, + description: '集中收录 Pi 的技能、扩展、主题与提示模板:写清依赖、前置条件与安装方式,一条命令装好即用。', body, fontsCss, siteUrl: siteUrl ?? '',