| name | canvas-code-execution |
| description | Develops Canvas code execution features with Pyodide/iframe sandboxing. Use when working on Python/JS execution, package management, or sandbox security. |
| allowed-tools | Read, Write, Edit, Glob |
| context | fork |
Canvas Code Execution Skill
When to Use
Use this skill when:
- Implementing Python execution via Pyodide
- Adding JavaScript execution via iframe sandbox
- Working with package management (micropip)
- Handling execution errors and timeouts
- Adding new supported languages
Architecture Overview
lib/canvas/
├── execution-errors.ts # Typed error handling with user-friendly messages
├── import-detector.ts # Python import parsing and package detection
├── package-manager.ts # Pyodide singleton + micropip integration
├── package-security.ts # Allowlist/blocklist for packages
├── monaco-loader.ts # Monaco editor with CDN fallback
└── types.ts # Canvas types and language support
components/canvas/
├── canvas-preview.tsx # Execution preview with Pyodide/iframe
├── canvas-editor.tsx # Monaco editor wrapper
└── canvas-panel.tsx # Full Canvas panel with split view
Security Model (NEVER VIOLATE)
1. Package Allowlist Only
import { isPackageAllowed, isPackageBlocked } from '@/lib/canvas/package-security';
if (!isPackageAllowed(packageName)) {
throw new CanvasExecutionError({
code: CanvasErrorCode.PACKAGE_NOT_ALLOWED,
context: { packageName }
});
}
2. Block Dynamic Imports
3. Iframe Sandbox Restrictions
const sandbox = [
'allow-scripts',
].join(' ');
4. Execution Timeouts
const EXECUTION_TIMEOUT = 30_000;
const PYODIDE_LOAD_TIMEOUT = 60_000;
const PACKAGE_INSTALL_TIMEOUT = 120_000;
Error Handling Pattern
CanvasExecutionError Class
import { CanvasExecutionError, CanvasErrorCode } from '@/lib/canvas/execution-errors';
throw new CanvasExecutionError({
code: CanvasErrorCode.EXECUTION_SYNTAX_ERROR,
context: {
lineNumber: 5,
traceback: pythonTraceback,
},
});
CanvasExecutionError.fromPythonError(traceback, duration);
CanvasExecutionError.timeout(duration, 'execution');
CanvasExecutionError.packageError(packageName, 'not_allowed');
const display = error.toUserDisplay();
Error Severity Levels
| Severity | Meaning | Action |
|---|
user | Code error (syntax, runtime) | Show fix suggestions |
recoverable | Timeout, network | Retry button |
reload_required | Memory exceeded, crash | Refresh button |
fatal | CDN unreachable, sandbox violation | Feature unavailable |
Import Detection
Analyzing Code for Packages
import { analyzeImports, validateImports } from '@/lib/canvas/import-detector';
const analysis = analyzeImports(pythonCode);
Quick Validation
const { valid, errors, warnings } = validateImports(code);
if (!valid) {
}
Package Manager Usage
Singleton Pattern
import { PyodidePackageManager, getPackageManager } from '@/lib/canvas/package-manager';
const manager = getPackageManager({
verbose: process.env.NODE_ENV === 'development',
onProgress: (progress) => {
},
});
await manager.initialize();
const result = await manager.executeWithPackages(code);
Progress Callbacks
interface PackageLoadProgress {
status: 'analyzing' | 'downloading' | 'installing' | 'complete' | 'error';
currentPackage: string | null;
packagesTotal: number;
packagesLoaded: number;
estimatedSizeKB: number;
downloadedKB: number;
message: string;
}
Adding New Packages
1. Add to Allowlist
newpackage: {
name: "newpackage",
displayName: "New Package",
description: "Short student-friendly description",
tier: PackageTier.STANDARD,
category: PackageCategory.DATA,
sizeKB: 2000,
memoryKB: 15_000,
isPurePython: true,
aliases: ["np"],
dependencies: ["numpy"],
warnSlowLoad: false,
warnMemory: false,
},
2. Update Blocklist If Needed
dangerouspackage: {
reason: "Security reason visible to students",
alternatives: ["safe-alternative"],
},
Monaco Editor Integration
Loading with Fallback
import { initializeMonaco, isMonacoReady, resetMonacoLoader } from '@/lib/canvas/monaco-loader';
Reset on Error
try {
await initializeMonaco();
} catch (error) {
resetMonacoLoader();
}
Language Support
Execution Matrix
export const EXECUTION_SUPPORT: Record<string, 'pyodide' | 'iframe' | 'none'> = {
python: 'pyodide',
javascript: 'iframe',
typescript: 'iframe',
html: 'iframe',
react: 'iframe',
css: 'iframe',
sql: 'none',
java: 'none',
};
Adding New Language
- Add to
SUPPORTED_LANGUAGES tiers in types.ts
- Set execution support in
EXECUTION_SUPPORT
- Add Monaco language ID mapping in
getMonacoLanguage()
- Add file extension in
getFileExtension()
Component Integration
Canvas Preview
<CanvasPreview
content={code}
language="python"
onExecutionResult={(result) => {
}}
onExecutionStart={() => {
}}
/>
Error Display
import { CanvasExecutionError } from '@/lib/canvas/execution-errors';
function ExecutionErrorDisplay({ error }: { error: CanvasExecutionError }) {
const display = error.toUserDisplay();
return (
<div className={cn('p-4 rounded-lg', severityStyles[display.severity])}>
<h4 className="font-medium">{display.title}</h4>
<p className="text-sm mt-1">{display.message}</p>
{display.recovery.length > 0 && (
<ul className="text-xs mt-2 space-y-1">
{display.recovery.map((r, i) => (
<li key={i}>• {r}</li>
))}
</ul>
)}
{display.showRefresh && (
<Button onClick={() => window.location.reload()} size="sm">
Refresh Page
</Button>
)}
</div>
);
}
Testing Checklist