> ## Documentation Index
> Fetch the complete documentation index at: https://editor.pascal.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a plugin

> Extend Pascal with custom scene nodes, rendering, tools, and editor panels using the plugin API.

Pascal plugins add new kinds of objects to a scene. A plugin can define how its
objects are validated, rendered in 3D, drawn in floor plans, edited, and exposed to
AI tools. It can also provide a panel in the editor.

Start with the open-source [Nature plugin](https://github.com/pascalorg/plugin-trees).
It is a complete, standalone example that adds trees, flowers, and grass to Pascal.
Clone it when you want a working package to modify instead of starting from an empty
repository.

```bash theme={null}
git clone https://github.com/pascalorg/plugin-trees.git
cd plugin-trees
bun install
```

Follow the repository's README for its current build, test, and local integration
commands.

## Plugin structure

Every plugin exports a manifest with a globally unique ID, the Pascal plugin API
version it targets, and its node definitions:

```ts theme={null}
import type { Plugin } from "@pascal-app/core";
import { plantDefinition } from "./plant";

export const gardenPlugin: Plugin = {
  id: "acme:garden",
  apiVersion: 1,
  nodes: [plantDefinition],
};
```

Use a namespaced ID such as `company:plugin-name`. The current `apiVersion` is `1`;
Pascal rejects plugins that target an incompatible version.

Declare the `@pascal-app/*` packages used by your plugin as **peer dependencies**.
The host must provide these packages so your plugin and the editor share one node
registry. Bundling another copy of `@pascal-app/core` creates a separate registry
and the plugin will not load correctly.

## Define a node

The manifest's `nodes` array contains `NodeDefinition` objects. Each definition
starts with a schema and defaults, then opts into only the Pascal capabilities it
needs.

```ts theme={null}
import {
  BaseNode,
  nodeType,
  objectId,
  type NodeDefinition,
} from "@pascal-app/core";
import { z } from "zod";

const PlantNode = BaseNode.extend({
  id: objectId("plant"),
  type: nodeType("acme:plant"),
  species: z.string().default("fern"),
  height: z.number().positive().default(1),
});

export const plantDefinition = {
  kind: "acme:plant",
  schemaVersion: 1,
  schema: PlantNode,
  category: "furnish",
  defaults: () => ({
    object: "node",
    parentId: null,
    visible: true,
    metadata: {},
    species: "fern",
    height: 1,
  }),
  capabilities: {
    selectable: { hitVolume: "bbox" },
    duplicable: true,
    deletable: true,
  },
  presentation: {
    label: "Plant",
    paletteSection: "furnish",
  },
  renderer: {
    kind: "parametric",
    module: () => import("./plant-renderer"),
  },
  tool: () => import("./plant-tool"),
} satisfies NodeDefinition<typeof PlantNode>;
```

A definition can contribute any combination of:

| Contribution                 | Purpose                                                     |
| ---------------------------- | ----------------------------------------------------------- |
| `parametrics`                | Inspector fields and an optional custom properties panel    |
| `renderer`                   | A custom React Three Fiber renderer                         |
| `geometry`                   | Pure Three.js geometry for Pascal's generic geometry system |
| `system`                     | Shared runtime or per-frame work for all nodes of this kind |
| `floorplan`                  | A 2D representation for floor-plan view and export          |
| `tool` and `affordanceTools` | Placement and manipulation tools                            |
| `presentation`               | Labels, icons, and editor palette placement                 |
| `mcp`                        | Descriptions used when AI tools inspect or create the node  |

Keep renderer and editor modules lazy, as shown above. This lets hosts avoid loading
plugin UI and rendering code until it is needed.

## Add an editor panel

The core manifest is independent of any editor UI. Export an `EditorHostPanel`
separately if your plugin needs a sidebar panel:

```ts theme={null}
import type { EditorHostPanel } from "@pascal-app/editor";

export const gardenPanel: EditorHostPanel = {
  id: "acme:garden:catalog",
  pluginId: "acme:garden",
  label: "Garden",
  description: "Place plants from the Acme garden collection.",
  creator: {
    name: "Acme",
    url: "https://acme.example",
  },
  pluginUrl: "https://github.com/acme/pascal-garden",
  icon: { kind: "iconify", name: "lucide:sprout" },
  component: () => import("./garden-panel"),
};
```

Register the panel with `registerEditorHostPanel`. Pascal uses `creator` and
`pluginUrl` on the plugin detail page and loads the panel component inside an error
boundary.

## Load the plugin in a host

The host decides where plugins come from. Register discovery before importing the
Pascal bootstrap module:

```ts theme={null}
import { setPluginDiscovery } from "@pascal-app/core";
import { gardenPlugin } from "@acme/pascal-garden";

setPluginDiscovery(async () => [gardenPlugin]);

await import("./pascal-bootstrap");
```

Plugin loading is add-only for the browser session. Duplicate node kinds fail at
startup instead of silently replacing each other.

<Note>
  The Plugins sidebar controls whether an already loaded plugin is enabled for
  the current project. Installing or uninstalling there does not download or
  remove an npm package. Uninstalling hides the plugin's panel, tools,
  renderers, systems, and floor-plan output while preserving its nodes in the
  scene graph, so no project data is deleted.
</Note>

## Test your plugin

Before integrating a plugin into a host:

1. Validate every node against its schema and test any geometry or floor-plan
   functions as pure functions.
2. Add a registry test that loads the manifest and confirms each expected node kind
   is registered.
3. Load the plugin in a Pascal host with `setPluginDiscovery` and confirm the
   development console reports its ID and node count.
4. Create, save, reload, uninstall, and reinstall its nodes to check the complete
   project lifecycle.

The [Nature plugin source](https://github.com/pascalorg/plugin-trees) demonstrates
the manifest, node definitions, shared rendering systems, placement tools, editor
panel, and tests together in one package.

## Current API boundaries

Plugin API v1 does not add routes, application pages, host stores, or new material
and floor-plan primitive types. A plugin can use its own state and can create
materials inside its renderer or system, but it does not extend Pascal's global
stores. Keeping this boundary narrow lets the same plugin contract work for
first-party and external nodes.
