Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
24 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
5feefc7
feat(models): fallback CTAs when no hub model page exists
christian-byrne May 5, 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
188 changes: 188 additions & 0 deletions .claude/skills/add-model-page/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
---
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.'
---

# 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`

---

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

Find the ComfyUI_frontend repo root. Key files:

| File | Purpose |
|------|---------|
| `apps/website/src/config/models.ts` | Model array |
| `apps/website/src/i18n/translations.ts` | i18n string map |
| `apps/website/scripts/generate-models.ts` | Derives model data from workflow templates |

Run the generator to find the model:

```bash
cd ComfyUI_frontend
pnpm tsx apps/website/scripts/generate-models.ts \
| jq '.[] | select(.name | ascii_downcase | contains("MODEL_NAME"))'
```

Replace `MODEL_NAME` with the normalized slug (substring match).

The output object contains:
- `name` — exact filename (e.g. `flux1_dev.safetensors`) or display name for partner nodes
- `url` — HuggingFace download URL → use as `huggingFaceUrl` (empty `''` for partner nodes)
- `directory` — model subdirectory (e.g. `diffusion_models`) or `partner_nodes` for API models
- `workflowCount` — integer
- `suggestedSlug` — use this as `slug` if reasonable
- `suggestedDisplayName` — use as seed for i18n `displayName`

If the generator returns no match and the model is a known API/partner model, add it
manually as `directory: 'partner_nodes'` with an empty `huggingFaceUrl`.

---

## Phase 3 — Check for existing entry

```bash
grep -n "slug: '${SLUG}'" apps/website/src/config/models.ts
```
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: insert new entry

### 4A.1 Build the entry

```typescript
{
slug: 'MODEL-SLUG',
name: 'model_name.safetensors', // or display name for partner nodes
displayName: 'models.MODEL-SLUG.displayName' as TranslationKey,
description: 'models.MODEL-SLUG.description' as TranslationKey,
directory: 'diffusion_models', // from generate-models; or 'partner_nodes'
huggingFaceUrl: 'https://huggingface.co/...', // empty '' for partner nodes
docsUrl: 'https://docs.comfy.org/...', // optional: tutorial link
blogUrl: 'https://blog.comfy.org/...', // optional: blog post link
ogImage: '/images/models/MODEL-SLUG-og.png',
thumbnail: '/images/models/MODEL-SLUG-thumb.webp',
featured: true,
workflowCount: N,
tags: ['tag1', 'tag2'] as const,
publishedDate: 'YYYY-MM-DD',
modifiedDate: 'YYYY-MM-DD',
}
```

Tags: split the slug on `-`, drop pure numeric tokens, keep meaningful words.

### 4A.2 Insert into models array

The array in `apps/website/src/config/models.ts` is sorted by `workflowCount`
descending. Find the first entry whose `workflowCount` is less than the new
entry's count and insert before it. If all are greater, append at the end.

### 4A.3 Add i18n keys

In `apps/website/src/i18n/translations.ts`, find the line before
`} as const satisfies` and insert:

```typescript
'models.MODEL-SLUG.displayName': {
en: 'Human Display Name',
'zh-CN': ''
},
'models.MODEL-SLUG.description': {
en: 'One sentence describing what this model does.',
'zh-CN': ''
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the skill currently instructs empty zh-CN values
rg -n "'zh-CN': ''" .claude/skills/add-model-page/SKILL.md

# Verify fallback logic in i18n helper uses nullish-coalescing (does not treat empty string as missing)
rg -n "return translations\\[key\\]\\[locale\\] \\?\\? translations\\[key\\]\\.en" apps/website/src/i18n/translations.ts

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 174


🏁 Script executed:

# Check if there are other instances of empty zh-CN in the skills directory
rg -n "'zh-CN': ''" .claude/skills/ --type markdown

# Verify the context around lines 113 and 117 to ensure they match the snippet
sed -n '110,120p' .claude/skills/add-model-page/SKILL.md

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 412


Populate empty zh-CN translation values in the model template.

Empty strings in translation keys prevent fallback to English because the i18n handler uses nullish coalescing (??), which treats empty string as a valid value. This causes blank Chinese UI text. Update both keys on lines 113 and 117 to use the English values:

Suggested fix
   'models.MODEL-SLUG.displayName': {
     en: 'Human Display Name',
-    'zh-CN': ''
+    'zh-CN': 'Human Display Name'
   },
   'models.MODEL-SLUG.description': {
     en: 'One sentence describing what this model does.',
-    'zh-CN': ''
+    'zh-CN': 'One sentence describing what this model does.'
   },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/add-model-page/SKILL.md around lines 110 - 118, The Chinese
translation entries for the keys 'models.MODEL-SLUG.displayName' and
'models.MODEL-SLUG.description' are empty strings, which prevents i18n fallback
to English; update the 'zh-CN' values for both keys to the same text as their
'en' values (i.e., set 'zh-CN' to "Human Display Name" for
models.MODEL-SLUG.displayName and to "One sentence describing what this model
does." for models.MODEL-SLUG.description) so the UI shows the English text
instead of blank in zh-CN.

```

