logo-management

Logo Management Skill

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "logo-management" with this command: npx skills add sgcarstrends/sgcarstrends/sgcarstrends-sgcarstrends-logo-management

Logo Management Skill

Logo package lives in packages/logos/ .

packages/logos/ ├── src/ │ ├── services/logo/ # fetch.ts, list.ts, download.ts │ ├── infra/storage/ # Vercel Blob service │ └── utils/normalize.ts # Brand name normalization └── scripts/ # fetch-logos.ts, upload-to-blob.ts

Brand Name Normalization

// packages/logos/src/utils/normalize.ts export function normalizeBrandName(brand: string): string { return brand.toLowerCase().trim() .replace(/\s+/g, "-") // Spaces → hyphens .replace(/[^a-z0-9-]/g, "") // Remove special chars .replace(/-+/g, "-"); // Dedupe hyphens }

// Brand aliases for common variations const BRAND_ALIASES: Record<string, string> = { "mercedes": "mercedes-benz", "vw": "volkswagen", "landrover": "land-rover", };

Logo Fetching

// packages/logos/src/services/logo/fetch.ts export async function getLogoUrl(brand: string): Promise<string | null> { const normalizedBrand = normalizeBrandName(brand); const cacheKey = logo:url:${normalizedBrand};

// Check Redis cache const cached = await redis.get<string>(cacheKey); if (cached) return cached;

// Try different extensions for (const ext of ["svg", "png", "jpg"]) { const url = ${LOGO_CDN_BASE}/${normalizedBrand}.${ext}; const response = await fetch(url, { method: "HEAD" }); if (response.ok) { await redis.set(cacheKey, url, { ex: 7 * 24 * 60 * 60 }); return url; } } return null; }

Vercel Blob Storage

// packages/logos/src/infra/storage/blob.ts import { put, list, del } from "@vercel/blob";

export class LogoBlobService { async upload(brand: string, file: Buffer): Promise<string> { const fileName = logos/${normalizeBrandName(brand)}.png; const blob = await put(fileName, file, { access: "public", addRandomSuffix: false }); await redis.set(logo:blob:${normalizeBrandName(brand)}, blob.url, { ex: 7 * 24 * 60 * 60 }); return blob.url; }

async list(): Promise<string[]> { const { blobs } = await list({ prefix: "logos" }); return blobs.map(blob => blob.url); }

async delete(brand: string): Promise<void> { await del(logos/${normalizeBrandName(brand)}.png); await redis.del(logo:blob:${normalizeBrandName(brand)}); } }

Scripts

Fetch logos from CDN

pnpm -F @sgcarstrends/logos fetch-logos

Scrape logos from websites

pnpm -F @sgcarstrends/logos scrape-logos

Usage in Apps

// API route import { getLogoUrl } from "@sgcarstrends/logos";

app.get("/logos/:brand", async (c) => { const logoUrl = await getLogoUrl(c.req.param("brand")); if (!logoUrl) return c.json({ error: "Logo not found" }, 404); return c.json({ logoUrl }); });

// React component <Image src={logoUrl || "/images/logo-placeholder.png"} alt={${brand} logo} width={64} height={64} />

Environment Variables

BLOB_READ_WRITE_TOKEN=vercel_blob_token_here

Best Practices

  • Normalize Names: Always normalize brand names before lookup

  • Cache Aggressively: Use multi-layer caching (memory → Redis → Blob)

  • Fallbacks: Provide placeholder for missing logos

  • Batch Operations: Use batch uploads for multiple logos

References

  • Vercel Blob: Use Context7 for latest docs

  • packages/logos/CLAUDE.md for package details

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

framer-motion-animations

No summary provided by upstream source.

Repository SourceNeeds Review
General

api-testing

No summary provided by upstream source.

Repository SourceNeeds Review
General

design-language-system

No summary provided by upstream source.

Repository SourceNeeds Review
General

chart-implementation

No summary provided by upstream source.

Repository SourceNeeds Review