src / media / designSystems.ts

/**
 * @file Design systems catalog — 58 real-world DESIGN.md files from awesome-design-md.
 * The model auto-selects the best match based on conversation context.
 */

import { tool, type Tool, type ToolsProviderController } from "@lmstudio/sdk";
import { z } from "zod";
import { readFile, writeFile, mkdir } from "fs/promises";
import { join } from "path";
import * as os from "os";

const REPO_RAW_BASE = "https://raw.githubusercontent.com/VoltAgent/awesome-design-md/main/designs";
const FETCH_TIMEOUT_MS = 10_000;

interface DesignEntry {
  id: string;
  name: string;
  category: string;
  keywords: string[];
}

const DESIGN_SYSTEMS: DesignEntry[] = [
  // AI & Machine Learning
  { id: "claude", name: "Claude", category: "AI", keywords: ["anthropic", "ai", "chat", "clean", "warm"] },
  { id: "cohere", name: "Cohere", category: "AI", keywords: ["ai", "nlp", "enterprise", "coral"] },
  { id: "elevenlabs", name: "ElevenLabs", category: "AI", keywords: ["ai", "audio", "voice", "dark", "futuristic"] },
  { id: "minimax", name: "Minimax", category: "AI", keywords: ["ai", "generative", "gradient"] },
  { id: "mistral.ai", name: "Mistral AI", category: "AI", keywords: ["ai", "open-source", "orange", "bold"] },
  { id: "ollama", name: "Ollama", category: "AI", keywords: ["ai", "local", "minimal", "terminal"] },
  { id: "opencode.ai", name: "OpenCode AI", category: "AI", keywords: ["ai", "code", "developer"] },
  { id: "replicate", name: "Replicate", category: "AI", keywords: ["ai", "api", "models", "clean"] },
  { id: "runwayml", name: "RunwayML", category: "AI", keywords: ["ai", "video", "creative", "cinematic", "dark", "film"] },
  { id: "together.ai", name: "Together AI", category: "AI", keywords: ["ai", "inference", "gradient", "purple"] },
  { id: "voltagent", name: "VoltAgent", category: "AI", keywords: ["ai", "agent", "framework", "green"] },
  { id: "x.ai", name: "xAI", category: "AI", keywords: ["ai", "grok", "dark", "futuristic", "elon"] },
  // Developer Tools
  { id: "cursor", name: "Cursor", category: "Developer", keywords: ["ide", "code", "editor", "dark", "modern"] },
  { id: "expo", name: "Expo", category: "Developer", keywords: ["react-native", "mobile", "app", "blue"] },
  { id: "linear.app", name: "Linear", category: "Developer", keywords: ["project", "management", "minimal", "clean", "purple"] },
  { id: "lovable", name: "Lovable", category: "Developer", keywords: ["ai", "builder", "friendly", "pink"] },
  { id: "mintlify", name: "Mintlify", category: "Developer", keywords: ["docs", "documentation", "green", "clean"] },
  { id: "posthog", name: "PostHog", category: "Developer", keywords: ["analytics", "product", "quirky", "hedgehog"] },
  { id: "raycast", name: "Raycast", category: "Developer", keywords: ["launcher", "productivity", "dark", "gradient"] },
  { id: "resend", name: "Resend", category: "Developer", keywords: ["email", "api", "minimal", "clean"] },
  { id: "sentry", name: "Sentry", category: "Developer", keywords: ["errors", "monitoring", "dark", "purple"] },
  { id: "supabase", name: "Supabase", category: "Developer", keywords: ["database", "backend", "green", "dark"] },
  { id: "superhuman", name: "Superhuman", category: "Developer", keywords: ["email", "productivity", "luxurious", "fast"] },
  { id: "vercel", name: "Vercel", category: "Developer", keywords: ["deploy", "frontend", "black", "white", "minimal", "clean"] },
  { id: "warp", name: "Warp", category: "Developer", keywords: ["terminal", "dark", "gradient", "modern"] },
  { id: "zapier", name: "Zapier", category: "Developer", keywords: ["automation", "workflows", "orange", "friendly"] },
  // Infrastructure
  { id: "clickhouse", name: "ClickHouse", category: "Infrastructure", keywords: ["database", "analytics", "yellow"] },
  { id: "composio", name: "Composio", category: "Infrastructure", keywords: ["integration", "api", "purple"] },
  { id: "hashicorp", name: "HashiCorp", category: "Infrastructure", keywords: ["cloud", "infrastructure", "black", "white"] },
  { id: "mongodb", name: "MongoDB", category: "Infrastructure", keywords: ["database", "nosql", "green"] },
  { id: "sanity", name: "Sanity", category: "Infrastructure", keywords: ["cms", "content", "red", "creative"] },
  { id: "stripe", name: "Stripe", category: "Infrastructure", keywords: ["payments", "fintech", "purple", "gradient", "polished"] },
  // Design & Productivity
  { id: "airtable", name: "Airtable", category: "Design", keywords: ["spreadsheet", "database", "colorful", "friendly"] },
  { id: "cal", name: "Cal.com", category: "Design", keywords: ["calendar", "scheduling", "clean"] },
  { id: "clay", name: "Clay", category: "Design", keywords: ["crm", "networking", "warm", "organic"] },
  { id: "figma", name: "Figma", category: "Design", keywords: ["design", "ui", "collaborative", "colorful", "creative"] },
  { id: "framer", name: "Framer", category: "Design", keywords: ["design", "web", "animation", "modern", "bold"] },
  { id: "intercom", name: "Intercom", category: "Design", keywords: ["support", "chat", "friendly", "blue"] },
  { id: "miro", name: "Miro", category: "Design", keywords: ["whiteboard", "collaboration", "yellow", "creative"] },
  { id: "notion", name: "Notion", category: "Design", keywords: ["notes", "wiki", "minimal", "clean", "elegant", "editorial"] },
  { id: "pinterest", name: "Pinterest", category: "Design", keywords: ["images", "visual", "gallery", "red", "creative", "mood"] },
  { id: "webflow", name: "Webflow", category: "Design", keywords: ["web", "design", "builder", "blue", "professional"] },
  // Fintech
  { id: "coinbase", name: "Coinbase", category: "Fintech", keywords: ["crypto", "finance", "blue", "trust"] },
  { id: "kraken", name: "Kraken", category: "Fintech", keywords: ["crypto", "exchange", "purple", "dark"] },
  { id: "revolut", name: "Revolut", category: "Fintech", keywords: ["banking", "finance", "dark", "modern"] },
  { id: "wise", name: "Wise", category: "Fintech", keywords: ["transfer", "finance", "green", "friendly"] },
  // Enterprise & Consumer
  { id: "airbnb", name: "Airbnb", category: "Consumer", keywords: ["travel", "hospitality", "warm", "friendly", "coral"] },
  { id: "apple", name: "Apple", category: "Consumer", keywords: ["premium", "minimal", "luxury", "clean", "elegant", "white", "editorial"] },
  { id: "ibm", name: "IBM", category: "Enterprise", keywords: ["enterprise", "corporate", "blue", "serious"] },
  { id: "nvidia", name: "NVIDIA", category: "Enterprise", keywords: ["gpu", "gaming", "green", "dark", "tech", "futuristic"] },
  { id: "spacex", name: "SpaceX", category: "Enterprise", keywords: ["space", "engineering", "dark", "cinematic", "bold", "futuristic"] },
  { id: "spotify", name: "Spotify", category: "Consumer", keywords: ["music", "audio", "green", "dark", "vibrant", "creative"] },
  { id: "uber", name: "Uber", category: "Consumer", keywords: ["transport", "urban", "black", "clean", "modern"] },
  // Car Brands
  { id: "bmw", name: "BMW", category: "Cars", keywords: ["luxury", "automotive", "blue", "premium", "elegant"] },
  { id: "ferrari", name: "Ferrari", category: "Cars", keywords: ["luxury", "speed", "red", "premium", "bold", "cinematic"] },
  { id: "lamborghini", name: "Lamborghini", category: "Cars", keywords: ["luxury", "supercar", "yellow", "bold", "aggressive"] },
  { id: "renault", name: "Renault", category: "Cars", keywords: ["automotive", "french", "yellow", "modern"] },
  { id: "tesla", name: "Tesla", category: "Cars", keywords: ["electric", "futuristic", "minimal", "dark", "clean", "tech"] },
];

