简体中文 English 繁體中文 日本語 Русский
本文为静态镜像,内容以交互版为准 在交互式文档中心打开 →

ErisPulse-Dashboard

ErisPulse-Dashboard は、ErisDev が直接管理する Web 管理パネルモジュール であり、ErisPulse に視覚的な実行時管理インターフェースを提供します。モジュールの起動・停止、設定の編集、ログの表示、イベントストリームの監視などが可能です。

Important

Dashboard は ErisPulse フレームワークの組み込み機能ではなく、個別にインストールする必要があります:

epsdk install Dashboard

Dashboard は、他の ErisPulse モジュールがサイドバーにカスタム管理ページを登録することもサポートしています。登録後、ユーザーは Dashboard でそのモジュールの専用ウィンドウページに切り替えることができ、追加のフロントエンド開発を必要としません。

Note

ウィンドウの登録はオプション機能です。

  • Dashboard モジュールがインストールされていない、またはロードされていない場合、sdk.Dashboard.register_view() を呼び出すと例外が発生します
  • 他のモジュール機能に影響を与えないように、登録コードを try/except で囲むことを推奨します
  • 登録前に Dashboard の利用可能性を確認することを推奨します:hasattr(sdk, 'Dashboard') and sdk.Dashboard

動作原理

モジュール on_load()
  → sdk.Dashboard.register_view(...) を呼び出す
  → Dashboard は後端にウィンドウ情報を保存
  → WebSocket でフロントエンドに通知
  → フロントエンドは動的にサイドバーのナビゲーション項目とページコンテナを作成
  → ユーザーがクリックするとモジュールのウィンドウが表示される

APIの登録

sdk.Dashboard.register_view(
    id="MyModule",                    # 必須、一意な識別子
    title="私のモジュール",            # 中文名
    title_en="My Module",             # 英文名
    icon_svg='<svg>...</svg>',        # サイドバーのアイコン SVG
    html_content='<div>...</div>',     # ページ HTML 内容
    js_content='function xxx() {}',    # ページ JavaScript ロジック
    css_content='.my-style {}',        # オプションのカスタム CSS
    iframe_url='',                     # iframe モードの URL(html_content と二択)
    loader="loadMyModuleView",         # このページに切り替わる際に呼び出される JS 関数名
    group="group_extensions",          # サイドバーのグループ
    group_title="",                    # カスタムグループの中文名
    group_title_en="",                 # カスタマグループの英文名
)

パラメータの説明

パラメータ 型 必須 説明
id str はい ウィンドウの一意な識別子、モジュール名を使用することを推奨
title str いいえ 中文表示名、デフォルトは id を使用
title_en str いいえ 英文表示名、デフォルトは title を使用
icon_svg str いいえ サイドバーのアイコンの完全な SVG 文字列
html_content str いいえ* インジェクションモードのページ HTML 内容
js_content str いいえ ページ JavaScript コード
css_content str いいえ ページのカスタム CSS スタイル
iframe_url str いいえ* iframe モードの URL、設定すると html_content は無視される
loader str いいえ ページがアクティブになった際に自動的に呼び出される JS 関数名
group str いいえ サイドバーのグループ識別子、デフォルトは group_extensions
group_title str いいえ カスタムグループの中文タイトル
group_title_en str いいえ カスタムグループの英文タイトル

*html_content と iframe_url のどちらか一方は必ず提供する必要がある。両方指定しない場合、ページは空白になる。


2 種の注入モード

モード 1:HTML/JS 注入(推奨)

HTML、JS、CSS の文字列を直接提供し、Dashboard がページに内容を注入します。このモードでは Dashboard のスタイルと完全に一致し、Dashboard が提供する CSS クラス名の使用が推奨されます。

sdk.Dashboard.register_view(
    id="HelloPage",
    title="こんにちはページ", title_en="Hello",
    icon_svg='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/></svg>',
    html_content='<h1 class="page-title">Hello World</h1><div class="card"><div class="card-body">これはサンプルページです</div></div>',
    group="group_tools",
)

API ルート、JS によるインタラクションなどを含む、完全な天気モジュールの例は、下記の 完全なモジュールの例 を参照してください。

モード 2:iframe 埋め込み

