跳转到内容

前端 SDK

前端 bundle 必须只注册一次,并使用和 manifest.json 完全一致的 plugin ID。

import { definePlugin, registerPlugin } from "@haloforge/plugin-sdk";
import { MyPanel } from "./Panel";
import "./styles.css";
export default registerPlugin("dev.example.my-plugin", definePlugin({
panel: MyPanel,
}));
Helper用途
definePlugin()Level 1/2 面板和 slot
defineModulePlugin()Level 0 模块
defineAssistantPlugin()Level 3 assistant
registerPlugin()将 bundle 注册到 HaloForge runtime
invokePlugin()调用本插件 Rust 命令
invokeOtherPlugin()调用其他插件命令
usePluginSettings()读取和更新插件设置
useHostTheme()读取宿主主题和 token
useHostAI()复用 AIChat 模型和 transport
usePluginNavigation()让 Level 0 插件内部页面接入 HaloForge 后退/前进
usePluginWindows()请求 HaloForge 通过多窗口分发器打开插件 route/resource
pluginCurrentWindow() / usePluginCurrentWindow()更新或重置当前 HaloForge 窗口标题
usePluginWindowTitle()让系统窗口预览标题跟随当前插件文档
AppSelect渲染 HaloForge 风格下拉控件
log() / createPluginLogger()将插件诊断写入 HaloForge 应用日志
import { invokePlugin } from "@haloforge/plugin-sdk";
interface PingResult {
ok: boolean;
message: string;
}
export async function ping() {
return invokePlugin<PingResult>("ping", { source: "panel" });
}

需要在 HaloForge 诊断日志中保留下来的前端事件,使用 createPluginLogger(),不要只依赖 DevTools 里的 console.log

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 会写入 ~/.haloforge/logs/haloforge.log.YYYY-MM-DDdetails 保持 JSON 可序列化,不要写入 API key、bearer token、完整 prompt 或原始图片/base64。

当前插件窗口内部页面变化使用 usePluginNavigation()。插件希望 HaloForge 把自己的 route 或 resource 打开到最合适的宿主窗口时,使用 usePluginWindows()

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() 会和插件 manifest 的 window 策略一起交给宿主分发。插件声明意图;窗口创建、聚焦、复用、恢复和安全检查仍由 HaloForge 控制。

插件可以更新操作系统窗口预览里显示的原生标题,例如 Windows 任务栏缩略图标题。面向文档的插件可以用它显示当前文件名。

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 会把插件标题格式化成 Title - Subtitle - HaloForge,并在拥有该标题的面板卸载或禁用 hook 时重置标题。隐藏或后台插件面板不能覆盖其他插件当前激活窗口的标题。

combo box 和下拉框使用 AppSelect,不要直接写原生 <select>

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>
);
}

避免:

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

如果 SDK 没有你需要的宿主能力,先在 haloforge-plugin-api 中新增公开 helper。