/** Local disk cache directory for design system files. */
const CACHE_DIR = join(os.homedir(), ".maestro-toolbox", "design-systems");

/** In-memory cache to avoid repeated disk reads within the same session. */
const memoryCache = new Map<string, string>();

/** Read from local disk cache. */
async function readLocalCache(id: string): Promise<string | null> {
  try {
    const filePath = join(CACHE_DIR, `${id}.md`);
    const content = await readFile(filePath, "utf-8");
    return content || null;
  } catch {
    return null;
  }
}

/** Write to local disk cache for offline access. */
async function writeLocalCache(id: string, content: string): Promise<void> {
  try {
    await mkdir(CACHE_DIR, { recursive: true });
    const filePath = join(CACHE_DIR, `${id}.md`);
    await writeFile(filePath, content, "utf-8");
  } catch {
    // Silent fail — disk cache is best-effort
  }
}

/**
 * Fetch a DESIGN.md with 3-tier caching:
 * 1. In-memory (instant, same session)
 * 2. Local disk (~/.maestro-toolbox/design-systems/)
 * 3. Remote GitHub (saved to disk on success)
 */
async function fetchDesignMd(id: string): Promise<string | null> {
  // Tier 1: In-memory cache
  if (memoryCache.has(id)) return memoryCache.get(id)!;

  // Tier 2: Local disk cache
  const localContent = await readLocalCache(id);
  if (localContent) {
    memoryCache.set(id, localContent);
    return localContent;
  }

  // Tier 3: Remote fetch + save to disk
  try {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
    const url = `${REPO_RAW_BASE}/${id}/DESIGN.md`;
    const response = await fetch(url, { signal: controller.signal });
    clearTimeout(timeout);
    if (!response.ok) return null;
    const content = await response.text();
    // Cap at 8K chars to avoid blowing up context
    const trimmed = content.length > 8000 ? content.substring(0, 8000) + "\n\n[... truncated for context efficiency]" : content;
    memoryCache.set(id, trimmed);
    // Persist to disk so next time it works offline
    await writeLocalCache(id, trimmed);
    return trimmed;
  } catch {
    return null;
  }
}

