Kito is a multi-editor plugin architecture for Figma, Penpot, and Framer — built to unify how plugins are developed across the design ecosystem. Write your plugin once. Run it everywhere. Open, modular, and built for the next generation of design tools.
kito provides a clean, type-safe abstraction layer for building plugins that work across multiple design tools. The library enforces explicit context separation to prevent common plugin development mistakes.
- Explicit Context Separation:
kito/editorvskito/uiimports - Design Tool Abstraction: Unified API for Figma and Penpot
- TypeScript Support: Full type safety and IntelliSense
- Zero Configuration: Auto-detects design tool context
- Modern Build: ESM-only, tree-shakeable, optimized bundles
Add to your project's package.json:
{
"dependencies": {
"kito": "file:./kito"
}
}Then run:
bun installbun add kito// Import for Editor context (runs in plugin sandbox)
import { Environment } from 'kito/editor'
// Detect current design tool
const tool = Environment.getCurrent()
console.log(`Running in ${tool}`) // 'figma' or 'penpot'
// Get command that triggered the plugin
const command = Environment.getCurrentCommand()
console.log(`Command: ${command}`) // 'tidy' or null
// Check specific design tool
if (Environment.isFigma()) {
console.log('Using Figma API')
} else if (Environment.isPenpot()) {
console.log('Using Penpot API')
}// Import for UI context (runs in iframe)
import { Environment } from 'kito/ui'
// Detect current design tool (async in UI context)
const tool = await Environment.getCurrent()
console.log(`Running in ${tool}`) // 'figma' or 'penpot'
// Check specific design tool
if (await Environment.isFigma()) {
console.log('Using Figma UI')
} else if (await Environment.isPenpot()) {
console.log('Using Penpot UI')
}Environment.getCurrent()- Get current design toolEnvironment.getCurrentCommand()- Get command that triggered pluginEnvironment.getEnvironmentInfo()- Get complete environment detailsEnvironment.isFigma()- Check if running in FigmaEnvironment.isPenpot()- Check if running in PenpotEnvironment.isFramer()- Check if running in Framer
Environment.getCurrent()- Get current design tool (async)Environment.getEnvironmentInfo()- Get complete environment details (async)Environment.isFigma()- Check if running in Figma (async)Environment.isPenpot()- Check if running in Penpot (async)Environment.isFramer()- Check if running in Framer (async)
| Tool | Editor Context | UI Context | Status |
|---|---|---|---|
| Figma | ✅ | ✅ | Stable |
| Penpot | ✅ | ✅ | Stable |
| Framer | 🚧 | 🚧 | Planned |
kito is built with TypeScript and provides full type definitions:
import type { DesignTool, EnvironmentInfo } from 'kito/editor'
const tool: DesignTool = Environment.getCurrent()
const info: EnvironmentInfo = Environment.getEnvironmentInfo()The library uses standard package exports, so no special bundler configuration is needed:
// No special configuration needed
module.exports = {
resolve: {
extensions: ['.js', '.json']
}
}// No special configuration needed
export default defineConfig({
resolve: {
extensions: ['.js', '.json']
}
})import resolve from '@rollup/plugin-node-resolve'
export default {
plugins: [
resolve({
exportConditions: ['import', 'module', 'default']
})
]
}- Bun 1.0+
- TypeScript 5.3+
cd kito
bun installbun run buildbun run devbun test# Run ESLint
bun run lint
# Fix auto-fixable issues
bun run lint:fix
# Run tests with coverage
bun run test:coverage
# Run tests in watch mode
bun run test:watchkito/
├── src/
│ ├── index.ts # Main entry (throws error)
│ ├── editor.ts # Editor context exports
│ ├── ui.ts # UI context exports
│ ├── editor/ # Editor-specific modules
│ ├── ui/ # UI-specific modules
│ ├── nodes/ # Node mapping system
│ ├── shared/ # Shared utilities
│ └── types/ # TypeScript definitions
├── tests/
│ ├── editor/ # Editor context tests
│ ├── nodes/ # Node mapping tests
│ └── mocks/ # Mock implementations
├── dist/ # Built output
├── eslint.config.js # ESLint configuration
└── tsconfig.json # TypeScript configuration
kito enforces explicit context separation to prevent common mistakes:
- Editor Context: Runs in plugin sandbox with
figma/penpotglobals - UI Context: Runs in iframe with
windowobject
This prevents:
- Trying to access
figmaAPI from UI code - Trying to access
windowfrom Editor code - Confusion about which context code is running in
MIT License - see LICENSE file for details.
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
kito uses a sophisticated ESLint setup with different rules for different contexts:
- Source code (
src/): Strict rules for production quality - Test files (
tests/): Relaxed rules for easier test development - Mock files (
tests/mocks/): Very relaxed rules for mock implementations
The project includes comprehensive tests for:
- Environment detection (Editor & UI contexts)
- Node mapping system (Figma & Penpot)
- Cross-platform compatibility
- Mock implementations
- Bun: Fast test runner, package manager, and runtime
- TypeScript: Full type safety
- ESLint: Code quality enforcement
- tsup: Modern build system
- Version 0.1.0: Foundation & Environment Detection
- Version 0.1.1: Node Mapping System & Testing Infrastructure
- Version 0.2.0: Basic Messaging
- Version 0.3.0: Actions & Lifecycle
- Version 0.4.0: Storage Operations
- Version 0.5.0: Canvas Operations
- Version 0.6.0: History Operations
- Version 0.7.0: OpenNode Mappers
- Version 0.8.0: Text Support
- Version 0.9.0: Hierarchy Operations
- Version 0.9.1: Cleanup & Documentation