Basic Examples
Create a Board with Sticky Notes
brainstorm-board.ts
import { BrainboardClient } from 'codiris-brainboard-sdk';
const client = new BrainboardClient({ apiKey: 'your-api-key' });
async function createBrainstormBoard() {
// Create the board
const { data: board } = await client.createBoard({
name: 'Team Brainstorm',
description: 'Ideas for Q1 2024',
});
// Add sticky notes in a row
const colors = ['#ffeb3b', '#f48fb1', '#90caf9', '#a5d6a7'];
const ideas = ['Mobile app', 'API v2', 'Dashboard redesign', 'AI features'];
await client.createObjects(board.id,
ideas.map((idea, i) => ({
type: 'sticky',
x: 100 + i * 220,
y: 100,
width: 200,
height: 200,
text: idea,
fill: colors[i % colors.length],
}))
);
return board;
}Process Flowchart
flowchart.ts
async function createFlowchart(boardId: string) {
// Create shapes for the flowchart
const shapes = await client.createObjects(boardId, [
// Start node
{
type: 'shape',
shapeType: 'circle',
x: 400, y: 50,
width: 80, height: 80,
fill: '#22c55e',
text: 'Start',
},
// Process step
{
type: 'shape',
shapeType: 'rectangle',
x: 350, y: 180,
width: 180, height: 80,
fill: '#3b82f6',
text: 'Receive Order',
cornerRadius: 8,
},
// Decision diamond
{
type: 'shape',
shapeType: 'diamond',
x: 380, y: 310,
width: 120, height: 120,
fill: '#f59e0b',
text: 'Valid?',
},
// End node
{
type: 'shape',
shapeType: 'circle',
x: 400, y: 480,
width: 80, height: 80,
fill: '#ef4444',
text: 'End',
},
]);
// Connect with arrows
const ids = shapes.map(s => s.id);
await client.createObjects(boardId, [
{
type: 'arrow',
startConnection: { objectId: ids[0], position: 'bottom' },
endConnection: { objectId: ids[1], position: 'top' },
stroke: '#666666',
},
{
type: 'arrow',
startConnection: { objectId: ids[1], position: 'bottom' },
endConnection: { objectId: ids[2], position: 'top' },
stroke: '#666666',
},
{
type: 'arrow',
startConnection: { objectId: ids[2], position: 'bottom' },
endConnection: { objectId: ids[3], position: 'top' },
stroke: '#22c55e',
},
]);
}AI Features
Generate Content with AI
ai-generate.ts
async function generateUserPersona(boardId: string, description: string) {
const { data: result } = await client.generateContent({
prompt: `Create a detailed user persona for: ${description}
Include:
- Name and demographics
- Goals and motivations
- Pain points
- Behaviors and habits`,
type: 'content',
boardId,
});
// The AI might return objects to add
if (result.objects) {
await client.createObjects(boardId, result.objects);
}
return result;
}Chat with Board Context
ai-chat.tsx
function BoardAssistant({ boardId }) {
const { messages, loading, sendMessage } = useBrainboardChat(boardId);
const [input, setInput] = useState('');
const suggestions = [
'What are the main themes on this board?',
'Summarize the key decisions',
'What patterns do you see?',
'Identify potential risks',
];
return (
<div className="assistant">
<div className="messages">
{messages.map((msg, i) => (
<div key={i} className={`message ${msg.role}`}>
<p>{msg.content}</p>
{msg.sources?.length > 0 && (
<div className="sources">
<small>Sources:</small>
{msg.sources.map((src, j) => (
<span key={j} className="source-tag">
{src.text.substring(0, 50)}...
</span>
))}
</div>
)}
</div>
))}
</div>
<div className="suggestions">
{suggestions.map(s => (
<button key={s} onClick={() => sendMessage(s, true)}>
{s}
</button>
))}
</div>
<form onSubmit={(e) => {
e.preventDefault();
sendMessage(input, true);
setInput('');
}}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Ask about this board..."
/>
<button type="submit" disabled={loading}>Send</button>
</form>
</div>
);
}Natural Language Commands
ai-commands.ts
// Examples of supported commands
await client.executeCommand(boardId, 'make all sticky notes blue');
await client.executeCommand(boardId, 'group selected items');
await client.executeCommand(boardId, 'align to the left', selectedIds);
await client.executeCommand(boardId, 'duplicate this 3 times');
await client.executeCommand(boardId, 'create a flowchart with 5 steps');
await client.executeCommand(boardId, 'add a title that says "Overview"');
await client.executeCommand(boardId, 'connect A to B with an arrow');
await client.executeCommand(boardId, 'rotate 45 degrees');Collaboration
Real-Time Presence
presence.tsx
function PresenceIndicator({ boardId }) {
const [activeUsers, setActiveUsers] = useState(new Map());
useBrainboardEvent('user:joined', (event) => {
setActiveUsers(prev => new Map(prev).set(event.data.userId, {
...event.data,
color: getRandomColor(),
}));
});
useBrainboardEvent('user:left', (event) => {
setActiveUsers(prev => {
const next = new Map(prev);
next.delete(event.data.userId);
return next;
});
});
useBrainboardEvent('cursor:moved', (event) => {
setActiveUsers(prev => {
const next = new Map(prev);
const user = next.get(event.data.userId);
if (user) {
next.set(event.data.userId, { ...user, cursor: event.data });
}
return next;
});
});
return (
<div className="presence-bar">
{Array.from(activeUsers.values()).map(user => (
<div
key={user.userId}
className="avatar"
style={{ backgroundColor: user.color }}
title={user.name}
>
{user.name.charAt(0)}
</div>
))}
</div>
);
}Share & Invite
share-panel.tsx
function SharePanel({ boardId }) {
const client = useBrainboardClient();
const { members, inviteMember, removeMember } = useBoardMembers(boardId);
const [email, setEmail] = useState('');
const [shareLink, setShareLink] = useState('');
const handleInvite = async (role) => {
await inviteMember(email, role);
setEmail('');
};
const generateLink = async (canEdit) => {
const { data } = await client.generateShareLink(boardId, { canEdit });
setShareLink(data.shareUrl);
};
return (
<div className="share-panel">
<h3>Share this board</h3>
{/* Email invite */}
<div className="invite-form">
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email address"
/>
<button onClick={() => handleInvite('editor')}>Invite as Editor</button>
<button onClick={() => handleInvite('viewer')}>Invite as Viewer</button>
</div>
{/* Share link */}
<div className="share-link">
<button onClick={() => generateLink(false)}>Get View Link</button>
<button onClick={() => generateLink(true)}>Get Edit Link</button>
{shareLink && (
<div className="link-display">
<input value={shareLink} readOnly />
<button onClick={() => navigator.clipboard.writeText(shareLink)}>
Copy
</button>
</div>
)}
</div>
{/* Member list */}
<div className="members">
<h4>Members</h4>
{members.map(member => (
<div key={member.id} className="member">
<span>{member.email}</span>
<span className="role">{member.role}</span>
{member.role !== 'owner' && (
<button onClick={() => removeMember(member.id)}>Remove</button>
)}
</div>
))}
</div>
</div>
);
}React Patterns
Board Dashboard
dashboard.tsx
function BoardDashboard() {
const [search, setSearch] = useState('');
const { boards, total, loading, createBoard, deleteBoard } = useBoards({
status: 'active',
search,
});
const handleCreate = async () => {
const board = await createBoard({ name: 'New Board' });
navigate(`/boards/${board.id}`);
};
return (
<div className="dashboard">
<header>
<input
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Search boards..."
/>
<button onClick={handleCreate}>Create Board</button>
</header>
<p>{total} boards found</p>
{loading ? (
<Spinner />
) : (
<BrainboardList
boards={boards}
gridColumns={3}
onSelect={(board) => navigate(`/boards/${board.id}`)}
emptyMessage="No boards yet. Create your first board!"
/>
)}
</div>
);
}Board Editor with Sidebar
editor.tsx
function BoardEditor({ boardId }) {
const { board, loading, updateBoard } = useBoard(boardId);
const { objects, selectedObjects, createObject } = useBoardObjects(boardId);
const [sidebarOpen, setSidebarOpen] = useState(true);
if (loading) return <Spinner />;
if (!board) return <NotFound />;
const addElement = (type) => {
createObject({
type,
x: 100 + Math.random() * 400,
y: 100 + Math.random() * 400,
text: type === 'sticky' ? 'New note' : undefined,
fill: type === 'sticky' ? '#ffeb3b' : '#3b82f6',
});
};
return (
<div className="editor">
<header>
<input
value={board.name}
onChange={(e) => updateBoard({ name: e.target.value })}
/>
<div className="toolbar">
<button onClick={() => addElement('sticky')}>Add Sticky</button>
<button onClick={() => addElement('shape')}>Add Shape</button>
<button onClick={() => addElement('text')}>Add Text</button>
</div>
</header>
<main>
<BrainboardEmbed
boardId={boardId}
width="100%"
height="calc(100vh - 64px)"
showToolbar
onObjectSelect={(objs) => console.log('Selected:', objs)}
/>
{sidebarOpen && (
<aside className="properties-panel">
<h3>Properties</h3>
{selectedObjects.length > 0 ? (
<ObjectProperties objects={selectedObjects} />
) : (
<p>Select an object to edit properties</p>
)}
</aside>
)}
</main>
</div>
);
}