Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
9bc0d2f
feat(website): add SEO model pages infrastructure
christian-byrne May 4, 2026
8fe2dee
fix(website): address review comments on model pages PR
christian-byrne May 4, 2026
8db4e46
[automated] Apply ESLint and Oxfmt fixes
actions-user May 4, 2026
213870a
refactor(website): fix model pages architecture and design token viol…
christian-byrne May 4, 2026
a94c75c
[automated] Apply ESLint and Oxfmt fixes
actions-user May 4, 2026
d6704a9
refactor(website): fix model pages architecture and design token viol…
christian-byrne May 4, 2026
2143f82
fix(website): fix knip violations, invalid variant, and update SKILL.md
christian-byrne May 4, 2026
32a3161
[automated] Apply ESLint and Oxfmt fixes
actions-user May 4, 2026
a492d32
fix(website): remove unused type exports from models.ts
christian-byrne May 4, 2026
c38736c
revert(knip): revert website entry point expansion
christian-byrne May 4, 2026
c52e398
fix(discovery): read existing slugs from generated-models.json not mo…
christian-byrne May 4, 2026
7e11c5a
feat(website): add model thumbnails via hub API; overhaul discovery w…
christian-byrne May 4, 2026
5acb517
[automated] Apply ESLint and Oxfmt fixes
actions-user May 4, 2026
6c9947c
fix(models): link primary CTA to /workflows/model/{slug} on comfy.org
christian-byrne May 4, 2026
ef8ec75
fix(skill): lowercase description and heading body
christian-byrne May 4, 2026
4467e67
feat(models): auto-extract docsUrl from workflow_templates index; exp…
christian-byrne May 4, 2026
9832571
chore: merge main into glary/seo-model-pages
christian-byrne May 4, 2026
448953d
[automated] Apply ESLint and Oxfmt fixes
actions-user May 4, 2026
924374f
feat(models): add hubSlug for verified hub pages; expand FAQ copy
christian-byrne May 4, 2026
3ce00a8
feat(models): populate thumbnails from workflow_templates webp files
christian-byrne May 4, 2026
2a81c2b
[automated] Apply ESLint and Oxfmt fixes
actions-user May 4, 2026
037ae5a
feat(models): richer per-type copy, slug validation, SKILL.md docs up…
christian-byrne May 4, 2026
b0986f1
[automated] Apply ESLint and Oxfmt fixes
actions-user May 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions .claude/skills/add-model-page/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
---
name: add-model-page
description: 'add, update, or remove a model page entry on the comfy org website. creates a PR to Comfy-Org/ComfyUI_frontend apps/website folder with the change and posts a Vercel preview link back to Slack.'
---

# add-model-page

add, update, or remove model pages in the ComfyUI website.

## Trigger phrases

- `Add a model page for <model-name>`
- `Update the model page for <model-name>`
- `Remove <model-name> from model pages`

## Phase 1 — Parse the request

Extract:

- **action**: `add` | `update` | `remove`
- **model-name**: raw string (e.g. `flux1-schnell`, `flux1_dev.safetensors`)

Normalize to a slug: lowercase, replace `_` and `.` with `-`, strip file extensions.
Example: `flux1_dev.safetensors` → `flux1-dev`

## Architecture overview

Models come from two sources merged at build time:

| File | Purpose |
| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `apps/website/src/config/generated-models.json` | Auto-generated from workflow_templates (slug, name, directory, huggingFaceUrl, workflowCount, displayName, thumbnailUrl, docsUrl) |
| `apps/website/src/config/model-metadata.ts` | Hand-curated overrides (docsUrl, blogUrl, featured) — only add entries that need overrides |
| `apps/website/src/config/models.ts` | Merges the two above; exports typed `Model[]` |

To regenerate the JSON from workflow_templates:

```bash
pnpm tsx apps/website/scripts/generate-models.ts
```

This writes `apps/website/src/config/generated-models.json` directly.
Thumbnails are populated from local `.webp` files in `workflow_templates/templates/` — no network access needed.

---

## Phase 2 — Gather model data (ADD / UPDATE)

Run the generator to get fresh data, then find the model:

