mirror of
https://git.openapi.site/https://github.com/desirecore/market.git
synced 2026-07-23 05:23:40 +08:00
feat(market): 为外部技能补充图标 (#50)
## Summary / 摘要 Add inline SVG icons to all 20 external pointer skills, so their cards no longer fall back to an empty icon. The market catalog version is bumped from `1.2.8` to `1.2.9`. 为全部 20 个外部 pointer skill 补齐内联 SVG 图标,市场卡片不再显示为空;市场版本由 `1.2.8` 升至 `1.2.9`。 ## Compatibility / 兼容性 Depends on desirecore/desirecore#1177, which permits the `icon` field in strict pointer-entry validation and forwards it through the offline list/detail responses. That PR must merge first. ## Skill → icon screenshot / Skill 与图标截图 The preview labels every card with its display name and id.  Covered skills: `agent-reach`, `ai-news-radar`, `amap-jsapi-skill`, `dingtalk-api`, `flyai-skill`, `follow-builders`, `ian-xiaohei-illustrations`, `karpathy-guidelines`, `khazix-skills`, `larksuite-cli`, `last30days`, `luckin-my-coffee`, `mattpocock-skills`, `minimax-image-gen`, `minimax-tts`, `mt-paotui-for-client`, `netease-skills`, `taste-skill`, `wechatpay-skills`, and `wecom-cli`. ## Validation / 验证 - `uv run scripts/i18n/validate-i18n.py` - `uv run scripts/i18n/translate.py --check` - Parsed all 20 `entry.json` icons as SVG XML - `git diff --check` - Rendered the committed preview asset locally
This commit is contained in:
@@ -17,7 +17,7 @@ Checks:
|
||||
8. Top-level description is 1-1024 chars (spec); top-level name is 1-64 chars (spec).
|
||||
9. Skill/Agent counts and builtin skill index match the repository contents.
|
||||
10. Skill, Agent, and entry.json category references exist in categories.json.
|
||||
11. entry.json pointers have the required marketplace fields and safe source URLs.
|
||||
11. entry.json pointers have the required marketplace fields, valid inline SVG icons, and safe source URLs.
|
||||
12. Market Skills set `disable-model-invocation` to true or omit it; false is prohibited.
|
||||
|
||||
Exit codes:
|
||||
@@ -38,6 +38,7 @@ import json
|
||||
import re
|
||||
import ssl
|
||||
import sys
|
||||
from xml.etree import ElementTree
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Any, Iterable
|
||||
@@ -389,7 +390,7 @@ def validate_entry_json(report: Report, entry_file: Path, category_ids: set[str]
|
||||
if not entry:
|
||||
return
|
||||
|
||||
required = ("id", "name", "category", "maintainer", "stewardship", "license", "redistribution", "source")
|
||||
required = ("id", "name", "category", "icon", "maintainer", "stewardship", "license", "redistribution", "source")
|
||||
for key in required:
|
||||
if key not in entry:
|
||||
report.add(Issue(rel, "entry-json", f"missing required field '{key}'"))
|
||||
@@ -401,6 +402,19 @@ def validate_entry_json(report: Report, entry_file: Path, category_ids: set[str]
|
||||
if not isinstance(category, str) or category not in category_ids:
|
||||
report.add(Issue(rel, "entry-json", f"category '{category}' is not declared in categories.json"))
|
||||
|
||||
icon = entry.get("icon")
|
||||
if not isinstance(icon, str) or not icon.strip():
|
||||
report.add(Issue(rel, "entry-json", "icon must be a non-empty inline SVG string"))
|
||||
else:
|
||||
try:
|
||||
root = ElementTree.fromstring(icon)
|
||||
if root.tag != "{http://www.w3.org/2000/svg}svg":
|
||||
report.add(Issue(rel, "entry-json", "icon root element must be svg in the SVG namespace"))
|
||||
elif not root.get("viewBox"):
|
||||
report.add(Issue(rel, "entry-json", "icon SVG must declare a viewBox"))
|
||||
except ElementTree.ParseError as exc:
|
||||
report.add(Issue(rel, "entry-json", f"icon must be valid SVG XML: {exc}"))
|
||||
|
||||
maintainer = entry.get("maintainer")
|
||||
if not isinstance(maintainer, dict) or not isinstance(maintainer.get("name"), str):
|
||||
report.add(Issue(rel, "entry-json", "maintainer.name is required"))
|
||||
|
||||
Reference in New Issue
Block a user