独自の HTML ページの URL を提供し(独自にルートを登録する必要があります)、Dashboard が iframe で埋め込みます。完全に独立した UI または複雑なインタラクションが必要な場合に適しています。

sdk.Dashboard.register_view(
    id="MyVisualizer",
    title="データ可視化", title_en="Data Visualizer",
    iframe_url="/MyVisualizer/view",
    group="group_tools",
)

iframe モードでは、認証のために URL に token パラメータが自動的に追加されます。

サイドバーのグループ化

モジュールは、ウィンドウが所属するサイドバーのグループを指定できます。Dashboard には、以下のグループが内蔵されています:

グループ識別子 中文名 位置
group_overview 概観 第1グループ
group_events イベント 第2グループ
group_extensions 拡張機能 第3グループ(デフォルト)
group_system システム 第4グループ
group_tools ツール 第5グループ

内蔵されたグループ名を指定すると、モジュールのウィンドウはそのグループの末尾に追加されます:

group="group_tools"  # "ツール"グループに追加

group_ で始まらないカスタムグループ名を使用することもできます。Dashboard は自動的に新しいグループを作成します:

group="my_group",
group_title="私のグループ",
group_title_en="My Group",

一般的 CSS クラス名

モジュールウィンドウで HTML インジェクションモードを使用する場合、ダッシュボードで既に用意されている CSS クラス名を使用することで、視覚的な一貫性を保つことができます。

クラス名 用途
page-title ページタイトル、例: <h1 class="page-title">タイトル</h1>
card カードコンテナ
card-header カードのタイトルバー
card-body カードのコンテンツ領域
grid-2 2 列のグリッドレイアウト
grid-3 3 列のグリッドレイアウト
btn 基本ボタン
btn-primary 主なボタン(青色)
btn-secondary 次要なボタン
btn-icon アイコン付きボタン
btn-danger 危険な操作を表すボタン

ダッシュボードは CSS 変数を使ってテーマカラーを制御しており、モジュールウィンドウでも直接参照することができます。

CSS 変数 用途
var(--bg-p) 主な背景色
var(--bg-s) 次の背景色
var(--bg-t) 3 番目の背景色(カードなど)
var(--tx-p) 主な文字色
var(--tx-s) 次の文字色
var(--tx-t) 補助的な文字色
var(--bd) ボーダー色
var(--accent) 強調色
var(--ok-c) 成功色
var(--er-c) エラーカラー

これらの変数は、ダッシュボードのライト/ダークテーマに応じて自動的に切り替えられ、モジュール側で追加の処理は不要です。

認証と API 呼び出し

モジュールウィンドウの JS で、モジュール自身の API を呼び出す際には、Dashboard のトークンを付けて認証を行う必要があります:

var token = localStorage.getItem('__ep_tk__');
var resp = await fetch('/YourModule/api/data', {
    headers: { 'Authorization': 'Bearer ' + token }
});
var data = await resp.json();

モジュールの API エンドポイントは、トークンの検証を行うかどうかを独自に決定できます。検証が必要な場合は、リクエストヘッダーから抽出できます:

async def _api_data(self, request):
    token = request.headers.get("Authorization", "").replace("Bearer ", "")
    if not token:
        return {"error": "Unauthorized"}, 401
    return {"data": "hello"}

完全なモジュールの例

以下は、ウィンドウの登録、APIデータの提供、およびアンロード時にリソースをクリーンアップする方法を示す、完全な天気モジュールの例です。

from ErisPulse import sdk
from ErisPulse.Core.Bases import BaseModule
from ErisPulse.Core.Event import command