```bash
pnpm tsx apps/website/scripts/generate-models.ts
jq '.[] | select(.slug | contains("MODEL_SLUG"))' \
apps/website/src/config/generated-models.json
```

The JSON fields are:

- `slug` — URL slug
- `name` — exact filename or display name for partner nodes
- `huggingFaceUrl` — download URL (empty for partner nodes)
- `directory` — `diffusion_models` | `loras` | … | `partner_nodes`
- `workflowCount` — integer
- `displayName` — human-readable name

If no match and it is a known API/partner model, add it to `API_PROVIDER_MAP` in
`generate-models.ts` and re-run. Otherwise tell the user.

---

## Phase 3 — Check for existing entry

```bash
jq --arg slug "${SLUG}" '.[] | select(.slug == $slug)' \
apps/website/src/config/generated-models.json
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- Match found + action is `add` → switch to UPDATE flow automatically
- No match + action is `update` → stop and tell the user

---

## Phase 4A — ADD: new partner/API model not in workflow_templates

For partner nodes (no local file), add an entry to `API_PROVIDER_MAP` in
`apps/website/scripts/generate-models.ts`:

```typescript
mymodel: { name: 'My Model', slug: 'my-model' },
```

Then re-run `pnpm tsx apps/website/scripts/generate-models.ts` — it will appear
in `generated-models.json` automatically.

If you also want a `docsUrl`, `blogUrl`, or a link to the hub model page, add an entry to `model-metadata.ts`:

```typescript
'my-model': {
docsUrl: 'https://docs.comfy.org/tutorials/...',
blogUrl: 'https://blog.comfy.org/...',
hubSlug: 'my-model', // slug at comfy.org/workflows/model/{hubSlug} — only set if the page exists (returns 200)
featured: true
}
```

No changes to `models.ts` or `translations.ts` are needed.

---

## Phase 4B — UPDATE: edit existing entry

Only `model-metadata.ts` needs editing for most updates (docsUrl, blogUrl,
featured). For `displayName` or `directory` changes, edit the entry directly in
`generated-models.json` (until the next generator run would overwrite it — then
fix the source in `generate-models.ts`).

---

## Phase 4C — REMOVE: delete entry

Remove the entry from `generated-models.json` (or mark it with `canonicalSlug`
pointing to the replacement). No translation file changes needed.

---

## Phase 5 — Verify TypeScript

```bash
pnpm typecheck 2>&1 | grep -E "error|warning" | head -20
```

Fix any type errors before proceeding. Common issues:

- `ModelDirectory` type not matching a new `directory` value — add it to the union
- JSON import shape mismatch — `generated-models.json` must match `OutputModel`

---

## Phase 6 — Create PR

```bash
BRANCH="add-model-page-MODEL-SLUG" # or update- / remove-
git checkout -b $BRANCH
git add apps/website/src/config/generated-models.json \
apps/website/scripts/generate-models.ts \
apps/website/src/config/model-metadata.ts
git commit -m "feat(models): add model page for MODEL-SLUG"
git push -u origin $BRANCH
gh pr create \
--title "Add model page: MODEL-SLUG" \
--body "$(cat <<'EOF'
Adds a new model page entry for MODEL-SLUG.

