import { type } from "arktype"; import path from "node:path"; import type { ModelConfig } from "../../ai/client.js"; import { CSSFile } from "../../ai/css-file.js"; import { Data } from "../../ai/data.js"; import { TypeScriptFile } from "../../ai/typescript-file.js"; import { VueFile } from "../../ai/vue-file.js"; import { alchemy } from "../../alchemy.js"; import type { Context } from "../../context.js"; import { Folder } from "../../fs/folder.js"; import { Resource } from "../../resource.js"; import type { Secret } from "../../secret.js"; /** * Theme component props for customizing the layout */ export interface ThemeComponentProps { /** * Name of the component */ name: string; /** * Description of what the component does */ description: string; /** * Features or functionality to include in the component */ features?: string[]; } /** * Properties for creating or updating a VitePress custom theme */ export interface CustomThemeProps { /** * Output directory for the theme files */ outDir: string; /** * Title of the theme */ title: string; /** * Description of the theme */ description: string; /** * Components to include in the theme */ components?: ThemeComponentProps[]; /** * Whether to include dark mode support * @default true */ darkMode?: boolean; /** * Custom CSS variables for theming */ customCssVars?: Record; /** * Custom plugins to add to the theme */ plugins?: string[]; /** * User prompt describing the design of the theme */ prompt: string; /** * Optional extension to the built-in system prompt */ systemPromptExtension?: string; /** * Base URL for the OpenAI API * @default 'https://api.openai.com/v1' */ baseURL?: string; /** * OpenAI API key to use for generating content * If not provided, will use OPENAI_API_KEY environment variable */ apiKey?: Secret; /** * Model configuration for the AI generator */ model?: ModelConfig; /** * Temperature for controlling randomness in generation * @default 0.7 */ temperature?: number; } /** * A generated file in the custom theme */ export interface ThemeFile { /** * Path to the file */ path: string; /** * Content of the file */ content: string; } /** * Output for the VitePress custom theme resource */ export interface CustomTheme extends CustomThemeProps, Resource<"vitepress::CustomTheme"> { /** * Path to the generated theme directory */ themePath: string; /** * List of generated files */ files: ThemeFile[]; /** * Time at which the theme was created */ createdAt: number; /** * Time at which the theme was last updated */ updatedAt: number; } /** * Resource for generating a custom VitePress theme using AI. * * Generates the necessary theme files including: * - Theme entry file (index.ts) * - Layout component (Layout.vue) * - Additional components based on configuration * - Style sheets and utilities * * @example * // Create a basic custom theme * const theme = await CustomTheme("docs-theme", { * outDir: "./.vitepress/theme", * title: "My Custom Theme", * description: "A clean, minimal theme for documentation", * prompt: "Create a clean documentation theme with a sidebar and search" * }); * * @example * // Create a theme with specific components * const complexTheme = await CustomTheme("blog-theme", { * outDir: "./.vitepress/theme", * title: "Blog Theme", * description: "A theme for technical blogs", * components: [ * { * name: "PostList", * description: "Component that displays a list of blog posts with pagination", * features: ["thumbnail images", "post dates", "categories", "excerpts"] * }, * { * name: "TableOfContents", * description: "Component that displays the current page's table of contents", * features: ["sticky positioning", "active link highlighting"] * } * ], * darkMode: true, * prompt: "Create a modern blog theme with post listings and detailed article pages" * }); * * @example * // Create a theme with custom CSS variables * const brandedTheme = await CustomTheme("branded-theme", { * outDir: "./.vitepress/theme", * title: "Branded Docs", * description: "A themed documentation site with custom branding", * customCssVars: { * "--vp-c-brand": "#3a70b0", * "--vp-c-brand-light": "#5785bc", * "--vp-c-brand-lighter": "#79a1ce", * "--vp-c-brand-dark": "#2c5989", * "--vp-c-brand-darker": "#1e3d5c" * }, * prompt: "Create a documentation theme with custom branding and color scheme" * }); */ export const CustomTheme = Resource( "vitepress::CustomTheme", async function ( this: Context, id: string, props: CustomThemeProps ): Promise { // Handle deletion if (this.phase === "delete") { // No need to actually delete files, as they're not critical infrastructure // In a real implementation, you might want to clean up files return this.destroy(); } // Ensure the output directory exists await Folder(props.outDir); // Create components directory if it doesn't exist await Folder(path.join(props.outDir, "components")); // Build the system prompt with optional extension const systemPrompt = ` You are creating a custom VitePress theme. A VitePress custom theme consists of multiple files: 1. An entry file (index.ts or index.js) that exports the theme object 2. A Layout component (Layout.vue) that handles the overall page structure 3. Optional additional Vue components for specific functionality 4. Optional style files for theming and customization The theme entry file must export an object with: - Layout: The root layout component (required) - enhanceApp: Function to enhance the Vue app (optional) - extends: Another theme to extend (optional) Follow these guidelines: 1. Use Vue 3 Composition API and