11<template >
2- <div class =" ebm-panel" >
3- <!-- ヘッダー -->
4- <div class =" ebm-header" >
5- <span class =" ebm-dot" :class =" { pulse: pulsePhase > 0 }" />
2+ <div class =" ebm-panel" :class = " { collapsed } " >
3+ <!-- ヘッダー(常時表示・クリック可能) -->
4+ <div class =" ebm-header" @click = " toggle " >
5+ <span class =" ebm-dot" :class =" { pulse: isPulsing, retained: isRetained }" />
66 EventBus Monitor
7+ <span class =" ebm-toggle" >{{ collapsed ? '▸' : '▾' }}</span >
78 </div >
89
9- <!-- フロー図: Core → EventBus → Vue -->
10- <div class =" ebm-flow" >
11- <div class =" ebm-node" :class =" { active: pulsePhase >= 1 }" >
12- <span class =" ebm-icon" >◈</span > Core
13- </div >
10+ <!-- 折りたたみ可能な本体 -->
11+ <template v-if =" ! collapsed " >
12+ <!-- フロー図: Core → EventBus → Vue -->
13+ <div class =" ebm-flow" >
14+ <div class =" ebm-node" :class =" { active: pulsePhase >= 1 && pulsePhase <= 3, retained: isRetained }" >
15+ <span class =" ebm-icon" >◈</span > Core
16+ </div >
1417
15- <div class =" ebm-connector" :class =" { active: pulsePhase >= 2 }" >
16- <span class =" ebm-arrow-line" />
17- <span class =" ebm-emit-label" :class =" { visible: pulsePhase >= 1 }" >
18- emit( <em >{{ currentEvent || '…' }}</em > )
19- </span >
20- </div >
18+ <div class =" ebm-connector" :class =" { active: pulsePhase >= 2 && pulsePhase <= 3, retained: isRetained }" >
19+ <span class =" ebm-arrow-line" />
20+ <span class =" ebm-emit-label" :class =" { visible: pulsePhase >= 1 || isRetained, retained: isRetained }" >
21+ emit( <em >{{ currentEvent || '…' }}</em > )
22+ </span >
23+ </div >
2124
22- <div class =" ebm-node accent" :class =" { active: pulsePhase >= 2 }" >
23- <span class =" ebm-icon" >⚡</span > EventBus
24- </div >
25+ <div class =" ebm-node accent" :class =" { active: pulsePhase >= 2 && pulsePhase <= 3, retained: isRetained }" >
26+ <span class =" ebm-icon" >⚡</span > EventBus
27+ </div >
2528
26- <div class =" ebm-connector" :class =" { active: pulsePhase >= 3 }" >
27- <span class =" ebm-arrow-line" />
28- <span class =" ebm-emit-label" :class =" { visible: pulsePhase >= 2 }" >
29- receive
30- </span >
31- </div >
29+ <div class =" ebm-connector" :class =" { active: pulsePhase >= 3 && pulsePhase <= 3, retained: isRetained }" >
30+ <span class =" ebm-arrow-line" />
31+ <span class =" ebm-emit-label" :class =" { visible: pulsePhase >= 2 || isRetained, retained: isRetained }" >
32+ receive
33+ </span >
34+ </div >
3235
33- <div class =" ebm-node vue" :class =" { active: pulsePhase >= 3 }" >
34- <span class =" ebm-icon" >◆</span > Vue UI
36+ <div class =" ebm-node vue" :class =" { active: pulsePhase === 3, retained: isRetained }" >
37+ <span class =" ebm-icon" >◆</span > Vue UI
38+ </div >
3539 </div >
36- </div >
3740
38- <!-- イベントログ -->
39- <div class =" ebm-log-header" >recent events</div >
40- <TransitionGroup name =" log" tag =" ul" class =" ebm-log" >
41- <li v-for =" ev in recentEvents" :key =" ev.id" class =" ebm-log-item" >
42- <span class =" ebm-tag" :style =" { borderColor: ev.color, color: ev.color }" >
43- {{ ev.name }}
44- </span >
45- <span v-if =" ev.summary" class =" ebm-summary" >{{ ev.summary }}</span >
46- </li >
47- </TransitionGroup >
41+ <!-- イベントログ -->
42+ <div class =" ebm-log-header" >recent events</div >
43+ <TransitionGroup name =" log" tag =" ul" class =" ebm-log" >
44+ <li v-for =" ev in recentEvents" :key =" ev.id" class =" ebm-log-item" >
45+ <span class =" ebm-tag" :style =" { borderColor: ev.color, color: ev.color }" >
46+ {{ ev.name }}
47+ </span >
48+ <span v-if =" ev.summary" class =" ebm-summary" >{{ ev.summary }}</span >
49+ </li >
50+ </TransitionGroup >
51+ </template >
4852 </div >
4953</template >
5054
5155<script setup>
52- defineProps ({
56+ import { computed , ref } from ' vue'
57+
58+ const props = defineProps ({
5359 pulsePhase: { type: Number , default: 0 },
5460 currentEvent: { type: String , default: ' ' },
5561 recentEvents: { type: Array , default : () => [] },
5662})
63+
64+ const emit = defineEmits ([' clear' ])
65+
66+ const collapsed = ref (false )
67+ const isRetained = computed (() => props .pulsePhase === 4 )
68+ const isPulsing = computed (() => props .pulsePhase > 0 && props .pulsePhase < 4 )
69+
70+ function toggle () {
71+ collapsed .value = ! collapsed .value
72+ // 再表示時にログをクリアする
73+ if (! collapsed .value ) {
74+ emit (' clear' )
75+ }
76+ }
5777 </script >
5878
5979<style scoped>
@@ -67,14 +87,17 @@ defineProps({
6787 border : 1px solid rgba (100 , 140 , 255 , 0.2 );
6888 border-radius : 8px ;
6989 padding : 10px 12px 8px ;
70- pointer-events : none ;
7190 z-index : 90 ;
7291 font-family : ' Courier New' , monospace ;
7392 font-size : 11px ;
7493 color : rgba (200 , 210 , 255 , 0.75 );
7594 backdrop-filter : blur (4px );
7695}
7796
97+ .ebm-panel.collapsed {
98+ padding-bottom : 10px ;
99+ }
100+
78101/* ── ヘッダー ────────────────────────────────── */
79102.ebm-header {
80103 display : flex ;
@@ -85,6 +108,21 @@ defineProps({
85108 color : rgba (150 , 170 , 255 , 0.6 );
86109 text-transform : uppercase ;
87110 margin-bottom : 10px ;
111+ cursor : pointer ;
112+ user-select : none ;
113+ }
114+
115+ .ebm-panel.collapsed .ebm-header {
116+ margin-bottom : 0 ;
117+ }
118+
119+ .ebm-header :hover {
120+ color : rgba (180 , 200 , 255 , 0.9 );
121+ }
122+
123+ .ebm-toggle {
124+ margin-left : auto ;
125+ font-size : 9px ;
88126}
89127
90128.ebm-dot {
@@ -98,6 +136,10 @@ defineProps({
98136 background : #7af ;
99137 box-shadow : 0 0 6px #7af ;
100138}
139+ .ebm-dot.retained {
140+ background : rgba (122 , 170 , 255 , 0.65 );
141+ box-shadow : 0 0 3px rgba (122 , 170 , 255 , 0.35 );
142+ }
101143
102144/* ── フロー図 ────────────────────────────────── */
103145.ebm-flow {
@@ -131,18 +173,36 @@ defineProps({
131173 background : rgba (40 , 70 , 160 , 0.5 );
132174 box-shadow : 0 0 10px rgba (100 , 160 , 255 , 0.3 );
133175}
176+ .ebm-node.retained {
177+ color : rgba (200 , 230 , 255 , 0.78 );
178+ border-color : rgba (120 , 180 , 255 , 0.35 );
179+ background : rgba (34 , 52 , 110 , 0.3 );
180+ box-shadow : 0 0 4px rgba (100 , 160 , 255 , 0.14 );
181+ }
134182.ebm-node.accent.active {
135183 color : #ffe08a ;
136184 border-color : rgba (250 , 200 , 80 , 0.6 );
137185 background : rgba (80 , 60 , 20 , 0.5 );
138186 box-shadow : 0 0 10px rgba (250 , 200 , 80 , 0.3 );
139187}
188+ .ebm-node.accent.retained {
189+ color : rgba (255 , 224 , 138 , 0.78 );
190+ border-color : rgba (250 , 200 , 80 , 0.35 );
191+ background : rgba (80 , 60 , 20 , 0.28 );
192+ box-shadow : 0 0 4px rgba (250 , 200 , 80 , 0.16 );
193+ }
140194.ebm-node.vue.active {
141195 color : #6ee7b7 ;
142196 border-color : rgba (52 , 211 , 153 , 0.6 );
143197 background : rgba (20 , 70 , 50 , 0.5 );
144198 box-shadow : 0 0 10px rgba (52 , 211 , 153 , 0.3 );
145199}
200+ .ebm-node.vue.retained {
201+ color : rgba (110 , 231 , 183 , 0.82 );
202+ border-color : rgba (52 , 211 , 153 , 0.35 );
203+ background : rgba (20 , 70 , 50 , 0.28 );
204+ box-shadow : 0 0 4px rgba (52 , 211 , 153 , 0.16 );
205+ }
146206
147207.ebm-icon {
148208 font-size : 9px ;
@@ -170,6 +230,10 @@ defineProps({
170230 background : rgba (120 , 180 , 255 , 0.7 );
171231 box-shadow : 0 0 4px rgba (120 , 180 , 255 , 0.5 );
172232}
233+ .ebm-connector.retained .ebm-arrow-line {
234+ background : rgba (120 , 180 , 255 , 0.42 );
235+ box-shadow : 0 0 2px rgba (120 , 180 , 255 , 0.22 );
236+ }
173237
174238.ebm-emit-label {
175239 font-size : 9px ;
@@ -183,6 +247,9 @@ defineProps({
183247.ebm-emit-label.visible {
184248 color : rgba (150 , 170 , 255 , 0.7 );
185249}
250+ .ebm-emit-label.retained {
251+ color : rgba (150 , 170 , 255 , 0.48 );
252+ }
186253.ebm-emit-label em {
187254 font-style : normal ;
188255 color : #fde68a ;
0 commit comments