Skip to main content
Glama
image.ts6.3 kB
import type { RendererContext, ImageInputSpec, SubmissionResult } from '../../../src/shared/types'; function resetCanvas(ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, backgroundColor?: string): void { ctx.save(); ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.clearRect(0, 0, canvas.width, canvas.height); if (backgroundColor) { ctx.fillStyle = backgroundColor; ctx.fillRect(0, 0, canvas.width, canvas.height); } ctx.restore(); } export function mountImageModule(spec: ImageInputSpec, ctx: RendererContext): void { ctx.contentEl.innerHTML = ''; // Create canvas container const canvasContainer = document.createElement('div'); canvasContainer.classList.add('canvas-container'); ctx.contentEl.appendChild(canvasContainer); const canvas = document.createElement('canvas'); canvas.classList.add('draw-canvas'); canvas.style.touchAction = 'none'; canvas.tabIndex = 0; canvasContainer.appendChild(canvas); // Create color picker const colorPicker = document.createElement('div'); colorPicker.classList.add('color-picker'); const colorInput = document.createElement('input'); colorInput.type = 'color'; colorInput.value = '#000000'; colorPicker.appendChild(colorInput); canvasContainer.appendChild(colorPicker); // Set initial canvas size function updateCanvasSize() { const container = ctx.contentEl; const containerRect = container.getBoundingClientRect(); const aspectRatio = spec.width / spec.height; // Use container dimensions while maintaining aspect ratio let canvasWidth = containerRect.width; let canvasHeight = containerRect.height; // Maintain aspect ratio if (canvasWidth / canvasHeight > aspectRatio) { canvasWidth = canvasHeight * aspectRatio; } else { canvasHeight = canvasWidth / aspectRatio; } // Set display size and internal resolution canvas.style.width = `${canvasWidth}px`; canvas.style.height = `${canvasHeight}px`; canvas.width = spec.width; canvas.height = spec.height; } updateCanvasSize(); window.addEventListener('resize', updateCanvasSize); const context = canvas.getContext('2d'); if (!context) { ctx.setStatus('Canvas is not supported in this environment.'); return; } const drawingContext: CanvasRenderingContext2D = context; // Load initial image if provided if (spec.initialImage) { const img = new Image(); img.onload = () => { drawingContext.drawImage(img, 0, 0, spec.width, spec.height); }; img.onerror = () => { ctx.setStatus('Failed to load initial image'); resetCanvas(drawingContext, canvas, spec.backgroundColor); }; img.src = spec.initialImage; } else { resetCanvas(drawingContext, canvas, spec.backgroundColor); } let drawing = false; let prevX = 0; let prevY = 0; let currentColor = '#000000'; function getPosition(event: PointerEvent): { x: number; y: number } { const rect = canvas.getBoundingClientRect(); const scaleX = canvas.width / rect.width; const scaleY = canvas.height / rect.height; return { x: (event.clientX - rect.left) * scaleX, y: (event.clientY - rect.top) * scaleY }; } function pointerDown(event: PointerEvent): void { drawing = true; canvas.setPointerCapture(event.pointerId); ctx.clearStatus(); const { x, y } = getPosition(event); prevX = x; prevY = y; drawingContext.beginPath(); drawingContext.moveTo(x, y); event.preventDefault(); } function pointerMove(event: PointerEvent): void { if (!drawing) return; const { x, y } = getPosition(event); drawingContext.lineCap = 'round'; drawingContext.lineJoin = 'round'; drawingContext.lineWidth = 4; drawingContext.strokeStyle = currentColor; drawingContext.beginPath(); drawingContext.moveTo(prevX, prevY); drawingContext.lineTo(x, y); drawingContext.stroke(); prevX = x; prevY = y; event.preventDefault(); } function releasePointer(event: PointerEvent): void { if (canvas.hasPointerCapture(event.pointerId)) { canvas.releasePointerCapture(event.pointerId); } } function pointerUp(event: PointerEvent): void { drawing = false; releasePointer(event); event.preventDefault(); } function pointerCancel(event: PointerEvent): void { drawing = false; releasePointer(event); event.preventDefault(); } canvas.addEventListener('pointerdown', pointerDown); canvas.addEventListener('pointermove', pointerMove); canvas.addEventListener('pointerup', pointerUp); canvas.addEventListener('pointercancel', pointerCancel); canvas.addEventListener('pointerleave', pointerCancel); canvas.addEventListener('contextmenu', (event) => event.preventDefault()); // Color picker functionality colorInput.addEventListener('change', (event) => { const target = event.target as HTMLInputElement; currentColor = target.value; colorPicker.style.backgroundColor = currentColor; }); // Initialize color picker appearance colorPicker.style.backgroundColor = currentColor; const clearButton = ctx.makeSecondaryButton('Clear', () => { ctx.clearStatus(); resetCanvas(drawingContext, canvas, spec.backgroundColor); }); const submitButton = ctx.makePrimaryButton(spec.submitLabel, () => { const dataUrl = canvas.toDataURL(spec.mimeType); const result: SubmissionResult = { kind: 'image', dataUrl, mimeType: spec.mimeType }; ctx.submit(result); }); // Add keyboard shortcuts const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Delete' || event.key === 'Backspace') { event.preventDefault(); ctx.clearStatus(); resetCanvas(drawingContext, canvas, spec.backgroundColor); } else if (event.key === 'Enter') { event.preventDefault(); const dataUrl = canvas.toDataURL(spec.mimeType); const result: SubmissionResult = { kind: 'image', dataUrl, mimeType: spec.mimeType }; ctx.submit(result); } }; canvas.addEventListener('keydown', handleKeyDown); document.addEventListener('keydown', handleKeyDown); ctx.renderActions(submitButton, [clearButton]); setTimeout(() => { canvas.focus(); }, 10); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/swairshah/InputMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server