openclaw-agent-dashboard 1.0.21 → 1.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/README.md +55 -321
  2. package/frontend-dist/assets/index-B7XqKAxm.css +1 -0
  3. package/frontend-dist/assets/index-CxJaSYyo.js +24 -0
  4. package/{frontend → frontend-dist}/index.html +2 -1
  5. package/{plugin/openclaw.plugin.json → openclaw.plugin.json} +2 -2
  6. package/package.json +21 -13
  7. package/.github/workflows/release.yml +0 -56
  8. package/VERSION_DISPLAY_delivery.md +0 -242
  9. package/VERSION_DISPLAY_implementation_summary.md +0 -315
  10. package/design_manifest.md +0 -100
  11. package/docs/CHANGELOG_AGENT_MODIFICATIONS.md +0 -132
  12. package/docs/MAINTAINER_RELEASE_WORKFLOW.md +0 -211
  13. package/docs/Openclaw-Agent-Dashboard/345/217/221/345/270/203/344/270/216/346/233/264/346/226/260.md +0 -147
  14. package/docs/RELEASE-LATEST.md +0 -189
  15. package/docs/RELEASE-MODEL-CONFIG.md +0 -95
  16. package/docs/WINDOWS_INSTALL_TROUBLESHOOTING.md +0 -171
  17. package/docs/design/.gitkeep +0 -0
  18. package/docs/design/VERSION_DISPLAY_design.md +0 -1236
  19. package/docs/release-guide.md +0 -259
  20. package/docs/release-operations-manual.md +0 -167
  21. package/docs/reviews/.gitkeep +0 -0
  22. package/docs/reviews/approval_history.json +0 -14
  23. package/docs/reviews/cr_VERSION_DISPLAY.md +0 -397
  24. package/docs/reviews/traceability_manifest.json +0 -279
  25. package/docs/specs/VERSION_DISPLAY_spec.md +0 -371
  26. package/docs/specs/tr3-install-system.md +0 -580
  27. package/docs/windows-collaboration-model-paths-troubleshooting.md +0 -0
  28. package/frontend/package-lock.json +0 -1240
  29. package/frontend/package.json +0 -19
  30. package/frontend/src/App.vue +0 -355
  31. package/frontend/src/components/AgentCard.vue +0 -796
  32. package/frontend/src/components/AgentConfigPanel.vue +0 -539
  33. package/frontend/src/components/AgentDetailPanel.vue +0 -738
  34. package/frontend/src/components/ErrorAnalysisView.vue +0 -546
  35. package/frontend/src/components/ErrorCenterPanel.vue +0 -844
  36. package/frontend/src/components/PerformanceMonitor.vue +0 -515
  37. package/frontend/src/components/SettingsPanel.vue +0 -236
  38. package/frontend/src/components/TokenAnalysisPanel.vue +0 -683
  39. package/frontend/src/components/chain/ChainEdge.vue +0 -85
  40. package/frontend/src/components/chain/ChainNode.vue +0 -166
  41. package/frontend/src/components/chain/TaskChainView.vue +0 -425
  42. package/frontend/src/components/chain/index.ts +0 -3
  43. package/frontend/src/components/chain/types.ts +0 -70
  44. package/frontend/src/components/collaboration/CollaborationFlowSection.vue +0 -1032
  45. package/frontend/src/components/collaboration/CollaborationFlowWrapper.vue +0 -113
  46. package/frontend/src/components/common/VersionDisplay.vue +0 -187
  47. package/frontend/src/components/performance/PerformancePanel.vue +0 -119
  48. package/frontend/src/components/performance/PerformanceSection.vue +0 -1137
  49. package/frontend/src/components/tasks/TaskStatusSection.vue +0 -973
  50. package/frontend/src/components/timeline/TimelineConnector.vue +0 -31
  51. package/frontend/src/components/timeline/TimelineRound.vue +0 -135
  52. package/frontend/src/components/timeline/TimelineStep.vue +0 -691
  53. package/frontend/src/components/timeline/TimelineToolLink.vue +0 -109
  54. package/frontend/src/components/timeline/TimelineView.vue +0 -540
  55. package/frontend/src/components/timeline/index.ts +0 -5
  56. package/frontend/src/components/timeline/types.ts +0 -120
  57. package/frontend/src/composables/index.ts +0 -7
  58. package/frontend/src/composables/useDebounce.ts +0 -48
  59. package/frontend/src/composables/useRealtime.ts +0 -52
  60. package/frontend/src/composables/useState.ts +0 -52
  61. package/frontend/src/composables/useThrottle.ts +0 -46
  62. package/frontend/src/composables/useVirtualScroll.ts +0 -106
  63. package/frontend/src/main.ts +0 -4
  64. package/frontend/src/managers/EventDispatcher.ts +0 -127
  65. package/frontend/src/managers/RealtimeDataManager.ts +0 -302
  66. package/frontend/src/managers/StateManager.ts +0 -128
  67. package/frontend/src/managers/index.ts +0 -5
  68. package/frontend/src/types/collaboration.ts +0 -135
  69. package/frontend/src/types/index.ts +0 -20
  70. package/frontend/src/types/performance.ts +0 -105
  71. package/frontend/src/types/task.ts +0 -38
  72. package/frontend/vite.config.ts +0 -18
  73. package/legacy_code_anatomy.md +0 -518
  74. package/plugin/README.md +0 -99
  75. package/plugin/config.json.example +0 -1
  76. package/plugin/package.json +0 -26
  77. package/scripts/build-plugin.js +0 -81
  78. package/scripts/bundle.sh +0 -62
  79. package/scripts/install-plugin.sh +0 -162
  80. package/scripts/install-python-deps.sh +0 -226
  81. package/scripts/install.js +0 -684
  82. package/scripts/install.sh +0 -367
  83. package/scripts/lib/common.sh +0 -137
  84. package/scripts/release-pack.sh +0 -110
  85. package/scripts/start.js +0 -50
  86. package/scripts/test_available_models.py +0 -284
  87. package/scripts/test_version_display.sh +0 -128
  88. package/scripts/test_websocket_ping.py +0 -44
  89. package/session_registry.json +0 -58
  90. package/tests/.gitkeep +0 -0
  91. package/tests/qa_regression_report.md +0 -359
  92. package/tests/qa_version_display_report.md +0 -598
  93. /package/{src/backend → dashboard}/agents.py +0 -0
  94. /package/{src/backend → dashboard}/api/__init__.py +0 -0
  95. /package/{src/backend → dashboard}/api/agent_config_api.py +0 -0
  96. /package/{src/backend → dashboard}/api/agents.py +0 -0
  97. /package/{src/backend → dashboard}/api/agents_config.py +0 -0
  98. /package/{src/backend → dashboard}/api/chains.py +0 -0
  99. /package/{src/backend → dashboard}/api/collaboration.py +0 -0
  100. /package/{src/backend → dashboard}/api/debug_paths.py +0 -0
  101. /package/{src/backend → dashboard}/api/error_analysis.py +0 -0
  102. /package/{src/backend → dashboard}/api/errors.py +0 -0
  103. /package/{src/backend → dashboard}/api/performance.py +0 -0
  104. /package/{src/backend → dashboard}/api/subagents.py +0 -0
  105. /package/{src/backend → dashboard}/api/timeline.py +0 -0
  106. /package/{src/backend → dashboard}/api/version.py +0 -0
  107. /package/{src/backend → dashboard}/api/websocket.py +0 -0
  108. /package/{src/backend → dashboard}/collaboration.py +0 -0
  109. /package/{src/backend → dashboard}/data/__init__.py +0 -0
  110. /package/{src/backend → dashboard}/data/agent_config_manager.py +0 -0
  111. /package/{src/backend → dashboard}/data/chain_reader.py +0 -0
  112. /package/{src/backend → dashboard}/data/config_reader.py +0 -0
  113. /package/{src/backend → dashboard}/data/error_analyzer.py +0 -0
  114. /package/{src/backend → dashboard}/data/session_reader.py +0 -0
  115. /package/{src/backend → dashboard}/data/subagent_reader.py +0 -0
  116. /package/{src/backend → dashboard}/data/task_history.py +0 -0
  117. /package/{src/backend → dashboard}/data/timeline_reader.py +0 -0
  118. /package/{src/backend → dashboard}/data/version_info_reader.py +0 -0
  119. /package/{src/backend → dashboard}/errors.py +0 -0
  120. /package/{src/backend → dashboard}/main.py +0 -0
  121. /package/{src/backend → dashboard}/mechanism_reader.py +0 -0
  122. /package/{src/backend → dashboard}/mechanisms.py +0 -0
  123. /package/{src/backend → dashboard}/performance.py +0 -0
  124. /package/{src/backend → dashboard}/requirements.txt +0 -0
  125. /package/{src/backend → dashboard}/session_reader.py +0 -0
  126. /package/{src/backend → dashboard}/status/__init__.py +0 -0
  127. /package/{src/backend → dashboard}/status/change_tracker.py +0 -0
  128. /package/{src/backend → dashboard}/status/error_detector.py +0 -0
  129. /package/{src/backend → dashboard}/status/status_cache.py +0 -0
  130. /package/{src/backend → dashboard}/status/status_calculator.py +0 -0
  131. /package/{src/backend → dashboard}/status_calculator.py +0 -0
  132. /package/{src/backend → dashboard}/subagent_reader.py +0 -0
  133. /package/{src/backend → dashboard}/watchers/__init__.py +0 -0
  134. /package/{src/backend → dashboard}/watchers/file_watcher.py +0 -0
  135. /package/{plugin/index.js → index.js} +0 -0
