Skip to content

feat: Custom Panel Plugin SDK — forge panel create for extensible dashboard panels #32

@alohays

Description

@alohays

Problem

monitor-forge ships 6 built-in panel types (news-feed, ai-brief, market-ticker, entity-tracker, instability-index, service-status), but there is no supported way to add custom panels without understanding the internal manifest generation pipeline.

The irony: the architecture was designed for extensibility but the developer-facing tooling was never built:

  1. Schema already accepts type: 'custom': PanelSchema in forge/src/config/schema.ts includes 'custom' as a valid panel type
  2. PanelBase has a full abstract API: render(), update(data), destroy() + utility helpers (triggerPulse(), showSkeleton(), createElement())
  3. PanelRegistry has registerPanelType(): The registration mechanism exists in src/core/panels/panel-registry.ts
  4. Manifest generator deliberately skips custom panels: Lines 103/110 of forge/src/generators/manifest-generator.ts have a no-op code path for type !== 'custom' — the stub exists but does nothing

The result: A developer who wants a "WeatherPanel" or "GitHubActivityPanel" must manually:

  • Understand the manifest generation pipeline
  • Create a class extending PanelBase in the correct location
  • Manually register it in the generated manifest (which gets overwritten on next build)
  • Figure out the data flow from SourceManager → Panel

This is the #1 missing piece that would transform monitor-forge from a template into a platform. worldmonitor requires forking and editing core source files. monitor-forge should let you extend without touching core code.

Solution

1. forge panel create <name> Scaffold Command

New command that generates a ready-to-use custom panel:

$ npx forge panel create weather-panel

  ✓ Created src/custom-panels/WeatherPanel.ts
  ✓ Updated panel registry for custom panels
  ✓ Panel "weather-panel" ready for development

  Next steps:
    1. Edit src/custom-panels/WeatherPanel.ts
    2. Add panel to config: forge panel add weather-panel --type custom
    3. Run forge dev to see it live

Generated file (src/custom-panels/WeatherPanel.ts):

import { PanelBase, type PanelConfig } from '@/core/panels/PanelBase';

/**
 * Custom panel: weather-panel
 *
 * PanelBase API:
 *   render()        — Create and return your panel's DOM (called once)
 *   update(data)    — Receive new data from sources (called on each poll)
 *   destroy()       — Cleanup timers, listeners, DOM (called on teardown)
 *
 * Available helpers (from PanelBase):
 *   this.triggerPulse()           — Flash header on data update
 *   this.showSkeleton(lines?)     — Show loading skeleton
 *   this.hideSkeleton()           — Hide loading skeleton
 *   this.markDataReceived()       — Hide skeleton + trigger pulse
 *   this.createElement(tag, cls)  — Create DOM element helper
 */
export class WeatherPanel extends PanelBase {
  render(): HTMLElement {
    const container = this.createElement('div', 'weather-panel');
    container.innerHTML = '<p>Weather panel — edit this file to customize</p>';
    this.showSkeleton();
    return container;
  }

  update(data: unknown): void {
    // Process incoming data from connected sources
    // Call this.markDataReceived() when rendering is complete
    this.markDataReceived();
  }

  destroy(): void {
    this.cleanupTimers();
  }
}

2. Manifest Generator Update

Update forge/src/generators/manifest-generator.ts to auto-discover and register custom panels:

// Scan src/custom-panels/ for .ts files
// Auto-generate import + registration:
import { WeatherPanel } from '../custom-panels/WeatherPanel';
registerPanelType('weather-panel', WeatherPanel);
  • Custom panels discovered at build time via filesystem scan of src/custom-panels/
  • Naming convention: PascalCase.tskebab-case panel type
  • No manual registration needed — forge build handles it

3. CLI Integration

  • forge panel create <name> — scaffold new custom panel
  • forge panel list — shows custom panels with [custom] badge alongside built-in panels
  • forge panel add <name> --type custom — add custom panel to config (existing command, works today)
  • forge panel remove <name> — works for custom panels too (existing command)

4. Hot-Reload Support

Custom panels in src/custom-panels/ are regular TypeScript files in the Vite source tree. Hot module replacement works automatically — edit the file, see changes instantly in the browser.

5. Convention Over Configuration

src/custom-panels/
├── WeatherPanel.ts          → type: "weather-panel"
├── GitHubActivityPanel.ts   → type: "github-activity-panel"
└── index.ts                 → auto-generated barrel export
  • File name determines panel type (PascalCase → kebab-case)
  • No config changes needed beyond forge panel add
  • Custom panels are first-class citizens in the panel system

Implementation Notes

Files to modify:

  • forge/src/commands/panel.ts — add create subcommand
  • forge/src/generators/manifest-generator.ts — custom panel discovery + registration codegen
  • forge/src/config/schema.ts — no changes needed ('custom' already valid)
  • src/core/panels/panel-registry.ts — no changes needed (registration API exists)

Files to create:

  • forge/src/templates/custom-panel.ts.template — scaffold template
  • src/custom-panels/.gitkeep — directory placeholder

Backward compatibility:

  • Zero breaking changes — existing configs without custom panels work identically
  • Custom panel directory is optional — if absent, build proceeds as before

Metadata

Metadata

Assignees

No one assigned

    Labels

    dxDeveloper experience improvementsenhancementNew feature or requesthigh-impactHigh-impact feature for project growthpriority: mediumShould fix, but not urgent

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions