canvas-types

📁 omerakben/omer-akben 📅 1 day ago
0
总安装量
1
周安装量
安装命令
npx skills add https://github.com/omerakben/omer-akben --skill canvas-types

Agent 安装分布

amp 1
cline 1
opencode 1
cursor 1
continue 1
kimi-cli 1

Skill 文档

Canvas Types & Patterns Skill

When to Use

Use this skill when:

  • Defining new Canvas types
  • Working with Zod schemas
  • Adding language support
  • Creating utility functions for Canvas

Core Type Definitions

Canvas Types

// From lib/canvas/types.ts

export const CANVAS_TYPES = [
  'code',           // Executable code with Monaco editor
  'document',       // Markdown/rich text editor
  'visualization',  // D3/Chart.js interactive visualizations
  'quiz',           // Interactive assessment
  'flashcards',     // Study cards with flip interaction
  'diagram',        // Mermaid/PlantUML diagrams
  'math',           // LaTeX mathematical expressions
  'react',          // React component preview
] as const;

export type CanvasType = (typeof CANVAS_TYPES)[number];

View Modes

export type ViewMode = 'code' | 'preview' | 'split';

Canvas State

export interface CanvasState {
  // Core state
  isOpen: boolean;
  content: string;
  type: CanvasType;
  title: string;
  language: string;
  viewMode: ViewMode;

  // History for undo/redo
  history: string[];
  historyIndex: number;

  // Generation state
  generationPrompt: string;
  isGenerating: boolean;
}

Zod Schemas

Canvas Configuration Schema

import { z } from 'zod';

export const CanvasConfigSchema = z.object({
  type: z.enum(CANVAS_TYPES),
  title: z.string().max(100),
  language: z.string().optional(),
  initialContent: z.string().optional().default(''),
  generationPrompt: z.string().optional(),
  educationalContext: z.object({
    topic: z.string().optional(),
    difficulty: z.enum(['beginner', 'intermediate', 'advanced']).optional(),
    learningObjective: z.string().optional(),
  }).optional(),
  // Artifact persistence tracking
  artifactId: z.string().uuid().optional(),
  conversationId: z.string().uuid().optional(),
  messageId: z.string().uuid().optional(),
  toolInvocationId: z.string().optional(),
});

export type CanvasConfig = z.infer<typeof CanvasConfigSchema>;

Content Validation

/**
 * Maximum canvas content size (1MB)
 * Prevents memory abuse while allowing substantial code examples
 */
export const CANVAS_MAX_CONTENT_SIZE = 1_048_576;

export const CanvasContentSchema = z.object({
  type: z.enum(CANVAS_TYPES),
  title: z.string().max(100),
  content: z.string().max(CANVAS_MAX_CONTENT_SIZE),
  language: z.string().optional(),
  metadata: z.object({
    educationalObjective: z.string().optional(),
    difficulty: z.enum(['beginner', 'intermediate', 'advanced']).optional(),
    estimatedTime: z.number().optional(),
    prerequisites: z.array(z.string()).optional(),
  }).optional(),
});

export type CanvasContent = z.infer<typeof CanvasContentSchema>;

Language Support

Tiered Language Support

export const SUPPORTED_LANGUAGES = {
  // P0 - Must have (curriculum analysis)
  tier1: [
    { id: 'python', name: 'Python', extensions: ['.py'], monacoId: 'python' },
    { id: 'javascript', name: 'JavaScript', extensions: ['.js'], monacoId: 'javascript' },
    { id: 'html', name: 'HTML', extensions: ['.html'], monacoId: 'html' },
    { id: 'css', name: 'CSS', extensions: ['.css'], monacoId: 'css' },
    { id: 'sql', name: 'SQL', extensions: ['.sql'], monacoId: 'sql' },
    { id: 'markdown', name: 'Markdown', extensions: ['.md'], monacoId: 'markdown' },
  ],
  // P1 - High priority
  tier2: [
    { id: 'typescript', name: 'TypeScript', extensions: ['.ts', '.tsx'], monacoId: 'typescript' },
    { id: 'java', name: 'Java', extensions: ['.java'], monacoId: 'java' },
    { id: 'r', name: 'R', extensions: ['.r'], monacoId: 'r' },
    { id: 'latex', name: 'LaTeX', extensions: ['.tex'], monacoId: 'latex' },
  ],
  // P2 - Secondary
  tier3: [
    { id: 'c', name: 'C', extensions: ['.c', '.h'], monacoId: 'c' },
    { id: 'cpp', name: 'C++', extensions: ['.cpp', '.hpp'], monacoId: 'cpp' },
  ],
} as const;

Execution Support Matrix

