Skip to content

Repository files navigation

@hasna/draw

npm license

Headless board SDK for quick notes and freehand drawings, in the spirit of Google Keep. A framework agnostic board and card model with a portable, renderer free drawing scene that is compatible with Excalidraw for interchange, plus optional React board, card, and draw surface components.

  • Headless core (@hasna/draw): framework agnostic. Runs anywhere (Node, Bun, edge, browser) with no DOM and no drawing engine. Create boards, add note and drawing cards, pin / archive / reorder / recolor, query and count, and import / export JSON and Excalidraw scenes.
  • React components (@hasna/draw/react): a Keep style <Board>, a <Card> that renders notes or a read only drawing preview, a hand rolled SVG <DrawSurface> for freehand sketching, and a <NoteEditor>. They read and write the same JSON the headless SDK produces.
  • CLI (draw): create boards, add cards, list, export to Excalidraw, and print stats, all over board JSON files.

The drawing scene is plain data shaped to map cleanly onto Excalidraw, so a sketch made in the browser round trips through the SDK unchanged, and a platform can later feed the same scene to a real Excalidraw canvas through toExcalidraw / fromExcalidraw.

Install

bun add @hasna/draw        # or: npm i @hasna/draw
bun install -g @hasna/draw # for the `draw` CLI

React is an optional peer dependency; install it only if you use @hasna/draw/react:

bun add react react-dom

Headless SDK

import { createBoard } from "@hasna/draw";

const board = createBoard({ title: "Ideas" })
  .addNote({ title: "Groceries", text: "milk\neggs", color: "yellow" })
  .addDrawing({ title: "Wireframe" })
  .addNote({ text: "Ship the beta", pinned: true });

board.query();                 // pinned cards first, then by order
board.stats();                 // { total, notes, drawings, pinned, archived, colors }
const json = board.serialize(true);

Load and validate an existing board (envelope or bare object, string or parsed):

import { loadBoard } from "@hasna/draw";

const restored = loadBoard(json);

Functional helpers are exported too (addCard, updateCard, deleteCard, reorderCard, pinCard, archiveCard, setCardColor, listCards, searchBoard, boardStats, serializeBoard, parseBoard, note, drawing, scene builders, …). See docs/sdk.md.

Drawing scenes

import { createScene, addStroke, toExcalidraw, fromExcalidraw } from "@hasna/draw";

const scene = addStroke(createScene(), [
  [0, 0],
  [10, 20],
  [30, 15],
]);

const file = toExcalidraw(scene);   // a portable .excalidraw file
const back = fromExcalidraw(file);  // normalized scene, unknown types dropped

A scene is just data: a versioned list of DrawElements (freedraw, line, arrow, rectangle, ellipse, diamond, text) with no canvas or renderer dependency. See docs/drawing.md.

React components

import { useState } from "react";
import { Board } from "@hasna/draw/react";
import type { Board as BoardData } from "@hasna/draw";

export function MyBoard({ initial }: { initial: BoardData }) {
  const [board, setBoard] = useState(initial);
  return <Board board={board} onChange={setBoard} editable />;
}

The components ship no CSS of their own. They render semantic classes (hasna-draw-board, hasna-draw-card, hasna-draw-card[data-color], hasna-draw-surface) you style yourself. See docs/react-board.md and the dashboard/ demo.

CLI

draw create board.json --title "Ideas"      # new empty board
draw add board.json --note "buy milk" --color yellow
draw add board.json --drawing --scene sketch.excalidraw.json
draw list board.json                         # table of cards
draw export board.json --to excalidraw       # export a scene as Excalidraw JSON
draw stats board.json                        # counts by kind / color / state

Development

bun install
bun test          # unit tests
bun run typecheck # tsc --noEmit
bun run build     # library + react + cli + type declarations
cd dashboard && bun install && bun run dev   # live Keep style board demo

License

Apache-2.0 © Hasna. Excalidraw interchange is a data format bridge; this package bundles no Excalidraw code.

About

Headless board SDK for notes/freehand drawings — board/card model, Excalidraw interchange, React components

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages