npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@xom11/whiteboard

v0.33.0

Published

Excalidraw + JSXGraph + KaTeX whiteboard component (drawing, geometry stamps, LaTeX stamps).

Readme

@xom11/whiteboard

Excalidraw-based whiteboard component dùng cho HocTotBachKhoa classroom: bút/shape/text/import ảnh + hai stamp tools (hình học JSXGraph, công thức LaTeX KaTeX).

Install

Cài qua git URL (repo public, không cần npm publish):

# pin tag (recommended)
npm install github:xom11/whiteboard#v0.2.0

# hoặc trong package.json
"@xom11/whiteboard": "github:xom11/whiteboard#v0.2.0"

Peer dependencies

Từ phiên bản này, @excalidraw/excalidraw, jsxgraphkatex được externalize khỏi bundle (giảm bundle ~70%). Consumer bắt buộc cài kèm:

npm install @excalidraw/excalidraw@^0.18.1 jsxgraph@^1.12.2 katex@^0.16.45 react@>=18 react-dom@>=18

Hoặc thêm vào package.json của consumer:

{
  "dependencies": {
    "@excalidraw/excalidraw": "^0.18.1",
    "jsxgraph": "^1.12.2",
    "katex": "^0.16.45",
    "react": ">=18",
    "react-dom": ">=18"
  }
}

Lý do externalize:

  • Tránh duplicate React khi consumer cũng dùng Excalidraw trực tiếp.
  • Bundle nhẹ hơn ~70% (jsxgraph ~600KB + katex ~280KB + excalidraw ~2MB không còn nằm trong dist của whiteboard).
  • Consumer kiểm soát version + dedupe qua npm/pnpm tự nhiên.

Usage

import { ExcalidrawWhiteboardView, type ExcalidrawSceneSnapshot } from '@xom11/whiteboard';

export function ClassroomBoard() {
  return (
    <ExcalidrawWhiteboardView
      role="teacher"
      roomId="room-123"
      initialScene={null}
      remoteScene={null}
      onSceneChange={(snapshot) => { /* persist + broadcast */ }}
      onFilesChange={(files, newIds) => { /* upload new files */ }}
    />
  );
}

AI dựng hình học 2D (opt-in)

Textarea AI chỉ xuất hiện khi truyền generateGeometryFigure. Callback chạy từ client nên phải gọi server boundary của ứng dụng; không bao giờ đặt API key vào component / biến môi trường public.

'use client';

import { Whiteboard, type GenerateGeometryFigure } from '@xom11/whiteboard';

const generateGeometryFigure: GenerateGeometryFigure = async (problem, { signal }) => {
  const response = await fetch('/api/geometry/ai', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ problem }),
    signal,
  });
  return response.json();
};

export function ClassroomBoard() {
  return <Whiteboard generateGeometryFigure={generateGeometryFigure} />;
}

Provider backend (chọn 1)

Từ phiên bản hỗ trợ multi-provider, có 2 lựa chọn:

A. Local Gemma 3 qua Ollama (mặc định, miễn phí, không cần API key)

Setup máy chạy server:

# macOS
brew install ollama
ollama serve                  # chạy nền cổng 11434

# Chọn 1 trong 2 model:
ollama pull gemma3:4b         # ~3.3GB Q4 — nhanh ~20s/đề, accuracy 75%
ollama pull gemma3:12b        # ~8GB Q4  — chậm ~60s/đề, accuracy 92% (recommended cho production)

# Linux
curl -fsSL https://ollama.com/install.sh | sh
ollama serve &
ollama pull gemma3:12b

Eval 12 đề THCS/lớp 10 (transpile success rate + dùng đúng primitive):

| Model | Transpile OK | Kind đúng | Refuse đúng | Avg latency | |-----------|--------------|-----------|-------------|-------------| | gemma3:4b | 75% | 63% | 0/1 | 20s | | gemma3:12b | 92% | 88% | 1/1 | 62s | | Claude Opus 4.7 | ~100% | ~100% | 1/1 | ~5s (cost ~$0.01/đề) |

Đề nghị: 12b cho production, 4b cho prototype/máy yếu, Anthropic cho accuracy tuyệt đối.

Server-side route Next.js:

import { generateFigure } from '@xom11/whiteboard/ai';

export async function POST(request: Request) {
  const { problem } = await request.json();
  // Default: provider=ollama, model=gemma3:4b, baseUrl=http://localhost:11434
  const result = await generateFigure(problem);
  return Response.json(
    result.ok ? { ok: true, state: result.state } : { ok: false, message: result.message },
  );
}

Env override (optional):

WHITEBOARD_AI_PROVIDER=ollama          # default
OLLAMA_BASE_URL=http://localhost:11434 # default
OLLAMA_DEFAULT_MODEL=gemma3:12b        # default gemma3:4b; recommend gemma3:12b cho production

B. Anthropic Claude (chính xác cao, tốn API cost)

import { generateFigure } from '@xom11/whiteboard/ai';

const result = await generateFigure(problem, {
  apiKey: process.env.ANTHROPIC_API_KEY ?? '',
});

Hoặc qua env:

