skills/omerakben/omer-akben/canvas-ai-tools

canvas-ai-tools

SKILL.md

Canvas AI Tools Skill

When to Use

Use this skill when:

  • Creating AI tools that generate Canvas content
  • Implementing code generation/editing tools
  • Building GenUI tools for Canvas interaction
  • Adding AI-powered features to Canvas

GenUI Tool Architecture

lib/ai/tools/           # Tool definitions
├── canvas-tools.ts     # open_canvas, update_canvas, etc.
├── code-tools.ts       # run_code, explain_code
└── index.ts            # Registry

lib/ai/genui/           # UI components for tools
├── canvas-genui.tsx    # Canvas generation UI
└── code-result.tsx     # Code execution results

Canvas Tool Definitions

open_canvas Tool

import { z } from 'zod';
import { tool } from 'ai';
import { CANVAS_TYPES, CanvasConfigSchema } from '@/lib/canvas/types';

export const openCanvasTool = tool({
  description: `Opens an interactive canvas for code editing, visualization, or document creation.

  Use this when the student needs to:
  - Write, edit, or run code
  - Create visualizations
  - Work on documents
  - Practice with interactive exercises

  The canvas provides a Monaco editor with syntax highlighting and execution support.`,

  parameters: z.object({
    type: z.enum(CANVAS_TYPES).describe('Type of canvas to open'),
    title: z.string().max(100).describe('Title for the canvas'),
    language: z.string().optional().describe('Programming language (for code type)'),
    initialContent: z.string().optional().describe('Starting content'),
    generationPrompt: z.string().optional().describe('Internal prompt for generation'),
    educationalContext: z.object({
      topic: z.string().optional(),
      difficulty: z.enum(['beginner', 'intermediate', 'advanced']).optional(),
      learningObjective: z.string().optional(),
    }).optional(),
  }),

  execute: async (args) => {
    // Validate with full schema
    const config = CanvasConfigSchema.parse(args);

    return {
      action: 'open_canvas' as const,
      canvasConfig: config,
    };
  },
});

update_canvas Tool

export const updateCanvasTool = tool({
  description: `Updates the content of the currently open canvas.

  Use this when:
  - Fixing errors in student code
  - Adding examples or explanations
  - Modifying existing content based on student request

  Only updates specified fields, preserving others.`,

  parameters: z.object({
    content: z.string().optional().describe('New content for the canvas'),
    title: z.string().max(100).optional().describe('New title'),
    language: z.string().optional().describe('Change programming language'),
  }),

  execute: async (args) => {
    return {
      action: 'update_canvas' as const,
      updates: args,
    };
  },
});

run_code Tool

export const runCodeTool = tool({
  description: `Executes the current code in the canvas and returns the result.

  Supports:
  - Python (via Pyodide)
  - JavaScript/TypeScript (via iframe sandbox)
  - HTML/CSS (preview)

  Returns output, errors, and execution time.`,

  parameters: z.object({
    includeVariables: z.boolean().optional().describe('Include final variable values'),
  }),

  execute: async (args) => {
    return {
      action: 'run_code' as const,
      options: args,
    };
  },
});

Tool Registry Pattern

Registering Canvas Tools

// lib/ai/tools/registry.ts
import { openCanvasTool, updateCanvasTool, runCodeTool } from './canvas-tools';

export const CANVAS_TOOLS = {
  open_canvas: openCanvasTool,
  update_canvas: updateCanvasTool,
  run_code: runCodeTool,
} as const;

// For AI SDK
export const canvasToolsArray = Object.values(CANVAS_TOOLS);

Using in Chat

import { streamText } from 'ai';
import { CANVAS_TOOLS } from '@/lib/ai/tools/registry';

const result = await streamText({
  model,
  messages,
  tools: CANVAS_TOOLS,
  maxToolRoundtrips: 3,
});

GenUI Component Pattern

Tool Result Rendering

// lib/ai/genui/canvas-genui.tsx
'use client';

import { useEffect } from 'react';
import { useCanvasStore } from '@/stores/canvas-store';
import type { OpenCanvasResult, UpdateCanvasResult } from '@/lib/canvas/types';

interface CanvasGenUIProps {
  toolResult: OpenCanvasResult | UpdateCanvasResult;
}

export function CanvasGenUI({ toolResult }: CanvasGenUIProps) {
  const { openCanvas, updateCanvas } = useCanvasStore();

  useEffect(() => {
    if (toolResult.action === 'open_canvas') {
      openCanvas(toolResult.canvasConfig);
    } else if (toolResult.action === 'update_canvas') {
      updateCanvas(toolResult.updates);
    }
  }, [toolResult, openCanvas, updateCanvas]);

  // Return minimal UI - canvas opens in side panel
  return (
    <div className="flex items-center gap-2 text-sm text-muted-foreground">
      <div className="h-2 w-2 rounded-full bg-green-500 animate-pulse" />
      {toolResult.action === 'open_canvas'
        ? `Opening canvas: ${toolResult.canvasConfig.title}`
        : 'Updating canvas...'}
    </div>
  );
}

Code Execution Result

// lib/ai/genui/code-result.tsx
import { CheckCircle, XCircle, Clock } from 'lucide-react';
import { cn } from '@/lib/utils';

interface CodeResultProps {
  result: {
    success: boolean;
    output: string;
    error: string | null;
    durationMs: number;
  };
}

