velvet-canvas
v1.0.1
Published
A powerful, customizable whiteboard library built with Svelte 5, Konva.js, and RoughJS. Create interactive drawing applications with hand-drawn aesthetics, connectors, and full state management. Now enhanced for production use with real-time collaboration
Readme
Velvet Canvas - Production-Ready Whiteboard SaaS
A powerful, customizable whiteboard library built with Svelte 5, Konva.js, and RoughJS. Create interactive drawing applications with hand-drawn aesthetics, connectors, and full state management. Now enhanced for production use with real-time collaboration, robust error handling, and enterprise features.
🚀 Key Production Features
Real-Time Collaboration
- Multi-user support with Yjs and WebSockets
- Supabase integration for scalable real-time sync
- Cursor presence showing other users' positions
- Conflict resolution for simultaneous edits
- Optimistic UI updates for better user experience
Robust Architecture
- Enhanced error handling throughout the application
- Memory leak prevention with proper cleanup
- Performance optimizations for large canvases
- Debounced operations to prevent excessive updates
- Throttled cursor updates to reduce bandwidth usage
Enterprise Features
- Undo/Redo history with configurable depth
- Version control for whiteboard states
- Auto-save functionality with local persistence
- Export/Import capabilities (JSON, PNG, PDF)
- Customizable permissions for team collaboration
🎨 Features
- Rich Drawing Tools: Rectangle, circle, freehand lines, straight lines, arrows, and text
- Advanced Text Editing: Full-featured rich text editor with formatting options
- 🔗 Connectors: Link elements together with dynamic connectors and anchor points
- ✏️ RoughJS Integration: Hand-drawn, artistic appearance with customizable roughness
- 🎯 Selection & Transformation: Select, move, rotate, and scale elements
- 💾 State Management: Save, load, export, and import whiteboard state as JSON
- 🎛️ Context-Based API: Clean, reactive API using Svelte 5 contexts
- 🖱️ Pan & Zoom: Navigate large canvases with pan tool
- ⌨️ Keyboard Shortcuts: Quick tool switching with keyboard shortcuts
- 🔄 Reactive: Built on Svelte 5 runes for optimal performance
- 🌐 Responsive Design: Works on all device sizes and orientations
Installation
npm install velvet-canvasQuick Start
1. Set Up the Canvas Context
Wrap your application with the CanvasContext component:
<script>
import { CanvasContext } from 'velvet-canvas';
</script>
<CanvasContext>
<!-- Your canvas content here -->
</CanvasContext>2. Add the Canvas Board
Include the CanvasBoard component to render the drawing surface:
<script>
import { CanvasContext, CanvasBoard } from 'velvet-canvas';
</script>
<CanvasContext>
<CanvasBoard />
</CanvasContext>3. Create a Toolbox (Optional)
Build your own toolbox using the context APIs:
<script>
import { getToolContext, getElementsContext, getStorageContext } from 'velvet-canvas';
const toolAPI = getToolContext();
const elementsAPI = getElementsContext();
const storageAPI = getStorageContext();
</script>
<div class="toolbar">
<button onclick={() => toolAPI.setTool('rectangle')}>
Rectangle
</button>
<button onclick={() => toolAPI.setTool('circle')}>
Circle
</button>
<button onclick={() => toolAPI.setTool('text')}>
Text
</button>
<button onclick={() => storageAPI.exportJSON()}>
Export
</button>
</div>4. Enable Real-Time Collaboration
Add collaboration support with Yjs:
<script>
import { CanvasContext } from 'velvet-canvas';
import { YjsManager } from 'velvet-canvas/collaboration'; // Hypothetical import
// Create a collaboration provider
const collaborationProvider = new YjsManager(
'my-whiteboard-room',
'wss://my-websocket-server.com'
);
</script>
<CanvasContext
collaborationProvider={collaborationProvider}
userIdentity={{
name: 'John Doe',
color: '#FF5733'
}}
>
<CanvasBoard />
</CanvasContext>API Reference
ToolContext
Manages the active drawing tool and drawing options.
<script>
import { getToolContext } from 'velvet-canvas';
const toolAPI = getToolContext();
</script>Properties
activeTool: ToolType- Current active tool (read-only)roughOptions: RoughOptions- Current RoughJS options (read-only)
Methods
setTool(tool: ToolType): Switch to a different toolsetRoughOptions(options: RoughOptions): Update drawing style options
Available Tools
'select'- Select and transform elements'connect'- Connect elements with lines'rectangle'- Draw rectangles'circle'- Draw circles'line'- Freehand drawing'straightLine'- Draw straight lines'arrow'- Draw arrows'text'- Add text elements'pan'- Pan around the canvas
Example
<script>
import { getToolContext } from 'velvet-canvas';
const toolAPI = getToolContext();
// Switch tools
toolAPI.setTool('rectangle');
toolAPI.setTool('circle');
// Update colors
toolAPI.setRoughOptions({
stroke: '#ff0000',
fill: 'rgba(255, 0, 0, 0.1)'
});
</script>ElementsContext
Access and manage canvas elements.
<script>
import { getElementsContext } from 'velvet-canvas';
const elementsAPI = getElementsContext();
</script>Properties
elements: SvelteMap<number, WhiteboardElement>- All elements on the canvas (read-only)selectedIds: SvelteSet<number>- IDs of currently selected elements (read-only)
Methods
clearAll(): Remove all elements and connectors, reset countersdeleteSelected(): Delete all currently selected elementsupdateSelectedColors(options: RoughOptions): Update colors of selected elementsonElementAdded(callback: (element: WhiteboardElement) => void): Subscribe to element additionsonElementsRemoved(callback: (removedIds: Set<number>) => void): Subscribe to element removals
Example
<script>
import { getElementsContext } from 'velvet-canvas';
const elementsAPI = getElementsContext();
// Listen for element additions
elementsAPI.onElementAdded((element) => {
console.log('Element added:', element.id, element.type);
});
// Update selected elements
elementsAPI.updateSelectedColors({
stroke: 'blue',
fill: 'lightblue'
});
// Delete selected
elementsAPI.deleteSelected();
</script>
<!-- Display element count -->
<p>Elements on canvas: {elementsAPI.elements.size}</p>
<p>Selected: {elementsAPI.selectedIds.size}</p>StorageContext
Manage save, load, export, and import operations.
<script>
import { getStorageContext } from 'velvet-canvas';
const storageAPI = getStorageContext();
</script>Methods
save(): WhiteboardData- Get current whiteboard state as data objectload(data: WhiteboardData): boolean- Load whiteboard state from data objectclear()- Clear all elements (with confirmation)exportJSON()- Download current state as JSON fileimportJSON()- Prompt user to import JSON file
Example
<script>
import { getStorageContext } from 'velvet-canvas';
const storageAPI = getStorageContext();
function handleSave() {
const data = storageAPI.save();
// Do something with the data (e.g., send to server)
console.log('Saved:', data);
}
function handleExport() {
storageAPI.exportJSON();
}
function handleImport() {
storageAPI.importJSON();
}
</script>
<button onclick={handleSave}>Save</button>
<button onclick={handleExport}>Export JSON</button>
<button onclick={handleImport}>Import JSON</button>UndoRedoContext
Handle undo/redo operations.
<script>
import { getUndoRedoContext } from 'velvet-canvas';
const undoRedoAPI = getUndoRedoContext();
</script>Properties
canUndo: boolean- Whether undo is possiblecanRedo: boolean- Whether redo is possible
Methods
undo()- Undo the last actionredo()- Redo the last undone action
Types
ToolType
type ToolType =
| "select"
| "connect"
| "rectangle"
| "circle"
| "line"
| "straightLine"
| "arrow"
| "text"
| "pan";WhiteboardElement
interface WhiteboardElement {
id: number;
type: string;
konvaNode: any; // Konva node instance
}WhiteboardData
interface WhiteboardData {
elements: ElementData[];
connectors: ConnectorData[];
elementIdCounter: number;
connectorIdCounter: number;
timestamp: number;
}RoughOptions
interface RoughOptions {
stroke?: string;
fill?: string;
strokeWidth?: number;
roughness?: number;
// ... other RoughJS options
}Architecture
The library is built on a modular architecture:
- CanvasManager: Manages Konva stage and layers
- ShapeManager: Factory for creating different shape types
- InteractionHandler: Handles mouse/touch interactions
- ConnectorManager: Manages connections between elements
- AnchorManager: Displays and manages connection anchors
- StorageManager: Handles save/load operations with enhanced error handling
- TextManager: Manages text editing with rich text support
- WhiteboardStore: Central reactive store using Svelte 5 runes
- CursorManager: Handles user cursor presence in collaborative mode
Customization
Styling the Canvas
You can customize the canvas container styles:
<CanvasContext canvasStyles={{ background: '#f0f0f0' }}>
<CanvasBoard />
</CanvasContext>Custom Shapes
The library uses RoughJS for hand-drawn aesthetics. You can customize the appearance via setRoughOptions:
toolAPI.setRoughOptions({
stroke: '#333',
strokeWidth: 2,
fill: 'rgba(0,0,0,0.1)',
roughness: 1.5,
bowing: 0.5
});Production Deployment
Environment Configuration
Set up your environment variables for production:
VITE_SUPABASE_URL=your_supabase_url
VITE_SUPABASE_ANON_KEY=your_supabase_anon_keyServer-Side Rendering
Velvet Canvas is compatible with SSR. Make sure to handle browser-specific APIs appropriately:
<script>
import { onMount } from 'svelte';
let isBrowser = false;
onMount(() => {
isBrowser = true;
// Initialize canvas only in browser
});
</script>
{#if isBrowser}
<CanvasContext>
<CanvasBoard />
</CanvasContext>
{/if}Performance Tips
- Use debounced operations for frequent updates
- Implement virtual scrolling for large canvases
- Optimize image handling with lazy loading
- Use proper cleanup to prevent memory leaks
Examples
Complete Example
<script>
import {
CanvasContext,
CanvasBoard,
getToolContext,
getElementsContext,
getStorageContext,
getUndoRedoContext
} from 'velvet-canvas';
const toolAPI = getToolContext();
const elementsAPI = getElementsContext();
const storageAPI = getStorageContext();
const undoRedoAPI = getUndoRedoContext();
function handleColorChange(event) {
toolAPI.setRoughOptions({ stroke: event.target.value });
}
</script>
<CanvasContext>
<div class="toolbar">
<button onclick={() => toolAPI.setTool('select')}>Select</button>
<button onclick={() => toolAPI.setTool('rectangle')}>Rectangle</button>
<button onclick={() => toolAPI.setTool('circle')}>Circle</button>
<button onclick={() => toolAPI.setTool('text')}>Text</button>
<input
type="color"
onchange={handleColorChange}
value={toolAPI.roughOptions.stroke || '#000000'}
/>
<button onclick={() => storageAPI.exportJSON()}>Export</button>
<button disabled={!undoRedoAPI.canUndo} onclick={() => undoRedoAPI.undo()}>Undo</button>
<button disabled={!undoRedoAPI.canRedo} onclick={() => undoRedoAPI.redo()}>Redo</button>
</div>
<CanvasBoard />
</CanvasContext>
<style>
.toolbar {
position: absolute;
top: 10px;
left: 10px;
z-index: 100;
display: flex;
gap: 8px;
padding: 8px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
border: 1px solid #e5e7eb;
}
</style>🚀 Coming Soon
We're continually working to enhance Velvet Canvas. Here's a peek at what's next on our roadmap:
- ✍️ Advanced Font and Text Styling APIs:
- Dedicated APIs within
ToolContextto easily manage text color, font family, font size, and styling options like bold and italic.
- Dedicated APIs within
- 🖼️ Image and Media Support:
- The ability to easily import, place, and transform images on the canvas.
- 📊 Chart and Diagram Components:
- Pre-built components for common diagrams like flowcharts, mind maps, and UML diagrams.
- 📱 Mobile Optimization:
- Enhanced touch gesture support and mobile-first UI components.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Development Setup
- Fork and clone the repository
- Install dependencies:
npm install - Start the development server:
npm run dev - Make your changes
- Run tests:
npm run test - Submit a pull request
Testing
Run the test suite:
npm run testRun end-to-end tests:
npm run test:e2eLicense
MIT
Support
Need help? Open an issue in our GitHub repository or join our community Discord.
Made with ❤️ for the whiteboard community
