Documentation Index
Fetch the complete documentation index at: /docs/llms.txt
Use this file to discover all available pages before exploring further.
Skip to main content
Extend Pascal
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.
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.
Follow the repository’s README for its current build, test, and local integration
commands.
Use a namespaced ID such as
A definition can contribute any combination of:
Keep renderer and editor modules lazy, as shown above. This lets hosts avoid loading
plugin UI and rendering code until it is needed.
Register the panel with
Plugin loading is add-only for the browser session. Duplicate node kinds fail at
startup instead of silently replacing each other.
This installs a versioned editor runtime and preserves local projects in
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 |
|---|---|---|
| Bones | An engineering X-ray: framing, foundation, electrical, plumbing, and ductwork derived from your model and sized to your jurisdiction | pascalorg/plugin-bones |
| Nature | Procedural trees, flowers, and grass | pascalorg/plugin-trees |
| Streetscape | Road networks with junctions, cross-sections, curbs, and roadside objects | sudhir9297/streetscape-pascal-plugin |
| Mint | Browse or generate 3D models and place them in the scene | mintdotgg/mint-pascal-plugin |
| Boots | First-person mode — walk the building you’re editing. Alpha | pascalorg/plugin-boots |
| Bloblins | Tamagotchi-style clay companions that hatch from DNA eggs and live in your house. Alpha | wass08/pascal-plugin-bloblins |
Start from a working plugin
Start with the open-source Nature plugin. 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.git clone https://github.com/pascalorg/plugin-trees.git
cd plugin-trees
bun install
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:- One npm package with a unique plugin ID and an explicit Pascal API compatibility range.
@pascal-app/*, React, React DOM, and Three.js as peer dependencies rather than bundled duplicate runtimes.- An immutable commit or release artifact, a lockfile, a license, and commands for type checking, tests, and building.
- No required install-time scripts and no undeclared network calls.
- A README that identifies the publisher, repository, support contact, capabilities, external origins, persisted project data, and any account or personal data used.
- Lazy UI and renderer entry points so loading plugin metadata does not initialize external services or expensive runtime code.
Plugin structure
Every plugin exports a manifest with a globally unique ID, the Pascal plugin API version it targets, and its node definitions:import type { Plugin } from "@pascal-app/core";
import { plantDefinition } from "./plant";
export const gardenPlugin: Plugin = {
id: "acme:garden",
apiVersion: 1,
nodes: [plantDefinition],
};
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’snodes array contains NodeDefinition objects. Each definition
starts with a schema and defaults, then opts into only the Pascal capabilities it
needs.
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>;
| 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 |
Add an editor panel
The core manifest is independent of any editor UI. Export anEditorHostPanel
separately if your plugin needs a sidebar panel:
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"),
};
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:import { setPluginDiscovery } from "@pascal-app/core";
import { gardenPlugin } from "@acme/pascal-garden";
setPluginDiscovery(async () => [gardenPlugin]);
await import("./pascal-bootstrap");
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:- 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.
- Ask the user to connect explicitly and explain the data exchange before authorization.
- Keep access and refresh tokens in
HttpOnly,Secureproduction cookies or an equivalent server-side credential store; plugin JavaScript must not receive them. - 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.
- 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.
- 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.
- 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.
Test your plugin
Before integrating a plugin into a host:- Validate every node against its schema and test any geometry or floor-plan functions as pure functions.
- Add a registry test that loads the manifest and confirms each expected node kind is registered.
- Load the plugin in a Pascal host with
setPluginDiscoveryand confirm the development console reports its ID and node count. - Create, save, reload, uninstall, and reinstall its nodes to check the complete project lifecycle.
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 lets you run the standalone editor without cloning the Pascal repository:npx @pascal-app/cli editor
npx @pascal-app/cli doctor
~/.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.
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.