JSPM

@deathnaitsa/nishi-canvas

1.0.1
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 5
  • Score
    100M100P100Q76310F
  • License MIT

Cross-platform Canvas Adapter with built-in image generators — runs anywhere without node-gyp headaches.

Package Exports

  • @deathnaitsa/nishi-canvas
  • @deathnaitsa/nishi-canvas/core
  • @deathnaitsa/nishi-canvas/generators/bubble
  • @deathnaitsa/nishi-canvas/generators/leaderboard
  • @deathnaitsa/nishi-canvas/generators/level
  • @deathnaitsa/nishi-canvas/generators/profile
  • @deathnaitsa/nishi-canvas/generators/welcome
  • @deathnaitsa/nishi-canvas/themes

Readme

@deathnaitsa/nishi-canvas

Cross-platform Canvas Adapter + Image Generator Kit

npm version license

Runs on Windows, Linux, macOS, WSL, Termux and Docker — zero node-gyp, zero config.
Drop-in replacement for @napi-rs/canvas with extra generators and utilities built-in.


Installation

npm install @deathnaitsa/nishi-canvas

@napi-rs/canvas ships as a bundled dependency with pre-built binaries for every platform — no compiling, no build tools required.

Want a different engine? Just install it alongside — the adapter picks it up automatically:

npm install skia-canvas   # higher quality Skia renderer
npm install canvas        # classic node-canvas (needs build tools)

Engine priority: @napi-rs/canvasskia-canvascanvas


Quick Start

import Canvas from '@deathnaitsa/nishi-canvas';

const canvas = await Canvas.createCanvas(700, 250);
const ctx    = canvas.getContext('2d');

// Extend ctx with extra utility methods
Canvas.extendContext(ctx, canvas);

ctx.fillStyle = '#5865F2';
ctx.fillRoundRect(10, 10, 680, 230, 16);  // built-in polyfill
ctx.setFont(32, 'sans-serif', 'bold');    // size, family, weight in one call
ctx.fillStyle = '#ffffff';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Hello!', 350, 125);

// Export as PNG Buffer
const png = await Canvas.toPNG(canvas);

Generators

One function call returns a PNG Buffer — no setup, no boilerplate.

Welcome Card

import { generateWelcome } from '@deathnaitsa/nishi-canvas';

const png = await generateWelcome({
  avatar:      'https://cdn.discordapp.com/avatars/...',
  username:    'Seblo',
  serverName:  'Nishi Server',
  memberCount: 1337,
  theme:       'dark',        // 'dark' | 'light' | 'neon' | 'midnight'
  accentColor: '#5865F2',     // optional override
  welcomeText: 'WELCOME',     // optional header label
  background:  'https://...', // image URL, hex color, or ['#color1','#color2'] gradient
});

Level / Rank Card

import { generateLevel } from '@deathnaitsa/nishi-canvas';

const png = await generateLevel({
  avatar:   'https://...',
  username: 'Seblo',
  level:    42,
  xp:       1500,
  maxXp:    3000,
  rank:     5,        // optional rank badge
  theme:    'midnight',
});

Profile Card

import { generateProfile } from '@deathnaitsa/nishi-canvas';

const png = await generateProfile({
  avatar:   'https://...',
  banner:   'https://...',  // or '#hex' color
  username: 'Seblo',
  tag:      '#0001',
  bio:      'Building cool stuff.',
  stats:    { Level: 42, XP: '15.2k', Rank: '#5', Messages: 2847 },
  badges:   ['Admin', 'Premium', 'Early Bird'],
  theme:    'neon',
});

Speech Bubble

import { generateBubble } from '@deathnaitsa/nishi-canvas';

const png = await generateBubble({
  text:    'Yo, was geht?',
  avatar:  'https://...',  // optional avatar next to bubble
  tailDir: 'left',         // 'left' | 'right' | 'top' | 'bottom'
  theme:   'dark',
});

Leaderboard

import { generateLeaderboard } from '@deathnaitsa/nishi-canvas';