@@ -1,120 +0,0 @@
1
- // Timeline 类型定义
2
-
3
- /** 步骤类型 */
4
- export type StepType =
5
- | 'user' // 用户消息
6
- | 'thinking' // Agent 思考
7
- | 'text' // Agent 文本响应
8
- | 'toolCall' // 工具调用
9
- | 'toolResult' // 工具结果
10
- | 'error' // 错误
11
-
12
- /** 步骤状态 */
13
- export type StepStatus = 'pending' | 'running' | 'success' | 'error'
14
-
15
- /** Token 使用 */
16
- export interface TokenUsage {
17
- input: number
18
- output: number
19
- cumulative?: number
20
- }
21
-
22
- /** 时序步骤 */
23
- export interface TimelineStep {
24
- id: string
25
- type: StepType
26
- status: StepStatus
27
- timestamp: number
28
- duration?: number
29
-
30
- // 内容
31
- content?: string
32
- thinking?: string
33
-
34
- // 工具调用
35
- toolName?: string
36
- toolCallId?: string
37
- toolArguments?: Record<string, unknown>
38
- toolResult?: string
39
- toolResultStatus?: 'ok' | 'error'
40
- toolResultError?: string // 工具失败时的错误信息
41
-
42
- // 工具链路关联
43
- pairedToolCallId?: string // toolResult 专用:对应的 toolCall ID
44
- pairedToolResultId?: string // toolCall 专用:对应的 toolResult ID
45
- executionTime?: number // 工具执行耗时(ms),toolResult 专用
46
-
47
- // 错误
48
- errorMessage?: string
49
- errorType?: string
50
-
51
- // 统计
52
- tokens?: TokenUsage
53
-
54
- // 展示控制
55
- collapsed?: boolean
56
-
57
- // 消息来源(用于区分真实用户和其他 Agent)
58
- senderId?: string // 发送者 Agent ID(如 'main')
59
- senderName?: string // 发送者显示名(如 '老K')
60
- }
61
-
62
- /** 时序统计 */
63
- export interface TimelineStats {
64
- totalDuration: number
65
- totalInputTokens: number
66
- totalOutputTokens: number
67
- toolCallCount: number
68
- stepCount: number
69
- }
70
-
71
- /** LLM 轮次 */
72
- export interface LLMRound {
73
- id: string // round_1, round_2, ...
74
- index: number // 轮次序号(从1开始)
75
- trigger: 'user_input' | 'tool_result' | 'subagent_result' | 'start'
76
- triggerBy?: string // 触发来源描述
77
- stepIds: string[] // 该轮次包含的步骤 ID 列表
78
- duration: number // 该轮次耗时(ms)
79
- tokens?: TokenUsage // 该轮次的 token 使用
80
- }
81
-
82
- /** 时序会话响应 */
83
- export interface TimelineResponse {
84
- sessionId: string | null
85
- agentId: string
86
- agentName?: string
87
- model?: string
88
- startedAt: number | null
89
- status: 'running' | 'completed' | 'error' | 'empty' | 'no_sessions'
90
- steps: TimelineStep[]
91
- stats: TimelineStats
92
- message?: string
93
- // LLM 轮次分组
94
- rounds?: LLMRound[]
95
- roundMode?: boolean
96
- }
97
-
98
- /** 步骤图标和颜色配置 */
99
- export const stepConfig: Record<StepType, { icon: string; bgColor: string; borderColor: string; label: string }> = {
100
- user: { icon: '👤', bgColor: '#f0f9ff', borderColor: '#3b82f6', label: '用户' },
101
- thinking: { icon: '🧠', bgColor: '#fef3c7', borderColor: '#f59e0b', label: '思考' },
102
- text: { icon: '🤖', bgColor: '#f0fdf4', borderColor: '#22c55e', label: '回复' },
103
- toolCall: { icon: '🔧', bgColor: '#f5f3ff', borderColor: '#8b5cf6', label: '调用' },
104
- toolResult: { icon: '✅', bgColor: '#ecfdf5', borderColor: '#10b981', label: '结果' },
105
- error: { icon: '⚠️', bgColor: '#fef2f2', borderColor: '#dc2626', label: '错误' }
106
- }
107
-
108
- /**
109
- * 获取用户步骤的显示标签
110
- * 如果有 senderName,显示发送者名称;否则显示"用户"
111
- */
112
- export function getUserStepLabel(step: TimelineStep): string {
113
- if (step.senderName) {
114
- return step.senderName
115
- }
116
- if (step.senderId) {
117
- return step.senderId
118
- }
119
- return '用户'
120
- }
@@ -1,7 +0,0 @@
1
- // 组合函数入口文件
2
-
3
- export { useDebounce } from './useDebounce'
4
- export { useThrottle } from './useThrottle'
5
- export { useRealtime } from './useRealtime'
6
- export { useState, useCache } from './useState'
7
- export { useVirtualScroll } from './useVirtualScroll'
@@ -1,48 +0,0 @@
1
- /**
2
- * 防抖组合函数
3
- */
4
-
5
- import { ref, onUnmounted } from 'vue'
6
-
7
- export function useDebounce<T extends (...args: unknown[]) => unknown>(
8
- fn: T,
9
- delay: number = 300
10
- ): { debouncedFn: T; cancel: () => void; flush: () => void } {
11
- const timer = ref<ReturnType<typeof setTimeout> | null>(null)
12
- let lastArgs: unknown[] | null = null
13
-
14
- const debouncedFn = ((...args: unknown[]) => {
15
- lastArgs = args
16
- if (timer.value) {
17
- clearTimeout(timer.value)
18
- }
19
- timer.value = setTimeout(() => {
20
- fn(...args)
21
- timer.value = null
22
- lastArgs = null
23
- }, delay)
24
- }) as T
25
-
26
- const cancel = () => {
27
- if (timer.value) {
28
- clearTimeout(timer.value)
29
- timer.value = null
30
- lastArgs = null
31
- }
32
- }
33
-
34
- const flush = () => {
35
- if (timer.value && lastArgs) {
36
- clearTimeout(timer.value)
37
- fn(...lastArgs)
38
- timer.value = null
39
- lastArgs = null
40
- }
41
- }
42
-
43
- onUnmounted(() => {
44
- cancel()
45
- })
46
-
47
- return { debouncedFn, cancel, flush }
48
- }
@@ -1,52 +0,0 @@
1
- /**
2
- * 实时数据组合函数
3
- */
4
-
5
- import { ref, onMounted, onUnmounted } from 'vue'
6
- import { getRealtimeManager } from '../managers'
7
- import type { ConnectionState } from '../types'
8
-
9
- export function useRealtime() {
10
- const manager = getRealtimeManager()
11
- const connectionState = ref<ConnectionState>(manager.getConnectionState())
12
- const isConnected = ref(false)
13
-
14
- let unsubscribeState: (() => void) | null = null
15
-
16
- const subscribe = <T>(event: string, callback: (data: T) => void) => {
17
- return manager.subscribe(event, callback as (data: unknown) => void)
18
- }
19
-
20
- const connect = () => {
21
- manager.connect()
22
- }
23
-
24
- const disconnect = () => {
25
- manager.disconnect()
26
- }
27
-
28
- onMounted(() => {
29
- unsubscribeState = manager.onStateChange((state) => {
30
- connectionState.value = state
31
- isConnected.value = state.status === 'connected'
32
- })
33
-
34
- if (!manager.isConnected()) {
35
- manager.connect()
36
- }
37
- })
38
-
39
- onUnmounted(() => {
40
- if (unsubscribeState) {
41
- unsubscribeState()
42
- }
43
- })
44
-
45
- return {
46
- connectionState,
47
- isConnected,
48
- subscribe,
49
- connect,
50
- disconnect
51
- }
52
- }
@@ -1,52 +0,0 @@
1
- /**
2
- * 状态管理组合函数
3
- */
4
-
5
- import { computed, type Ref } from 'vue'
6
- import { getStateManager } from '../managers'
7
-
8
- export function useState<T>(key: string, defaultValue: T): Ref<T> {
9
- const manager = getStateManager()
10
- return manager.useStore(key, defaultValue)
11
- }
12
-
13
- export function useCache<T>(key: string, fetcher: () => Promise<T>, ttl?: number) {
14
- const manager = getStateManager()
15
- const data = useState<T | null>(`cache:${key}`, null)
16
- const loading = useState(`cache:${key}:loading`, false)
17
- const error = useState<Error | null>(`cache:${key}:error`, null)
18
-
19
- const fetch = async () => {
20
- // 检查缓存
21
- if (manager.hasValidCache(key)) {
22
- data.value = manager.getCache<T>(key) ?? null
23
- return
24
- }
25
-
26
- loading.value = true
27
- error.value = null
28
-
29
- try {
30
- const result = await fetcher()
31
- data.value = result
32
- manager.setCache(key, result, ttl)
33
- } catch (e) {
34
- error.value = e as Error
35
- } finally {
36
- loading.value = false
37
- }
38
- }
39
-
40
- const invalidate = () => {
41
- manager.invalidateCache(key)
42
- data.value = null
43
- }
44
-
45
- return {
46
- data: computed(() => data.value),
47
- loading: computed(() => loading.value),
48
- error: computed(() => error.value),
49
- fetch,
50
- invalidate
51
- }
52
- }
@@ -1,46 +0,0 @@
1
- /**
2
- * 节流组合函数
3
- */
4
-
5
- import { ref, onUnmounted } from 'vue'
6
-
7
- export function useThrottle<T extends (...args: unknown[]) => unknown>(
8
- fn: T,
9
- interval: number = 100
10
- ): { throttledFn: T; cancel: () => void } {
11
- const lastExec = ref(0)
12
- const timer = ref<ReturnType<typeof setTimeout> | null>(null)
13
-
14
- const throttledFn = ((...args: unknown[]) => {
15
- const now = Date.now()
16
- const timeSinceLastExec = now - lastExec.value
17
-
18
- if (timeSinceLastExec >= interval) {
19
- lastExec.value = now
20
- fn(...args)
21
- } else {
22
- // 确保最后一次调用会被执行
23
- if (timer.value) {
24
- clearTimeout(timer.value)
25
- }
26
- timer.value = setTimeout(() => {
27
- lastExec.value = Date.now()
28
- fn(...args)
29
- timer.value = null
30
- }, interval - timeSinceLastExec)
31
- }
32
- }) as T
33
-
34
- const cancel = () => {
35
- if (timer.value) {
36
- clearTimeout(timer.value)
37
- timer.value = null
38
- }
39
- }
40
-
41
- onUnmounted(() => {
42
- cancel()
43
- })
44
-
45
- return { throttledFn, cancel }
46
- }
@@ -1,106 +0,0 @@
1
- /**
2
- * 虚拟滚动组合函数
3
- */
4
-
5
- import { ref, computed, onMounted, onUnmounted, type Ref } from 'vue'
6
-
7
- interface VirtualScrollOptions {
8
- itemHeight: number
9
- bufferSize?: number
10
- }
11
-
12
- export function useVirtualScroll(
13
- containerRef: Ref<HTMLElement | null>,
14
- totalCount: Ref<number>,
15
- options: VirtualScrollOptions
16
- ) {
17
- const { itemHeight, bufferSize = 5 } = options
18
-
19
- const scrollTop = ref(0)
20
- const containerHeight = ref(0)
21
-
22
- const visibleCount = computed(() => {
23
- return Math.ceil(containerHeight.value / itemHeight) + bufferSize * 2
24
- })
25
-
26
- const startIndex = computed(() => {
27
- const rawStart = Math.floor(scrollTop.value / itemHeight) - bufferSize
28
- return Math.max(0, rawStart)
29
- })
30
-
31
- const endIndex = computed(() => {
32
- return Math.min(totalCount.value, startIndex.value + visibleCount.value)
33
- })
34
-
35
- const offsetY = computed(() => {
36
- return startIndex.value * itemHeight
37
- })
38
-
39
- const totalHeight = computed(() => {
40
- return totalCount.value * itemHeight
41
- })
42
-
43
- const visibleItems = computed(() => {
44
- const items: { index: number; style: { transform: string } }[] = []
45
- for (let i = startIndex.value; i < endIndex.value; i++) {
46
- items.push({
47
- index: i,
48
- style: {
49
- transform: `translateY(${i * itemHeight}px)`
50
- }
51
- })
52
- }
53
- return items
54
- })
55
-
56
- const scrollToIndex = (index: number) => {
57
- if (containerRef.value) {
58
- containerRef.value.scrollTop = index * itemHeight
59
- }
60
- }
61
-
62
- const handleScroll = () => {
63
- if (containerRef.value) {
64
- scrollTop.value = containerRef.value.scrollTop
65
- }
66
- }
67
-
68
- const updateContainerHeight = () => {
69
- if (containerRef.value) {
70
- containerHeight.value = containerRef.value.clientHeight
71
- }
72
- }
73
-
74
- let resizeObserver: ResizeObserver | null = null
75
-
76
- onMounted(() => {
77
- if (containerRef.value) {
78
- containerRef.value.addEventListener('scroll', handleScroll, { passive: true })
79
-
80
- resizeObserver = new ResizeObserver(() => {
81
- updateContainerHeight()
82
- })
83
- resizeObserver.observe(containerRef.value)
84
- updateContainerHeight()
85
- }
86
- })
87
-
88
- onUnmounted(() => {
89
- if (containerRef.value) {
90
- containerRef.value.removeEventListener('scroll', handleScroll)
91
- }
92
- if (resizeObserver) {
93
- resizeObserver.disconnect()
94
- }
95
- })
96
-
97
- return {
98
- startIndex,
99
- endIndex,
100
- offsetY,
101
- totalHeight,
102
- visibleItems,
103
- scrollToIndex,
104
- updateContainerHeight
105
- }
106
- }
@@ -1,4 +0,0 @@
1
- import { createApp } from 'vue'
2
- import App from './App.vue'
3
-
4
- createApp(App).mount('#app')
@@ -1,127 +0,0 @@
1
- /**
2
- * 事件分发器
3
- * 负责组件间事件通信
4
- */
5
-
6
- type EventHandler = (payload?: unknown) => void
7
-
8
- interface QueuedEvent {
9
- event: string
10
- payload?: unknown
11
- }
12
-
13
- export class EventDispatcher {
14
- private listeners: Map<string, Set<EventHandler>> = new Map()
15
- private eventQueue: QueuedEvent[] = []
16
- private isFlushing = false
17
- private maxQueueSize = 100
18
-
19
- /**
20
- * 分发事件
21
- */
22
- emit(event: string, payload?: unknown): void {
23
- const handlers = this.listeners.get(event)
24
- if (handlers) {
25
- handlers.forEach(handler => {
26
- try {
27
- handler(payload)
28
- } catch (e) {
29
- console.error(`Error in event handler for ${event}:`, e)
30
- }
31
- })
32
- }
33
- }
34
-
35
- /**
36
- * 监听事件
37
- * 返回取消监听函数
38
- */
39
- on(event: string, handler: EventHandler): () => void {
40
- if (!this.listeners.has(event)) {
41
- this.listeners.set(event, new Set())
42
- }
43
- this.listeners.get(event)!.add(handler)
44
-
45
- return () => {
46
- this.listeners.get(event)?.delete(handler)
47
- }
48
- }
49
-
50
- /**
51
- * 监听一次事件
52
- */
53
- once(event: string, handler: EventHandler): void {
54
- const wrapper: EventHandler = (payload) => {
55
- this.off(event, wrapper)
56
- handler(payload)
57
- }
58
- this.on(event, wrapper)
59
- }
60
-
61
- /**
62
- * 取消监听
63
- */
64
- off(event: string, handler: EventHandler): void {
65
- this.listeners.get(event)?.delete(handler)
66
- }
67
-
68
- /**
69
- * 将事件加入队列
70
- */
71
- enqueue(event: string, payload?: unknown): void {
72
- if (this.eventQueue.length >= this.maxQueueSize) {
73
- console.warn('Event queue is full, dropping oldest event')
74
- this.eventQueue.shift()
75
- }
76
- this.eventQueue.push({ event, payload })
77
- }
78
-
79
- /**
80
- * 刷新队列中的所有事件
81
- */
82
- flush(): void {
83
- if (this.isFlushing) return
84
-
85
- this.isFlushing = true
86
- try {
87
- while (this.eventQueue.length > 0) {
88
- const { event, payload } = this.eventQueue.shift()!
89
- this.emit(event, payload)
90
- }
91
- } finally {
92
- this.isFlushing = false
93
- }
94
- }
95
-
96
- /**
97
- * 获取队列长度
98
- */
99
- getQueueLength(): number {
100
- return this.eventQueue.length
101
- }
102
-
103
- /**
104
- * 清空队列
105
- */
106
- clearQueue(): void {
107
- this.eventQueue = []
108
- }
109
-
110
- /**
111
- * 清除所有监听器
112
- */
113
- clearAll(): void {
114
- this.listeners.clear()
115
- this.eventQueue = []
116
- }
117
- }
118
-
119
- // 单例实例
120
- let instance: EventDispatcher | null = null
121
-
122
- export function getEventDispatcher(): EventDispatcher {
123
- if (!instance) {
124
- instance = new EventDispatcher()
125
- }
126
- return instance
127
- }