src / media / designReferences.ts

/**
 * @file Design reference knowledge — 7 curated design reference docs
 * covering typography, color, spatial design, motion, interaction,
 * responsive design, and UX writing.
 *
 * Based on Impeccable (impeccable.style) design knowledge.
 * Files stored locally at ~/.maestro-toolbox/design-references/
 */

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

const REFERENCES_DIR = join(os.homedir(), ".maestro-toolbox", "design-references");

interface ReferenceEntry {
  id: string;
  name: string;
  description: string;
}

const DESIGN_REFERENCES: ReferenceEntry[] = [
  { id: "typography", name: "Typography", description: "Font selection, pairing, modular scale, fluid type, web font loading, OpenType features, accessibility" },
  { id: "color-and-contrast", name: "Color & Contrast", description: "OKLCH color space, tinted neutrals, 60-30-10 rule, WCAG contrast, dark mode, dangerous color combinations" },
  { id: "spatial-design", name: "Spatial Design", description: "4pt spacing system, CSS Grid, visual hierarchy, squint test, cards-are-overused, container queries, optical adjustments" },
  { id: "motion-design", name: "Motion Design", description: "100/300/500ms rule, easing curves, only animate transform/opacity, stagger, reduced motion, perceived performance" },
  { id: "interaction-design", name: "Interaction Design", description: "8 interactive states, focus-visible, form design, modals with dialog, Popover API, undo > confirm, keyboard navigation" },
  { id: "responsive-design", name: "Responsive Design", description: "Mobile-first, content-driven breakpoints, pointer/hover queries, safe areas, responsive images, layout adaptation" },
  { id: "ux-writing", name: "UX Writing", description: "Button labels (verb+object), error message formula, empty states, voice vs tone, accessibility, translation" },
];

/** In-memory cache for loaded reference files. */
const refCache = new Map<string, string>();

async function loadReference(id: string): Promise<string | null> {
  if (refCache.has(id)) return refCache.get(id)!;
  try {
    const filePath = join(REFERENCES_DIR, `${id}.md`);
    const content = await readFile(filePath, "utf-8");
    if (content) {
      refCache.set(id, content);
      return content;
    }
    return null;
  } catch {
    return null;
  }
}

export function createDesignReferenceTools(): Tool[] {
  const listTool = tool({
    name: "list_design_references",
    description:
      "List all 7 available design reference guides covering typography, color, spatial design, " +
      "motion, interaction, responsive design, and UX writing. Each contains expert-level rules, " +
      "anti-patterns, and CSS examples. Load specific references before generating HTML/CSS.",
    parameters: {},
    implementation: async () => {
      return {
        count: DESIGN_REFERENCES.length,
        references: DESIGN_REFERENCES.map(r => ({
          id: r.id,
          name: r.name,
          description: r.description,
        })),
        usage: "Call load_design_reference(id) to load the full reference guide.",
      };
    },
  });

  const loadTool = tool({
    name: "load_design_reference",
    description:
      "Load a design reference guide with expert rules, anti-patterns, and CSS examples. " +
      "Available references: typography, color-and-contrast, spatial-design, motion-design, " +
      "interaction-design, responsive-design, ux-writing. " +
      "Load relevant references BEFORE generating HTML/CSS for best results.",
    parameters: {
      id: z.string().describe("Reference ID: typography, color-and-contrast, spatial-design, motion-design, interaction-design, responsive-design, ux-writing"),
    },
    implementation: async ({ id }: { id: string }, { status }) => {
      const entry = DESIGN_REFERENCES.find(r => r.id === id.toLowerCase());
      if (!entry) {
        return {
          error: `Unknown reference '${id}'.`,
          available: DESIGN_REFERENCES.map(r => r.id),
        };
      }
      status(`Loading ${entry.name} reference...`);
      const content = await loadReference(entry.id);
      if (!content) {
        return {
          error: `Reference file not found at ~/.maestro-toolbox/design-references/${entry.id}.md. Files may need to be reinstalled.`,
        };
      }
      return {
        id: entry.id,
        name: entry.name,
        reference: content,
      };
    },
  });

  return [listTool, loadTool];
}