class Main(BaseModule):
    def __init__(self):
        self.sdk = sdk
        self.logger = sdk.logger.get_child("Weather")
        self.config = self._load_config()

    @staticmethod
    def get_load_strategy():
        from ErisPulse.loaders import ModuleLoadStrategy
        return ModuleLoadStrategy(lazy_load=False, priority=50)

    async def on_load(self, event):
        self._register_routes()
        self._register_dashboard_view()
        self.logger.info("天気モジュールがロードされました")

    async def on_unload(self, event):
        self._unregister_routes()
        if hasattr(self.sdk, 'Dashboard') and self.sdk.Dashboard:
            self.sdk.Dashboard.unregister_view("Weather")
        self.logger.info("天気モジュールがアンロードされました")

    def _load_config(self):
        config = self.sdk.config.getConfig("Weather")
        if not config:
            default = {"city": "北京", "api_key": ""}
            self.sdk.config.setConfig("Weather", default)
            return default
        return config

    def _register_routes(self):
        r = self.sdk.router
        r.register_http_route("Weather", "/api/current",
                              handler=self._api_current, methods=["GET"])

    def _unregister_routes(self):
        r = self.sdk.router
        try:
            r.unregister_http_route("Weather", "/api/current")
        except Exception:
            pass

    async def _api_current(self, request):
        return {
            "city": self.config.get("city", "北京"),
            "temp": 25,
            "humidity": 60,
        }

    def _register_dashboard_view(self):
        try:
            dashboard = self.sdk.Dashboard
            dashboard.register_view(
                id="Weather",
                title="天気", title_en="Weather",
                icon_svg='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>',
                html_content='''
                    <h1 class="page-title">天気照会</h1>
                    <p style="color:var(--tx-s);margin-bottom:16px">現在の天気情報を表示します</p>
                    <div class="grid-2">
                        <div class="card">
                            <div class="card-header">現在の天気</div>
                            <div class="card-body">
                                <div id="weather-info" style="font-size:14px;color:var(--tx-s)">クリックして更新</div>
                            </div>
                        </div>
                        <div class="card">
                            <div class="card-header">操作</div>
                            <div class="card-body">
                                <button class="btn btn-primary" onclick="refreshWeather()">更新</button>
                            </div>
                        </div>
                    </div>
                ''',
                js_content='''
                    async function loadWeatherView() { await refreshWeather(); }
                    async function refreshWeather() {
                        var el = document.getElementById('weather-info');
                        if (!el) return;
                        el.textContent = '読み込み中...';
                        try {
                            var resp = await fetch('/Weather/api/current', {
                                headers: { 'Authorization': 'Bearer ' + localStorage.getItem('__ep_tk__') }
                            });
                            var data = await resp.json();
                            el.innerHTML = '<p>都市: ' + (data.city || '--') + '</p>' +
                                           '<p>温度: ' + (data.temp || '--') + '°C</p>' +
                                           '<p>湿度: ' + (data.humidity || '--') + '%</p>';
                        } catch (e) {
                            el.textContent = '読み込み失敗: ' + e.message;
                        }
                    }
                ''',
                loader="loadWeatherView",
                group="group_tools",
            )
        except Exception as e:
            self.logger.warning(f"Dashboardウィンドウの登録に失敗しました: {e}")

視窗の登録解除

モジュールのアンロード時に、unregister_view() を呼び出して登録済みの視窗をクリーンアップする必要があります。

async def on_unload(self, event):
    if hasattr(self.sdk, 'Dashboard') and self.sdk.Dashboard:
        self.sdk.Dashboard.unregister_view("Weather")

登録解除後、Dashboard のフロントエンドは WebSocket を介してサイドバーのナビゲーション項目とページコンテンツをリアルタイムに削除します。ユーザーによるリフレッシュは不要です。

注意事項

  1. ロード順序 — Dashboard のロード優先度は 99999(高優先度)です。あなたのモジュールの優先度はこの値より低くする必要があります(例:50)。これにより、Dashboard が先にロード完了するようにします。
  2. 防御的プログラミング — Dashboard モジュールがインストールされていない、またはロードされていない可能性があるため、ウィンドウを登録する際には try/except で囲んでください。
  3. リソースのクリーンアップ — on_unload で unregister_view() を呼び出し、登録されたウィンドウを削除します。
  4. ID の一意性 — id パラメータは、Dashboard 全体で一意である必要があります。モジュール名を直接使用することを推奨します。
  5. SVG アイコン — icon_svg は完全な <svg> タグである必要があります。推奨サイズは viewBox="0 0 24 24" です。stroke="currentColor" を使用して、Dashboard のテーマ色を継承します。
  6. JS 関数名 — js_content 内の関数名は一意である必要があります(例:loadWeatherView)。他のモジュールとの衝突を避けるためです。
  7. 動的更新 — モジュールがウィンドウを登録または解除した後、Dashboard のフロントエンドは WebSocket を使用してサイドバーをリアルタイムに更新します。ページをリフレッシュする必要はありません。