feat(FN-3079): add plugin dashboard views and dependency graph plugin
- Add plugin dashboard view registration and hosting across core, dashboard routes, and plugin SDK exports - Integrate plugin-provided views into app navigation, mobile/header UI, and view state hooks with coverage - Add fusion-plugin-dependency-graph example plugin with persisted storage, dashboard view UI, and manifest wiring - Update plugin authoring and architecture docs for dashboard view extension points - Add a changeset for @runfusion/fusion covering plugin dashboard view support Fusion-Task-Id: FN-3079
This commit is contained in:
@@ -1288,6 +1288,42 @@ describe("PluginLoader", () => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
describe("getPluginDashboardViews", () => {
|
||||
it("returns empty array when no plugins loaded", async () => {
|
||||
await pluginStore.init();
|
||||
const loader = new PluginLoader({ pluginStore, taskStore: mockTaskStore });
|
||||
expect(loader.getPluginDashboardViews()).toEqual([]);
|
||||
});
|
||||
|
||||
it("aggregates dashboard views from multiple plugins and keeps uiSlots separate", async () => {
|
||||
await pluginStore.init();
|
||||
const loader = new PluginLoader({ pluginStore, taskStore: mockTaskStore });
|
||||
(loader as any).plugins.set("views-a", {
|
||||
manifest: makeManifest({ id: "views-a" }),
|
||||
state: "started",
|
||||
hooks: {},
|
||||
uiSlots: [{ slotId: "task-detail-tab", label: "Tab", componentPath: "./tab.js" }],
|
||||
dashboardViews: [{ viewId: "graph", label: "Graph", componentPath: "./graph.js", placement: "more" }],
|
||||
} as FusionPlugin);
|
||||
(loader as any).plugins.set("views-b", {
|
||||
manifest: makeManifest({ id: "views-b" }),
|
||||
state: "started",
|
||||
hooks: {},
|
||||
dashboardViews: [{ viewId: "timeline", label: "Timeline", componentPath: "./timeline.js" }],
|
||||
} as FusionPlugin);
|
||||
|
||||
const views = loader.getPluginDashboardViews();
|
||||
expect(views).toHaveLength(2);
|
||||
expect(views.map((entry) => entry.pluginId + ":" + entry.view.viewId)).toEqual([
|
||||
"views-a:graph",
|
||||
"views-b:timeline",
|
||||
]);
|
||||
expect(loader.getPluginUiSlots()).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
// ── getPluginRuntimes ─────────────────────────────────────────────
|
||||
|
||||
describe("getPluginRuntimes", () => {
|
||||
|
||||
@@ -140,6 +140,7 @@ export type {
|
||||
PluginRouteDefinition,
|
||||
PluginRouteMethod,
|
||||
PluginUiSlotDefinition,
|
||||
PluginDashboardViewDefinition,
|
||||
PluginRuntimeManifestMetadata,
|
||||
PluginRuntimeFactory,
|
||||
PluginRuntimeRegistration,
|
||||
|
||||
@@ -22,6 +22,7 @@ import type {
|
||||
PluginToolDefinition,
|
||||
PluginRouteDefinition,
|
||||
PluginUiSlotDefinition,
|
||||
PluginDashboardViewDefinition,
|
||||
PluginRuntimeRegistration,
|
||||
PluginInstallation,
|
||||
PluginSkillContribution,
|
||||
@@ -775,6 +776,22 @@ export class PluginLoader extends EventEmitter<{
|
||||
return slots;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all top-level dashboard view definitions from loaded plugins.
|
||||
*/
|
||||
getPluginDashboardViews(): Array<{ pluginId: string; view: PluginDashboardViewDefinition }> {
|
||||
const views: Array<{ pluginId: string; view: PluginDashboardViewDefinition }> = [];
|
||||
for (const [pluginId, plugin] of this.plugins) {
|
||||
if (plugin.dashboardViews) {
|
||||
for (const view of plugin.dashboardViews) {
|
||||
views.push({ pluginId, view });
|
||||
}
|
||||
}
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all runtime registrations from loaded plugins.
|
||||
* Returns plugin ownership metadata along with the runtime registration.
|
||||
|
||||
@@ -179,6 +179,28 @@ export interface PluginUiSlotDefinition {
|
||||
componentPath: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Top-level dashboard view definition for plugin-provided navigation destinations.
|
||||
* This is separate from embedded uiSlots and is rendered via host-managed registry.
|
||||
*/
|
||||
export interface PluginDashboardViewDefinition {
|
||||
/** Unique view identifier within a plugin namespace. */
|
||||
viewId: string;
|
||||
/** Human-readable label shown in dashboard navigation. */
|
||||
label: string;
|
||||
/**
|
||||
* Path to module exporting the dashboard view component.
|
||||
* Stored for authoring symmetry/future expansion; host currently resolves via static registry.
|
||||
*/
|
||||
componentPath: string;
|
||||
/** Optional icon name (lucide-react icon name or custom icon identifier). */
|
||||
icon?: string;
|
||||
/** Optional sort order for nav presentation. Lower numbers appear first. */
|
||||
order?: number;
|
||||
/** Preferred navigation placement for this top-level view. */
|
||||
placement?: "primary" | "more";
|
||||
}
|
||||
|
||||
// ── Plugin Runtimes ─────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
@@ -356,6 +378,8 @@ export interface FusionPlugin {
|
||||
tools?: PluginToolDefinition[];
|
||||
routes?: PluginRouteDefinition[];
|
||||
uiSlots?: PluginUiSlotDefinition[];
|
||||
/** Plugin-contributed top-level dashboard views. */
|
||||
dashboardViews?: PluginDashboardViewDefinition[];
|
||||
/** Agent runtime registration for providing custom runtime implementations */
|
||||
runtime?: PluginRuntimeRegistration;
|
||||
/** Plugin-contributed skills surfaced by the skill resolver. */
|
||||
|
||||
Reference in New Issue
Block a user