const png = await generateLeaderboard({
  title: 'Top Players',
  entries: [
    { username: 'Seblo', value: 52000, label: 'XP', avatar: 'https://...' },
    { username: 'Anna',  value: 41500, label: 'XP' },
    { username: 'Max',   value: 37200, label: 'XP' },
  ],
  theme:      'dark',
  maxEntries: 10,
  width:      500,
});

Themes

Name Description
dark Dark navy (default)
light Clean white
neon Black + neon green
midnight Deep purple
import { registerTheme } from '@deathnaitsa/nishi-canvas';

// Register a custom theme (missing keys fall back to dark)
registerTheme('sakura', {
  accent:     '#ff69b4',
  background: '#fff0f5',
  text:       '#2c2f33',
});

const png = await generateWelcome({ username: 'Test', theme: 'sakura' });

// Or pass a plain object directly — no registration needed
const png2 = await generateLevel({ username: 'Test', theme: { accent: '#ff0000' } });

Extended Context API

Call Canvas.extendContext(ctx, canvas) once to get these extra methods:

Method Description
ctx.fillRoundRect(x, y, w, h, r) Fill rounded rectangle
ctx.strokeRoundRect(x, y, w, h, r) Stroke rounded rectangle
ctx.circle(x, y, r, fill?, stroke?) Draw full circle
ctx.avatarCircle(img, cx, cy, r) Image clipped to circle
ctx.roundedImage(img, x, y, w, h, r) Image clipped to rounded rect
ctx.setFont(size, family, weight, style) Set font in one call
ctx.centeredText(text, x, y, boundWidth) Horizontally centered text
ctx.multilineText(text, x, y, maxW, lineH) Word-wrapped text
ctx.truncateText(text, maxWidth) Truncate with
ctx.progressBar(x, y, w, h, progress, fill, bg) Progress bar
ctx.linearGradient(stops, x1, y1, x2, y2) Linear gradient from array
ctx.shadow(color, blur, offsetX, offsetY) Set shadow in one call
ctx.clearShadow() Remove all shadows
ctx.with(fn) save() → fn → restore()
ctx.clear() Clear entire canvas

Color Utilities

import { alpha, darken, lighten, mix, contrastColor } from '@deathnaitsa/nishi-canvas';

alpha('#5865F2', 0.5)          // → 'rgba(88, 101, 242, 0.5)'
darken('#5865F2', 20)          // → darker hex
lighten('#5865F2', 20)         // → lighter hex
mix('#ff0000', '#0000ff', 0.5) // → mixed color
contrastColor('#5865F2')       // → '#ffffff' or '#000000' for readable contrast

Image Loading

Handles any source type automatically:

import { loadImageFromAny, tryLoadImage } from '@deathnaitsa/nishi-canvas';

const img  = await loadImageFromAny('https://example.com/avatar.png'); // URL
const img2 = await loadImageFromAny('./local/image.png');              // local path
const img3 = await loadImageFromAny(someBuffer);                       // Buffer
const img4 = await loadImageFromAny('data:image/png;base64,...');      // Base64

// Returns null on failure instead of throwing
const img5 = await tryLoadImage(maybeInvalidUrl);

Font Management

import { loadBuiltinFonts, registerFontFile, loadFontsFromDir } from '@deathnaitsa/nishi-canvas';

// Auto-load all fonts from the fonts/ directory
await loadBuiltinFonts();

// Register a single font manually
await registerFontFile('./fonts/Roboto-Bold.ttf', 'Roboto', { weight: 'bold' });

// Load all fonts from a directory
await loadFontsFromDir('./my-fonts/');

Project Structure

@deathnaitsa/nishi-canvas
├── dist/        ← Obfuscated build (ESM + CJS)
│   ├── index.js
│   ├── index.cjs
│   ├── core/
│   ├── generators/
│   ├── themes/
│   └── utils/
└── fonts/       ← Drop .ttf/.otf files here

License

MIT © DeathNaitsa