export const EXECUTION_SUPPORT: Record<string, 'pyodide' | 'iframe' | 'none'> = {
  python: 'pyodide',
  javascript: 'iframe',
  html: 'iframe',
  typescript: 'iframe',  // Transpiled to JS
  react: 'iframe',
  css: 'iframe',
  sql: 'none',
  java: 'none',
  c: 'none',
  cpp: 'none',
  r: 'none',
  latex: 'none',
  markdown: 'none',
  mermaid: 'none',
};

Utility Functions

Default Language by Canvas Type

export function getDefaultLanguage(type: CanvasType): string {
  const defaults: Record<CanvasType, string> = {
    code: 'python',
    visualization: 'html',
    react: 'typescript',
    document: 'markdown',
    diagram: 'mermaid',
    math: 'latex',
    quiz: 'json',
    flashcards: 'json',
  };
  return defaults[type] || 'plaintext';
}

Monaco Language Mapping

export function getMonacoLanguage(language: string): string {
  const mapping: Record<string, string> = {
    python: 'python',
    javascript: 'javascript',
    typescript: 'typescript',
    tsx: 'typescript',
    jsx: 'javascript',
    html: 'html',
    css: 'css',
    markdown: 'markdown',
    json: 'json',
    sql: 'sql',
    java: 'java',
    c: 'c',
    cpp: 'cpp',
    csharp: 'csharp',
    go: 'go',
    rust: 'rust',
    latex: 'latex',
    mermaid: 'markdown', // No native mermaid support
  };
  return mapping[language] || 'plaintext';
}

Execution Check

export function canExecute(language: string): boolean {
  return (
    EXECUTION_SUPPORT[language] !== 'none' &&
    EXECUTION_SUPPORT[language] !== undefined
  );
}

File Extension Mapping

export function getFileExtension(language: string): string {
  const extensions: Record<string, string> = {
    python: '.py',
    javascript: '.js',
    typescript: '.ts',
    html: '.html',
    css: '.css',
    markdown: '.md',
    json: '.json',
    sql: '.sql',
    java: '.java',
    c: '.c',
    cpp: '.cpp',
  };
  return extensions[language] || '.txt';
}

Tool Result Types

Canvas Actions

export interface OpenCanvasResult {
  action: 'open_canvas';
  canvasConfig: {
    type: CanvasType;
    title: string;
    language: string;
    initialContent: string;
    generationPrompt: string;
    educationalContext?: {
      topic?: string;
      difficulty?: 'beginner' | 'intermediate' | 'advanced';
      learningObjective?: string;
    };
  };
}

export interface UpdateCanvasResult {
  action: 'update_canvas';
  updates: {
    content?: string;
    title?: string;
    language?: string;
  };
}

Adding New Canvas Types

Step 1: Add to Types

// In types.ts
export const CANVAS_TYPES = [
  // ... existing
  'new_type',  // Add here
] as const;

Step 2: Set Default Language

// In getDefaultLanguage()
const defaults: Record<CanvasType, string> = {
  // ... existing
  new_type: 'json',  // Add default
};

Step 3: Update GenUI Components

// In genui renderer
switch (canvasType) {
  // ... existing cases
  case 'new_type':
    return <NewTypeCanvas {...props} />;
}

Adding New Languages

Step 1: Add to Tier

// In SUPPORTED_LANGUAGES
tier2: [
  // ... existing
  { id: 'rust', name: 'Rust', extensions: ['.rs'], monacoId: 'rust' },
],

Step 2: Set Execution Support

// In EXECUTION_SUPPORT
rust: 'none',  // Or 'iframe' if you implement WASM execution

Step 3: Add Monaco Mapping

// In getMonacoLanguage()
rust: 'rust',

Step 4: Add File Extension

// In getFileExtension()
rust: '.rs',

Type Guards

Canvas Type Guard

export function isCanvasType(value: unknown): value is CanvasType {
  return typeof value === 'string' && CANVAS_TYPES.includes(value as CanvasType);
}

Execution Support Guard

export function supportsExecution(language: string): language is 'python' | 'javascript' | 'typescript' | 'html' | 'react' | 'css' {
  return EXECUTION_SUPPORT[language] !== 'none' && EXECUTION_SUPPORT[language] !== undefined;
}

Validation Patterns

Safe Parsing

import { CanvasConfigSchema } from '@/lib/canvas/types';

function parseCanvasConfig(input: unknown) {
  const result = CanvasConfigSchema.safeParse(input);

  if (!result.success) {
    console.error('Invalid canvas config:', result.error.errors);
    return null;
  }

  return result.data;
}

Content Size Check

function isContentValid(content: string): boolean {
  const byteSize = new TextEncoder().encode(content).length;
  return byteSize <= CANVAS_MAX_CONTENT_SIZE;
}

Testing Checklist

  • All Canvas types have default languages
  • All languages have Monaco mappings
  • All languages have file extensions
  • Execution support is correctly marked
  • Zod schemas validate correctly
  • Type guards work as expected
  • Max content size is enforced