A modern plugin framework for Adobe Illustrator. | Documentation
UXP has transformed plugin development for Photoshop, InDesign, and other Adobe apps — but it hasn't arrived for Illustrator yet. In the meantime, the only official option is CEP, which was deprecated in 2013 and hasn't seen meaningful updates since.
NUXP bridges that gap. It provides 442+ pre-built TypeScript functions that talk directly to the Illustrator SDK — no new C++ to write. Just call the API from TypeScript and build your plugin UI with any JavaScript framework you like. The included frontend uses Vue 3, but since NUXP communicates over HTTP/JSON, you can swap in React, Svelte, or anything else.
Building Illustrator plugins today means choosing between bad options:
- CEP — Deprecated since 2013. Ships an ancient Chromium with known vulnerabilities. Scripting in ExtendScript, a JavaScript dialect frozen at ES3 (1999). No npm, no TypeScript, no modern tooling.
- Raw C++ SDK — Write everything in C++. Thread safety is your problem — one wrong call from the wrong thread crashes Illustrator silently, no error message. Rebuild the entire plugin to test a one-line change.
- UXP — Adobe's official replacement. Already shipping for Photoshop and InDesign. Not available for Illustrator. Has been "coming soon" for years.
NUXP gives you a fourth option: just write TypeScript. A C++ plugin handles the SDK behind the scenes — you never touch it. You call typed functions, get JSON responses, and build your UI with any framework you want. Hot reload, npm, Vite, Pinia — the workflow matches how we build software today.
You can even develop without Illustrator installed. A mock bridge simulates the entire SDK, so you can build and iterate on your UI before you ever load the plugin for real.
NUXP replaces the "CEP Panel" approach with a standalone web application that communicates with Illustrator via a C++ plugin exposing a local HTTP server.
%%{init: {'theme': 'base', 'themeVariables': {
'primaryColor': '#1B3A6B',
'primaryTextColor': '#FFFFFF',
'primaryBorderColor': '#F5C518',
'lineColor': '#F5C518',
'secondaryColor': '#C41E24',
'secondaryTextColor': '#FFFFFF',
'tertiaryColor': '#2A5298',
'tertiaryTextColor': '#FFFFFF',
'edgeLabelBackground': 'transparent'
}}}%%
flowchart LR
subgraph FE [" Frontend App (Tauri / Web) "]
direction TB
SDK_CLIENT["TypeScript SDK Client"]
SSE_CLIENT["SSE Event Client"]
end
SDK_CLIENT <-- "HTTP / JSON" --> HTTP_SERVER
SSE_CLIENT <-- "SSE Stream" --> SSE
subgraph BE [" C++ Plugin (Illustrator) "]
direction TB
HTTP_SERVER["HttpServer"]
CFG["ConfigManager"]
SSE["SSE"]
end
HTTP_SERVER --> AI
AI[("Adobe Illustrator SDK (19 Suites, 442+ Functions)")]
style FE fill:#1B3A6B,stroke:#F5C518,color:#FFFFFF
style BE fill:#1B3A6B,stroke:#F5C518,color:#FFFFFF
style SDK_CLIENT fill:#2A5298,stroke:#F5C518,color:#FFFFFF
style SSE_CLIENT fill:#2A5298,stroke:#F5C518,color:#FFFFFF
style HTTP_SERVER fill:#2A5298,stroke:#F5C518,color:#FFFFFF
style CFG fill:#2A5298,stroke:#F5C518,color:#FFFFFF
style SSE fill:#2A5298,stroke:#F5C518,color:#FFFFFF
style AI fill:#C41E24,stroke:#F5C518,color:#FFFFFF
For details on threading, handle management, and code generation, see Architecture.
NUXP auto-generates typed C++ and TypeScript bindings for the Illustrator SDK. The code generator parses SDK headers with tree-sitter, classifies parameter types, and produces complete HTTP/JSON wrappers.
442 functions across 19 suites, with 100% routing of all parsed functions:
| Suite | Functions | Description |
|---|---|---|
| AIArtSuite | 72 | Core art object manipulation |
| AIDocumentSuite | 68 | Document management |
| AIArtboardSuite | 40 | Artboard properties and layout |
| AILayerSuite | 39 | Layer management |
| AIDictionarySuite | 36 | Dictionary (metadata) access |
| AIToolSuite | 32 | Tool management |
| AIEntrySuite | 23 | Dictionary entry read/write |
| AIBlendStyleSuite | 23 | Opacity, blending modes |
| AIUserSuite | 20 | User interaction, dialogs |
| AIArtSetSuite | 16 | Art set operations |
| AIUndoSuite | 16 | Undo/redo transactions |
| AIMaskSuite | 15 | Clipping mask operations |
| AILayerListSuite | 12 | Layer list traversal |
| AITimerSuite | 8 | Timer callbacks |
| AIAppContextSuite | 7 | Application context |
| AINotifierSuite | 5 | Event notifications |
| AIMdMemorySuite | 5 | Memory management |
| AIGroupSuite | 4 | Group operations |
| AITransformArtSuite | 1 | Transform operations |
The code generator handles these C++ type categories automatically:
| Category | Examples | Marshaling |
|---|---|---|
| Handles (18 types) | AIArtHandle, AILayerHandle, AIDictionaryRef | Integer IDs via HandleRegistry |
| Managed Handles (2 types) | ai::ArtboardProperties, ai::ArtboardList | Owned via ManagedHandleRegistry |
| Primitives (10+ types) | AIBoolean, AIReal, ai::int32, size_t | Direct JSON mapping |
| Strings (3 types) | ai::UnicodeString, const char*, ai::FilePath | UTF-8 string conversion |
| Structs (3 types) | AIRealRect, AIRealPoint, AIRealMatrix | Nested JSON objects |
| Enums | AIEntryType, ai::ArtboardID | Integer cast |
| Non-standard returns | AIReal, AIArtHandle, const char* | Direct value or handle registration |
The 442 auto-generated functions cover most SDK operations. For edge cases involving complex types (AIColor, AIPathStyle, AIGradient) or the Adobe Text Engine, NUXP includes hand-written C++ endpoints that are also callable from TypeScript.
If you need to wrap additional SDK functionality, you can write C++ — but most users won't need to. See plugin/src/endpoints/ for examples.
Server-Sent Events push Illustrator state changes to the frontend in real-time:
- Document open/close/switch
- Selection changes
- Layer modifications
- Art creation/deletion
Beyond SDK suite wrappers, NUXP includes:
- Custom Route Generator - Define HTTP endpoints in JSON, get type-safe TypeScript clients (and matching C++ handlers). Supports path parameters, request/response schemas, and config inheritance.
- SSE Event Generator - Define events in JSON, get a typed TypeScript event bus with real-time payloads from Illustrator.
- Node.js v18+
- CMake v3.15+ (for building the C++ plugin)
- C++ Compiler: Xcode Command Line Tools (
xcode-select --install) - Adobe Illustrator 2024+ (for running the plugin)
- Adobe Illustrator SDK (see below)
git clone https://github.com/your-org/nuxp.git
cd nuxp
# Install codegen dependencies
cd codegen && npm install && cd ..
# Install demo app (frontend) dependencies
cd demo && npm install && cd ..The demo app includes a mock bridge that simulates the C++ plugin, allowing frontend development without Illustrator:
cd demo
VITE_USE_MOCK=true npm run devOpen http://localhost:5173 to see the debug panel and design system demo.
Note: The SDK is proprietary and cannot be included in this repository. You must download it from Adobe (requires free Adobe account).
- Go to Adobe Illustrator SDK Download
- Sign in with your Adobe ID
- Download the Illustrator 2026 SDK (or matching your Illustrator version)
- Download the
.dmgfile
# Run the setup script with your downloaded DMG
./scripts/setup-sdk.sh ~/Downloads/AI_2026_SDK_Mac.dmgThe setup script extracts and organizes:
- Illustrator API headers
- PICA/Sweet Pea headers (platform types)
- ATE (Adobe Text Engine) headers
- Creates
IllustratorSDK.hconvenience header
./scripts/generate.shThis parses SDK headers and generates:
- C++ endpoint handlers ->
plugin/src/endpoints/generated/ - TypeScript SDK client ->
sdk/src/generated/
Note: The TypeScript SDK output is now at
sdk/src/generated/. If upgrading from an older version, update any custom scripts that referenceshell/ordemo/src/sdk/generated/.
cd plugin
cmake -B build
cmake --build buildOr with Xcode:
cd plugin
cmake -B build-xcode -G Xcode
cmake --build build-xcode --config ReleasemacOS Note: NUXP's CMake build automatically configures the bundle metadata required by Illustrator (
CFBundlePackageType=ARPI,CFBundleSignature=ART5). If your plugin doesn't load, see Troubleshooting below.
Customizing the Plugin Name:
You can customize your plugin's identity when configuring CMake:
cmake -B build \
-DPLUGIN_NAME="MyPlugin" \
-DPLUGIN_DISPLAY_NAME="My Awesome Plugin" \
-DPLUGIN_VERSION="1.0.0" \
-DPLUGIN_BUNDLE_ID="com.mycompany.illustrator.myplugin"Or use CMake presets (see plugin/CMakePresets.json):
cmake --preset custom-example
cmake --build build| Variable | Default | Description |
|---|---|---|
PLUGIN_NAME |
NUXPPlugin |
File name (no spaces) |
PLUGIN_DISPLAY_NAME |
NUXP |
Name shown in Illustrator |
PLUGIN_VERSION |
1.0.0 |
Semantic version |
PLUGIN_BUNDLE_ID |
com.nuxp.illustrator.plugin |
macOS bundle identifier |
cmake --install buildOr manually copy to your Illustrator plugins folder:
~/Library/Application Support/Adobe/Adobe Illustrator 2024/Plug-ins/
cd demo
npm run devWith Illustrator running and the plugin loaded, the frontend will connect to the real SDK.
nuxp/
├── sdk/ # TypeScript SDK package (@nuxp/sdk)
│ └── src/
│ ├── bridge/ # Bridge, AutoQueue, request serialization
│ ├── adapters/ # HTTP, SSE, Plugin, Document, Placement adapters
│ ├── services/ # Settings, logging, fonts, SVG, symbols, document index
│ ├── primitives/ # Low-level art/text/group/layer/duplication functions
│ ├── geometry/ # Coordinate transforms, artboard bounds
│ ├── generated/ # Auto-generated suite clients (19 suites, 442+ functions)
│ ├── tauri/ # Desktop filesystem and dialog wrappers
│ ├── types/ # Shared type definitions
│ ├── utils/ # Async safety, environment detection, unit conversions
│ └── schemas/ # Zod validation schemas
│
├── plugin/ # C++ Illustrator plugin
│ ├── CMakeLists.txt
│ ├── sdk/ # Adobe SDK headers (gitignored, you provide)
│ ├── lib/ # Third-party deps (httplib.h, json.hpp)
│ └── src/
│ ├── Plugin.cpp # Entry point
│ ├── HttpServer.cpp # Local HTTP server (cpp-httplib)
│ ├── HandleManager.* # Thread-safe handle registries (18 types)
│ ├── HandleRegistry.hpp # Non-owning handle template
│ ├── ManagedHandleRegistry.hpp # Owning handle template (RAII)
│ ├── MainThreadDispatch.* # SDK call queuing to main thread
│ ├── SSE.* # Server-Sent Events
│ ├── utils/ # Color, Document, Geometry, Layer, Selection, String utils
│ └── endpoints/ # HTTP endpoint handlers
│ ├── *.cpp # Hand-written endpoints
│ └── generated/ # Auto-generated (19 suites, 442 functions)
│
├── demo/ # Vue 3 demo app
│ ├── src/
│ │ ├── components/ # Vue components
│ │ ├── services/ # API client, MockBridge
│ │ ├── sdk/ # Demo-specific SDK bootstrap (imports @nuxp/sdk)
│ │ ├── stores/ # Pinia state management
│ │ └── views/ # Page components
│ └── package.json
│
├── codegen/ # SDK header parser & code generator
│ ├── src/
│ │ ├── parser/ # Tree-sitter based header parser
│ │ ├── generator/ # CppGenerator, TypeScriptGenerator,
│ │ │ # SSEGenerator, CustomRouteGenerator
│ │ └── config/ # type-map.json, routes.json, events.json
│ └── package.json
│
├── scripts/
│ └── generate.sh # Run codegen and copy outputs
│
└── docs/
└── getting-started.md # Detailed setup guide
For rapid UI iteration without Illustrator:
cd demo
VITE_USE_MOCK=true npm run devThe mock bridge provides simulated responses for all SDK calls.
The demo app includes a built-in Script Toolkit with 15 ready-to-run operations inspired by popular ExtendScript script collections. Click "Scripts" in the sidebar to try them:
- Document: Info, object counter, artboard list
- Layers: List, visibility toggle, lock/unlock
- Selection: Info, deselect all
- Objects: Rename, opacity, duplicate
- Text: List frames, create frame
- View: Fit artboard, fit selection
- Start Illustrator with the plugin loaded
- Run the demo app:
cd demo && npm run dev - Changes to Vue components hot-reload automatically
Most users won't need to do this — 442+ functions are already generated. But if you need additional SDK coverage:
- Automatic (recommended): Add headers to the SDK and run
./scripts/generate.sh - Manual: Create custom endpoint handlers in
plugin/src/endpoints/
cd codegen
npm test # all 346 tests
npm test -- --testPathPattern="CppGenerator" # single suiteThe TypeScript SDK (@nuxp/sdk) provides the communication layer between your frontend and the C++ plugin. See sdk/README.md for full SDK documentation.
Before using any SDK functions, create a Bridge and register it:
import { createBridge, setBridgeInstance } from '@nuxp/sdk'
const bridge = createBridge({ port: 8080 })
setBridgeInstance(bridge) // wires up all generated suite functionsEach Illustrator SDK suite gets a typed module with one function per method:
import { GetArtType, GetArtBounds, GetArtName } from '@nuxp/sdk/generated/AIArtSuite'
const artType = await GetArtType(handleId) // returns number
const bounds = await GetArtBounds(handleId) // returns AIRealRect
const info = await GetArtName(handleId) // returns { name, isDefaultName }These call Bridge.callSuite() internally and route through the CentralDispatcher on the C++ side.
Complex operations that cannot be auto-generated (path styles, text frames, selection queries, XMP metadata) are defined in codegen/src/config/routes.json and exposed as typed functions:
import { GetDocumentInfo, GetViewZoom, QueryPathItems } from '@nuxp/sdk/generated/customRoutes'
import { GetSelection, GetPathStyle, CreateTextFrame } from '@nuxp/sdk/generated/customRoutes'
const doc = await GetDocumentInfo() // returns { name, path, saved, artboards }
const zoom = await GetViewZoom() // returns { zoom }
const paths = await QueryPathItems() // returns { items, count }
const sel = await GetSelection() // returns { handles, count }
const style = await GetPathStyle(String(handleId)) // returns fill/stroke details
const text = await CreateTextFrame({ x: 100, y: -200 }) // returns { success, artId }Custom routes hit dedicated HTTP endpoints (e.g., GET /api/doc/info, GET /api/selection) rather than the central dispatcher.
Use Bridge.callSuite() directly when you want maximum flexibility or are prototyping:
import { createBridge } from '@nuxp/sdk'
const bridge = createBridge({ port: 8080 })
// Single call
const result = await bridge.callSuite<{ type: number }>('AIArtSuite', 'GetArtType', { art: handleId })
// Parallel batch (independent calls)
const [a, b] = await bridge.batch([
{ suite: 'AIArt', method: 'GetArtName', args: { art: h1 } },
{ suite: 'AIArt', method: 'GetArtName', args: { art: h2 } },
])When to use which pattern:
- Generated suite functions -- best for standard SDK operations; fully typed, one function per SDK method
- Custom route functions -- best for complex operations (path styles, queries, text, XMP) that go beyond simple SDK calls
- Direct bridge calls -- best for prototyping, dynamic suite calls, or parallel batch operations
NUXP is designed as a foundation for building your own Illustrator plugins. Here's how to customize it for your project:
Configure your plugin's name and branding when building:
cd plugin
cmake -B build \
-DPLUGIN_NAME="AcmeTools" \
-DPLUGIN_DISPLAY_NAME="Acme Design Tools" \
-DPLUGIN_VERSION="1.0.0" \
-DPLUGIN_BUNDLE_ID="com.acme.illustrator.tools" \
-DPLUGIN_AUTHOR="Acme Inc." \
-DPLUGIN_DESCRIPTION="Professional design tools for Illustrator"
cmake --build build| Option | Description | Example |
|---|---|---|
PLUGIN_NAME |
Output filename (no spaces) | AcmeTools |
PLUGIN_DISPLAY_NAME |
Name in Illustrator UI | Acme Design Tools |
PLUGIN_VERSION |
Semantic version | 1.0.0 |
PLUGIN_BUNDLE_ID |
macOS identifier | com.acme.illustrator.tools |
PLUGIN_AUTHOR |
Your name/company | Acme Inc. |
For persistent configuration, create plugin/CMakeUserPresets.json:
{
"version": 6,
"configurePresets": [{
"name": "my-plugin",
"inherits": "default",
"cacheVariables": {
"PLUGIN_NAME": "AcmeTools",
"PLUGIN_DISPLAY_NAME": "Acme Design Tools",
"PLUGIN_BUNDLE_ID": "com.acme.illustrator.tools"
}
}]
}Then build with: cmake --preset my-plugin && cmake --build build
Update demo/index.html and demo/src/App.vue with your branding. The Tauri configuration in demo/src-tauri/tauri.conf.json controls the desktop app name and window title.
The 442+ auto-generated functions cover most Illustrator SDK operations. If you need something not already covered, you can define new routes in JSON and the code generator produces the TypeScript client for you.
Define endpoints in codegen/src/config/routes.json:
{
"namespace": "MyApp",
"routes": [
{
"name": "GetWidgetInfo",
"method": "GET",
"path": "/api/widget/:id",
"pathParams": [{ "name": "id", "description": "Widget ID" }],
"response": [
{ "name": "name", "type": "string" },
{ "name": "count", "type": "number" }
]
}
]
}Run npm run generate to produce a typed TypeScript client function automatically.
For advanced users who want to write custom C++ handlers, see Adding Custom Endpoints and Endpoint Organization.
If you want to keep NUXP as a separate upstream dependency (rather than forking), you can link against NUXP's core infrastructure from your own project. This allows you to:
- Pull in NUXP updates without merge conflicts
- Keep your plugin code separate from NUXP infrastructure
- Contribute fixes back to NUXP easily
your-workspace/
├── nuxp/ # Clone of NUXP (upstream)
│ └── plugin/
└── your-plugin/ # Your project
├── CMakeLists.txt
├── plugin/
│ └── src/
│ ├── YourPlugin.cpp # Your plugin entry point
│ └── endpoints/ # Your endpoint handlers
└── frontend/ # Your frontend (optional)
cmake_minimum_required(VERSION 3.20)
project(YourPlugin VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Include NUXP's core infrastructure
# This provides: HttpServer, SSE, SuitePointers, HandleManager, utils
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../nuxp/plugin nuxp-build)
# Your plugin sources
set(YOUR_SOURCES
src/YourPlugin.cpp
src/endpoints/FeatureOneEndpoints.cpp
src/endpoints/FeatureTwoEndpoints.cpp
)
# Create your plugin
add_library(YourPlugin SHARED ${YOUR_SOURCES})
# Link NUXP core infrastructure
target_link_libraries(YourPlugin PRIVATE nuxp-core)
# Add YOUR Adobe SDK location (NUXP doesn't provide this)
set(AI_SDK_PATH "${CMAKE_CURRENT_SOURCE_DIR}/sdk")
target_include_directories(YourPlugin PRIVATE
${AI_SDK_PATH}
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Platform-specific settings (see NUXP's CMakeLists.txt for full example)
if(APPLE)
set_target_properties(YourPlugin PROPERTIES
BUNDLE TRUE
BUNDLE_EXTENSION "aip"
MACOSX_BUNDLE TRUE
)
target_compile_definitions(YourPlugin PRIVATE MAC_ENV)
target_link_libraries(YourPlugin PRIVATE
"-framework CoreFoundation"
"-framework Cocoa"
)
endif()When you link against nuxp-core, you get:
| Component | Description |
|---|---|
HttpServer |
Background HTTP server with CORS and path parameter routing |
SSE |
Server-Sent Events for real-time push notifications |
MainThreadDispatch |
Safe SDK calls from HTTP thread |
SuitePointers |
Adobe SDK suite acquisition |
HandleManager |
Thread-safe handle lifecycle management (18 handle types + 2 managed) |
StringUtils |
String conversion utilities |
ColorUtils |
Color manipulation helpers |
GeometryUtils |
Geometry and transform utilities |
LayerUtils |
Layer management helpers |
DocumentUtils |
Document operations |
SelectionUtils |
Selection handling |
Your project must provide:
- Adobe SDK headers - Download from Adobe, add to your include paths
- Plugin entry point - Your own
Plugin.cppwithStartupPlugin(),ShutdownPlugin(), etc. - Endpoint handlers - Your feature-specific HTTP endpoints
- Route registration - Call your handlers from
HttpServer::ConfigureRoutes()
// YourPlugin.cpp
#include "SuitePointers.hpp"
#include "HttpServer.hpp"
#include "SSE.hpp"
extern "C" SPBasicSuite* sSPBasic;
ASErr StartupPlugin(SPInterfaceMessage* message) {
sSPBasic = message->d.basic;
// Initialize NUXP infrastructure
SuitePointers::Acquire();
HttpServer::Start(8080);
return kNoErr;
}
ASErr ShutdownPlugin(SPInterfaceMessage* message) {
HttpServer::Stop();
SuitePointers::Release();
return kNoErr;
}
// ... other plugin callbacks- Clone NUXP alongside your project
- Create your CMakeLists.txt referencing NUXP via
add_subdirectory() - Write your plugin code using NUXP's infrastructure
- Find a bug in NUXP? Fix it in the nuxp/ folder, commit, push upstream
- Pull NUXP updates with
git pullin the nuxp/ folder
cd demo
npm run buildOutput: demo/dist/
cd plugin
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config ReleaseGitHub Actions workflows are included for:
- Building the C++ plugin
- Building the Vue frontend
- Running codegen tests
Set the ILLUSTRATOR_SDK_URL secret to a URL where the SDK can be downloaded.
- Fork the repository
- Create a feature branch
- Run tests:
cd codegen && npm test - Submit a pull request
Adobe's Undocumented Bundle Requirements
Illustrator silently ignores plugins that don't have the correct bundle metadata. No error message, no log entry - the plugin simply won't appear. These settings are not documented in Adobe's SDK but are absolutely required:
Info.plist Key Required Value Wrong Value (won't load) CFBundlePackageTypeARPIBNDL,APPLCFBundleSignatureART5????
If your plugin builds but doesn't load:
-
Check Info.plist values: Open
YourPlugin.aip/Contents/Info.plistand verify:CFBundlePackageTypeisARPI(Adobe Resource Plug-In)CFBundleSignatureisART5(Illustrator's signature)
-
Check PIPL resource exists: The plugin needs a compiled PIPL resource:
ls YourPlugin.aip/Contents/Resources/pipl/plugin.pipl
-
Verify PIPL contents (optional):
DeRez -only 'PiPL' YourPlugin.aip/Contents/Resources/pipl/plugin.pipl
NUXP's CMake build automatically sets the correct values via Info.plist.in. If you're using Xcode directly or creating custom Info.plist files, ensure these values are correct.
See plugin/mac/README.md for detailed macOS build troubleshooting.
MIT
Full Documentation | Getting Started | Architecture
NUXP is not affiliated with or endorsed by Adobe. Adobe, Illustrator, and related marks are trademarks of Adobe Inc.