## Changes
- `generated-models.json`: regenerated with new entry (workflowCount N, directory DIRECTORY)
- `model-metadata.ts`: editorial overrides (docsUrl, featured) if needed
EOF
)"
```

For UPDATE use branch `update-model-page-MODEL-SLUG`.
For REMOVE use `remove-model-page-MODEL-SLUG`.

---

## Error states

| Situation | Response |
| ------------------------------- | ---------------------------------------------------------------- |
| Model not in workflow templates | Ask user to verify spelling or add it manually as a partner node |
| Slug already exists (add) | Switch to update flow automatically |
| Slug not found (update/remove) | Stop and ask user to confirm |
| Typecheck fails | Fix the error before pushing |
123 changes: 123 additions & 0 deletions .github/workflows/model-page-discovery.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
name: Model Page Discovery

on:
schedule:
- cron: '0 9 * * 1'
workflow_dispatch:

jobs:
discover:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write

steps:
- name: Fetch model labels from hub API
id: hub
shell: bash
run: |
set -euo pipefail
curl -fsSL 'https://comfy.org/api/hub/labels?type=model' -o hub-labels.json
echo "Fetched $(jq '.labels | length' hub-labels.json) model labels from hub"

- name: Checkout ComfyUI_frontend
uses: actions/checkout@v6
with:
sparse-checkout: apps/website/src/config/generated-models.json

- name: Compare against existing models
id: compare
shell: bash
run: |
set -euo pipefail

HUB_SLUGS=$(jq -r '[.labels[].name]' hub-labels.json)

EXISTING_SLUGS=$(node -e "
const fs = require('fs');
const models = JSON.parse(
fs.readFileSync(
'apps/website/src/config/generated-models.json',
'utf8'
)
);
console.log(JSON.stringify(models.map(m => m.slug)));
" 2>/dev/null || echo '[]')
Comment thread
coderabbitai[bot] marked this conversation as resolved.

ADDED_SLUGS=$(node -e "
const hub = $HUB_SLUGS;
const existing = new Set($EXISTING_SLUGS);
console.log(JSON.stringify(hub.filter(s => !existing.has(s))));
")

COUNT=$(node -e "console.log($ADDED_SLUGS.length)")
echo "new_count=$COUNT" >> \$GITHUB_OUTPUT
echo "new_slugs=$ADDED_SLUGS" >> \$GITHUB_OUTPUT

if [ "\$COUNT" -eq 0 ]; then
echo "No new models found."
else
echo "Found \$COUNT new model(s)"
fi

- name: Check for existing open discovery issue
id: existing_issue
if: steps.compare.outputs.new_count != '0'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
COUNT=$(gh issue list \
--repo "$GITHUB_REPOSITORY" \
--state open \
--search 'in:title "New models detected"' \
--json number \
--jq 'length')
echo "open_count=$COUNT" >> $GITHUB_OUTPUT

- name: Open GitHub issue for new models
if: |
steps.compare.outputs.new_count != '0' &&
steps.existing_issue.outputs.open_count == '0'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NEW_SLUGS: ${{ steps.compare.outputs.new_slugs }}
NEW_COUNT: ${{ steps.compare.outputs.new_count }}
shell: bash
run: |
SLUG_LIST=$(node -e "
const slugs = $NEW_SLUGS;
console.log(slugs.map(s => '- \`' + s + '\`').join('\n'));
")

gh issue create \
--repo "$GITHUB_REPOSITORY" \
--title "New models detected — add to model pages" \
--body "## $NEW_COUNT new model(s) found in hub

The weekly model discovery scan found model labels on the hub not yet in
\`apps/website/src/config/generated-models.json\`.

### New slugs ($NEW_COUNT)

$SLUG_LIST

### Next steps

1. Review which of these warrant an SEO model page
2. For local models: run \`SKIP_THUMBNAILS=1 pnpm generate:models\` and commit the result
3. For partner/API models: add to \`API_PROVIDER_MAP\` in \`generate-models.ts\`, regenerate, commit

---
*Generated by the [model-page-discovery workflow](https://github.com/$GITHUB_REPOSITORY/actions/workflows/model-page-discovery.yaml)*"

- name: Skip — open issue already exists
if: |
steps.compare.outputs.new_count != '0' &&
steps.existing_issue.outputs.open_count != '0'
run: echo "An open discovery issue already exists — skipping creation."

- name: No new models found
if: steps.compare.outputs.new_count == '0'
run: echo "No new models found — nothing to do."
3 changes: 2 additions & 1 deletion apps/website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"test:e2e:local": "cross-env PLAYWRIGHT_LOCAL=1 playwright test",
"test:visual": "playwright test --project visual",
"test:visual:update": "playwright test --project visual --update-snapshots",
"ashby:refresh-snapshot": "tsx ./scripts/refresh-ashby-snapshot.ts"
"ashby:refresh-snapshot": "tsx ./scripts/refresh-ashby-snapshot.ts",
"generate:models": "tsx ./scripts/generate-models.ts"
},
"dependencies": {
"@astrojs/sitemap": "catalog:",
Expand Down
Loading
Loading