Use `suggestedDisplayName` from generate-models for the English `displayName`.

---

## Phase 4B — UPDATE: edit existing entry

Find the entry by slug. Apply only the fields requested. Update `modifiedDate` to
today. Leave all other fields unchanged.

---

## Phase 4C — REMOVE: delete entry

1. Remove the entire object literal (including trailing comma) from the `models` array.
2. Remove the two translation keys from `translations.ts`.
3. Note in the PR description why the model was removed.

---

## Phase 5 — Verify TypeScript

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

Fix any type errors before proceeding. Common issues:
- Missing `as TranslationKey` cast on `displayName`/`description`
- New translation key not yet in `translations.ts` when `models.ts` references it

---

## Phase 6 — Create PR

```bash
BRANCH="add-model-page-MODEL-SLUG" # or update- / remove-
git checkout -b $BRANCH
git add apps/website/src/config/models.ts apps/website/src/i18n/translations.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
- `models.ts`: new entry with workflowCount N, directory DIRECTORY
- `translations.ts`: placeholder i18n keys (zh-CN needs translation)

Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
## Images needed
- `/images/models/MODEL-SLUG-og.png` (1200×630)
- `/images/models/MODEL-SLUG-thumb.webp`
EOF
)"
```

For UPDATE use branch `update-model-page-MODEL-SLUG`.
For REMOVE use `remove-model-page-MODEL-SLUG` and omit the images note.

---

## 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 |
130 changes: 130 additions & 0 deletions .github/workflows/model-page-discovery.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
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: Checkout ComfyUI_frontend
uses: actions/checkout@v6
with:
path: ComfyUI_frontend

- name: Checkout workflow_templates
uses: actions/checkout@v6
with:
repository: Comfy-Org/workflow_templates
path: workflow_templates

- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: '20'

- name: Setup pnpm
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0
with:
version: latest
run_install: false

- name: Install dependencies
run: pnpm install --frozen-lockfile
working-directory: ComfyUI_frontend

- name: Run model discovery script
run: pnpm tsx apps/website/scripts/generate-models.ts out.json
working-directory: ComfyUI_frontend
env:
WORKFLOW_TEMPLATES_PATH: ${{ github.workspace }}/workflow_templates

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

NEW_SLUGS=$(node -e "
const fs = require('fs');
const models = JSON.parse(fs.readFileSync('out.json', 'utf8'));
const arr = Array.isArray(models) ? models : Object.values(models);
const slugs = arr.map(m => m.suggestedSlug ?? m.slug).filter(Boolean);
console.log(JSON.stringify(slugs));
")

EXISTING_SLUGS=$(node -e "
const fs = require('fs');
const content = fs.readFileSync(
'apps/website/src/config/models.ts',
'utf8'
);
const matches = [
...content.matchAll(/slug\s*:\s*['\"]([^'\"]+)['\"]/g)
];
const slugs = matches.map(m => m[1]);
console.log(JSON.stringify(slugs));
" 2>/dev/null || echo '[]')
Comment thread
coderabbitai[bot] marked this conversation as resolved.

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

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): $ADDED_SLUGS"
fi

- name: Open GitHub issue for new models
if: steps.compare.outputs.new_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 workflow_templates

The weekly model discovery scan found models not yet in
\`apps/website/src/config/models.ts\`.

### New slugs ($NEW_COUNT)

$SLUG_LIST

### Next steps

1. Review which of these warrant an SEO model page
2. Run \`pnpm tsx apps/website/scripts/generate-models.ts\` locally
3. Add entries to \`apps/website/src/config/models.ts\` via PR

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

Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
- name: No new models found
if: steps.compare.outputs.new_count == '0'
run: echo "No new models found — nothing to do."
Loading
Loading