# 插件系统 (Plugin System)

- Summary: Five 插件系统基于 `BasePlugin` 命名空间构建，提供了一套标准化的状态管理、生命周期控制和事件通信机制。
- Schema: 核心类型定义位于 `BasePlugin` 命名空间。
- Concepts: BasePlugin, State, Lifecycle, Controller.
- Configuration: 插件特定的参数配置。
- Examples: 标准插件开发示例。

## Schema

> Definition: BasePlugin

`BasePlugin` 是一个命名空间，包含 `Controller` (抽象基类), `State` (状态接口), `EventMap` (事件映射) 等核心定义。

    export namespace BasePlugin {
      export interface State {
        enabled: boolean
        visible?: boolean
        config?: unknown
      }
      export abstract class Controller<PluginState, PluginEventMap, PluginServerData, PluginData> {
        public five: Five
        public abstract state: PluginState
        public hooks: Subscribe<PluginEventMap>
        // ...
      }
    }

## Concepts

### Directory Structure (目录结构)

一个标准的插件通常包含以下文件：

    PluginName/
      ├── index.ts        # 插件入口，导出工厂函数和类型
      ├── Controller.ts   # 插件控制器，核心逻辑实现
      └── typing.ts       # 类型定义

### BasePlugin

所有插件推荐继承 `BasePlugin.Controller`。它定义了规范的泛型接口：

- `PluginState`: 插件状态类型，继承自 `BasePlugin.State`。
- `PluginEventMap`: 插件事件类型，继承自 `BasePlugin.EventMap`。
- `PluginServerData`: 服务端原始数据类型。
- `PluginData`: 解析后的插件数据类型。

### State (状态)

插件的状态驱动其行为。`BasePlugin.State` 定义了最基本的状态：

- `enabled`: 是否启用。
- `visible`: UI 是否可见。
- `config`: 配置项。

### Lifecycle (生命周期)

插件的主要生命周期方法：

- constructor: 初始化配置和初始状态。
- load: 加载和解析数据。
- enable: 启用插件，注册事件监听，添加副作用。
- disable: 禁用插件，移除事件监听，清理副作用。
- dispose: 销毁插件，彻底清理资源。

## Configuration

### BasePlugin Configuration

`BasePlugin` 控制器构造函数接受可选的配置对象：

Parameter | Type | Default | Description
--- | --- | --- | ---
`staticPrefix` | `string` | `'//vr-image-4.realsee-cdn.cn'` | 静态资源前缀。
`i18n` | `(key: string) => string` | `undefined` | 国际化转换函数。

## Examples

### Plugin Development Walkthrough (插件开发指南)

以创建一个 `MyPlugin` 为例。

#### 1. 定义类型 (typing.ts)

定义插件所需的参数、状态、数据和事件类型。

    import { BasePlugin } from '../BasePlugin'

    // 1. 定义参数
    export interface Params extends BasePlugin.Config {
      initialState?: Partial<State>
      config?: Config
    }

    // 2. 定义配置
    export interface Config {
      color?: string
    }

    // 3. 定义状态 (继承 BasePlugin.State)
    export interface State extends BasePlugin.State {
      activeId?: string
    }

    // 4. 定义数据类型
    export interface ServerData { /* 服务端数据结构 */ }
    export interface PluginData { /* 解析后的数据结构 */ }

    // 5. 定义事件 (继承 BasePlugin.EventMap)
    export interface EventMap extends BasePlugin.EventMap<State, PluginData> {
      click: (id: string) => void
    }

#### 2. 实现控制器 (Controller.ts)

继承 `BasePlugin.Controller` 并实现核心逻辑。

    import { Five } from '@realsee/five'
    import { BasePlugin } from '../BasePlugin'
    import * as MyPluginType from './typing'

    export class Controller extends BasePlugin.Controller<
      MyPluginType.State,
      MyPluginType.EventMap,
      MyPluginType.ServerData,
      MyPluginType.PluginData
    > {
      public state: MyPluginType.State

      constructor(five: Five, params?: MyPluginType.Params) {
        super(five)
        this.state = {
          enabled: true,
          visible: true,
          ...params?.initialState
        }
        if (this.state.enabled) {
          this._enable()
        }
      }

      public async load(serverData: MyPluginType.ServerData) {
        // 解析数据
      }

      public enable() {
        if (this.state.enabled) return
        this.state.enabled = true
        this._enable()
        this.hooks.emit('stateChange', { state: this.state, userAction: true })
      }

      public disable() {
        if (!this.state.enabled) return
        this.state.enabled = false
        this._disable()
        this.hooks.emit('stateChange', { state: this.state, userAction: true })
      }

      public dispose = () => {
        this.disable()
        this.hooks.emit('dispose')
      }

      private _enable() {
        // five.scene.add(...)
        // five.on('tap', this.onTap)
      }

      private _disable() {
        // five.scene.remove(...)
        // five.off('tap', this.onTap)
      }
    }

#### 3. 导出插件 (index.ts)

提供一个工厂函数。

    import { Five } from '@realsee/five'
    import { Controller } from './Controller'
    import * as MyPluginType from './typing'

    export const MyPlugin = (five: Five, params?: MyPluginType.Params) =>
      new Controller(five, params)

    export type { MyPluginType, Controller as MyPluginController }

## Common Pitfalls (最佳实践)

1. 状态驱动: 始终通过修改 `state` 来驱动插件行为，而不是直接操作 DOM 或 3D 对象。
2. 清理资源: 在 `disable` 和 `dispose` 中务必清理所有事件监听和副作用，防止内存泄漏。
3. 类型安全: 充分利用 TypeScript 泛型，确保事件和状态的类型安全。

    tags: [插件, 插件开发, 扩展, 自定义功能, 状态管理, plugin, baseplugin, development, guide, extension, lifecycle]