Skip to content
docs
GitHub

Manual SDK example

Build and install a complete React plugin using the standalone SDK.

This complete example adds a Workspace info panel and Open workspace info command. It calls buildPlugin() directly, without the CLI or generator. The generator is the easier starting point for most projects; this version shows the underlying pieces.

Source is available in examples/workspace-info. Use Node.js 22.14+ and pnpm 11.25.0. Versions are SDK 1.1.0-alpha.1, React/React DOM 19.2.8 and TypeScript 7.0.2. The build helper depends on tsup 8.5.1.

Terminal window
git clone https://github.com/lomi-dev/docs-app.git
cd docs-app/examples/workspace-info

Before npm publication, prepare a local SDK archive using Tools and diagnostics, then provide its actual path. If plugin-archives is next to docs-app:

Terminal window
pnpm add --save-exact --ignore-scripts @lomi-dev/plugin-sdk@file:../../../plugin-archives/lomi-dev-plugin-sdk-1.1.0-alpha.1.tgz
pnpm check
pnpm build

This saves a file: dependency and updates the example lockfile, without using application sources. Once the matching package set is published, replace the archive with exact version @lomi-dev/[email protected], review the lockfile and retain it. Use pnpm install --frozen-lockfile --ignore-scripts after establishing that environment.

buildPlugin() does not type-check. The example’s build script therefore runs pnpm check first. Check the exit status: failed builds preserve the previous package/.

  1. Start the desktop application and open a project folder.
  2. In Settings > Plugins > Import plugin, select docs-app/examples/workspace-info/package.
  3. Enable Workspace info, then select Trust and enable.
  4. Open Commands with Ctrl+Shift+P, or Cmd+Shift+P on macOS, and run Open workspace info.
  5. Toggle Show project path. Switch workspaces and return to confirm that the panel keeps this choice.

Import the generated package folder. The parent folder contains source code, dependencies and build tools that do not belong in an installed plugin.

This defines author dependencies and type checking. These are exact npm versions; for the local candidate, replace the SDK with an archive as shown above.

{
"name": "lomi-workspace-info-example",
"version": "1.0.0",
"private": true,
"type": "module",
"packageManager": "[email protected]",
"scripts": {
"check": "tsc --noEmit",
"build": "pnpm check && node build.mjs"
},
"dependencies": {
"@lomi-dev/plugin-sdk": "1.1.0-alpha.1",
"react": "19.2.8",
"react-dom": "19.2.8"
},
"devDependencies": {
"@types/react": "19.2.18",
"@types/react-dom": "19.2.7",
"tsup": "8.5.1",
"typescript": "7.0.2"
}
}
{
"schemaVersion": 1,
"hostApi": 1,
"id": "example.workspace-info",
"name": "Workspace info",
"version": "1.0.0",
"description": "Show the active project folder and workspace.",
"entry": "dist/index.js",
"activation": "lazy",
"stylesheets": ["workspace-info.css"],
"contributes": {
"views": [
{
"id": "example.workspace-info.panel",
"title": "Workspace info",
"placement": "central",
"multiple": false,
"stateVersion": 1
}
],
"commands": [
{
"id": "example.workspace-info.open",
"label": "Open workspace info",
"description": "Show the active project folder and workspace.",
"context": { "workspace": true }
}
]
}
}
import {
useHostContext,
type PluginContext,
type ViewProps,
} from '@lomi-dev/plugin-sdk';
function WorkspaceInfo({ panel, setState }: ViewProps) {
const host = useHostContext();
const showPath =
typeof panel.state === 'object' &&
panel.state !== null &&
!Array.isArray(panel.state) &&
panel.state.showPath === true;
return (
<section className="workspace-info">
<h2>{host.workspaceName ?? 'No workspace selected'}</h2>
<label>
<input
type="checkbox"
checked={showPath}
onChange={(event) => setState({ showPath: event.target.checked })}
/>
Show project path
</label>
{showPath && <p>{host.projectPath ?? 'No project selected'}</p>}
<p>Appearance: {host.appearance}</p>
</section>
);
}
export function activate(context: PluginContext) {
context.registerView('example.workspace-info.panel', WorkspaceInfo);
context.registerCommand('example.workspace-info.open', async () => {
await context.openView('example.workspace-info.panel', { showPath: true });
});
}
import { buildPlugin } from '@lomi-dev/plugin-sdk/build';
await buildPlugin({ assets: ['workspace-info.css'] });
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
"skipLibCheck": true,
"rootDir": "src"
},
"include": ["src"]
}
.workspace-info {
padding: 16px;
color: var(--color-surface-text);
background: var(--plugin-example-dworkspace-hinfo-surface, var(--color-surface));
overflow-wrap: anywhere;
}
.workspace-info label {
display: flex;
align-items: center;
gap: 8px;
}

useHostContext() updates the panel when the active workspace or appearance changes. setState() writes a small JSON value into the saved panel descriptor. This example has no unsaved document, so it does not need a dirty-view guard.

The host releases the view and command registrations when the plugin is deactivated. If you later add event listeners, timers or retained data stores, follow the lifecycle rules.

Change the authoring files and run pnpm build again. Disable the installed plugin, import the new package folder and approve the replacement. Restart when prompted. Installed packages are snapshots, so editing the authoring directory does not update a running plugin.

Option Default Meaning
entry src/index.tsx Source file to compile; unused when the manifest has no entry
manifest plugin.json Manifest inside the author project
assets [] Files and directories copied into the package
outDir package Output directory; use a separate directory dedicated to builds
cwd process.cwd() Author project directory
signal None Optional AbortSignal for cancellation

The function returns Promise<string> with the absolute output path. The calling script handles failures. Successful builds replace outDir, so do not keep manually authored files there.

plugin.json.entry determines the generated JavaScript filename and extension. For example, source src/main.tsx can produce dist/plugin.mjs. The helper emits browser ES2022 ESM, shares React/SDK and validates final imports. See Styles and assets for resource rules.

The helper itself creates neither a ZIP nor a release report. Share the complete package/ folder, or add the CLI and use its package command. Import and qualify the built folder in Lomi through the desktop qualification guide.

Source: SDK contract, build helper.