Skip to content

Latest commit

 

History

History
521 lines (376 loc) · 17.9 KB

File metadata and controls

521 lines (376 loc) · 17.9 KB

Site Directory - Agent Instructions

Overview

Astro 5 static site with Vue 3 interactive islands. Consumes template data from ../templates/ and deploys to templates.comfy.org via Vercel.

⚠️ Scope Boundaries

This site/ package is independent from the parent workflow_templates/ Nx monorepo.

When adding CI workflows, scripts, or configuration:

  • All CI workflows MUST use paths: ['site/**'] triggers
  • All CI jobs MUST use working-directory: site
  • All pnpm-lock.yaml cache paths MUST reference site/pnpm-lock.yaml
  • Do NOT modify parent monorepo configs (nx.json, root package.json, etc.)
  • Do NOT add workflows that trigger on changes outside site/

Commands

Setup

# Install dependencies (required before any other command)
pnpm install

Development

# Start Astro dev server (localhost:4321)
pnpm run dev

# Preview production build locally
pnpm run preview

Build

# Full build (runs prebuild: sync + sync:tutorials + generate:ai + generate:previews)
pnpm run build

# Run Astro CLI directly
pnpm run astro

Sync Commands

# Sync ALL templates for all locales (200+ templates × 11 languages)
pnpm run sync

# Sync top 50 templates by usage (faster for development)
pnpm run sync -- --top-50

# Sync top N templates by usage
pnpm run sync -- --limit 100

# Sync English only (faster for development)
pnpm run sync:en-only

# Sync a specific locale
pnpm run sync:locale zh
pnpm run sync:locale ja

# Sync tutorials from docs repo (builds knowledge base for AI context)
pnpm run sync:tutorials

AI Content Generation

# Full AI generation for all templates (requires OPENAI_API_KEY)
pnpm run generate:ai

# Test mode - process first template only
pnpm run generate:ai:test

# Generate for specific template only
pnpm run generate:ai:template
# Or with explicit template name:
pnpm run generate:ai -- --template <name>

# Skip AI calls (use placeholder content)
pnpm run generate:ai -- --skip-ai

# Force regenerate all (ignore cache)
pnpm run generate:ai:force
# Or:
pnpm run generate:ai -- --force

# Dry run - show what would be regenerated without making changes
pnpm run generate:ai:dry-run
# Or:
pnpm run generate:ai -- --dry-run

# Combined: test specific template without AI
pnpm run generate:ai -- --template <name> --skip-ai

Cache Management

# View cache statistics (entries, size, model usage, timeline)
pnpm run cache:status

# Clear all cached AI content (requires --force or -f)
pnpm run cache:clear --force
# Or preview what would be deleted:
pnpm run cache:clear --dry-run

Preview Generation

# Generate preview images for templates
pnpm run generate:previews

# Generate OG (Open Graph) images for social sharing
pnpm run generate:og

Testing

# Run E2E tests with Playwright
pnpm run test:e2e

# Run E2E tests in headed mode (visible browser)
pnpm run test:e2e:headed

# Run visual regression tests
pnpm run test:visual

# Update visual regression snapshots
pnpm run test:visual:update

Code Quality

# Run ESLint (fails on warnings)
pnpm run lint

# Run ESLint with auto-fix
pnpm run lint:fix

# Format all files with Prettier
pnpm run format

# Check formatting without writing
pnpm run format:check

SEO & Research Tools

# Validate sitemap structure and URLs (run after build)
pnpm run validate:sitemap

# Run SEO audit on built site (checks meta, OG, structured data)
pnpm run audit:seo

# Research competitor keywords (outputs to docs/competitor-analysis.md)
pnpm run research:competitors

# Research "People Also Ask" questions (outputs to docs/paa-research.md)
pnpm run research:paa

Full Prebuild Pipeline

The prebuild script runs automatically before build and executes in parallel phases:

Phase 1 (parallel):

  • sync - Sync template metadata
  • sync:tutorials - Sync tutorial content

Phase 2 (parallel):

  • generate:ai - Generate AI content
  • generate:previews - Generate preview images
  • generate:og - Generate OG images

Build Profiling