export function CodeResultGenUI({ result }: CodeResultProps) {
  return (
    <div className={cn(
      'rounded-lg border p-4',
      result.success ? 'bg-green-50 border-green-200' : 'bg-red-50 border-red-200'
    )}>
      <div className="flex items-center gap-2 mb-2">
        {result.success ? (
          <CheckCircle className="h-4 w-4 text-green-600" />
        ) : (
          <XCircle className="h-4 w-4 text-red-600" />
        )}
        <span className="font-medium">
          {result.success ? 'Execution Successful' : 'Execution Failed'}
        </span>
        <span className="text-xs text-muted-foreground ml-auto flex items-center gap-1">
          <Clock className="h-3 w-3" />
          {result.durationMs}ms
        </span>
      </div>

      {result.output && (
        <pre className="text-sm bg-white/50 rounded p-2 overflow-x-auto">
          {result.output}
        </pre>
      )}

      {result.error && (
        <pre className="text-sm text-red-700 bg-white/50 rounded p-2 overflow-x-auto">
          {result.error}
        </pre>
      )}
    </div>
  );
}

AI Tool Message Handling

Processing Tool Calls

// In chat message handler
import { useChat } from 'ai/react';
import { CanvasGenUI } from '@/lib/ai/genui/canvas-genui';

function ChatMessages() {
  const { messages } = useChat();

  return (
    <>
      {messages.map((message) => (
        <div key={message.id}>
          {/* Regular content */}
          {message.content}

          {/* Tool invocations */}
          {message.toolInvocations?.map((tool) => {
            if (tool.toolName === 'open_canvas' || tool.toolName === 'update_canvas') {
              return (
                <CanvasGenUI
                  key={tool.toolCallId}
                  toolResult={tool.result}
                />
              );
            }
            // Handle other tools...
          })}
        </div>
      ))}
    </>
  );
}

Code Generation Best Practices

Prompt Engineering for Code

const codeGenerationPrompt = `
Generate {language} code that:
1. Is educational and well-commented
2. Follows best practices for {language}
3. Is appropriate for {difficulty} level students
4. Demonstrates the concept: {concept}

Include:
- Clear variable names
- Step-by-step comments
- Example output as comments

Educational context:
- Topic: {topic}
- Learning objective: {learningObjective}
`;

Response Format

// AI should return structured code blocks
interface CodeGenerationResult {
  code: string;
  explanation: string;
  examples: string[];
  nextSteps: string[];
}

Tool Invocation Tracking

Artifact Persistence

// Track tool invocations for artifact persistence
interface CanvasArtifact {
  artifactId: string;
  conversationId: string;
  messageId: string;
  toolInvocationId: string;
  type: CanvasType;
  content: string;
  createdAt: Date;
  updatedAt: Date;
}

// Save artifact when canvas content changes
async function saveCanvasArtifact(
  conversationId: string,
  messageId: string,
  toolInvocationId: string,
  content: string
) {
  await db.insert(canvasArtifacts).values({
    id: nanoid(),
    conversationId,
    messageId,
    toolInvocationId,
    content,
    // ...
  });
}

Error Handling in Tools

Tool Error Response

export const openCanvasTool = tool({
  // ...
  execute: async (args, { abortSignal }) => {
    try {
      // Check signal for cancellation
      if (abortSignal?.aborted) {
        return { action: 'cancelled' };
      }

      const config = CanvasConfigSchema.parse(args);
      return {
        action: 'open_canvas' as const,
        canvasConfig: config,
      };
    } catch (error) {
      if (error instanceof z.ZodError) {
        return {
          action: 'error',
          error: 'Invalid canvas configuration',
          details: error.errors,
        };
      }
      throw error; // Re-throw unexpected errors
    }
  },
});

UI Error Display

function ToolErrorDisplay({ error }: { error: { message: string; details?: unknown } }) {
  return (
    <div className="rounded-lg border border-destructive/50 bg-destructive/10 p-3">
      <p className="text-sm text-destructive font-medium">{error.message}</p>
      {error.details && (
        <pre className="text-xs mt-2 text-muted-foreground">
          {JSON.stringify(error.details, null, 2)}
        </pre>
      )}
    </div>
  );
}

Model-Specific Considerations

Tuning for Different Models

// lib/ai/config/model-canvas.ts

export const CANVAS_MODEL_CONFIG = {
  'gemini-3-flash': {
    // Gemini tends to be less verbose
    toolCallBehavior: 'lenient',
    promptSuffix: 'When using tools, provide complete implementations.',
  },
  'grok-4.1-fast': {
    // Grok excels at agentic tool calling
    toolCallBehavior: 'strict',
    maxToolRoundtrips: 5,
  },
  'claude-4.5': {
    toolCallBehavior: 'balanced',
    maxToolRoundtrips: 3,
  },
} as const;

Testing Checklist

  • open_canvas creates correct configuration
  • update_canvas merges properly
  • run_code returns structured results
  • GenUI components render correctly
  • Tool errors display user-friendly messages
  • Artifact tracking persists data
  • Tool cancellation works
  • Model-specific tuning applied
Weekly Installs
3
GitHub Stars
1
First Seen
Feb 28, 2026
Installed on
opencode3
gemini-cli3
codebuddy3
github-copilot3
codex3
kimi-cli3