WHITEBOARD_AI_PROVIDER=anthropic
ANTHROPIC_API_KEY=sk-ant-...

C. Provider tuỳ biến (vd OpenAI, OpenRouter, vLLM): implement interface AIProvider:

import { generateFigure, type AIProvider } from '@xom11/whiteboard/ai';

const customProvider: AIProvider = {
  name: 'my-llm',
  defaultModel: 'my-model',
  async call(req) {
    // req.systemPrompt, req.userPrompt, req.schema (JSON Schema cho envelope)
    // ...
    return { kind: 'json', data: envelopeObject };
  },
};

const result = await generateFigure(problem, { provider: customProvider });

Smoke test local Ollama

brew install ollama && ollama serve & ollama pull gemma3:4b
OLLAMA_SMOKE=1 npx jest ollama.smoke

Migration to v0.8.0 (geometry-3d redesign)

geometry3dStamp được viết lại theo UX của GeoGebra 3D Calculator:

  • Click trên mặt nền / trục / mặt phẳng / mặt cầu để đặt điểm constraint (không còn prompt nhập toạ độ).
  • Drag điểm trên Move tool — điểm trượt theo surface (z-axis chỉ thay đổi z, mặt nền giữ z=0).
  • Algebra panel mới (tab bên trái) hiển thị mỗi object: label, biểu thức symbolic, giá trị numeric, menu ⋮ (đổi tên / màu / ẩn / xoá).
  • 16 tool: Move, Point, Point-on-Object, Segment, Line, Ray, Vector, Polygon, Plane (3 điểm), Pyramid, Prism, Tetrahedron, Cube, Sphere, Cylinder, Cone.

Backward compat: Stamps lưu từ v0.7.0 load OK (legacy points → constraint free). API consumer giữ nguyên.

Tạm thời bỏ: Chord-shortcut 2 phím (G S cho segment, v.v.) — sẽ trở lại với letter-mapping mới.

Migration to v0.7.0 (BREAKING)

DEFAULT_STAMPS v0.7.0 chỉ gồm 2 stamps stable: geometry + latex. 3D + graph2d chuyển sang opt-IN (experimental).

Giữ behavior cũ (4 stamps):

import { Whiteboard, ALL_STAMPS } from '@xom11/whiteboard';

<Whiteboard stamps={ALL_STAMPS} />

Chỉ thêm 3D (giữ default + 3D):

import { Whiteboard, DEFAULT_STAMPS, geometry3dStamp } from '@xom11/whiteboard';

<Whiteboard stamps={[...DEFAULT_STAMPS, geometry3dStamp]} />

Subpath imports (tree-shake):

Mỗi stamp có thể import riêng để bundle nhẹ hơn:

import { Whiteboard } from '@xom11/whiteboard';
import { geometryStamp } from '@xom11/whiteboard/geometry-2d';
import { latexStamp } from '@xom11/whiteboard/latex';

<Whiteboard stamps={[geometryStamp, latexStamp]} />

Drop Next.js peer dep

next không còn là peer dependency. Whiteboard dùng React.lazy + Suspense thuần. Consumer cần Next.js App Router vẫn hoạt động (dist có sẵn 'use client' directive).

Extending — thêm stamp mới

Fork repo + viết stamp mới trong ~30 phút. Tham khảo:

import { STAMP_CATALOG, findCatalogEntry } from '@xom11/whiteboard';

// Render admin UI từ catalog
STAMP_CATALOG.forEach((entry) => {
  console.log(entry.id, entry.title, entry.bundleSize.js + 'KB gzip');
});

Development

npm install
npm test
npm run build      # tsup → dist/{index.js, index.mjs, index.d.ts}
npm run dev        # tsup watch mode

E2E tests

Playwright smoke tests chạy qua headless Chromium, tự start vite demo:

npx playwright install chromium    # cài browser binary (chỉ làm 1 lần)
npm run test:e2e                   # chạy specs

Chi tiết xem tests/e2e/README.md.

Workflow phát hành phiên bản mới

npm ci --ignore-scripts ở consumer skip prepare hook → phải commit dist/:

npm run build
git add dist/
git commit -am "release vX.Y.Z"
npm version patch        # bump package.json + tạo tag
git push --follow-tags

Consumer pin tag mới trong package.json rồi npm install.

Architecture

  • src/ExcalidrawWhiteboardView.tsx — main wrapper, quản lý sync teacher ↔ student, stamp lifecycle.
  • src/ExcalidrawWithMenus.tsx — Excalidraw + custom MainMenu/Footer/WelcomeScreen.
  • src/stamp/ — hai stamp tools (hình học, LaTeX) + helpers (JSXGraph board, KaTeX renderer, serialize/restore).
  • src/serialize.ts — pick sync-able subset của Excalidraw appState.
  • src/types.ts — re-export Excalidraw types + project types.

Extracted from

Hoctotbachkhoa/hoctotbachkhoa (apps/web/components/classroom/excalidrawBoard/) — git history preserved via git filter-repo. Repo sau đó transfer từ Hoctotbachkhoa/whiteboard sang xom11/whiteboard (v0.2.0 trở đi).