# Run full build with detailed timing breakdown
pnpm run build:profile

# Run sequential prebuild (for debugging)
pnpm run prebuild:sequential

Project Structure

site/
├── scripts/
│   ├── generate-ai.ts       # Main AI generation pipeline
│   └── sync-tutorials.ts    # Syncs tutorials from docs repo
├── knowledge/
│   ├── prompts/
│   │   ├── system.md        # Base system prompt
│   │   ├── tutorial.md      # Tutorial content template
│   │   ├── showcase.md      # Showcase content template
│   │   ├── comparison.md    # Comparison content template
│   │   └── breakthrough.md  # Breakthrough content template
│   ├── models/              # Model-specific documentation
│   ├── concepts/            # Domain concept documentation
│   └── tutorials/           # Synced tutorials from docs repo
├── overrides/templates/     # Human-edited content (preserved)
├── src/content/templates/   # Generated content (git-ignored)
└── .content-cache/          # AI generation cache (git-ignored)

Content Template Types

When generating content, select appropriate template based on:

  • tutorial: Default for most templates, step-by-step guides
  • showcase: Templates with strong visual outputs
  • comparison: Templates that compete with alternatives
  • breakthrough: New model releases, cutting-edge features

Key Features

  • Content template selection: Automatically selects tutorial/showcase/comparison/breakthrough based on template metadata
  • Tutorial context injection: Matches templates to relevant docs.comfy.org tutorials for better AI context
  • Quality validation: Checks word count, step count, FAQ count, keyword presence, and meta description length
  • Smart caching with versioning:
    • Cache manifest tracks template hash, prompt version hash, generation timestamp, and model used
    • Prompt changes automatically invalidate affected cache entries
    • --force flag to regenerate all content
    • --dry-run flag to preview what would be regenerated
    • Cache statistics output (hits, misses, regenerated)

Key Files

  • generate-ai.ts - Main generation pipeline with CLI options
  • sync-tutorials.ts - Syncs tutorials from docs repo to knowledge base
  • sync-templates.ts - Syncs template metadata (supports 11 languages)
  • knowledge/prompts/system.md - Base GPT-4o system prompt
  • ../docs/ai-content-generation-strategy.md - Full strategy documentation

Internationalization (i18n)

Supported Languages

Code Language Native Name RTL
en English English No
zh Chinese (Simplified) 简体中文 No
zh-TW Chinese (Traditional) 繁體中文 No
ja Japanese 日本語 No
ko Korean 한국어 No
es Spanish Español No
fr French Français No
ru Russian Русский No
tr Turkish Türkçe No
ar Arabic العربية Yes
pt-BR Portuguese (Brazil) Português No

URL Structure

  • English (default): /workflows/, /workflows/{slug}/
  • Other locales: /{locale}/workflows/, /{locale}/workflows/{slug}/

Key i18n Files

  • src/i18n/config.ts - Language definitions, locale list
  • src/i18n/utils.ts - URL localization, locale detection
  • src/i18n/ui.ts - UI string translations
  • src/components/HreflangTags.astro - SEO hreflang tags
  • src/lib/templates.ts - Template utilities with locale support

Content Flow

  1. Source data: ../templates/index.{lang}.json (12 files)
  2. Sync script reads all locale files and writes to src/content/templates/
  3. English templates at root, localized at src/content/templates/{locale}/
  4. Pages generated at /{locale}/workflows/ via [locale]/templates/ routes

Environment Variables

Variable Required Description
OPENAI_API_KEY For AI gen OpenAI API key
SKIP_AI_GENERATION Optional Set true for placeholder mode
PUBLIC_HUB_API_URL Optional Hub API base URL used by local builds and manual runs
PUBLIC_COMFY_CLOUD_URL Optional Comfy Cloud app URL used for CTA links

The site code and build scripts still read PUBLIC_HUB_API_URL for hub API calls and PUBLIC_COMFY_CLOUD_URL for CTA links.

  • CI preview builds map HUB_API_URL_PREVIEW to PUBLIC_HUB_API_URL
  • CI production builds map HUB_API_URL_PRODUCTION to PUBLIC_HUB_API_URL
  • CI preview builds map COMFY_CLOUD_URL_PREVIEW to PUBLIC_COMFY_CLOUD_URL
  • CI production builds map COMFY_CLOUD_URL_PRODUCTION to PUBLIC_COMFY_CLOUD_URL
  • Local development should set these public env vars directly when a non-default backend is needed

