From 6b085460bdb914337f598a5dfc8c6a38ebf97204 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Fri, 14 Aug 2026 10:17:47 +0800 Subject: [PATCH] fix: render plugin-supplied labels and package references faithfully MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three places where the templates dropped meaning a plugin had already expressed. The interface badge printed its label raw. Every other consumer-supplied string goes through `| loc`, so a plugin handing it a per-locale map -- which the block API accepts everywhere else -- rendered the map itself: `{'en': 'command', 'zh': '命令'}` in the badge. `dependencies` was inert text while `required_by` was a link, so an edge the build had already resolved could only be walked uphill. The resolver now records which slug a dep string resolves to, and the template links the ones that name a package this index carries. External deps stay text, because there is nothing to point at. A table cell could only be flat text, so a plugin's own table could not reference the pages its index already has. A cell may now be `{"text": ..., "slug": ...}`; the plugin supplies the slug and the core builds the URL, exactly as it does for `required_by`. A plain string behaves as before. Found while building dsh-index, whose Agent pages list their member packages with a version and a commit -- a table that names five packages and links to none of them. --- .agents/skills/web-design-guidelines/SKILL.md | 39 +++++++++++++++++++ .claude/skills/web-design-guidelines | 1 + skills-lock.json | 10 +++++ xpkgindex/build.py | 4 ++ xpkgindex/models.py | 3 ++ xpkgindex/templates/_macros.html | 11 +++++- xpkgindex/templates/package.html | 12 +++++- 7 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 .agents/skills/web-design-guidelines/SKILL.md create mode 120000 .claude/skills/web-design-guidelines create mode 100644 skills-lock.json diff --git a/.agents/skills/web-design-guidelines/SKILL.md b/.agents/skills/web-design-guidelines/SKILL.md new file mode 100644 index 0000000..ceae92a --- /dev/null +++ b/.agents/skills/web-design-guidelines/SKILL.md @@ -0,0 +1,39 @@ +--- +name: web-design-guidelines +description: Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices". +metadata: + author: vercel + version: "1.0.0" + argument-hint: +--- + +# Web Interface Guidelines + +Review files for compliance with Web Interface Guidelines. + +## How It Works + +1. Fetch the latest guidelines from the source URL below +2. Read the specified files (or prompt user for files/pattern) +3. Check against all rules in the fetched guidelines +4. Output findings in the terse `file:line` format + +## Guidelines Source + +Fetch fresh guidelines before each review: + +``` +https://raw.githubusercontent.com/vercel-labs/web-interface-guidelines/main/command.md +``` + +Use WebFetch to retrieve the latest rules. The fetched content contains all the rules and output format instructions. + +## Usage + +When a user provides a file or pattern argument: +1. Fetch guidelines from the source URL above +2. Read the specified files +3. Apply all rules from the fetched guidelines +4. Output findings using the format specified in the guidelines + +If no files specified, ask the user which files to review. diff --git a/.claude/skills/web-design-guidelines b/.claude/skills/web-design-guidelines new file mode 120000 index 0000000..886b26d --- /dev/null +++ b/.claude/skills/web-design-guidelines @@ -0,0 +1 @@ +../../.agents/skills/web-design-guidelines \ No newline at end of file diff --git a/skills-lock.json b/skills-lock.json new file mode 100644 index 0000000..1a96f31 --- /dev/null +++ b/skills-lock.json @@ -0,0 +1,10 @@ +{ + "version": 1, + "skills": { + "web-design-guidelines": { + "source": "vercel-labs/agent-skills", + "sourceType": "github", + "computedHash": "a6a44d5498f7e8f68289902f3dedfc6f38ae0cee1e96527c80724cf27f727c2a" + } + } +} diff --git a/xpkgindex/build.py b/xpkgindex/build.py index 0602ae0..5060bb0 100644 --- a/xpkgindex/build.py +++ b/xpkgindex/build.py @@ -108,6 +108,10 @@ def _reverse_deps(packages: List[Package]) -> None: if target is not None and target is not pkg: if pkg.identity.slug not in target.required_by: target.required_by.append(pkg.identity.slug) + # The same edge, pointing the other way. `required_by` was a + # link and `dependencies` was inert text, so a relationship + # the site had already resolved was only walkable uphill. + pkg.dep_slugs[dep] = target.identity.slug def _apply_default_namespace(packages: List[Package], meta: IndexMeta) -> None: diff --git a/xpkgindex/models.py b/xpkgindex/models.py index 1720837..eb4ce7c 100644 --- a/xpkgindex/models.py +++ b/xpkgindex/models.py @@ -214,6 +214,9 @@ class Package: versions: List[Version] = field(default_factory=list) latest: str = "" deps: List[str] = field(default_factory=list) + # dep string -> slug of the package it resolves to, for deps that name a + # package this index carries. Absent for external ones, which stay text. + dep_slugs: Dict[str, str] = field(default_factory=dict) required_by: List[str] = field(default_factory=list) facets: Dict[str, str] = field(default_factory=dict) diff --git a/xpkgindex/templates/_macros.html b/xpkgindex/templates/_macros.html index aa41fe4..75df6ab 100644 --- a/xpkgindex/templates/_macros.html +++ b/xpkgindex/templates/_macros.html @@ -193,7 +193,16 @@ {% for h in b.data.get('head', []) %}{{ h | loc }}{% endfor %} {% for row in b.data.get('rows', []) %} - {% for cell in row %}{{ cell }}{% endfor %} + {% for cell in row %} + {%- if cell is mapping and cell.get('slug') -%} + {# A cell may name a package instead of being flat text, so a + plugin's own table can point at the pages this index already + has. The plugin supplies the slug; the URL is the core's to + build, exactly as it is for `required_by`. #} + {{ cell.get('text', cell.slug) }} + {%- elif cell is mapping -%}{{ cell.get('text', '') }} + {%- else -%}{{ cell }}{%- endif -%} + {% endfor %} {% endfor %} diff --git a/xpkgindex/templates/package.html b/xpkgindex/templates/package.html index 9eecea5..ee84db4 100644 --- a/xpkgindex/templates/package.html +++ b/xpkgindex/templates/package.html @@ -15,7 +15,10 @@

{% if parts[0] %}{{ parts[0] }}{% endif %}{{ parts[1] }}

{% if pkg.interface %} - {{ pkg.interface.data.get('label', pkg.tone) }} + {# `| loc` like every other consumer-supplied string: a plugin may hand + this a per-locale map, and without the filter the map was printed as + its Python repr -- `{'en': 'command', 'zh': '命令'}` in the badge. #} + {{ pkg.interface.data.get('label', pkg.tone) | loc }} {% endif %} {% if pkg.type and pkg.type != 'package' %}{{ pkg.type }}{% endif %} @@ -116,7 +119,12 @@

{{ t('pkg.facts') }}

{{ t('pkg.dependencies') }}
{% if pkg.deps %} - {% for d in pkg.deps %}{{ d }}{% endfor %} + {% for d in pkg.deps %} + {%- set slug = pkg.dep_slugs.get(d) -%} + {%- if slug -%} + {{ d }} + {%- else -%}{{ d }}{%- endif -%} + {% endfor %} {% else %}{{ t('pkg.none') }}{% endif %}
{{ t('pkg.required_by') }}