Skip to content

Conversation

@esafwan
Copy link
Contributor

@esafwan esafwan commented Jan 30, 2026

Adds the Canvas feature that enables AI agents to create and edit interactive web artifacts (HTML/CSS/JS) through a visual editor, similar to Claude's Artifacts or Gemini Canvas.

What Was Done

1. Canvas DocType

File: huf/huf/doctype/canvas/

Created Canvas DocType to manage web artifacts:

  • Fields: title, slug, agent, description, allow_editing
  • Auto-creates directory structure at www/canvas/<slug>/
  • Copies starter template on creation
  • Auto-sanitizes slug for URL safety

2. Canvas Tools (Agent Tools)

Files: huf/ai/canvas_tools.py, huf/ai/sdk_tools.py

Three built-in tools for agents to manipulate canvas files:

a) Read Canvas File

read_canvas_file(slug: str, path: str)
# Reads: index.html, index.css, index.js, index.py, agents.md

b) Write Canvas Files

write_canvas_files(slug: str, files: dict)
# Writes multiple files at once
# Example: {"index.html": "<html>...", "index.css": "..."}

c) Validate Canvas

validate_canvas(slug: str)
# Validates HTML structure, CSS/JS syntax

3. Canvas API

File: huf/ai/canvas_api.py

Two API endpoints:

  • get_canvas_metadata(slug) - Returns canvas and agent info
  • send_canvas_message(slug, message) - Sends user message to agent, agent edits canvas

4. Editor UI

File: huf/www/canvas_edit/

Portal page with split-pane interface:

  • Left: Chat sidebar for agent interaction
  • Right: Live preview iframe
  • Header: Title, Save/Reset buttons
  • Route: /canvas/edit/<slug>

Note: HTML/CSS structure complete (577 lines), JavaScript needs implementation.

5. Canvas Renderer

File: huf/www/canvas_renderer.py

Serves canvas HTML files:

  • Route: /canvas/<slug>
  • Injects CSS/JS files into HTML
  • Public preview of canvas artifacts

6. Examples

Added two example canvases:

  • Calculator (www/canvas/netflix/) - Functional calculator with dark theme
  • Pacman Landing (www/canvas/website/) - Vintage game landing page

How to Use

1. Create a Canvas

# Via Frappe UI or code
canvas = frappe.get_doc({
    "doctype": "Canvas",
    "title": "My Web App",
    "slug": "my-web-app",
    "agent": "canvas",  # Assign an agent
    "allow_editing": 1
})
canvas.insert()

This creates directory: www/canvas/my-web-app/ with starter template.

2. Edit Canvas

Navigate to: /canvas/edit/my-web-app

Chat with agent:

  • "Add a blue button that says Click Me"
  • "Change the background to dark theme"
  • "Add a counter that increments on button click"

Agent uses tools to read/write files, preview updates automatically.

3. View Canvas

Navigate to: /canvas/my-web-app

See the rendered artifact (HTML with CSS/JS injected).


How to Test

Test 1: Create Canvas

# In bench console
bench --site [site] console

# Create canvas
canvas = frappe.get_doc({
    "doctype": "Canvas",
    "title": "Test Canvas",
    "slug": "test-canvas",
    "agent": "canvas",
    "allow_editing": 1
})
canvas.insert()

# Verify directory exists
import os
path = frappe.get_site_path("public", "files", "www", "canvas", "test-canvas")
print(os.path.exists(path))  # Should print True

Test 2: Test Canvas Tools

# Read file
from huf.ai.sdk_tools import handle_read_canvas_file
result = handle_read_canvas_file(slug="test-canvas", path="index.html")
print(result["success"])  # Should print True

# Write files
from huf.ai.sdk_tools import handle_write_canvas_files
result = handle_write_canvas_files(
    slug="test-canvas",
    files={"index.html": "<html><body><h1>Hello</h1></body></html>"}
)
print(result["success"])  # Should print True

# Validate
from huf.ai.sdk_tools import handle_validate_canvas
result = handle_validate_canvas(slug="test-canvas")
print(result["success"])  # Should print True

Test 3: View Example Canvases

# View calculator
http://localhost:8000/canvas/netflix

# View Pacman landing
http://localhost:8000/canvas/website

Test 4: Editor UI (Manual)

# Open editor
http://localhost:8000/canvas/edit/test-canvas

# Verify:
# - Page loads without errors
# - Title shows "Huf CoPilot (beta)"
# - Subtitle shows canvas name
# - Chat sidebar visible on left
# - Preview iframe visible on right

Note: Chat functionality requires JavaScript implementation (currently placeholder).

Test 5: API Endpoints

# Test get_canvas_metadata
curl http://localhost:8000/api/method/huf.ai.canvas_api.get_canvas_metadata \
  -d '{"slug": "test-canvas"}'

# Should return canvas and agent info

Security

  • Path Validation: Only whitelisted files allowed (prevents directory traversal)
  • Sandboxed Preview: Iframe uses sandbox="allow-scripts"
  • CSRF Protection: Token injection from Frappe session
  • Permission Checks: Respects allow_editing flag

Known Limitations

  1. JavaScript Implementation: Editor UI JavaScript is placeholder (1 line)

    • Chat interface needs implementation
    • Preview refresh needs implementation
    • Save/Reset buttons need implementation
  2. Agent Configuration: Currently hardcoded to "canvas" agent

    • Should use assigned agent from Canvas record
  3. No Version History: Canvas edits are not versioned yet


Integration