Mirror the production values in Vercel Project Settings for PUBLIC_HUB_API_URL and PUBLIC_COMFY_CLOUD_URL. cron-rebuild-site.yml triggers a Vercel deploy hook, so scheduled rebuilds use Vercel-managed environment variables rather than the GitHub Actions secret mapping.

CI/CD Workflows

GitHub Actions workflows live at the repo root (.github/workflows/):

Workflow Trigger Description
lint-site.yml Push/PR to site/** Runs lint and format checks
e2e-tests-site.yml Push/PR to site/** Runs Playwright E2E tests
visual-regression-site.yml Push/PR to site/** Runs visual regression tests
seo-audit-site.yml Push/PR to site/** Runs SEO audit on built site
lighthouse.yml Push/PR to site/** Runs Lighthouse CI checks
deploy-site.yml Manual dispatch Deploys to Vercel production

Preview deployments are handled automatically by Vercel on every PR.

Pre-commit Hooks

Husky + lint-staged is configured for pre-commit hooks:

  • JS/TS/Astro files: ESLint fix + Prettier
  • JSON/MD/YAML/CSS files: Prettier

Run pnpm install to set up hooks (via prepare script).

Related Documentation

  • docs/ai-content-generation-strategy.md - Content strategy
  • docs/PRD.md - Product requirements
  • docs/TDD.md - Technical design
  • docs/ROADMAP.md - AI content generation roadmap
  • docs/design-integration-guide.md - REQUIRED READING when implementing Figma designs
  • docs/seo-setup-guide.md - Search engine setup instructions

Vue 3 & Astro Coding Standards

All Vue components MUST use standard Vue 3 Composition API and idiomatic Astro patterns. Write senior-level, production-quality code.

Vue 3 — Required Patterns

  • <script setup lang="ts"> for all components — no Options API
  • Standard reactivity: ref(), computed(), watch(), watchEffect()
  • Props via defineProps<T>(), emits via defineEmits<T>()
  • Cross-component state via shared composables in src/composables/ using module-level reactive refs
  • Template refs via useTemplateRef() or ref<HTMLElement | null>(null)
  • Lifecycle: onMounted(), onUnmounted() — always clean up listeners

Vue 3 — Forbidden Patterns

  • document.dispatchEvent(new CustomEvent(...)) for component communication — use composables
  • document.addEventListener(...) to listen for custom events from other Vue components
  • Event bus libraries or mitt — use shared composables with reactive state instead
  • Options API (data(), methods, computed:, watch: as object)
  • this.$emit, this.$refs, or any this-based API
  • Mixins — use composables

Astro — Required Patterns

  • Astro components (.astro) for static/SSR content, Vue islands (client:load/client:visible) for interactivity
  • Pass data from Astro to Vue via props only — serialize to plain objects
  • For Astro-to-Vue runtime communication (e.g. a button in .astro triggering Vue state), attach event listeners to specific DOM elements by ID inside the Vue component's onMounted() — do NOT use inline <script> tags with dispatchEvent
  • Cross-island state sharing via shared composables (module-level refs are singletons in the browser bundle)

Island Architecture (Astro + Vue 3)

Astro renders pages as static HTML at build time. Interactive sections use Vue 3 components mounted as islands via client:* directives. Each island is an independent Vue app instance — they do NOT share a Vue app context.

When to use .astro vs .vue

Use case Component type Notes
Static content, layouts, SEO .astro No client JS shipped
Interactive UI needed on page load .vue + client:load Filters, search, drawers
Interactive UI below the fold .vue + client:visible Hydrates when scrolled into view
SSR-only Vue (no interactivity) .vue without client:* Renders at build, zero client JS

Data flow: Astro → Vue island

Astro pages own data fetching. Serialize content collection entries to plain JSON-compatible objects before passing as props:

---
const templates = await getCollection('templates');
const serialized = templates.map((t) => ({
  name: t.data.name,
  title: t.data.title,
  // ... only plain values: string, number, boolean, arrays, plain objects
}));
---

<MyVueIsland client:load templates={serialized} locale={locale} />

Vue islands receive data via defineProps<T>(). Never pass Date, Map, Set, class instances, or functions — only JSON-serializable data.

Cross-island communication

provide/inject, $emit, and $parent do NOT work across islands (separate Vue apps). Use shared composables with module-level reactive state in src/composables/:

// src/composables/useHubStore.ts
import { ref } from 'vue';

const mobileDrawerOpen = ref(false); // module-level singleton
const searchFocusTrigger = ref(0);

export function useHubStore() {
  return {
    mobileDrawerOpen,
    searchFocusTrigger,
    toggleMobileDrawer() {
      mobileDrawerOpen.value = !mobileDrawerOpen.value;
    },
    requestSearchFocus() {
      searchFocusTrigger.value++;
    },
  };
}

Both islands import the same composable → share the same refs → fully reactive:

// Island A (HubBrowse.vue)
const store = useHubStore();
store.requestSearchFocus(); // triggers watcher in Island B

// Island B (SearchPopover.vue)
const store = useHubStore();
watch(
  () => store.searchFocusTrigger.value,
  () => {
    inputRef.value?.focus();
  }
);

Astro → Vue runtime bridge

When a DOM element in .astro markup (e.g. a hamburger button rendered server-side) needs to trigger Vue state, the Vue island owns the listener — not the Astro file:

// In the Vue island's <script setup>
const store = useHubStore();

onMounted(() => {
  document.getElementById('astro-button-id')?.addEventListener('click', store.someAction);
});

onUnmounted(() => {
  document.getElementById('astro-button-id')?.removeEventListener('click', store.someAction);
});

Do NOT add inline <script> tags in .astro files that dispatchEvent(new CustomEvent(...)). The Vue island is responsible for bridging to any Astro-rendered DOM elements.

Within a single island: standard Vue

Components within the same island (parent → child Vue components) use normal Vue patterns:

  • Props down: defineProps<T>()
  • Events up: defineEmits<T>() + @event in parent template
  • Provide/inject: works within the same island's component tree
  • Composables: for shared logic within the island

⚠️ Design Implementation Warning

When implementing designs from Figma, do NOT remove or modify these critical components:

  1. SEO: SEOHead.astro, HreflangTags.astro, structuredData prop
  2. i18n: t() function calls, localizeUrl(), lang/dir attributes
  3. Telemetry: <Analytics />, vitals script

See docs/design-integration-guide.md for the complete checklist.

Build Optimization Notes

Optimizations Applied (Feb 2026)

Astro Config (astro.config.mjs):

  • Enabled build.concurrency using all available CPU cores for parallel page generation
  • Configured Sharp image service with pixel limits to prevent memory issues
  • Enabled experimental.responsiveImages for automatic srcset generation
  • Added Vite optimizations: manual chunking, dependency pre-bundling, disabled dev sourcemaps

Parallel Prebuild (scripts/prebuild-parallel.ts):

  • Phase 1 runs sync + sync:tutorials in parallel (independent data sources)
  • Phase 2 runs generate:ai + generate:previews in parallel
  • Outputs timing breakdown and time saved vs sequential execution

Preview Generation (scripts/generate-previews.ts):

  • Uses parallel workers based on CPU count (os.cpus().length - 1)
  • Incremental generation: only regenerates when workflow file is newer than preview
  • Outputs timing summary

Build Profiling (pnpm run build:profile):

  • Runs full build pipeline with detailed timing for each phase
  • Reports output file count and size
  • Useful for identifying bottlenecks

Caching:

  • AI content cached in .content-cache/ with hash-based invalidation
  • Preview images regenerated only when source workflow changes
  • Astro's built-in caching for content collections

Tips for faster builds:

  • Use --skip-ai flag during development if AI content isn't needed
  • Run pnpm run build:profile to identify slow phases
  • Preview generation is I/O bound; consider SSD or ramdisk for large sites