Markdown Converter
Agent skill for markdown-converter
This is a React-based design canvas application built with TypeScript, using CanvasKit for high-performance rendering. The architecture follows strict single-responsibility principles with atomic state management using Jotai.
Sign in to like and favorite skills
This is a React-based design canvas application built with TypeScript, using CanvasKit for high-performance rendering. The architecture follows strict single-responsibility principles with atomic state management using Jotai.
element-atoms.ts, canvaskit-canvas.tsx, cursor-manager.ts)component-name.tsxutil-name.tstype-definitions.tsuse-hook-name.ts or hook-name-hooks.tsAll store files use kebab-case naming and are split by responsibility:
Atoms (state definitions):
document-atoms.ts - Document name, tool selectionelement-atoms.ts - Element storage, CRUD operations, element familiesselection-atoms.ts - Selection state, box selection coordinatesinteraction-atoms.ts - Dragging, resizing, drawing, context menu statesviewport-atoms.ts - Zoom, pan, canvas/viewport dimensionsrenderer-atoms.ts - CanvasKit instance, renderer configurationclipboard-atoms.ts - Copy/paste operationsderived-atoms.ts - Cross-module derived atoms (to avoid circular imports)Hooks (state access patterns):
document-hooks.ts, element-hooks.ts, selection-hooks.ts, etc.index.ts re-exports everything for backward compatibilitycanvaskit-canvas.tsx - Main canvas component using atomic state hooksdrawing/CanvasKitRenderer.ts - Pure renderer class, no React statemanagers/ - Utility classes for calculations (no React state)hooks/ - Custom canvas-specific hooksutils.ts - Pure utility functions for coordinate transforms, element detectionconst [state, setState] = useState() - use const [value, setValue] = useAtom(someAtom) insteaduseRef() for state - use atoms for all application stateatomFamily for dynamic collections (elements by ID)elementAtomFamily(id) for granular updateselementIdsAtom maintains order and existenceelementsAtom is a derived atom that combines IDs with element dataelementPositionAtomFamily and elementStyleAtomFamilyisDraggingAtom, isResizingAtom, isDrawingAtom, isBoxSelectingAtomscreenToCanvas transformation)screenToCanvas(clientX, clientY, viewportInfo, transform); // Converts screen → canvas canvasToScreen(canvasX, canvasY, viewportInfo, transform); // Converts canvas → screen
// In renderer - zoom around canvas center, not top-left const centerX = dimensions.width / dpr / 2; const centerY = dimensions.height / dpr / 2; canvasContext.translate(centerX, centerY); canvasContext.scale(zoom, zoom); canvasContext.translate(-centerX, -centerY);
type ElementType = "rect" | "ellipse" | "frame" | "text"; type Tool = "select" | "rect" | "ellipse" | "frame" | "text" | "path";
BaseElement with id, position (x, y, w, h), styling, hierarchyTextElement adds text, color, fontSize, fontFamilyparentId relationshipsaddElementAtom, updateElementAtom, deleteElementsAtomupdateElementPositionAtomupdateElementStyleAtomreorderElementsAtom with specific operations: "bring-to-front" | "send-to-back" etc.nw, n, ne, w, e, sw, s, seResizeManager.calculateResize()nw-resize, ns-resize, ne-resize, ew-resize{ elementIds: string[], operation: "bring-to-front" | ... }any types (use proper typing or unknown)@store/index path mappingindex.ts files for clean APIsderived-atoms.tsgetBoundingClientRect() for coordinate normalizationviewportInfo and transform objectsscreenToCanvasconst assertions for literal typeselement-atoms.ts, resize-manager.ts, canvas-component.tsx)*Atom suffix (e.g., selectionAtom)use* prefix (e.g., useSelection)*Manager suffix (e.g., ResizeManager)// CORRECT: Always use Jotai atomic hooks - NEVER React state const [selection, setSelection] = useSelection(); const [isDragging, setIsDragging] = useIsDragging(); const [elements] = useElements(); // WRONG: Never use React state // const [selection, setSelection] = useState([]); // ❌ FORBIDDEN // const [isDragging, setIsDragging] = useState(false); // ❌ FORBIDDEN // Use composite hooks for related operations const { addElement, updateElement } = useElementOperations(); // Create context for coordinate transformations const viewportInfo = { x: 0, y: 0, width, height, zoom, rect }; const transform = { zoom, panX: pan.x, panY: pan.y };
// Coordinate transformation pattern const canvasCoords = screenToCanvas(clientX, clientY, viewportInfo, transform); // Element detection pattern const clickedElement = getElementAtPoint(canvasCoords, elements); const resizeHandle = getResizeHandle(canvasCoords, element, zoom); // Atomic state updates setIsDragging(true); setDragStart(canvasCoords); setSelection([elementId]);
useState, useReducer, or useRef for state must be replaced with atomsBefore any code is considered complete, verify:
Follow these patterns consistently throughout the codebase. When adding new features, maintain the atomic state architecture and single-responsibility principle. Always prefer atomic hooks over local React state, and ensure coordinate transformations are handled correctly for the center-origin zoom system.