Canvas tools work alongside:

  • Knowledge base tools
  • Conversation data tools
  • Other agent tools

Tools are automatically injected when canvas_slug is in context.


Routes Added

# In hooks.py
website_route_rules = [
    {"from_route": "/canvas/edit/<slug>", "to_route": "canvas_edit"},
    {"from_route": "/canvas/<slug>", "to_route": "canvas_renderer"}
]

Migration

# Pull latest code
cd apps/huf && git pull

# Run migration (creates Canvas DocType)
bench --site [site] migrate

# Restart
bench restart

Checklist

  • Canvas DocType created
  • Canvas tools implemented (read, write, validate)
  • Canvas API endpoints created
  • Editor UI structure complete
  • Canvas renderer implemented
  • Example artifacts added
  • Security measures implemented
  • Documentation complete
  • JavaScript implementation (TODO)
  • Integration tests (TODO)

- Add validate() method for slug sanitization
- Add after_insert() hook for auto-directory creation
- Add on_update() hook to ensure directory exists
- Add _ensure_canvas_directory() method to copy starter template
- Add reset_to_starter() API endpoint for reset functionality
- Add standalone hook functions for Frappe doc events

This enables automatic canvas directory creation and starter template
copying when a Canvas DocType is created or updated.
- Add canvas tool injection in AgentManager when canvas_slug in context
- Modify create_canvas_tools() to accept canvas_slug parameter
- Update run_agent_sync() to accept context and persist_conversation params
- Fix file path references: all use agents.md (lowercase) consistently
- Update TODO with reset button wiring task

Canvas tools (read_canvas_file, write_canvas_files, validate_canvas) are now
automatically available to agents when running in canvas context.
…API calls

- Fix sandbox security warning: remove allow-same-origin, keep only allow-scripts
- Fix JavaScript syntax error: remove duplicate function definitions
- Fix send button: add provider and model parameters to run_agent_sync call
- Clean up loadCanvasMetadata() function (remove duplicate code)
- Clean up sendMessage() function (remove duplicate code)
- Improve error handling and loading indicators

Issues resolved:
1. Sandbox warning about allow-scripts + allow-same-origin
2. SyntaxError: Unexpected token 'function'
3. Send button not working (missing provider/model params)
- Create canvas_renderer.py with get_context() function
- Create canvas_renderer.html template
- Add route /canvas/<slug> to hooks.py
- Renderer reads index.html from canvas directory
- Automatically injects separate CSS/JS files if they exist
- Handles errors gracefully with proper 404 responses

This enables the iframe to load canvas previews at /canvas/<slug>
- Create canvas_edit.py context handler for portal page
- Include frappe-web.bundle.js script in HTML
- Initialize frappe object and CSRF token from cookies
- Add proper HTML structure with head/body tags
- Context handler validates canvas exists and provides metadata

This fixes 'frappe is not defined' error in portal page
- Add callFrappeMethod helper that works with or without frappe.call
- Fallback to fetch API if frappe.call is not available
- Properly handle CSRF token in both cases
- Use defer attribute for script loading
- Add console logging for debugging

This ensures canvas editor works even if frappe-web.bundle.js doesn't load properly
- Remove frappe-web.bundle.js dependency (was 404)
- Use pure fetch API with CSRF token from cookies
- Better error handling with detailed error messages
- Simpler, more reliable approach for portal pages

This fixes the 404 bundle error and 400 BAD REQUEST API errors
- Add csrf_token to context in canvas_edit.py
- Inject csrf_token into HTML template via window.csrf_token
- Update getCSRFToken() to use window.csrf_token first, then cookie fallback
- Add warning if CSRF token is missing

This fixes the 400 BAD REQUEST error by ensuring CSRF token is available
- Remove window.csrf_token injection (was causing mismatch)
- Read CSRF token directly from 'csrf_token' cookie
- Add better error handling and logging
- Change credentials to 'include' for proper cookie handling

The issue was that template context token didn't match session token.
Now reading directly from the session cookie like Frappe expects.
- Use {{ frappe.session.csrf_token }} directly in template
- This is the standard Frappe way per Jinja API docs
- Remove manual csrf_token context setting
- Simplify getCSRFToken() to just read window.csrf_token

This matches the documented Frappe Jinja API approach.
Changes:
1. Added 3 new built-in tool types to Agent Tool Function DocType:
   - Read Canvas File
   - Write Canvas Files
   - Validate Canvas

2. Implemented built-in handlers in sdk_tools.py:
   - handle_read_canvas_file()
   - handle_write_canvas_files()
   - handle_validate_canvas()
   - All with comprehensive debug logging

3. Added debug logging to canvas_api.py:
   - Logs agent tools loaded from DB
   - Tracks context passing

4. Tools now work like other built-in types (Get Document, etc.)
   instead of requiring Custom Function configuration

This eliminates the need for manual Custom Function setup and
ensures Canvas tools are recognized by the LLM provider.
- Update huf/www/canvas/website/index.html to feature a Pacman-themed landing page.
- Add huf/www/canvas/website/index.js with basic Pacman character movement logic.
- Create huf/www/canvas/netflix directory with a complete Calculator implementation.
- Add server-side context in huf/www/canvas/netflix/index.py.
- Add developer guidelines in huf/www/canvas/netflix/AGENTS.md.
@esafwan esafwan changed the title Feature/canvas feat: add Canvas feature for AI-powered web artifacts Feb 7, 2026
@esafwan esafwan changed the title feat: add Canvas feature for AI-powered web artifacts feat: add Canvas feature for AI generated webpages Feb 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant