Extension System in Modly: Key Files and Architecture Explained
The extension system in Modly relies on five core TypeScript modules—electron.d.ts, extensionsStore.ts, preflight.ts, workflowRunStore.ts, and ExtensionNode.tsx—that coordinate IPC contracts, state management, workflow validation, runtime execution, and UI rendering to enable GitHub-hosted model and process extensions.
The lightningpixel/modly repository implements a plugin architecture that allows developers to extend the application by dropping GitHub-hosted repositories into the app. Understanding the extension system in Modly requires familiarity with a specific set of TypeScript files that bridge the Electron main process, React renderer, and Python runtime. These modules handle everything from manifest.json validation to process execution, creating a clean separation between declaration, state, and runtime concerns.
IPC Contract Definitions
src/shared/types/electron.d.ts
This file defines the TypeScript interface for the Electron IPC bridge that enables communication between the renderer (React) and main processes. It declares the methods available on window.electron.extensions, including list, installFromGitHub, installFromLocal, uninstall, reload, and runProcess.
When the UI needs to interact with the extension system, it calls these typed methods. The main process implements the underlying logic that reads the extension folder, validates the manifest.json structure, and launches Python processes for model or process extensions.
State Management Layer
src/shared/stores/extensionsStore.ts
The central Zustand store that caches loaded extensions and provides actions for installing, uninstalling, and reloading them. This file tracks install progress and errors through window.electron.extensions.onInstallProgress.
The store maintains two critical arrays—modelExtensions and processExtensions—that populate the UI throughout the application. When you invoke loadExtensions(), the store calls the Electron IPC methods defined in electron.d.ts and updates the cached state with the validated extension list.
import { useExtensionsStore } from '@shared/stores/extensionsStore'
function InstalledExtensions() {
const { modelExtensions, processExtensions, loadExtensions } = useExtensionsStore()
React.useEffect(() => { loadExtensions() }, [])
return (
<div>
<h3>Models</h3>
{modelExtensions.map(e => <div key={e.id}>{e.name}</div>)}
<h3>Processes</h3>
{processExtensions.map(e => <div key={e.id}>{e.name}</div>)}
</div>
)
}
Workflow Integration Components
src/areas/workflows/preflight.ts
This module performs static validation of workflows before execution. It verifies that every extension node refers to a known extension and that required inputs/outputs are satisfied. The validator uses getWorkflowExtension, a helper that lookups extensions by ID in the store, to confirm node validity.
When validation fails, the system reports missing-extension issues that surface as UI warnings, preventing runtime errors from undefined extensions.
src/areas/workflows/workflowRunStore.ts
The runtime execution engine that steps through workflow nodes. When encountering a node of type extensionNode, it invokes window.electron.extensions.runProcess (or model-specific endpoints) to execute the extension's Python entry point.
This file relies on the extension's manifest.json to determine the command to run and maps inputs/outputs accordingly. It handles errors returned from the backend and bubbles them up to the UI with full stack traces.
// inside workflowRunStore.ts
if (node.type === 'extensionNode') {
const ext = getWorkflowExtension(node.data.extensionId ?? '', allExtensions)
const result = await window.electron.extensions.runProcess(
ext.id,
{ file: inputFilePath },
node.data.params
)
if (!result.success) throw new Error(result.error ?? 'Process extension failed')
// result.result contains the processed file path
}
src/areas/workflows/nodes/ExtensionNode.tsx
The React component that renders extension nodes inside the visual workflow editor. It displays the extension name, configurable parameters, and connection pins (input/output sockets) based on the extension's declared schema.
This component pulls the extension list from useExtensionsStore and resolves output types to color-code connection handles in the node graph.
Built-in Process Extensions
Modly ships with several built-in process extensions located in src/areas/workflows/nodes/*/processor.*. These follow the same runtime contract as external extensions but reside in the builtin-extensions folder.
Key examples include:
- Mesh Optimizer (
src/areas/workflows/nodes/mesh-optimizer/processor.ts) – Optimizes GLTF meshes - Mesh Exporter (
src/areas/workflows/nodes/mesh-exporter/processor.ts) – Exports to GLTF format - Mesh Smoother (
src/areas/workflows/nodes/mesh-smoother/processor.py) – Python-based smoothing algorithms - Mesh Repair (
src/areas/workflows/nodes/mesh-repair/processor.py) – Automated mesh repair routines - Mesh Remesher – Additional geometry processing utilities
Each processor reads incoming files, executes Python routines, and returns processed assets that downstream nodes consume.
Extension Lifecycle in Modly
The extension system follows a five-stage lifecycle:
-
Discovery – On startup,
extensionsStore.loadExtensions()invokeswindow.electron.extensions.list(). The main process scansextensionsDir, validates eachmanifest.json, and returns typed arrays ofModelExtensionorProcessExtensionobjects. -
Installation – Calling
extensionsStore.installFromGitHub(url)triggers the Electron bridge to clone the repository, run install scripts, and stream progress events (downloading,extracting,validating,setting_up). -
Workflow Integration – Dragging an Extension Node into the visual editor creates a node with
data.extensionIdset to the extension identifier (e.g.,pack/process-node). -
Execution –
workflowRunStoreiterates through nodes; forextensionNodetypes, it callswindow.electron.extensions.runProcess(extensionId, input, params), launching the Python entry point defined in the manifest. -
Result Propagation – Processed assets feed back into the workflow graph, allowing downstream nodes like Mesh Exporter to consume the output.
await useExtensionsStore.getState().installFromGitHub(
'https://github.com/lightningpixel/modly-hunyuan3d-mini-extension'
)
Summary
src/shared/types/electron.d.tsdefines the IPC contract between React and Electron main process for all extension operations.src/shared/stores/extensionsStore.tsmanages extension state, installation progress, and provides theuseExtensionsStorehook.src/areas/workflows/preflight.tsvalidates workflows before execution to ensure all referenced extensions exist and have correct signatures.src/areas/workflows/workflowRunStore.tsexecutes workflow nodes by invoking extension processes through the Electron bridge.src/areas/workflows/nodes/ExtensionNode.tsxrenders extension nodes in the visual editor with proper input/output handles.- Built-in extensions in
src/areas/workflows/nodes/*/demonstrate the same contract as external extensions, providing reference implementations for mesh processing.
Frequently Asked Questions
How does Modly validate extension manifests before execution?
The src/areas/workflows/preflight.ts module performs static analysis on workflow graphs before runtime. It uses getWorkflowExtension to verify that every extensionNode references a valid extension ID and that the node's inputs and outputs match the types declared in the extension's manifest.json. Failed validations surface as UI warnings rather than runtime crashes.
What is the difference between model and process extensions in the Modly extension system?
Model extensions typically load AI models or specialized processing capabilities that run inference tasks, while process extensions handle data transformations like mesh optimization or file format conversion. Both follow the same IPC contract defined in electron.d.ts, but they populate separate arrays in extensionsStore.ts (modelExtensions vs processExtensions) to categorize their capabilities in the UI.
How do I install a third-party extension from GitHub in Modly?
Use the installFromGitHub method from the extensions store: await useExtensionsStore.getState().installFromGitHub('https://github.com/user/repo'). The main process clones the repository, validates the manifest.json, runs any defined install scripts, and streams progress events back to the UI through the onInstallProgress listener.
Where are built-in extensions located compared to external ones?
Built-in extensions reside in the src/areas/workflows/nodes/ directory within the application source (e.g., mesh-optimizer/processor.ts), while external extensions are cloned into the configured extensionsDir folder at runtime. Both types implement the same interface, but built-in extensions ship with the application and do not require separate installation through the GitHub workflow.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →