Files
AISkills/flat-artist/flatrender-template-seo/SKILL.md
T
Soroush Asadi 4ffbcac9ee refactor: bundle the whole template suite under flat-artist/ + fix references
flat-artist is now the single container: all 16 template skills + the R&D
references/ moved inside flat-artist/. Cross-references updated — the orchestrator
points to bundled `<name>/SKILL.md`, sub-skills point to `../<name>/SKILL.md`,
and the R&D report path is relative. README catalog updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 19:31:53 +03:30

14 KiB
Raw Blame History

name, description
name description
flatrender-template-seo Classifies and merchandises a FlatRender video template when an admin or developer creates or publishes it. Decides the single primary category, the faceted tags, the SEO title/description/ keywords/slug, the Persian-first presentation copy, and the related-templates set — every choice driven by the template's actual design, editable content, and usability. Use whenever creating, editing, publishing, or seeding a template (project_container) and you must fill category_ids, tag_ids, keywords, slug, description, news_text, or write its catalog/detail copy. Persian (fa) is the source of truth; English (en) mirrors it 1:1.

FlatRender Template SEO & Taxonomy

Decide a template's category, tags, SEO, presentation copy, and related set — grounded in what the template actually is, not what you wish it ranked for. fa is canonical; en mirrors it.

When to use

  • An admin creates/edits a template in TemplatesAdmin.tsx, or you set fields on content.project_containers.
  • You run/extend scripts/seed_remotion_templates.py (which currently leaves SEO + taxonomy EMPTY — see below).
  • You publish (is_published = true) and need the template to be findable and well-presented.

