> ## 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.

> [!NOTE]
>
> Today, a Pascal host must review and bundle a plugin before it can be enabled.
> The Plugins sidebar toggles bundled code for one project; it does not download
> code from GitHub or npm. Runtime installation from the hosted catalog is planned,
> but is not part of Plugin API v1 yet.

## Plugins that ship with Pascal

The hosted editor already bundles several plugins. Open the **Plugins** panel in the left
sidebar to see them, and **Install** the ones you want for the scene you're working on —
some arrive installed, and the more specialised ones stay off until you ask for them.

| Plugin | What it adds | Source |
| - | - | - |
| **Site Planner** | A toolbar over the 3D and 2D views: find a lot by address, build a ready house on it, and check setbacks and zoning | [Site Planner guide](https://editor.pascal.app/docs/building/site-planner.md) |
| **Bones** | An engineering X-ray: framing, foundation, electrical, plumbing, and ductwork derived from your model and sized to your jurisdiction | [pascalorg/plugin-bones](https://github.com/pascalorg/plugin-bones) |
| **Nature** | Procedural trees, flowers, and grass | [pascalorg/plugin-trees](https://github.com/pascalorg/plugin-trees) |
| **Streetscape** | Road networks with junctions, cross-sections, curbs, and roadside objects | [sudhir9297/streetscape-pascal-plugin](https://github.com/sudhir9297/streetscape-pascal-plugin) |
| **Mint** | Browse or generate 3D models and place them in the scene | [mintdotgg/mint-pascal-plugin](https://github.com/mintdotgg/mint-pascal-plugin) |
| **Boots** | First-person mode — walk the building you're editing. Alpha | [pascalorg/plugin-boots](https://github.com/pascalorg/plugin-boots) |
| **Bloblins** | Tamagotchi-style clay companions that hatch from DNA eggs and live in your house. Alpha | [wass08/pascal-plugin-bloblins](https://github.com/wass08/pascal-plugin-bloblins) |

Every one of them is built on the same public API described below, so they double as
worked examples of what a plugin can do.

## Start from a working plugin

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.

## Prepare a publishable repository

Keep the plugin in its own public repository and make one release reproducible before
asking a Pascal host to load it. A reviewable repository should include:

1. One npm package with a unique plugin ID and an explicit Pascal API compatibility
   range.
2. `@pascal-app/*`, React, React DOM, and Three.js as peer dependencies rather than
   bundled duplicate runtimes.
3. An immutable commit or release artifact, a lockfile, a license, and commands for
   type checking, tests, and building.
4. No required install-time scripts and no undeclared network calls.
5. A README that identifies the publisher, repository, support contact, capabilities,
   external origins, persisted project data, and any account or personal data used.
6. Lazy UI and renderer entry points so loading plugin metadata does not initialize
   external services or expensive runtime code.

The [Pascal CLI](https://editor.pascal.app/docs/developers/local-editor.md) manages a persistent local editor, but it
does not validate or package plugins yet. Until a machine-readable artifact format
ships, Nature's package and README are the reference layout.

## 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.

## Keep the plugin independent

Plugin source belongs in the publisher's repository, not in the Pascal editor
repository. Publish or pin an immutable package version and keep every
`@pascal-app/*`, React, and Three.js import as a peer dependency. A host currently
reviews and bundles the package before making it available; the Plugins sidebar only
enables code the host already has.

Do not require a change to a built-in node switch, host store, or Pascal package to add
your plugin. Host-specific wiring should be limited to discovery, optional panel
registration, styling/build configuration, and any explicitly reviewed server adapter.

## 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.

## Connected services and personal data

Plugin API v1 does not discover or mount server routes. A host may separately add a
reviewed server adapter for a plugin that connects to an external service, but that
adapter is host integration—not a portable plugin contribution.

Before a host enables an account-connected plugin, the plugin and adapter must:

1. List every OAuth scope, external origin, account field read, user-provided field
   sent, provider field persisted into a Pascal project, and browser/server storage
   location.
2. Ask the user to connect explicitly and explain the data exchange before
   authorization.
3. Keep access and refresh tokens in `HttpOnly`, `Secure` production cookies or an
   equivalent server-side credential store; plugin JavaScript must not receive them.
4. Bind the external-service session to the active Pascal user so switching Pascal
   accounts in one browser cannot expose the previous user's external account.
5. Proxy only allowlisted upstream paths and headers, enforce same-origin checks on
   mutations, avoid logging credentials or personal data, and provide logout and
   revocation behavior.
6. Document who owns OAuth client registration and register every exact production,
   preview, and local callback before release. Confirm the client ID, public versus
   confidential client type, PKCE method, grants, scopes, resource audience, and token
   authentication method with the provider.
7. Run the complete production flow after registration: connect, callback, refresh,
   provider API access, logout or revocation, and switching between two Pascal accounts
   in the same browser.

If the provider displays **Unknown client**, the authorization server does not recognize
the configured client ID. If it displays a redirect mismatch, the client exists but the
exact callback is not allowlisted. Both are provider-side provisioning failures; changing
Pascal's callback dynamically would weaken the OAuth boundary and is not a workaround.

An external model or asset URL still discloses ordinary request metadata such as the
user's IP address and user agent to that asset host. Include that provider in the
plugin's data disclosure.

## 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. A reviewed host may mount a separate server adapter as described above; the
manifest itself cannot request or register one. Keeping this boundary narrow lets the
same plugin contract work for first-party and external nodes.

## Run a local editor with the CLI

The [Pascal CLI](https://editor.pascal.app/docs/developers/local-editor.md) lets you run the standalone editor without
cloning the Pascal repository:

```bash theme={null}
npx @pascal-app/cli editor
npx @pascal-app/cli doctor
```

This installs a versioned editor runtime and preserves local projects in
`~/.pascal/data/pascal.db`. It does not install a plugin repository. To test a plugin,
continue to use the reviewed host integration described in [Load the plugin in a
host](#load-the-plugin-in-a-host).

`pascal plugin list` can inspect the local managed-plugin lock. Plugin discovery,
validation, packaging, installation, removal, and catalog commands are not available
in the current CLI release.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.