Codiris
Back to Docs

Examples & Recipes

Practical code examples for common use cases.

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>
  );
}