Cross-reference: ../remotion-template-catalog/SKILL.md (template TYPE → pattern), ../remotion-template-composition/SKILL.md (what's editable, used to write "what you can customize"), ../persian-fonts/SKILL.md (RTL copy & Persian numerals).

The real data model (use these exact names — do not invent fields)

Template = content.project_containers. Admin sets via TemplatesAdmin.tsxPOST/PUT /v1/templates (Create/UpdateContainerRequest). Settable, SEO/taxonomy-relevant fields:

Purpose Admin label form key DTO DB column
Name (→ H1) نام name Name name
Slug اسلاگ slug Slug slug (CITEXT UNIQUE)
Description / presentation copy توضیحات description Description description
Keywords (opaque free text) کلمات کلیدی (سئو) keywords Keywords keywords (TEXT)
Announcement (NOT seo) متن خبر news_text NewsText news_text
Categories (M2M) دسته‌بندی‌ها category_ids[] CategoryIds container_categories
Tags (M2M) برچسب‌ها tag_ids[] TagIds container_tags
Primary mode حالت اصلی primary_mode PrimaryMode primary_mode enum

Hard constraints from the platform (work within these):

  • No meta_title / meta_description on a template. The only SEO string on a container is keywords. Per-page meta_title/meta_description/meta_keywords/bot_follow exist ONLY on content.categories (and on blogs). Workaround: put the page's discoverable meta intent into keywords; pack the human title/description into name + description; lift the rest of the meta from the assigned category's meta_* fields (so choose the category deliberately — its SEO is inherited).
  • The public detail page emits title-only metadata (templates/[id]/page.tsx{ title: "${name} — FlatRender" }), and the public mapper (admin-api.ts containerToAdminProject) DROPS keywords, coverImageUrl→OG, and hardcodes categoryName: undefined. So today your keywords/category never reach the public <head>. Still fill them: they power the backend list filters and are the fix-point when the frontend SEO gap is closed. When you can, also fix the mapper to surface keywords + cover OG; otherwise flag it.
  • No related-templates table/column/endpoint exists. "Related" is computed by re-querying GET /v1/templates?categoryId={guid} or ?tagSlug={slug} and excluding the current template. There is no precomputed set — pick the category/tags such that this query returns good neighbors.
  • Tags are not shown anywhere in the templates UI today, and the list page ignores the real DB categories: the sidebar is ~12 hardcoded buckets (11 real categories + "all"), and because the public mapper hardcodes categoryName: undefined, every template defaults to "social". Tags/categories still matter for the backend filters and future UI — set them correctly regardless.
  • demo_script_tag exists in DB + DTO but is NOT in the admin form — you cannot set it there.
  • Categories are a tree (parent_id); GET /v1/categories only eager-loads one child level. Keep the primary set flat (812). Tags have applies_to_mode; categories do not.

Step-by-step decision process

Run this for every template, in order. Each step feeds the next.

0. Read the design + content + usability. What does it output (format)? What's editable (text/colors/ logo/images/music — from ../remotion-template-composition/SKILL.md)? Who hires it and when? 2D or 3D (real Remotion build: SVG vs @remotion/three)? Which aspects exist (16:9 / 1:1 / 9:16 child projects)?

1. Category — pick exactly ONE primary. Category = the output format, never the occasion/industry. Test: which single shelf would a user expect this on? If the answer is a format (logo reveal, story, slideshow) it's the category; if it's when/why/who (Nowruz, real estate, teens) that's a TAG. Use the flat set: اینترو و لوگو / Intro & Logo · استوری و ریلز / Story & Reels · پست شبکه اجتماعی / Social Post · تبلیغ و پروموشن / Promo & Ad · اسلایدشو / Slideshow · معرفی محصول / Product Showcase · دعوت‌نامه و مناسبت / Invitation & Event · تیتراژ و زیرنویس / Titles & Lower-thirds · ارائه و اینفوگرافیک / Presentation & Infographic · یوتیوب و اینترو کانال / YouTube & Channel. No "Other"; no category with <5 templates. → set category_ids (the primary first; its sort = index 0).

2. Tags — 612, from a controlled fa↔en vocabulary, each TRUE of this design, across facets: use-case · occasion · industry · style/aesthetic · aspect ratio · color/mood · 2D-3D · audience. Empty facets are fine — don't invent tags. Aspect ratio is a TAG (one template, three outputs), never a separate catalog entry. Tag 3D only if it really renders 3D. Reuse existing content.tags (match by slug/latin_name); create a new tag only as a deliberate dictionary edit. → set tag_ids.

3. SEO title / description / keywords / slug.

  • Title (lives in name): {Template Name} | {Category} {use/occasion}. Keep the keyword in the first ~30 chars (Persian runs long); brand is appended by the layout, don't double it.
  • Description (lives in description, also reused as presentation copy): benefit + what + how-to-edit + soft CTA. fa 120155 / en 120158 chars. One natural keyword, no stuffing.
  • Keywords (lives in keywords): how Iranians actually search — «قالب آماده», «ساخت/دانلود استوری», «تیزر تبلیغاتی», «اینترو لوگو» — include loanword + Persian spelling both ways (استوری/Story, اینترو/Intro). 1 primary + 23 secondaries. Note: keywords is an opaque free-text column — the platform does NOT tokenize, split, index, or search on it (no comma parsing anywhere in form or backend). Commas are purely an authoring convention for human readability; write it however reads cleanest.
  • Slug (slug): the slugifier KEEPS Persian (no transliteration). Prefer a curated latin/transliterated keyword slug for shareable URLs (Persian slugs become %D9%82... when copied). Lowercase, hyphenated, short, keyword-bearing. Never change a published slug.

4. Presentation copy (write into description; structure it so it doubles as SEO + usability): H1 = human Persian name → one-line benefit hook → "این قالب برای چیست؟" (use-case+occasion+audience) → "چه چیزهایی قابل تغییر است؟" (bullet the REAL editable fields — don't claim editable music if there's none) → "چطور بسازم؟" (انتخاب → ویرایش متن/رنگ → دانلود) → spec strip (aspects, duration, 2D/3D). Benefit before feature; second person; every claim true of THIS template.

5. Related set (caller-computed; pick category/tags so this works). Query GET /v1/templates?categoryId=... then ?tagSlug=...; rank occasion > category+use-case > style > industry; show 68; exclude this template's own aspect siblings (dedupe by template id); cap same-style clones; label pack-mates «از همین مجموعه».

fa / en parity & RTL rules

  • Fill fa first, then en as a faithful mirror. Two self-consistent pages — never a Persian page with an English title. Same template id; localized labels resolve per current locale.
  • Persian uses Vazirmatn / RTL (see ../persian-fonts/SKILL.md); use Persian digits in user-facing copy; don't fight the [dir="rtl"] block. Tag/category slugs stay latin for stability; display names are localized.
  • hreflang fa↔en + x-default is the right target even though the detail page doesn't emit it yet — flag the gap.

Worked examples

A. Nowruz 3D greeting (تبریک نوروز سه‌بعدی)

  • Category: دعوت‌نامه و مناسبت / Invitation & Event.
  • Tags: occasion=نوروز/Nowruz · use-case=تبریک/greeting · style=لاکچری/luxury · 2D-3D=سه‌بعدی/3D · aspect=۹:۱۶,۱:۱,۱۶:۹ · color=طلایی/gold · audience=کسب‌وکار کوچک/small business.
  • name (fa): «قالب تبریک نوروز سه‌بعدی | مناسبت و تبریک» · (en): "Nowruz 3D Greeting Template | Invitation & Event"
  • description (fa): «کارت تبریک نوروزی سه‌بعدی و لاکچری؛ نام برند، متن تبریک و رنگ طلایی را در چند دقیقه شخصی‌سازی کن و ویدیوی آماده انتشار بساز.»
  • keywords: قالب تبریک نوروز, تبریک عید, نوروز سه بعدی, دانلود کارت تبریک, Nowruz greeting
  • slug: nowruz-3d-greeting · Related: other نوروز items first, then Invitation & Event + greeting.

B. Instagram sale promo (پروموشن فروش ویژه اینستاگرام)

  • Category: استوری و ریلز / Story & Reels (output is a 9:16 vertical — format wins over "sale").
  • Tags: use-case=فروش ویژه/flash sale · industry=فروشگاه آنلاین/e-shop · style=نئون/neon · aspect=عمودی ۹:۱۶/vertical · color=انرژیک/energetic · audience=فروشگاه/store · 2D.
  • name (fa): «قالب استوری فروش ویژه | استوری و ریلز» · (en): "Flash-Sale Story Template | Story & Reels"
  • description (fa): «قالب آماده استوری برای اعلام تخفیف و فروش ویژه؛ متن، قیمت، رنگ و لوگوی فروشگاهت را جایگزین کن و استوری حرفه‌ای ۹:۱۶ بساز.»
  • keywords: قالب استوری فروش, تخفیف اینستاگرام, ساخت استوری تبلیغاتی, دانلود قالب استوری, flash sale reels
  • slug: instagram-flash-sale-story · Related: same use-case (flash sale) across categories, then Story & Reels.

C. Corporate logo reveal (لوگو موشن شرکتی)

  • Category: اینترو و لوگو / Intro & Logo.
  • Tags: use-case=معرفی برند/brand intro · style=مینیمال/minimal · audience=شرکتی/corporate · aspect=۱۶:۹,۱:۱,۹:۱۶ · color=آبی/blue · 2D (or 3D only if it truly is).
  • name (fa): «قالب لوگو موشن شرکتی | اینترو و لوگو» · (en): "Corporate Logo Reveal Template | Intro & Logo"
  • description (fa): «اینترو حرفه‌ای برای نمایش لوگوی شرکت؛ لوگو، نام برند و رنگ سازمانی را وارد کن و یک اینترو تمیز و مینیمال بساز.»
  • keywords: لوگو موشن, اینترو لوگو, ساخت اینترو, لوگو موشن شرکتی, logo motion intro
  • slug: corporate-logo-reveal · Related: other Intro & Logo + brand-intro use-case.

Where each value goes

  • Admin UI (TemplatesAdmin.tsx): name→name, slug→slug, presentation copy→description, SEO keywords→keywords, categories→category_ids (multi-select chips), tags→tag_ids, mode→primary_mode. Saves via /api/admin/resource/templates/v1/templates.
  • Categories/Tags are managed on their own admin pages (admin-resources.tsx/v1/categories, /v1/tags); category-level meta_title/meta_description/meta_keywords/bot_follow live there — set them once per category.
  • Seeder (scripts/seed_remotion_templates.py): currently SETS only name/slug/description/image/demo*/ is_published/primary_mode/sort and inserts ZERO container_categories / container_tags and NO keywords. To seed SEO+taxonomy: add keywords to the project_containers INSERT, and add INSERTs into content.container_categories (with sort = index) and content.container_tags. Otherwise each seeded template ships with no keywords, no category, no tags — invisible to backend category/tag filters until an admin opens it and assigns them.

Final checklist

  • Exactly ONE primary category, format-based, from the flat set → category_ids (primary at index 0).
  • 612 tags, controlled fa↔en vocab, each true of the design, across facets; aspect is a tag → tag_ids.
  • name carries the SEO title (keyword in first ~30 chars; no doubled brand).
  • description = benefit→for-what→customize(real fields)→how-to→specs; doubles as the meta description.
  • keywords = 1 primary + 23 secondary, loanword + Persian spelling (free text — commas are just an authoring convention, not parsed).
  • slug = curated latin keyword slug, stable, never changed after publish.
  • fa filled first, en a 1:1 mirror; Persian digits + RTL in copy.
  • Related works via category/tag query; own aspect siblings excluded.
  • Gaps flagged: no template meta_*, public mapper drops keywords/cover OG/category, no related table, no hreflang on detail page — note these where relevant rather than pretending they're set.