export function createDesignSystemTools(ctl: ToolsProviderController): Tool[] {
  const listTool = tool({
    name: "list_design_systems",
    description:
      "List all 58 available design systems from real brands (Apple, Figma, Spotify, etc.). " +
      "Each has a full DESIGN.md with colors, typography, components, and layout rules. " +
      "Use this to find the best design reference for the current project.",
    parameters: {
      filter: z.string().optional().describe("Optional keyword to filter results (e.g. 'dark', 'minimal', 'creative', 'editorial')."),
    },
    implementation: async ({ filter }: { filter?: string }) => {
      let results = DESIGN_SYSTEMS;
      if (filter) {
        const term = filter.toLowerCase();
        results = DESIGN_SYSTEMS.filter(
          d => d.name.toLowerCase().includes(term) ||
               d.category.toLowerCase().includes(term) ||
               d.keywords.some(k => k.includes(term)),
        );
      }
      return {
        count: results.length,
        systems: results.map(d => ({ id: d.id, name: d.name, category: d.category, style: d.keywords.join(", ") })),
      };
    },
  });

  const loadTool = tool({
    name: "load_design_system",
    description:
      "Load a DESIGN.md from a real brand's design system. Returns complete design specs: " +
      "colors (hex), typography (fonts, sizes), components (buttons, cards), layout rules, and more. " +
      "Use the loaded specs to guide your HTML/CSS generation for consistent, professional results. " +
      "Choose the design system that best matches the mood/style of the user's project.",
    parameters: {
      id: z.string().describe("Design system ID (e.g. 'apple', 'figma', 'spotify'). Use list_design_systems to see all options."),
    },
    implementation: async ({ id }: { id: string }, { status }) => {
      const entry = DESIGN_SYSTEMS.find(d => d.id === id.toLowerCase());
      if (!entry) {
        return {
          error: `Unknown design system '${id}'. Use list_design_systems to see available options.`,
          suggestion: DESIGN_SYSTEMS.filter(d => d.id.includes(id.toLowerCase()) || d.name.toLowerCase().includes(id.toLowerCase())).map(d => d.id),
        };
      }
      status(`Loading ${entry.name} design system...`);
      const content = await fetchDesignMd(entry.id);
      if (!content) {
        return { error: `Failed to fetch DESIGN.md for ${entry.name}. No local cache available and remote fetch failed. Try again when internet is available — the file will be cached locally for future offline use.` };
      }
      return {
        id: entry.id,
        name: entry.name,
        category: entry.category,
        design_md: content,
      };
    },
  });

  const preDownloadTool = tool({
    name: "predownload_design_systems",
    description:
      "Pre-download ALL 58 design system files for offline use. " +
      "Run this once to cache every DESIGN.md locally at ~/.maestro-toolbox/design-systems/. " +
      "After this, load_design_system works instantly without internet.",
    parameters: {},
    implementation: async (_args: Record<string, never>, { status }) => {
      await mkdir(CACHE_DIR, { recursive: true });
      let downloaded = 0;
      let skipped = 0;
      let failed = 0;

      for (const entry of DESIGN_SYSTEMS) {
        // Skip if already cached on disk
        const cached = await readLocalCache(entry.id);
        if (cached) {
          skipped++;
          continue;
        }

        status(`Downloading ${entry.name} (${downloaded + skipped + failed + 1}/${DESIGN_SYSTEMS.length})...`);
        try {
          const controller = new AbortController();
          const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
          const url = `${REPO_RAW_BASE}/${entry.id}/DESIGN.md`;
          const response = await fetch(url, { signal: controller.signal });
          clearTimeout(timeout);
          if (!response.ok) { failed++; continue; }
          const content = await response.text();
          const trimmed = content.length > 8000 ? content.substring(0, 8000) + "\n\n[... truncated for context efficiency]" : content;
          await writeLocalCache(entry.id, trimmed);
          memoryCache.set(entry.id, trimmed);
          downloaded++;
          // Small delay to avoid rate limiting
          await new Promise(r => setTimeout(r, 200));
        } catch {
          failed++;
        }
      }

      return {
        total: DESIGN_SYSTEMS.length,
        downloaded,
        already_cached: skipped,
        failed,
        cache_dir: CACHE_DIR,
      };
    },
  });

  return [listTool, loadTool, preDownloadTool];
}