Skip to content

Latest commit

 

History

History
114 lines (103 loc) · 7.95 KB

File metadata and controls

114 lines (103 loc) · 7.95 KB

Comfy Labs — project memory

A gallery website ("app store") hosting many tiny AI apps. Each gallery card opens a self-contained app (text-to-image, logo maker, …) that runs its own ComfyUI Cloud workflow on cloud GPUs. The whole point is modularity: adding a new app = drop 2 files + 1 registry line.

Stack & deploy

  • Next.js 16 (App Router, Turbopack), React 19, TypeScript. Path alias @/* → repo root.
  • Hosting: Vercel project comfy-cloud-app (team pablos-projects-6ecde209). Live URL: https://comfy-cloud-app.vercel.app
  • GitHub: PabloWiedemann/comfy-labs (private). Git is connected to Vercel — git push to main auto-deploys. Prefer pushing over vercel deploy.
  • Local folder: ~/Dev/comfy-labs.
  • Env vars (set in Vercel Production + Development, and in local .env.local, which is gitignored):
    • COMFY_API_KEY — Comfy Cloud key (format comfyui-…, from platform.comfy.org/profile/api-keys)
    • COMFY_API_BASEhttps://cloud.comfy.org
    • Preview env vars are NOT set (CLI needed an interactive git-branch prompt); add via dashboard if PR previews are needed.

Architecture

apps/<slug>/app.config.ts   default-exports an AppDefinition (metadata + input→node mapping)
apps/<slug>/workflow.json   ComfyUI workflow in API format
lib/types.ts                AppDefinition, InputField, Workflow types
lib/registry.ts             imports each app; getAllApps() / getApp(slug)  ← grows per app
lib/comfy-client.ts         verified Cloud API wrapper                      ← DO NOT edit per app
lib/build-workflow.ts       buildWorkflow(app, userInputs) patches workflow ← DO NOT edit per app
hooks/useGenerate.ts        generate(slug, inputs) → poll → result          ← DO NOT edit per app
app/page.tsx                gallery of cards (maps registry)
app/apps/[slug]/page.tsx    server: lookup slug, strip workflow, 404 if missing
app/apps/[slug]/AppRunner.tsx  client: renders form from config, runs useGenerate
app/api/generate/route.ts   POST { slug, inputs } → registry → buildWorkflow → submit
app/api/status/[id]/route.ts   GET → reads job, resolves signed output URL  ← app-agnostic
app/api/cancel/[id]/route.ts   POST → cancel job                            ← app-agnostic

Request flow: AppRunneruseGenerate(slug, inputs)POST /api/generate {slug,inputs}getApp(slug)buildWorkflowsubmitWorkflow → poll /api/status/{jobId} → signed URL → render.

ComfyUI Cloud API — VERIFIED CONTRACT (docs were wrong; trust this)

Probed live against https://cloud.comfy.org. All encoded in lib/comfy-client.ts:

  1. Submit: POST /api/prompt with header X-API-Key AND body { prompt: <workflow>, extra_data: { api_key_comfy_org: <KEY> } }. ⚠️ Partner/API nodes (Gemini, Flux, Kling, …) FAIL with "Unauthorized: Please login first" unless extra_data.api_key_comfy_org is present. The header alone is not enough.
  2. Status + outputs: GET /api/jobs/{prompt_id} (PLURAL jobs). Single source of truth — returns status, outputs (with filenames), preview_output, error_message. The documented /api/job/{id}/status (singular) only returns a bare status string — don't use it.
  3. Terminal status string is "success", not "completed". Raw lifecycle: queued_limited → allocated → executing → success. normalizeStatus() maps these.
  4. Output file: GET /api/view?filename=&subfolder=&type=output → 302 redirect to a signed storage.googleapis.com URL (no auth needed). We follow it manually and hand the URL to the browser.

The seed apps (all use the same verified node)

All three use the GeminiNanoBanana2 core node (model option "Nano Banana 2 (Gemini 3.1 Flash Image)"), differing only by system_prompt + presets:

  • text-to-image — general
  • logo-maker — clean vector logos
  • sticker-maker — die-cut glossy stickers

GeminiNanoBanana2 required inputs: prompt (STRING), model (COMBO, the option above), seed (INT), aspect_ratio (COMBO: auto,1:1,2:3,3:2,3:4,4:3,4:5,5:4,9:16,16:9,21:9), resolution (COMBO: 1K,2K,4K), response_modalities (COMBO: IMAGE | IMAGE+TEXT), thinking_level (COMBO: MINIMAL | HIGH). Output index 0 is IMAGE → feed into SaveImage.

Discovering nodes / models / templates for NEW apps

Use the ComfyUI Cloud MCP tools (this repo's owner has the MCP installed):

  • search_nodes (set api_node_only: true for partner nodes) → exact class_type + input specs
  • search_templates → ready-made workflows from comfy.org (try first)
  • search_models → checkpoints/loras
  • partner_generate / submit_workflow → run workflows directly to test before wiring into an app Always verify a new workflow runs (submit → /api/jobs/{id} → success) BEFORE building the app UI.

Design system (UI) — ALWAYS follow this

The UI is light mode only, built on shadcn/ui (Base UI variant) + Tailwind v4, with a soft light-purple theme (Figma Make feel). (It started as a Wise-green system; the structure is the same, the hue moved to purple.)

  • Component library is the source of truth. Build UI ONLY from components in components/ui/* (button, card, input, textarea, select, label, badge, skeleton, …). Do NOT hand-roll buttons/inputs/cards or add bespoke CSS for them. Need a component that doesn't exist yet? Add it with npx shadcn@latest add <name> — never reinvent it.
  • Reuse before creating. Check components/ui/ first; compose existing primitives.
  • Theme tokens live in app/globals.css (:root). Use semantic classes (bg-primary, text-brand, text-muted-foreground, border-border, bg-accent) — never hardcode hex colors in components. Palette: text is near-black "Midnight" #2c2b30 (--foreground/--brand, headings included) or grey --muted-foreground #6e6e78; the accent is "Hydro Blue" #4d27f7 (--primary/--ring, white label on it) used scarcely on CTAs/focus/links; --secondary #dfdcfc ("Purple") and --accent #eeebfa ("Light Purple") are the pale surfaces; --border/--input = "Light Grey" #dcdae1; --background #fafafb off-white.
  • Look: soft, "AI"-creative-tool depth (Figma Make-like). Buttons are rounded-xl; cards use a large radius (rounded-2xl) with drop shadows (shadow-card / shadow-card-hover utilities) for depth — NOT border strokes (avoid visible borders; let off-white/white contrast + shadow do the separation). Deep-indigo headings, muted body copy, generous whitespace.
  • Animated background: a full-viewport WebGL shader gradient (app/components/ShaderBackground.tsx) mounted once globally in app/layout.tsx. Mostly off-white with a small, deep "Lilac" purple glow
    • a lightened Hydro-Blue shimmer; drifts/animates subtly and reacts to mouse + scroll. Page roots are transparent so the shader shows through. (The old public/hero.jpg backdrop was removed.)
  • Fonts: Satoshi (loaded from Fontshare in globals.css, set as --font-sans). Don't add other fonts.
  • app/page.tsx (gallery) and app/apps/[slug]/AppRunner.tsx are the reference implementations — match their patterns when adding screens. AppRunner already renders any app's form from its config, so new apps usually need ZERO UI work.

Conventions

  • Never commit secrets. .env.local and .vercel are gitignored; only .env.example is tracked.
  • Commit message footer: Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>.
  • Verify before deploy: npm run build (typecheck + routes) must pass.
  • To add an app, use the add-comfy-app skill in .claude/skills/.

Out of scope (future ideas)

  • Real thumbnail images (currently emoji).
  • Auth / rate-limiting / per-user credits (API key is shared server-side — anyone with the URL spends the owner's credits).
  • Image-input apps (img2img, sketch-to-3D): lib/comfy-client.ts already has uploadImage(); needs an image input type wired into AppRunner + a verified workflow.