Skip to content

Frontend SDK

The frontend bundle must register exactly once with the same plugin ID as manifest.json.

import { definePlugin, registerPlugin } from "@haloforge/plugin-sdk";
import { MyPanel } from "./Panel";
import "./styles.css";
export default registerPlugin("dev.example.my-plugin", definePlugin({
panel: MyPanel,
}));
HelperUse
definePlugin()Level 1/2 plugin panels and slots
defineModulePlugin()Level 0 module plugins
defineAssistantPlugin()Level 3 assistant plugins
registerPlugin()Register the frontend bundle with HaloForge
invokePlugin()Call this plugin’s Rust backend commands
invokeOtherPlugin()Call another plugin’s declared commands
usePluginSettings()Read and update plugin settings
useHostTheme()Read active host theme and tokens
useHostAI()Reuse AIChat model and transport integration
usePluginNavigation()Sync Level 0 plugin pages with HaloForge Back/Forward
usePluginWindows()Ask HaloForge to open plugin routes/resources through the multi-window dispatcher
pluginCurrentWindow() / usePluginCurrentWindow()Update or reset the active HaloForge window title
usePluginWindowTitle()Keep the native window preview title synced with the current plugin document
AppSelectRender HaloForge-styled dropdown/listbox controls
log() / createPluginLogger()Write plugin diagnostics into the HaloForge app log
import { invokePlugin } from "@haloforge/plugin-sdk";
interface PingResult {
ok: boolean;
message: string;
}
export async function ping() {
return invokePlugin<PingResult>("ping", { source: "panel" });
}

Use createPluginLogger() for frontend events that should be visible in HaloForge diagnostics, not only in browser DevTools.

import { createPluginLogger } from "@haloforge/plugin-sdk";
const logger = createPluginLogger("image-generation");
await logger.info("Generation started", {
model: "gpt-image-2.0",
size: "1024x1024",
count: 1,
});
await logger.error("Generation failed", {
status: 502,
elapsedMs: 1842,
error: "upstream gateway timeout",
});

HaloForge writes these records to ~/.haloforge/logs/haloforge.log.YYYY-MM-DD. Keep details JSON-serializable and do not include API keys, bearer tokens, full prompts, or raw image/base64 payloads.

Use usePluginNavigation() for page changes inside the current plugin window. Use usePluginWindows() when a plugin wants HaloForge to open one of its routes or resources in the best host window.

import { usePluginNavigation, usePluginWindows } from "@haloforge/plugin-sdk";
export function DocumentsPanel() {
const navigation = usePluginNavigation();
const windows = usePluginWindows();
function openDetail(id: string) {
navigation.pushRoute(`/detail/${id}`, { params: { id } });
}
async function openDocument(path: string) {
await windows.openResource(path, {
route: "/document",
params: { path },
reuseKey: "resource",
openMode: "reuse_or_new",
});
}
}

usePluginWindows() combines the call with the plugin manifest window policy. The plugin declares intent; HaloForge owns window creation, focus, reuse, restore, and safety checks.

Plugins can update the native window title shown in the operating system window preview, such as the Windows taskbar thumbnail. Use this for document-oriented plugins that should show the active file name.

import { usePluginWindowTitle } from "@haloforge/plugin-sdk";
export function MarkdownPanel({ activeModule, document }) {
usePluginWindowTitle(
document?.name?.trim() || null,
{
subtitle: "Markdown",
enabled: activeModule === "markdown",
},
);
return <main>{document?.name ?? "No document"}</main>;
}

HaloForge formats plugin titles as Title - Subtitle - HaloForge and resets the title when the owning panel unmounts or disables the hook. Hidden or background plugin panels cannot overwrite another plugin’s active window title.

Use AppSelect instead of a raw <select> when rendering combo boxes.

import { AppSelect } from "@haloforge/plugin-sdk";
export function ModePicker({ value, onChange }) {
return (
<AppSelect value={value} onChange={(event) => onChange(event.target.value)}>
<option value="overview">Overview</option>
<option value="diagnostics">Diagnostics</option>
</AppSelect>
);
}

The SDK may use an internal bridge to talk to the current HaloForge runtime. Plugin source should not read those internals directly.

Avoid:

window.__HF_HOST
window.__HALOFORGE_PLUGIN_HOST__
invoke("plugin_invoke", ...)

Use the SDK helper that matches the feature. If no helper exists, add one to haloforge-plugin-api first.