qdadm 1.13.0 → 1.19.2
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.
- package/package.json +8 -4
- package/src/chain/ActiveStack.ts +79 -98
- package/src/chain/StackHydrator.ts +3 -2
- package/src/chain/index.ts +7 -1
- package/src/components/QdadmRoot.vue +52 -0
- package/src/components/edit/FormActions.vue +9 -6
- package/src/components/edit/LookupPickerDialog.vue +6 -3
- package/src/components/index.ts +6 -0
- package/src/composables/useEntityItemFormPage.ts +1 -0
- package/src/composables/useEntityItemShowPage.ts +1 -0
- package/src/composables/useFieldManager.ts +100 -3
- package/src/composables/useListPage.ts +50 -59
- package/src/composables/useListPage.utils.ts +101 -0
- package/src/composables/useNavigation.ts +26 -3
- package/src/composables/useOptionsLookup.ts +5 -1
- package/src/gen/generateManagers.test.js +27 -0
- package/src/gen/generateManagers.ts +12 -0
- package/src/hooks/HookRegistry.ts +14 -435
- package/src/i18n/I18n.ts +344 -0
- package/src/i18n/IncrementalDomainProvider.ts +153 -0
- package/src/i18n/InlineTranslationProvider.ts +4 -0
- package/src/i18n/LazyTranslationProvider.ts +102 -0
- package/src/i18n/MessagesRegistry.ts +4 -0
- package/src/i18n/Resolver.ts +4 -0
- package/src/i18n/__tests__/I18n.test.ts +169 -0
- package/src/i18n/__tests__/IncrementalDomainProvider.test.ts +146 -0
- package/src/i18n/__tests__/LazyTranslationProvider.test.ts +100 -0
- package/src/i18n/__tests__/Resolver.test.ts +271 -0
- package/src/i18n/defaults/DefaultCoreProvider.ts +28 -0
- package/src/i18n/defaults/core.en.yml +55 -0
- package/src/i18n/defaults/core.fr.yml +55 -0
- package/src/i18n/index.ts +55 -0
- package/src/i18n/loaders/raw-modules.d.ts +15 -0
- package/src/i18n/loaders/yaml.ts +35 -0
- package/src/i18n/strategies.ts +4 -0
- package/src/i18n/types.ts +34 -0
- package/src/i18n/useI18n.ts +34 -0
- package/src/index.ts +37 -0
- package/src/kernel/EventRouter.ts +17 -300
- package/src/kernel/Kernel.i18n.ts +29 -0
- package/src/kernel/Kernel.modules.ts +6 -0
- package/src/kernel/Kernel.registries.ts +10 -2
- package/src/kernel/Kernel.routing.ts +43 -1
- package/src/kernel/Kernel.ts +43 -0
- package/src/kernel/Kernel.types.ts +52 -1
- package/src/kernel/Kernel.vue.ts +121 -15
- package/src/kernel/KernelContext.entities.ts +80 -0
- package/src/kernel/KernelContext.events.ts +57 -0
- package/src/kernel/KernelContext.i18n.ts +37 -0
- package/src/kernel/KernelContext.permissions.ts +38 -0
- package/src/kernel/KernelContext.routing.ts +280 -0
- package/src/kernel/KernelContext.ts +125 -834
- package/src/kernel/KernelContext.types.ts +173 -0
- package/src/kernel/KernelContext.zones.ts +54 -0
- package/src/kernel/SSEBridge.ts +7 -362
- package/src/kernel/SignalBus.ts +24 -148
- package/src/modules/debug/AuthCollector.ts +48 -1
- package/src/modules/debug/Collector.ts +16 -302
- package/src/modules/debug/DebugBridge.ts +10 -171
- package/src/modules/debug/DebugModule.ts +35 -5
- package/src/modules/debug/EntitiesCollector.ts +97 -1
- package/src/modules/debug/ErrorCollector.ts +2 -77
- package/src/modules/debug/I18nCollector.ts +9 -0
- package/src/modules/debug/LocalStorageAdapter.ts +3 -147
- package/src/modules/debug/RouterCollector.ts +101 -1
- package/src/modules/debug/SignalCollector.ts +2 -150
- package/src/modules/debug/ToastCollector.ts +2 -91
- package/src/modules/debug/ZonesCollector.ts +93 -1
- package/src/modules/debug/components/DebugBar.vue +19 -775
- package/src/modules/debug/components/adminPanelsConfig.ts +42 -0
- package/src/modules/debug/components/index.ts +4 -3
- package/src/modules/debug/components/panels/AuthPanel.vue +1 -1
- package/src/modules/debug/components/panels/EntitiesPanel.vue +5 -5
- package/src/modules/debug/components/panels/I18nPanel.vue +738 -0
- package/src/modules/debug/components/panels/RouterPanel.vue +1 -1
- package/src/modules/debug/components/panels/index.ts +10 -4
- package/src/modules/debug/index.ts +15 -0
- package/src/modules/debug/styles.scss +22 -18
- package/src/utils/index.ts +0 -3
- package/src/vite/qdadmDebugPlugin.ts +401 -0
- package/src/vite-env.d.ts +16 -0
- package/src/modules/debug/components/ObjectTree.vue +0 -123
- package/src/modules/debug/components/panels/EntriesPanel.vue +0 -100
- package/src/modules/debug/components/panels/SignalsPanel.vue +0 -188
- package/src/modules/debug/components/panels/ToastsPanel.vue +0 -45
- package/src/utils/debugInjector.ts +0 -306
|
@@ -1,787 +1,31 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
/**
|
|
3
|
-
* DebugBar
|
|
3
|
+
* qdadm DebugBar wrapper.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* -
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* - fullscreen: covers entire viewport
|
|
10
|
-
* - minimized: small corner button
|
|
5
|
+
* Wraps the panel-pluggable `@quazardous/qddebug` `DebugBar` with qdadm's
|
|
6
|
+
* admin-specific panels (auth, entities, router, zones, i18n) and the
|
|
7
|
+
* matching tab metadata (icons / labels / colors). Existing call sites that
|
|
8
|
+
* imported this file continue to work unchanged.
|
|
11
9
|
*/
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
14
|
-
import Button from 'primevue/button'
|
|
15
|
-
import { ZonesPanel, AuthPanel, EntitiesPanel, ToastsPanel, EntriesPanel, SignalsPanel, RouterPanel } from './panels'
|
|
10
|
+
import { DebugBar as QddebugBar } from '@quazardous/qddebug'
|
|
11
|
+
import { adminPanels, adminPanelsMeta } from './adminPanelsConfig'
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
x: number
|
|
21
|
-
y: number
|
|
22
|
-
width: number
|
|
23
|
-
height: number
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface PanelSizes {
|
|
27
|
-
bottomHeight: number
|
|
28
|
-
rightWidth: number
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface SavedState {
|
|
32
|
-
minimized: boolean
|
|
33
|
-
expanded: boolean
|
|
34
|
-
mode: DisplayMode
|
|
35
|
-
fullscreen: boolean
|
|
36
|
-
windowBounds: WindowBounds
|
|
37
|
-
panelSizes: PanelSizes
|
|
38
|
-
countAllBadges?: boolean
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
interface Collector {
|
|
42
|
-
getBadge: (countAll?: boolean) => number
|
|
43
|
-
getEntries: () => unknown[]
|
|
44
|
-
records?: boolean
|
|
45
|
-
enabled?: boolean
|
|
46
|
-
clear: () => void
|
|
47
|
-
toggle: () => void
|
|
48
|
-
markAsSeen?: () => void
|
|
49
|
-
[key: string]: unknown
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
interface CollectorEntry {
|
|
53
|
-
name: string
|
|
54
|
-
collector: Collector
|
|
55
|
-
badge: number
|
|
56
|
-
entries: unknown[]
|
|
57
|
-
records?: boolean
|
|
58
|
-
enabled?: boolean
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
interface DebugBridge {
|
|
62
|
-
enabled?: Ref<boolean>
|
|
63
|
-
tick?: Ref<number>
|
|
64
|
-
collectors?: Map<string, Collector>
|
|
65
|
-
toggle: () => void
|
|
66
|
-
clearAll: () => void
|
|
67
|
-
notify: () => void
|
|
68
|
-
[key: string]: unknown
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const props = withDefaults(defineProps<{
|
|
72
|
-
bridge: DebugBridge
|
|
13
|
+
defineProps<{
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
bridge: any
|
|
73
16
|
zIndex?: number
|
|
74
|
-
}>()
|
|
75
|
-
zIndex: 9999
|
|
76
|
-
})
|
|
17
|
+
}>()
|
|
77
18
|
|
|
78
|
-
//
|
|
19
|
+
// Preserve the legacy localStorage key so existing users keep their saved layout.
|
|
79
20
|
const STORAGE_KEY = 'qdadm-debug-bar'
|
|
80
|
-
|
|
81
|
-
const defaultWindowBounds: WindowBounds = { x: 100, y: 100, width: 500, height: 400 }
|
|
82
|
-
const defaultPanelSizes: PanelSizes = { bottomHeight: 180, rightWidth: 420 }
|
|
83
|
-
|
|
84
|
-
function loadState(): SavedState {
|
|
85
|
-
try {
|
|
86
|
-
const saved = localStorage.getItem(STORAGE_KEY)
|
|
87
|
-
if (saved) {
|
|
88
|
-
const state = JSON.parse(saved) as SavedState
|
|
89
|
-
// Validate windowBounds (min 280x200)
|
|
90
|
-
if (state.windowBounds) {
|
|
91
|
-
const b = state.windowBounds
|
|
92
|
-
if (typeof b.x !== 'number' || typeof b.y !== 'number' ||
|
|
93
|
-
typeof b.width !== 'number' || typeof b.height !== 'number' ||
|
|
94
|
-
b.width < 280 || b.height < 200) {
|
|
95
|
-
state.windowBounds = { ...defaultWindowBounds }
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
// Validate panelSizes
|
|
99
|
-
if (state.panelSizes) {
|
|
100
|
-
const p = state.panelSizes
|
|
101
|
-
if (typeof p.bottomHeight !== 'number' || p.bottomHeight < 100) {
|
|
102
|
-
p.bottomHeight = defaultPanelSizes.bottomHeight
|
|
103
|
-
}
|
|
104
|
-
if (typeof p.rightWidth !== 'number' || p.rightWidth < 200) {
|
|
105
|
-
p.rightWidth = defaultPanelSizes.rightWidth
|
|
106
|
-
}
|
|
107
|
-
} else {
|
|
108
|
-
state.panelSizes = { ...defaultPanelSizes }
|
|
109
|
-
}
|
|
110
|
-
return state
|
|
111
|
-
}
|
|
112
|
-
} catch { /* ignore */ }
|
|
113
|
-
return { minimized: true, expanded: false, mode: 'bottom', fullscreen: false, windowBounds: { ...defaultWindowBounds }, panelSizes: { ...defaultPanelSizes } }
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function saveState(): void {
|
|
117
|
-
try {
|
|
118
|
-
localStorage.setItem(STORAGE_KEY, JSON.stringify({
|
|
119
|
-
minimized: minimized.value,
|
|
120
|
-
expanded: expanded.value,
|
|
121
|
-
mode: displayMode.value,
|
|
122
|
-
fullscreen: fullscreen.value,
|
|
123
|
-
windowBounds: windowBounds.value,
|
|
124
|
-
panelSizes: panelSizes.value,
|
|
125
|
-
countAllBadges: countAllBadges.value
|
|
126
|
-
}))
|
|
127
|
-
} catch { /* ignore */ }
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const savedState = loadState()
|
|
131
|
-
|
|
132
|
-
// State
|
|
133
|
-
const minimized = ref<boolean>(savedState.minimized)
|
|
134
|
-
const expanded = ref<boolean>(savedState.expanded)
|
|
135
|
-
const displayMode = ref<DisplayMode>(savedState.mode) // 'bottom', 'right', or 'window'
|
|
136
|
-
const fullscreen = ref<boolean>(savedState.fullscreen || false)
|
|
137
|
-
const activeCollector = ref<number>(0)
|
|
138
|
-
const countAllBadges = ref<boolean>(savedState.countAllBadges ?? false) // false = show unseen only
|
|
139
|
-
|
|
140
|
-
// Reactive tick from bridge - collectors notify when they have new data
|
|
141
|
-
const bridgeTick = computed<number>(() => props.bridge?.tick?.value ?? 0)
|
|
142
|
-
|
|
143
|
-
// Window mode state
|
|
144
|
-
const windowBounds = ref<WindowBounds>(savedState.windowBounds || defaultWindowBounds)
|
|
145
|
-
const isDragging = ref<boolean>(false)
|
|
146
|
-
const isResizing = ref<boolean>(false)
|
|
147
|
-
const dragOffset = ref<{ x: number; y: number }>({ x: 0, y: 0 })
|
|
148
|
-
const resizeStart = ref<{ x: number; y: number; width: number; height: number }>({ x: 0, y: 0, width: 0, height: 0 })
|
|
149
|
-
|
|
150
|
-
// Docked panel sizes (resizable)
|
|
151
|
-
const panelSizes = ref<PanelSizes>(savedState.panelSizes || defaultPanelSizes)
|
|
152
|
-
const isPanelResizing = ref<boolean>(false)
|
|
153
|
-
const panelResizeStart = ref<{ y: number; x: number; height: number; width: number }>({ y: 0, x: 0, height: 0, width: 0 })
|
|
154
|
-
|
|
155
|
-
// Mobile detection
|
|
156
|
-
const MOBILE_BREAKPOINT = 768
|
|
157
|
-
const isMobile = ref<boolean>(window.innerWidth < MOBILE_BREAKPOINT)
|
|
158
|
-
|
|
159
|
-
function updateMobileState(): void {
|
|
160
|
-
isMobile.value = window.innerWidth < MOBILE_BREAKPOINT
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
// Responsive tabs state (applies to bottom and window modes with horizontal header, not mobile)
|
|
165
|
-
const headerRef = ref<HTMLElement | null>(null)
|
|
166
|
-
const headerWidth = ref<number>(1000)
|
|
167
|
-
const isHorizontalHeader = computed<boolean>(() => !isMobile.value && (displayMode.value === 'bottom' || displayMode.value === 'window' || fullscreen.value) && displayMode.value !== 'right')
|
|
168
|
-
const tabsCompact = computed<boolean>(() => isHorizontalHeader.value && headerWidth.value < 600)
|
|
169
|
-
const tabsDropdown = computed<boolean>(() => isHorizontalHeader.value && headerWidth.value < 400)
|
|
170
|
-
const showTabsDropdown = ref<boolean>(false)
|
|
171
|
-
|
|
172
|
-
// Close dropdown when exiting dropdown mode
|
|
173
|
-
watch(tabsDropdown, (isDropdown: boolean) => {
|
|
174
|
-
if (!isDropdown) showTabsDropdown.value = false
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
let resizeObserver: ResizeObserver | null = null
|
|
178
|
-
onMounted(() => {
|
|
179
|
-
resizeObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => {
|
|
180
|
-
for (const entry of entries) {
|
|
181
|
-
headerWidth.value = entry.contentRect.width
|
|
182
|
-
}
|
|
183
|
-
})
|
|
184
|
-
// Close dropdown on outside click
|
|
185
|
-
document.addEventListener('click', onDocumentClick)
|
|
186
|
-
// Mobile detection on resize
|
|
187
|
-
window.addEventListener('resize', updateMobileState)
|
|
188
|
-
})
|
|
189
|
-
onUnmounted(() => {
|
|
190
|
-
resizeObserver?.disconnect()
|
|
191
|
-
document.removeEventListener('click', onDocumentClick)
|
|
192
|
-
window.removeEventListener('resize', updateMobileState)
|
|
193
|
-
})
|
|
194
|
-
watch(headerRef, (el: HTMLElement | null) => {
|
|
195
|
-
resizeObserver?.disconnect()
|
|
196
|
-
if (el) resizeObserver?.observe(el)
|
|
197
|
-
}, { immediate: true })
|
|
198
|
-
|
|
199
|
-
function onDocumentClick(e: MouseEvent): void {
|
|
200
|
-
if (showTabsDropdown.value && !(e.target as Element).closest('.debug-tabs-dropdown')) {
|
|
201
|
-
showTabsDropdown.value = false
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Computed
|
|
206
|
-
const isEnabled = computed<boolean>(() => props.bridge?.enabled?.value ?? false)
|
|
207
|
-
|
|
208
|
-
const collectors = computed<CollectorEntry[]>(() => {
|
|
209
|
-
void bridgeTick.value // Reactive dependency on bridge tick
|
|
210
|
-
const result: CollectorEntry[] = []
|
|
211
|
-
if (props.bridge?.collectors) {
|
|
212
|
-
for (const [name, collector] of props.bridge.collectors) {
|
|
213
|
-
result.push({
|
|
214
|
-
name,
|
|
215
|
-
collector,
|
|
216
|
-
badge: collector.getBadge(countAllBadges.value),
|
|
217
|
-
entries: collector.getEntries(),
|
|
218
|
-
records: collector.records, // Does this collector record events?
|
|
219
|
-
enabled: collector.enabled // Per-collector enabled state
|
|
220
|
-
})
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
return result
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
const currentCollector = computed<CollectorEntry | undefined>(() => collectors.value[activeCollector.value])
|
|
227
|
-
|
|
228
|
-
// Separate badges for minimized state (always show unseen)
|
|
229
|
-
const errorBadge = computed<number>(() => {
|
|
230
|
-
void bridgeTick.value // Reactive dependency
|
|
231
|
-
const c = collectors.value.find((c: CollectorEntry) => c.name === 'ErrorCollector' || c.name === 'errors')
|
|
232
|
-
return c?.badge || 0
|
|
233
|
-
})
|
|
234
|
-
const signalBadge = computed<number>(() => {
|
|
235
|
-
void bridgeTick.value // Reactive dependency
|
|
236
|
-
const c = collectors.value.find((c: CollectorEntry) => c.name === 'SignalCollector' || c.name === 'signals')
|
|
237
|
-
return c?.badge || 0
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
// Mark previous collector as seen when leaving tab
|
|
241
|
-
watch(activeCollector, (newIdx: number, oldIdx: number | undefined) => {
|
|
242
|
-
if (oldIdx !== undefined && collectors.value[oldIdx]?.collector?.markAsSeen) {
|
|
243
|
-
collectors.value[oldIdx].collector.markAsSeen!()
|
|
244
|
-
props.bridge?.notify()
|
|
245
|
-
}
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
// Mark current collector as seen when collapsing panel
|
|
249
|
-
watch(expanded, (isExpanded: boolean, wasExpanded: boolean) => {
|
|
250
|
-
if (wasExpanded && !isExpanded) {
|
|
251
|
-
const idx = activeCollector.value
|
|
252
|
-
if (collectors.value[idx]?.collector?.markAsSeen) {
|
|
253
|
-
collectors.value[idx].collector.markAsSeen!()
|
|
254
|
-
props.bridge?.notify()
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
function toggleCountMode(): void {
|
|
260
|
-
countAllBadges.value = !countAllBadges.value
|
|
261
|
-
saveState()
|
|
262
|
-
props.bridge?.notify()
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Actions
|
|
266
|
-
function toggle(): void {
|
|
267
|
-
expanded.value = !expanded.value
|
|
268
|
-
saveState()
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function expand(): void {
|
|
272
|
-
minimized.value = false
|
|
273
|
-
// On mobile, go directly to fullscreen
|
|
274
|
-
if (isMobile.value) {
|
|
275
|
-
fullscreen.value = true
|
|
276
|
-
} else {
|
|
277
|
-
displayMode.value = 'right'
|
|
278
|
-
}
|
|
279
|
-
expanded.value = true
|
|
280
|
-
saveState()
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function minimize(): void {
|
|
284
|
-
minimized.value = true
|
|
285
|
-
// Keep expanded state - will be restored when un-minimizing
|
|
286
|
-
fullscreen.value = false
|
|
287
|
-
saveState()
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function toggleMode(): void {
|
|
291
|
-
// Toggle between bottom and right (not window)
|
|
292
|
-
if (displayMode.value === 'window') {
|
|
293
|
-
displayMode.value = 'bottom'
|
|
294
|
-
} else {
|
|
295
|
-
displayMode.value = displayMode.value === 'bottom' ? 'right' : 'bottom'
|
|
296
|
-
}
|
|
297
|
-
saveState()
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function enterWindowMode(): void {
|
|
301
|
-
displayMode.value = 'window'
|
|
302
|
-
expanded.value = true
|
|
303
|
-
// Ensure window is visible within viewport with min size
|
|
304
|
-
const vw = window.innerWidth
|
|
305
|
-
const vh = window.innerHeight
|
|
306
|
-
const bounds = windowBounds.value
|
|
307
|
-
const width = Math.max(280, Math.min(bounds.width, vw - 40))
|
|
308
|
-
const height = Math.max(200, Math.min(bounds.height, vh - 40))
|
|
309
|
-
windowBounds.value = {
|
|
310
|
-
x: Math.max(20, Math.min(vw - width - 20, bounds.x)),
|
|
311
|
-
y: Math.max(20, Math.min(vh - height - 20, bounds.y)),
|
|
312
|
-
width,
|
|
313
|
-
height
|
|
314
|
-
}
|
|
315
|
-
saveState()
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
function exitWindowMode(): void {
|
|
319
|
-
displayMode.value = 'bottom'
|
|
320
|
-
saveState()
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
function getModeTitle(): string {
|
|
324
|
-
if (displayMode.value === 'window') return 'Docked mode'
|
|
325
|
-
return displayMode.value === 'bottom' ? 'Dock to right side' : 'Dock to bottom'
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Window drag handlers
|
|
329
|
-
function startDrag(e: MouseEvent): void {
|
|
330
|
-
if (displayMode.value !== 'window') return
|
|
331
|
-
isDragging.value = true
|
|
332
|
-
dragOffset.value = {
|
|
333
|
-
x: e.clientX - windowBounds.value.x,
|
|
334
|
-
y: e.clientY - windowBounds.value.y
|
|
335
|
-
}
|
|
336
|
-
document.addEventListener('mousemove', onDrag)
|
|
337
|
-
document.addEventListener('mouseup', stopDrag)
|
|
338
|
-
e.preventDefault()
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
function onDrag(e: MouseEvent): void {
|
|
342
|
-
if (!isDragging.value) return
|
|
343
|
-
windowBounds.value = {
|
|
344
|
-
...windowBounds.value,
|
|
345
|
-
x: Math.max(0, Math.min(window.innerWidth - 100, e.clientX - dragOffset.value.x)),
|
|
346
|
-
y: Math.max(0, Math.min(window.innerHeight - 50, e.clientY - dragOffset.value.y))
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
function stopDrag(): void {
|
|
351
|
-
if (isDragging.value) {
|
|
352
|
-
isDragging.value = false
|
|
353
|
-
document.removeEventListener('mousemove', onDrag)
|
|
354
|
-
document.removeEventListener('mouseup', stopDrag)
|
|
355
|
-
saveState()
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// Window resize handlers
|
|
360
|
-
function startResize(e: MouseEvent): void {
|
|
361
|
-
if (displayMode.value !== 'window') return
|
|
362
|
-
isResizing.value = true
|
|
363
|
-
resizeStart.value = {
|
|
364
|
-
x: e.clientX,
|
|
365
|
-
y: e.clientY,
|
|
366
|
-
width: windowBounds.value.width,
|
|
367
|
-
height: windowBounds.value.height
|
|
368
|
-
}
|
|
369
|
-
document.addEventListener('mousemove', onResize)
|
|
370
|
-
document.addEventListener('mouseup', stopResize)
|
|
371
|
-
e.preventDefault()
|
|
372
|
-
e.stopPropagation()
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
const MIN_WINDOW_WIDTH = 280
|
|
376
|
-
const MIN_WINDOW_HEIGHT = 200
|
|
377
|
-
|
|
378
|
-
function onResize(e: MouseEvent): void {
|
|
379
|
-
if (!isResizing.value) return
|
|
380
|
-
const deltaX = e.clientX - resizeStart.value.x
|
|
381
|
-
const deltaY = e.clientY - resizeStart.value.y
|
|
382
|
-
windowBounds.value = {
|
|
383
|
-
...windowBounds.value,
|
|
384
|
-
width: Math.max(MIN_WINDOW_WIDTH, resizeStart.value.width + deltaX),
|
|
385
|
-
height: Math.max(MIN_WINDOW_HEIGHT, resizeStart.value.height + deltaY)
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
function stopResize(): void {
|
|
390
|
-
if (isResizing.value) {
|
|
391
|
-
isResizing.value = false
|
|
392
|
-
document.removeEventListener('mousemove', onResize)
|
|
393
|
-
document.removeEventListener('mouseup', stopResize)
|
|
394
|
-
saveState()
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// Docked panel resize handlers
|
|
399
|
-
const MIN_PANEL_HEIGHT = 100
|
|
400
|
-
const MAX_PANEL_HEIGHT = 600
|
|
401
|
-
const MIN_PANEL_WIDTH = 200
|
|
402
|
-
const MAX_PANEL_WIDTH = 800
|
|
403
|
-
|
|
404
|
-
function startPanelResize(e: MouseEvent, direction: 'vertical' | 'horizontal'): void {
|
|
405
|
-
if (displayMode.value === 'window' || fullscreen.value) return
|
|
406
|
-
isPanelResizing.value = true
|
|
407
|
-
panelResizeStart.value = {
|
|
408
|
-
y: e.clientY,
|
|
409
|
-
x: e.clientX,
|
|
410
|
-
height: panelSizes.value.bottomHeight,
|
|
411
|
-
width: panelSizes.value.rightWidth
|
|
412
|
-
}
|
|
413
|
-
const handler = direction === 'vertical' ? onPanelResizeVertical : onPanelResizeHorizontal
|
|
414
|
-
const stop = () => stopPanelResize(handler, stop)
|
|
415
|
-
document.addEventListener('mousemove', handler)
|
|
416
|
-
document.addEventListener('mouseup', stop)
|
|
417
|
-
e.preventDefault()
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
function onPanelResizeVertical(e: MouseEvent): void {
|
|
421
|
-
if (!isPanelResizing.value) return
|
|
422
|
-
const deltaY = panelResizeStart.value.y - e.clientY
|
|
423
|
-
panelSizes.value = {
|
|
424
|
-
...panelSizes.value,
|
|
425
|
-
bottomHeight: Math.max(MIN_PANEL_HEIGHT, Math.min(MAX_PANEL_HEIGHT, panelResizeStart.value.height + deltaY))
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
function onPanelResizeHorizontal(e: MouseEvent): void {
|
|
430
|
-
if (!isPanelResizing.value) return
|
|
431
|
-
const deltaX = panelResizeStart.value.x - e.clientX
|
|
432
|
-
panelSizes.value = {
|
|
433
|
-
...panelSizes.value,
|
|
434
|
-
rightWidth: Math.max(MIN_PANEL_WIDTH, Math.min(MAX_PANEL_WIDTH, panelResizeStart.value.width + deltaX))
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
function stopPanelResize(moveHandler: (e: MouseEvent) => void, upHandler: () => void): void {
|
|
439
|
-
if (isPanelResizing.value) {
|
|
440
|
-
isPanelResizing.value = false
|
|
441
|
-
document.removeEventListener('mousemove', moveHandler)
|
|
442
|
-
document.removeEventListener('mouseup', upHandler)
|
|
443
|
-
saveState()
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// State before entering fullscreen (to restore on exit)
|
|
448
|
-
let preFullscreenExpanded = false
|
|
449
|
-
|
|
450
|
-
function toggleFullscreen(): void {
|
|
451
|
-
if (!fullscreen.value) {
|
|
452
|
-
// Entering fullscreen - save current state
|
|
453
|
-
preFullscreenExpanded = expanded.value
|
|
454
|
-
fullscreen.value = true
|
|
455
|
-
expanded.value = true
|
|
456
|
-
} else {
|
|
457
|
-
// Exiting fullscreen - restore previous state
|
|
458
|
-
fullscreen.value = false
|
|
459
|
-
expanded.value = preFullscreenExpanded
|
|
460
|
-
}
|
|
461
|
-
saveState()
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
function toggleEnabled(): void {
|
|
465
|
-
props.bridge?.toggle()
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
function clearAll(): void {
|
|
469
|
-
props.bridge?.clearAll()
|
|
470
|
-
props.bridge?.notify()
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
function clearCollector(collector: Collector): void {
|
|
474
|
-
collector.clear()
|
|
475
|
-
props.bridge?.notify()
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
function toggleCollector(collector: Collector): void {
|
|
479
|
-
collector.toggle()
|
|
480
|
-
props.bridge?.notify()
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
function notifyBridge(): void {
|
|
484
|
-
props.bridge?.notify()
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
function getCollectorIcon(name: string | undefined): string {
|
|
488
|
-
const icons: Record<string, string> = {
|
|
489
|
-
errors: 'pi-exclamation-triangle',
|
|
490
|
-
signals: 'pi-bolt',
|
|
491
|
-
toasts: 'pi-bell',
|
|
492
|
-
zones: 'pi-th-large',
|
|
493
|
-
auth: 'pi-user',
|
|
494
|
-
entities: 'pi-database',
|
|
495
|
-
router: 'pi-directions',
|
|
496
|
-
ErrorCollector: 'pi-exclamation-triangle',
|
|
497
|
-
SignalCollector: 'pi-bolt',
|
|
498
|
-
ToastCollector: 'pi-bell',
|
|
499
|
-
ZonesCollector: 'pi-th-large',
|
|
500
|
-
AuthCollector: 'pi-user',
|
|
501
|
-
EntitiesCollector: 'pi-database',
|
|
502
|
-
RouterCollector: 'pi-directions'
|
|
503
|
-
}
|
|
504
|
-
return (name && icons[name]) || 'pi-database'
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
function getCollectorLabel(name: string | undefined): string {
|
|
508
|
-
const labels: Record<string, string> = {
|
|
509
|
-
errors: 'Errors',
|
|
510
|
-
signals: 'Signals',
|
|
511
|
-
toasts: 'Toasts',
|
|
512
|
-
zones: 'Zones',
|
|
513
|
-
auth: 'Auth',
|
|
514
|
-
entities: 'Entities',
|
|
515
|
-
router: 'Router',
|
|
516
|
-
ErrorCollector: 'Errors',
|
|
517
|
-
SignalCollector: 'Signals',
|
|
518
|
-
ToastCollector: 'Toasts',
|
|
519
|
-
ZonesCollector: 'Zones',
|
|
520
|
-
AuthCollector: 'Auth',
|
|
521
|
-
EntitiesCollector: 'Entities',
|
|
522
|
-
RouterCollector: 'Router'
|
|
523
|
-
}
|
|
524
|
-
return (name && labels[name]) || name || ''
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
function getCollectorColor(name: string | undefined): string {
|
|
528
|
-
const colors: Record<string, string> = {
|
|
529
|
-
errors: '#ef4444',
|
|
530
|
-
toasts: '#f59e0b',
|
|
531
|
-
signals: '#8b5cf6',
|
|
532
|
-
zones: '#06b6d4',
|
|
533
|
-
auth: '#10b981',
|
|
534
|
-
entities: '#3b82f6',
|
|
535
|
-
router: '#ec4899'
|
|
536
|
-
}
|
|
537
|
-
return (name && colors[name]) || '#6b7280'
|
|
538
|
-
}
|
|
539
21
|
</script>
|
|
540
22
|
|
|
541
23
|
<template>
|
|
542
|
-
<
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
</text>
|
|
550
|
-
</svg>
|
|
551
|
-
<div v-if="errorBadge > 0 || signalBadge > 0" class="debug-badges">
|
|
552
|
-
<span v-if="errorBadge > 0" class="debug-badge debug-badge-error" title="Errors">
|
|
553
|
-
<i class="pi pi-exclamation-triangle" />{{ errorBadge }}
|
|
554
|
-
</span>
|
|
555
|
-
<span v-if="signalBadge > 0" class="debug-badge debug-badge-signal" title="Signals">
|
|
556
|
-
<i class="pi pi-bolt" />{{ signalBadge }}
|
|
557
|
-
</span>
|
|
558
|
-
</div>
|
|
559
|
-
</div>
|
|
560
|
-
|
|
561
|
-
<!-- Full panel -->
|
|
562
|
-
<div v-else class="debug-panel" :class="[
|
|
563
|
-
isMobile ? 'debug-mobile' : (fullscreen ? 'debug-fullscreen' : `debug-${displayMode}`),
|
|
564
|
-
{ 'debug-expanded': expanded }
|
|
565
|
-
]" :style="!isMobile && displayMode === 'window' && !fullscreen ? {
|
|
566
|
-
zIndex,
|
|
567
|
-
left: windowBounds.x + 'px',
|
|
568
|
-
top: windowBounds.y + 'px',
|
|
569
|
-
width: windowBounds.width + 'px',
|
|
570
|
-
height: windowBounds.height + 'px'
|
|
571
|
-
} : !isMobile && displayMode === 'bottom' && expanded && !fullscreen ? {
|
|
572
|
-
zIndex,
|
|
573
|
-
height: panelSizes.bottomHeight + 'px'
|
|
574
|
-
} : !isMobile && displayMode === 'right' && expanded && !fullscreen ? {
|
|
575
|
-
zIndex,
|
|
576
|
-
width: panelSizes.rightWidth + 'px'
|
|
577
|
-
} : { zIndex }">
|
|
578
|
-
<!-- Header -->
|
|
579
|
-
<div ref="headerRef" class="debug-header" @mousedown="displayMode === 'window' && !fullscreen ? startDrag($event) : null" :class="{ 'debug-header-draggable': displayMode === 'window' && !fullscreen }">
|
|
580
|
-
<div class="debug-title" :class="{ 'debug-title-draggable': displayMode === 'window' && !fullscreen }">
|
|
581
|
-
<svg viewBox="0 0 100 100" width="18" height="18">
|
|
582
|
-
<polygon points="50,5 93,27.5 93,72.5 50,95 7,72.5 7,27.5" fill="#1E3A8A"/>
|
|
583
|
-
<text x="48" y="50" text-anchor="middle" dominant-baseline="central" font-family="system-ui" font-size="58" font-weight="800" letter-spacing="-4">
|
|
584
|
-
<tspan fill="#60A5FA">Q</tspan><tspan fill="#93C5FD">D</tspan>
|
|
585
|
-
</text>
|
|
586
|
-
</svg>
|
|
587
|
-
<span>Debug</span>
|
|
588
|
-
<span v-if="errorBadge > 0" class="debug-header-badge debug-header-badge-error" title="Errors">
|
|
589
|
-
<i class="pi pi-exclamation-triangle" />{{ errorBadge }}
|
|
590
|
-
</span>
|
|
591
|
-
<span v-if="signalBadge > 0" class="debug-header-badge debug-header-badge-signal" title="Signals">
|
|
592
|
-
<i class="pi pi-bolt" />{{ signalBadge }}
|
|
593
|
-
</span>
|
|
594
|
-
</div>
|
|
595
|
-
|
|
596
|
-
<!-- Collector tabs - dropdown mode when very narrow -->
|
|
597
|
-
<div v-if="expanded && tabsDropdown" class="debug-tabs-dropdown">
|
|
598
|
-
<button class="debug-dropdown-trigger" @click="showTabsDropdown = !showTabsDropdown">
|
|
599
|
-
<i :class="['pi', getCollectorIcon(currentCollector?.name)]" :style="{ color: getCollectorColor(currentCollector?.name) }" />
|
|
600
|
-
<span>{{ getCollectorLabel(currentCollector?.name) }}</span>
|
|
601
|
-
<Badge v-if="currentCollector && currentCollector.badge > 0" :value="currentCollector.badge" severity="secondary" />
|
|
602
|
-
<i class="pi pi-chevron-down" />
|
|
603
|
-
</button>
|
|
604
|
-
<div v-if="showTabsDropdown" class="debug-dropdown-menu" @click="showTabsDropdown = false">
|
|
605
|
-
<button
|
|
606
|
-
v-for="(c, idx) in collectors"
|
|
607
|
-
:key="c.name"
|
|
608
|
-
class="debug-dropdown-item"
|
|
609
|
-
:class="{ 'debug-dropdown-item-active': activeCollector === idx }"
|
|
610
|
-
@click="activeCollector = idx"
|
|
611
|
-
>
|
|
612
|
-
<i :class="['pi', getCollectorIcon(c.name)]" :style="{ color: getCollectorColor(c.name) }" />
|
|
613
|
-
<span>{{ getCollectorLabel(c.name) }}</span>
|
|
614
|
-
<Badge v-if="c.badge > 0" :value="c.badge" severity="secondary" />
|
|
615
|
-
</button>
|
|
616
|
-
</div>
|
|
617
|
-
</div>
|
|
618
|
-
|
|
619
|
-
<!-- Collector tabs - normal/compact mode -->
|
|
620
|
-
<div v-else-if="expanded" class="debug-tabs" :class="{ 'debug-tabs-compact': tabsCompact }">
|
|
621
|
-
<button
|
|
622
|
-
v-for="(c, idx) in collectors"
|
|
623
|
-
:key="c.name"
|
|
624
|
-
class="debug-tab"
|
|
625
|
-
:class="{ 'debug-tab-active': activeCollector === idx }"
|
|
626
|
-
:title="getCollectorLabel(c.name)"
|
|
627
|
-
@click="activeCollector = idx"
|
|
628
|
-
>
|
|
629
|
-
<i :class="['pi', getCollectorIcon(c.name)]" :style="{ color: getCollectorColor(c.name) }" />
|
|
630
|
-
<span v-if="!tabsCompact" class="debug-tab-label">{{ getCollectorLabel(c.name) }}</span>
|
|
631
|
-
<Badge v-if="c.badge > 0" :value="c.badge" severity="secondary" />
|
|
632
|
-
<!-- Per-collector play/pause for recording collectors (bottom mode only, not compact, not mobile) -->
|
|
633
|
-
<button
|
|
634
|
-
v-if="c.records && displayMode === 'bottom' && !tabsCompact && !isMobile"
|
|
635
|
-
class="debug-tab-toggle"
|
|
636
|
-
:class="{ 'debug-tab-toggle-paused': !c.enabled }"
|
|
637
|
-
:title="c.enabled ? 'Pause recording' : 'Resume recording'"
|
|
638
|
-
@click.stop="toggleCollector(c.collector)"
|
|
639
|
-
>
|
|
640
|
-
<i :class="['pi', c.enabled ? 'pi-pause' : 'pi-play']" />
|
|
641
|
-
</button>
|
|
642
|
-
<!-- Per-collector clear button for recording collectors (bottom mode only, not compact, not mobile) -->
|
|
643
|
-
<button
|
|
644
|
-
v-if="c.records && c.badge > 0 && displayMode === 'bottom' && !tabsCompact && !isMobile"
|
|
645
|
-
class="debug-tab-clear"
|
|
646
|
-
title="Clear entries"
|
|
647
|
-
@click.stop="clearCollector(c.collector)"
|
|
648
|
-
>
|
|
649
|
-
<i class="pi pi-trash" />
|
|
650
|
-
</button>
|
|
651
|
-
</button>
|
|
652
|
-
</div>
|
|
653
|
-
|
|
654
|
-
<div class="debug-actions">
|
|
655
|
-
<Button :icon="isEnabled ? 'pi pi-pause' : 'pi pi-play'" :severity="isEnabled ? 'success' : 'secondary'" size="small" text rounded :title="isEnabled ? 'Pause' : 'Resume'" @click="toggleEnabled" />
|
|
656
|
-
<Button icon="pi pi-trash" severity="secondary" size="small" text rounded title="Clear all" @click="clearAll" />
|
|
657
|
-
<!-- Badge count mode toggle -->
|
|
658
|
-
<Button :icon="countAllBadges ? 'pi pi-hashtag' : 'pi pi-eye'" :severity="countAllBadges ? 'info' : 'secondary'" size="small" text rounded :title="countAllBadges ? 'Showing all (click for unseen only)' : 'Showing unseen (click for all)'" @click="toggleCountMode" />
|
|
659
|
-
<!-- Mode toggle (bottom/right) - not shown in window/fullscreen/mobile mode -->
|
|
660
|
-
<button
|
|
661
|
-
v-if="!isMobile && displayMode !== 'window' && !fullscreen"
|
|
662
|
-
class="debug-mode-btn"
|
|
663
|
-
:title="getModeTitle()"
|
|
664
|
-
@click="toggleMode"
|
|
665
|
-
>{{ displayMode === 'bottom' ? '|' : '―' }}</button>
|
|
666
|
-
<!-- Enter window mode button - not in fullscreen/mobile -->
|
|
667
|
-
<Button v-if="!isMobile && displayMode !== 'window' && !fullscreen" icon="pi pi-external-link" severity="secondary" size="small" text rounded title="Detach window" @click="enterWindowMode" />
|
|
668
|
-
<!-- Exit window mode button (dock back) - not on mobile -->
|
|
669
|
-
<Button v-if="!isMobile && displayMode === 'window'" icon="pi pi-link" severity="secondary" size="small" text rounded title="Dock panel" @click="exitWindowMode" />
|
|
670
|
-
<!-- Fullscreen (not in window/mobile mode) -->
|
|
671
|
-
<Button v-if="!isMobile && displayMode !== 'window'" :icon="fullscreen ? 'pi pi-window-minimize' : 'pi pi-window-maximize'" severity="secondary" size="small" text rounded :title="fullscreen ? 'Exit fullscreen' : 'Fullscreen'" @click="toggleFullscreen" />
|
|
672
|
-
<!-- Expand/collapse (not in window/fullscreen/mobile mode) -->
|
|
673
|
-
<Button v-if="!isMobile && displayMode !== 'window' && !fullscreen" :icon="displayMode === 'right' ? (expanded ? 'pi pi-chevron-right' : 'pi pi-chevron-left') : (expanded ? 'pi pi-chevron-down' : 'pi pi-chevron-up')" severity="secondary" size="small" text rounded @click="toggle" />
|
|
674
|
-
<!-- Minimize - always show (on mobile, this is the only way to close) -->
|
|
675
|
-
<Button icon="pi pi-times" severity="secondary" size="small" text rounded title="Close" @click="minimize" />
|
|
676
|
-
</div>
|
|
677
|
-
</div>
|
|
678
|
-
|
|
679
|
-
<!-- Resize handle for window mode (not in fullscreen/mobile) -->
|
|
680
|
-
<div v-if="!isMobile && displayMode === 'window' && !fullscreen" class="debug-resize-handle" @mousedown="startResize" />
|
|
681
|
-
|
|
682
|
-
<!-- Resize handle for bottom mode (drag to resize height) - not on mobile -->
|
|
683
|
-
<div
|
|
684
|
-
v-if="!isMobile && displayMode === 'bottom' && expanded && !fullscreen"
|
|
685
|
-
class="debug-panel-resize debug-panel-resize-top"
|
|
686
|
-
@mousedown="startPanelResize($event, 'vertical')"
|
|
687
|
-
/>
|
|
688
|
-
|
|
689
|
-
<!-- Resize handle for right mode (drag to resize width) - not on mobile -->
|
|
690
|
-
<div
|
|
691
|
-
v-if="!isMobile && displayMode === 'right' && expanded && !fullscreen"
|
|
692
|
-
class="debug-panel-resize debug-panel-resize-left"
|
|
693
|
-
@mousedown="startPanelResize($event, 'horizontal')"
|
|
694
|
-
/>
|
|
695
|
-
|
|
696
|
-
<!-- Content -->
|
|
697
|
-
<div v-if="expanded && currentCollector" class="debug-content">
|
|
698
|
-
<!-- Content header with controls (right/window/fullscreen/mobile mode, for recording collectors) -->
|
|
699
|
-
<div v-if="(isMobile || displayMode === 'right' || displayMode === 'window' || fullscreen) && currentCollector.records" class="debug-content-header">
|
|
700
|
-
<span class="debug-content-title">{{ getCollectorLabel(currentCollector.name) }}</span>
|
|
701
|
-
<div class="debug-content-controls">
|
|
702
|
-
<button
|
|
703
|
-
class="debug-tab-toggle"
|
|
704
|
-
:class="{ 'debug-tab-toggle-paused': !currentCollector.enabled }"
|
|
705
|
-
:title="currentCollector.enabled ? 'Pause recording' : 'Resume recording'"
|
|
706
|
-
@click="toggleCollector(currentCollector.collector)"
|
|
707
|
-
>
|
|
708
|
-
<i :class="['pi', currentCollector.enabled ? 'pi-pause' : 'pi-play']" />
|
|
709
|
-
</button>
|
|
710
|
-
<button
|
|
711
|
-
v-if="currentCollector.badge > 0"
|
|
712
|
-
class="debug-tab-clear"
|
|
713
|
-
title="Clear entries"
|
|
714
|
-
@click="clearCollector(currentCollector.collector)"
|
|
715
|
-
>
|
|
716
|
-
<i class="pi pi-trash" />
|
|
717
|
-
</button>
|
|
718
|
-
</div>
|
|
719
|
-
</div>
|
|
720
|
-
|
|
721
|
-
<!-- Zones collector - always render for toolbar access -->
|
|
722
|
-
<ZonesPanel
|
|
723
|
-
v-if="currentCollector.name === 'zones' || currentCollector.name === 'ZonesCollector'"
|
|
724
|
-
:collector="(currentCollector.collector as any)"
|
|
725
|
-
:entries="(currentCollector.entries as any[])"
|
|
726
|
-
@update="notifyBridge"
|
|
727
|
-
/>
|
|
728
|
-
|
|
729
|
-
<!-- Signals collector -->
|
|
730
|
-
<SignalsPanel
|
|
731
|
-
v-else-if="currentCollector.name === 'signals' || currentCollector.name === 'SignalCollector'"
|
|
732
|
-
:collector="(currentCollector.collector as any)"
|
|
733
|
-
:entries="(currentCollector.entries as any[])"
|
|
734
|
-
/>
|
|
735
|
-
|
|
736
|
-
<!-- Auth collector -->
|
|
737
|
-
<AuthPanel
|
|
738
|
-
v-else-if="currentCollector.name === 'auth' || currentCollector.name === 'AuthCollector'"
|
|
739
|
-
:collector="(currentCollector.collector as any)"
|
|
740
|
-
:entries="(currentCollector.entries as any[])"
|
|
741
|
-
/>
|
|
742
|
-
|
|
743
|
-
<!-- Entities collector -->
|
|
744
|
-
<EntitiesPanel
|
|
745
|
-
v-else-if="currentCollector.name === 'entities' || currentCollector.name === 'EntitiesCollector'"
|
|
746
|
-
:collector="(currentCollector.collector as any)"
|
|
747
|
-
:entries="(currentCollector.entries as any[])"
|
|
748
|
-
@update="notifyBridge"
|
|
749
|
-
/>
|
|
750
|
-
|
|
751
|
-
<!-- Router collector -->
|
|
752
|
-
<RouterPanel
|
|
753
|
-
v-else-if="currentCollector.name === 'router' || currentCollector.name === 'RouterCollector'"
|
|
754
|
-
:collector="(currentCollector.collector as any)"
|
|
755
|
-
:entries="(currentCollector.entries as any[])"
|
|
756
|
-
/>
|
|
757
|
-
|
|
758
|
-
<!-- Toasts collector (vertical modes) -->
|
|
759
|
-
<ToastsPanel
|
|
760
|
-
v-else-if="(currentCollector.name === 'toasts' || currentCollector.name === 'ToastCollector') && (isMobile || displayMode !== 'bottom' || fullscreen)"
|
|
761
|
-
:entries="(currentCollector.entries as any[])"
|
|
762
|
-
/>
|
|
763
|
-
|
|
764
|
-
<!-- Empty state for generic collectors without entries -->
|
|
765
|
-
<div v-else-if="currentCollector.entries.length === 0" class="debug-empty">
|
|
766
|
-
<i class="pi pi-inbox" />
|
|
767
|
-
<span>No entries</span>
|
|
768
|
-
</div>
|
|
769
|
-
|
|
770
|
-
<!-- Default entries - horizontal (bottom mode, not mobile) -->
|
|
771
|
-
<EntriesPanel
|
|
772
|
-
v-else-if="!isMobile && displayMode === 'bottom' && !fullscreen"
|
|
773
|
-
:entries="(currentCollector.entries as any[])"
|
|
774
|
-
mode="horizontal"
|
|
775
|
-
/>
|
|
776
|
-
|
|
777
|
-
<!-- Default entries - vertical (right/fullscreen/mobile mode) -->
|
|
778
|
-
<EntriesPanel
|
|
779
|
-
v-else
|
|
780
|
-
:entries="(currentCollector.entries as any[])"
|
|
781
|
-
mode="vertical"
|
|
782
|
-
/>
|
|
783
|
-
</div>
|
|
784
|
-
</div>
|
|
785
|
-
</Teleport>
|
|
24
|
+
<QddebugBar
|
|
25
|
+
:bridge="bridge"
|
|
26
|
+
:z-index="zIndex"
|
|
27
|
+
:storage-key="STORAGE_KEY"
|
|
28
|
+
:panels="adminPanels"
|
|
29
|
+
:collector-meta="adminPanelsMeta"
|
|
30
|
+
/>
|
|
786
31
|
</template>
|
|
787
|
-
|