@djangocfg/debuger 2.1.219
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/README.md +345 -0
- package/dist/chunk-ESIQODSI.mjs +115 -0
- package/dist/chunk-ESIQODSI.mjs.map +1 -0
- package/dist/chunk-PAWJFY3S.mjs +6 -0
- package/dist/chunk-PAWJFY3S.mjs.map +1 -0
- package/dist/chunk-YQE3KTBG.mjs +64 -0
- package/dist/chunk-YQE3KTBG.mjs.map +1 -0
- package/dist/customEmitter-BO-1IWxm.d.ts +57 -0
- package/dist/emitters/index.cjs +74 -0
- package/dist/emitters/index.cjs.map +1 -0
- package/dist/emitters/index.d.ts +33 -0
- package/dist/emitters/index.mjs +4 -0
- package/dist/emitters/index.mjs.map +1 -0
- package/dist/index.cjs +887 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +162 -0
- package/dist/index.mjs +694 -0
- package/dist/index.mjs.map +1 -0
- package/dist/logger/index.cjs +123 -0
- package/dist/logger/index.cjs.map +1 -0
- package/dist/logger/index.d.ts +50 -0
- package/dist/logger/index.mjs +4 -0
- package/dist/logger/index.mjs.map +1 -0
- package/package.json +67 -0
- package/src/DebugButton.tsx +143 -0
- package/src/DebugPanel.tsx +171 -0
- package/src/bridges/index.ts +1 -0
- package/src/bridges/monitorBridge.ts +63 -0
- package/src/emitters/Emitter.ts +51 -0
- package/src/emitters/audioEmitter.ts +74 -0
- package/src/emitters/customEmitter.ts +42 -0
- package/src/emitters/index.ts +10 -0
- package/src/hooks/useAudioEventLog.ts +147 -0
- package/src/hooks/useCustomEventLog.ts +39 -0
- package/src/hooks/useDebugShortcut.ts +27 -0
- package/src/hooks/useStoreSnapshot.ts +70 -0
- package/src/index.ts +62 -0
- package/src/logger/index.ts +3 -0
- package/src/logger/logStore.ts +89 -0
- package/src/logger/logger.ts +95 -0
- package/src/logger/types.ts +39 -0
- package/src/panels/AudioDebugPanel.tsx +157 -0
- package/src/panels/LogsPanel.tsx +267 -0
- package/src/panels/StorePanel.tsx +71 -0
- package/src/store/debugStore.ts +42 -0
- package/src/styles/index.css +5 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
import { clearAudioEventBuffer, subscribeAudioEvents, subscribeCustomEvents } from './chunk-YQE3KTBG.mjs';
|
|
2
|
+
export { clearAudioEventBuffer, emitAudioEvent, emitDebugEvent, hasAudioListeners, hasCustomListeners, subscribeAudioEvents, subscribeCustomEvents } from './chunk-YQE3KTBG.mjs';
|
|
3
|
+
import { useDebugLogStore, useDebugFilteredLogs, useDebugLogCount, useDebugErrorCount } from './chunk-ESIQODSI.mjs';
|
|
4
|
+
export { createDebugLogger, debugLog, useDebugErrorCount, useDebugFilteredLogs, useDebugLogCount, useDebugLogStore } from './chunk-ESIQODSI.mjs';
|
|
5
|
+
import { __name } from './chunk-PAWJFY3S.mjs';
|
|
6
|
+
import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
|
|
7
|
+
import { CheckCircle, AlertCircle, AlertTriangle, Info, Bug, ChevronUp, ChevronDown, Search, Download, Trash2, Radio, ScrollText, Maximize2, Minimize2, X, Database } from 'lucide-react';
|
|
8
|
+
import { Badge, Input, Tooltip, TooltipTrigger, Button, TooltipContent, CopyButton, ScrollArea } from '@djangocfg/ui-core/components';
|
|
9
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
10
|
+
import { create } from 'zustand';
|
|
11
|
+
import { useHotkey } from '@djangocfg/ui-core/hooks';
|
|
12
|
+
import { monitorStore } from '@djangocfg/monitor/client';
|
|
13
|
+
import { LazyJsonTree } from '@djangocfg/ui-tools';
|
|
14
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
15
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
16
|
+
|
|
17
|
+
var useDebugStore = create((set) => ({
|
|
18
|
+
isOpen: false,
|
|
19
|
+
tab: "logs",
|
|
20
|
+
isUnlocked: false,
|
|
21
|
+
open: /* @__PURE__ */ __name(() => set({ isOpen: true }), "open"),
|
|
22
|
+
close: /* @__PURE__ */ __name(() => set({ isOpen: false }), "close"),
|
|
23
|
+
toggle: /* @__PURE__ */ __name(() => set((s) => ({ isOpen: !s.isOpen })), "toggle"),
|
|
24
|
+
setTab: /* @__PURE__ */ __name((tab) => set({ tab }), "setTab"),
|
|
25
|
+
unlock: /* @__PURE__ */ __name((_key) => {
|
|
26
|
+
const valid = true;
|
|
27
|
+
set({ isUnlocked: true });
|
|
28
|
+
return valid;
|
|
29
|
+
}, "unlock")
|
|
30
|
+
}));
|
|
31
|
+
function useDebugShortcut() {
|
|
32
|
+
const toggle = useDebugStore((s) => s.toggle);
|
|
33
|
+
const isUnlocked = useDebugStore((s) => s.isUnlocked);
|
|
34
|
+
const isDev = typeof process !== "undefined" && true;
|
|
35
|
+
useHotkey(
|
|
36
|
+
"meta+d",
|
|
37
|
+
(e) => {
|
|
38
|
+
e.preventDefault();
|
|
39
|
+
if (!isDev && !isUnlocked) return;
|
|
40
|
+
toggle();
|
|
41
|
+
},
|
|
42
|
+
{ preventDefault: true }
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
__name(useDebugShortcut, "useDebugShortcut");
|
|
46
|
+
var _installed = false;
|
|
47
|
+
function installMonitorBridge() {
|
|
48
|
+
if (typeof window === "undefined") return;
|
|
49
|
+
if (_installed) return;
|
|
50
|
+
let prevLen = monitorStore.getState().buffer.length;
|
|
51
|
+
const unsub = monitorStore.subscribe((state) => {
|
|
52
|
+
const buffer = state.buffer;
|
|
53
|
+
if (buffer.length <= prevLen) {
|
|
54
|
+
prevLen = buffer.length;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const newEvents = buffer.slice(prevLen);
|
|
58
|
+
prevLen = buffer.length;
|
|
59
|
+
for (const event of newEvents) {
|
|
60
|
+
const level = event.level === "error" ? "error" : event.level === "warn" ? "warn" : "info";
|
|
61
|
+
useDebugLogStore.getState().addLog({
|
|
62
|
+
level,
|
|
63
|
+
component: `monitor:${event.event_type ?? "event"}`,
|
|
64
|
+
message: event.message ?? "",
|
|
65
|
+
data: {
|
|
66
|
+
...event.url && { url: event.url },
|
|
67
|
+
...event.session_id && { session_id: event.session_id },
|
|
68
|
+
...event.http_status !== void 0 && {
|
|
69
|
+
http_status: event.http_status,
|
|
70
|
+
http_method: event.http_method,
|
|
71
|
+
http_url: event.http_url
|
|
72
|
+
},
|
|
73
|
+
...event.extra ? { extra: event.extra } : {}
|
|
74
|
+
},
|
|
75
|
+
stack: event.stack_trace
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
_installed = true;
|
|
80
|
+
return () => {
|
|
81
|
+
unsub();
|
|
82
|
+
_installed = false;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
__name(installMonitorBridge, "installMonitorBridge");
|
|
86
|
+
var ROW_ESTIMATE_PX = 32;
|
|
87
|
+
var OVERSCAN = 5;
|
|
88
|
+
var LOG_LEVEL_CONFIG = {
|
|
89
|
+
debug: { icon: Bug, color: "text-muted-foreground" },
|
|
90
|
+
info: { icon: Info, color: "text-blue-500" },
|
|
91
|
+
warn: { icon: AlertTriangle, color: "text-yellow-500" },
|
|
92
|
+
error: { icon: AlertCircle, color: "text-red-500" },
|
|
93
|
+
success: { icon: CheckCircle, color: "text-green-500" }
|
|
94
|
+
};
|
|
95
|
+
var LOG_LEVELS = Object.keys(LOG_LEVEL_CONFIG);
|
|
96
|
+
function LogEntryRow({ entry, expanded, onToggle }) {
|
|
97
|
+
const config = LOG_LEVEL_CONFIG[entry.level];
|
|
98
|
+
const Icon = config.icon;
|
|
99
|
+
const pad = /* @__PURE__ */ __name((n, len = 2) => String(n).padStart(len, "0"), "pad");
|
|
100
|
+
const d = entry.timestamp;
|
|
101
|
+
const time = `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${pad(d.getMilliseconds(), 3)}`;
|
|
102
|
+
const hasData = entry.data && Object.keys(entry.data).length > 0;
|
|
103
|
+
const hasStack = !!entry.stack;
|
|
104
|
+
const isExpandable = hasData || hasStack;
|
|
105
|
+
return /* @__PURE__ */ jsxs("div", { className: "border-b border-border/50 last:border-0", children: [
|
|
106
|
+
/* @__PURE__ */ jsxs(
|
|
107
|
+
"button",
|
|
108
|
+
{
|
|
109
|
+
type: "button",
|
|
110
|
+
onClick: isExpandable ? onToggle : void 0,
|
|
111
|
+
disabled: !isExpandable,
|
|
112
|
+
className: cn(
|
|
113
|
+
"flex w-full items-start gap-2 px-3 py-1.5 text-left text-xs",
|
|
114
|
+
isExpandable ? "hover:bg-muted/50 cursor-pointer" : "cursor-default"
|
|
115
|
+
),
|
|
116
|
+
children: [
|
|
117
|
+
/* @__PURE__ */ jsx(Icon, { className: cn("h-3.5 w-3.5 mt-0.5 shrink-0", config.color) }),
|
|
118
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground font-mono shrink-0", children: time }),
|
|
119
|
+
/* @__PURE__ */ jsx(Badge, { variant: "outline", className: "shrink-0 text-[10px] px-1 py-0", children: entry.component }),
|
|
120
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: entry.message }),
|
|
121
|
+
isExpandable && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground", children: expanded ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3" }) })
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
),
|
|
125
|
+
expanded && isExpandable && /* @__PURE__ */ jsxs("div", { className: "px-3 pb-2 pl-8", children: [
|
|
126
|
+
hasData && /* @__PURE__ */ jsx("div", { className: "mt-1", children: /* @__PURE__ */ jsx(LazyJsonTree, { data: entry.data, mode: "compact" }) }),
|
|
127
|
+
hasStack && /* @__PURE__ */ jsx("pre", { className: "mt-2 text-[10px] text-red-400 whitespace-pre-wrap font-mono bg-red-950/20 p-2 rounded", children: entry.stack })
|
|
128
|
+
] })
|
|
129
|
+
] });
|
|
130
|
+
}
|
|
131
|
+
__name(LogEntryRow, "LogEntryRow");
|
|
132
|
+
function LogsPanel({ isActive }) {
|
|
133
|
+
const [expandedLogs, setExpandedLogs] = useState(/* @__PURE__ */ new Set());
|
|
134
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
135
|
+
const [selectedLevels, setSelectedLevels] = useState(
|
|
136
|
+
/* @__PURE__ */ new Set(["debug", "info", "warn", "error", "success"])
|
|
137
|
+
);
|
|
138
|
+
const [componentFilter, setComponentFilter] = useState("");
|
|
139
|
+
const logs = useDebugFilteredLogs();
|
|
140
|
+
const logCount = useDebugLogCount();
|
|
141
|
+
const errorCount = useDebugErrorCount();
|
|
142
|
+
const clearLogs = useDebugLogStore((s) => s.clearLogs);
|
|
143
|
+
const setFilter = useDebugLogStore((s) => s.setFilter);
|
|
144
|
+
const exportLogs = useDebugLogStore((s) => s.exportLogs);
|
|
145
|
+
const scrollRef = useRef(null);
|
|
146
|
+
const virtualizer = useVirtualizer({
|
|
147
|
+
count: logs.length,
|
|
148
|
+
getScrollElement: /* @__PURE__ */ __name(() => scrollRef.current, "getScrollElement"),
|
|
149
|
+
estimateSize: /* @__PURE__ */ __name(() => ROW_ESTIMATE_PX, "estimateSize"),
|
|
150
|
+
overscan: OVERSCAN
|
|
151
|
+
});
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
if (!isActive) return;
|
|
154
|
+
setFilter({
|
|
155
|
+
levels: Array.from(selectedLevels),
|
|
156
|
+
component: componentFilter || void 0,
|
|
157
|
+
search: searchQuery || void 0
|
|
158
|
+
});
|
|
159
|
+
}, [isActive, selectedLevels, componentFilter, searchQuery, setFilter]);
|
|
160
|
+
const handleToggleExpand = useCallback((id) => {
|
|
161
|
+
setExpandedLogs((prev) => {
|
|
162
|
+
const next = new Set(prev);
|
|
163
|
+
next.has(id) ? next.delete(id) : next.add(id);
|
|
164
|
+
return next;
|
|
165
|
+
});
|
|
166
|
+
}, []);
|
|
167
|
+
const handleToggleLevel = useCallback((level) => {
|
|
168
|
+
setSelectedLevels((prev) => {
|
|
169
|
+
const next = new Set(prev);
|
|
170
|
+
next.has(level) ? next.delete(level) : next.add(level);
|
|
171
|
+
return next;
|
|
172
|
+
});
|
|
173
|
+
}, []);
|
|
174
|
+
const handleExport = useCallback(() => {
|
|
175
|
+
const json = exportLogs();
|
|
176
|
+
const blob = new Blob([json], { type: "application/json" });
|
|
177
|
+
const url = URL.createObjectURL(blob);
|
|
178
|
+
const a = document.createElement("a");
|
|
179
|
+
a.href = url;
|
|
180
|
+
a.download = `debug-logs-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/:/g, "-")}.json`;
|
|
181
|
+
a.click();
|
|
182
|
+
URL.revokeObjectURL(url);
|
|
183
|
+
}, [exportLogs]);
|
|
184
|
+
const handleClear = useCallback(() => {
|
|
185
|
+
clearLogs();
|
|
186
|
+
setExpandedLogs(/* @__PURE__ */ new Set());
|
|
187
|
+
}, [clearLogs]);
|
|
188
|
+
const logsJson = exportLogs();
|
|
189
|
+
const virtualItems = virtualizer.getVirtualItems();
|
|
190
|
+
const totalSize = virtualizer.getTotalSize();
|
|
191
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
|
|
192
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-border px-3 py-1.5 shrink-0", children: [
|
|
193
|
+
/* @__PURE__ */ jsxs(Badge, { variant: "secondary", className: "text-[10px]", children: [
|
|
194
|
+
logCount,
|
|
195
|
+
" logs"
|
|
196
|
+
] }),
|
|
197
|
+
errorCount > 0 && /* @__PURE__ */ jsxs(Badge, { variant: "destructive", className: "text-[10px]", children: [
|
|
198
|
+
errorCount,
|
|
199
|
+
" errors"
|
|
200
|
+
] })
|
|
201
|
+
] }),
|
|
202
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 border-b border-border px-3 py-2 shrink-0", children: [
|
|
203
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex-1 min-w-[140px]", children: [
|
|
204
|
+
/* @__PURE__ */ jsx(Search, { className: "absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" }),
|
|
205
|
+
/* @__PURE__ */ jsx(
|
|
206
|
+
Input,
|
|
207
|
+
{
|
|
208
|
+
placeholder: "Search...",
|
|
209
|
+
value: searchQuery,
|
|
210
|
+
onChange: (e) => setSearchQuery(e.target.value),
|
|
211
|
+
className: "h-7 pl-7 text-xs"
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
] }),
|
|
215
|
+
/* @__PURE__ */ jsx(
|
|
216
|
+
Input,
|
|
217
|
+
{
|
|
218
|
+
placeholder: "Component...",
|
|
219
|
+
value: componentFilter,
|
|
220
|
+
onChange: (e) => setComponentFilter(e.target.value),
|
|
221
|
+
className: "h-7 w-24 text-xs"
|
|
222
|
+
}
|
|
223
|
+
),
|
|
224
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5", children: LOG_LEVELS.map((level) => {
|
|
225
|
+
const { icon: Icon, color } = LOG_LEVEL_CONFIG[level];
|
|
226
|
+
const active = selectedLevels.has(level);
|
|
227
|
+
return /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
228
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: active ? "secondary" : "ghost", size: "icon", className: "h-6 w-6", onClick: () => handleToggleLevel(level), children: /* @__PURE__ */ jsx(Icon, { className: cn("h-3.5 w-3.5", active && color) }) }) }),
|
|
229
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: level })
|
|
230
|
+
] }, level);
|
|
231
|
+
}) }),
|
|
232
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 ml-auto", children: [
|
|
233
|
+
/* @__PURE__ */ jsx(CopyButton, { value: logsJson, size: "icon", className: "h-6 w-6" }),
|
|
234
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
235
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", className: "h-6 w-6", onClick: handleExport, children: /* @__PURE__ */ jsx(Download, { className: "h-3.5 w-3.5" }) }) }),
|
|
236
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: "Export JSON" })
|
|
237
|
+
] }),
|
|
238
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
239
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", className: "h-6 w-6", onClick: handleClear, children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" }) }) }),
|
|
240
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: "Clear" })
|
|
241
|
+
] })
|
|
242
|
+
] })
|
|
243
|
+
] }),
|
|
244
|
+
/* @__PURE__ */ jsx("div", { ref: scrollRef, className: "flex-1 overflow-auto", children: logs.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-10 text-muted-foreground", children: [
|
|
245
|
+
/* @__PURE__ */ jsx(Bug, { className: "h-8 w-8 mb-2 opacity-40" }),
|
|
246
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm", children: "No logs yet" })
|
|
247
|
+
] }) : /* @__PURE__ */ jsx("div", { style: { height: `${totalSize}px`, position: "relative" }, children: virtualItems.map((vItem) => {
|
|
248
|
+
const entry = logs[vItem.index];
|
|
249
|
+
return /* @__PURE__ */ jsx(
|
|
250
|
+
"div",
|
|
251
|
+
{
|
|
252
|
+
"data-index": vItem.index,
|
|
253
|
+
ref: virtualizer.measureElement,
|
|
254
|
+
style: { position: "absolute", top: 0, left: 0, width: "100%", transform: `translateY(${vItem.start}px)` },
|
|
255
|
+
children: /* @__PURE__ */ jsx(
|
|
256
|
+
LogEntryRow,
|
|
257
|
+
{
|
|
258
|
+
entry,
|
|
259
|
+
expanded: expandedLogs.has(entry.id),
|
|
260
|
+
onToggle: () => handleToggleExpand(entry.id)
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
},
|
|
264
|
+
entry.id
|
|
265
|
+
);
|
|
266
|
+
}) }) })
|
|
267
|
+
] });
|
|
268
|
+
}
|
|
269
|
+
__name(LogsPanel, "LogsPanel");
|
|
270
|
+
var MAX_EVENTS = 200;
|
|
271
|
+
function useAudioEventLog(active) {
|
|
272
|
+
const [events, setEvents] = useState([]);
|
|
273
|
+
const [syncIntervalMs, setSyncIntervalMs] = useState(null);
|
|
274
|
+
const [kindCounts, setKindCounts] = useState({});
|
|
275
|
+
const [seekRate, setSeekRate] = useState(0);
|
|
276
|
+
const queueRef = useRef([]);
|
|
277
|
+
const counterRef = useRef(0);
|
|
278
|
+
const lastSyncTs = useRef(null);
|
|
279
|
+
const seekTimestamps = useRef([]);
|
|
280
|
+
const kindCountsRef = useRef({});
|
|
281
|
+
const clear = useCallback(() => {
|
|
282
|
+
clearAudioEventBuffer();
|
|
283
|
+
queueRef.current = [];
|
|
284
|
+
counterRef.current = 0;
|
|
285
|
+
kindCountsRef.current = {};
|
|
286
|
+
seekTimestamps.current = [];
|
|
287
|
+
lastSyncTs.current = null;
|
|
288
|
+
setEvents([]);
|
|
289
|
+
setSyncIntervalMs(null);
|
|
290
|
+
setKindCounts({});
|
|
291
|
+
setSeekRate(0);
|
|
292
|
+
}, []);
|
|
293
|
+
useEffect(() => {
|
|
294
|
+
if (!active) return;
|
|
295
|
+
const unsubscribe = subscribeAudioEvents((event) => {
|
|
296
|
+
const entry = { ...event, id: String(++counterRef.current) };
|
|
297
|
+
queueRef.current.push(entry);
|
|
298
|
+
if (event.kind === "sync") {
|
|
299
|
+
if (lastSyncTs.current !== null) {
|
|
300
|
+
const interval = event.ts - lastSyncTs.current;
|
|
301
|
+
entry._interval = interval;
|
|
302
|
+
}
|
|
303
|
+
lastSyncTs.current = event.ts;
|
|
304
|
+
}
|
|
305
|
+
if (event.kind === "seek") {
|
|
306
|
+
seekTimestamps.current.push(event.ts);
|
|
307
|
+
}
|
|
308
|
+
kindCountsRef.current = {
|
|
309
|
+
...kindCountsRef.current,
|
|
310
|
+
[event.kind]: (kindCountsRef.current[event.kind] ?? 0) + 1
|
|
311
|
+
};
|
|
312
|
+
});
|
|
313
|
+
return unsubscribe;
|
|
314
|
+
}, [active]);
|
|
315
|
+
useEffect(() => {
|
|
316
|
+
if (!active) return;
|
|
317
|
+
let rafId;
|
|
318
|
+
const flush = /* @__PURE__ */ __name(() => {
|
|
319
|
+
if (queueRef.current.length > 0) {
|
|
320
|
+
const incoming = queueRef.current.splice(0);
|
|
321
|
+
const lastSync = [...incoming].reverse().find((e) => e.kind === "sync");
|
|
322
|
+
if (lastSync?._interval !== void 0) {
|
|
323
|
+
setSyncIntervalMs(lastSync._interval);
|
|
324
|
+
}
|
|
325
|
+
setEvents((prev) => {
|
|
326
|
+
const next = [...prev, ...incoming];
|
|
327
|
+
return next.length > MAX_EVENTS ? next.slice(-MAX_EVENTS) : next;
|
|
328
|
+
});
|
|
329
|
+
setKindCounts({ ...kindCountsRef.current });
|
|
330
|
+
}
|
|
331
|
+
rafId = requestAnimationFrame(flush);
|
|
332
|
+
}, "flush");
|
|
333
|
+
rafId = requestAnimationFrame(flush);
|
|
334
|
+
return () => cancelAnimationFrame(rafId);
|
|
335
|
+
}, [active]);
|
|
336
|
+
useEffect(() => {
|
|
337
|
+
if (!active) return;
|
|
338
|
+
const id = setInterval(() => {
|
|
339
|
+
const now = Date.now();
|
|
340
|
+
seekTimestamps.current = seekTimestamps.current.filter((t) => now - t < 5e3);
|
|
341
|
+
setSeekRate(Math.round(seekTimestamps.current.length / 5 * 10) / 10);
|
|
342
|
+
}, 1e3);
|
|
343
|
+
return () => clearInterval(id);
|
|
344
|
+
}, [active]);
|
|
345
|
+
return { events, clear, seekRate, syncIntervalMs, kindCounts };
|
|
346
|
+
}
|
|
347
|
+
__name(useAudioEventLog, "useAudioEventLog");
|
|
348
|
+
var DEBUG_MODES = ["off", "on", "verbose"];
|
|
349
|
+
var KIND_STYLES = {
|
|
350
|
+
play: { label: "play", className: "text-green-400" },
|
|
351
|
+
pause: { label: "pause", className: "text-yellow-400" },
|
|
352
|
+
seek: { label: "seek", className: "text-blue-400" },
|
|
353
|
+
ended: { label: "ended", className: "text-muted-foreground" },
|
|
354
|
+
sync: { label: "sync", className: "text-muted-foreground/50" },
|
|
355
|
+
load: { label: "load", className: "text-purple-400" },
|
|
356
|
+
error: { label: "error", className: "text-red-400" },
|
|
357
|
+
engine: { label: "eng", className: "text-cyan-400" },
|
|
358
|
+
custom: { label: "custom", className: "text-orange-400" }
|
|
359
|
+
};
|
|
360
|
+
function getDebugAudioMode() {
|
|
361
|
+
try {
|
|
362
|
+
const v = localStorage.getItem("DEBUG_AUDIO");
|
|
363
|
+
if (v === "verbose") return "verbose";
|
|
364
|
+
if (v) return "on";
|
|
365
|
+
} catch {
|
|
366
|
+
}
|
|
367
|
+
return "off";
|
|
368
|
+
}
|
|
369
|
+
__name(getDebugAudioMode, "getDebugAudioMode");
|
|
370
|
+
function formatTs(ts) {
|
|
371
|
+
const d = new Date(ts);
|
|
372
|
+
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}:${String(d.getSeconds()).padStart(2, "0")}.${String(d.getMilliseconds()).padStart(3, "0")}`;
|
|
373
|
+
}
|
|
374
|
+
__name(formatTs, "formatTs");
|
|
375
|
+
function AudioDebugPanel({ isActive }) {
|
|
376
|
+
const { events, clear, seekRate, syncIntervalMs, kindCounts } = useAudioEventLog(isActive);
|
|
377
|
+
const [debugMode, setDebugMode] = useState(getDebugAudioMode);
|
|
378
|
+
const handleSetDebugAudio = useCallback((mode) => {
|
|
379
|
+
if (mode === "off") {
|
|
380
|
+
localStorage.removeItem("DEBUG_AUDIO");
|
|
381
|
+
} else {
|
|
382
|
+
localStorage.setItem("DEBUG_AUDIO", mode === "verbose" ? "verbose" : "1");
|
|
383
|
+
}
|
|
384
|
+
setDebugMode(mode);
|
|
385
|
+
}, []);
|
|
386
|
+
const kindEntries = Object.entries(kindCounts);
|
|
387
|
+
const activeKinds = kindEntries.filter(([, count]) => count > 0);
|
|
388
|
+
const reversedEvents = [...events].reverse();
|
|
389
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
|
|
390
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 border-b border-border px-3 py-2 text-xs", children: [
|
|
391
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
392
|
+
"Sync interval:",
|
|
393
|
+
" ",
|
|
394
|
+
/* @__PURE__ */ jsx("span", { className: "text-foreground font-mono", children: syncIntervalMs !== null ? `${syncIntervalMs.toFixed(1)}ms` : "\u2014" })
|
|
395
|
+
] }),
|
|
396
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
397
|
+
"Seeks/s: ",
|
|
398
|
+
/* @__PURE__ */ jsx("span", { className: "text-foreground font-mono", children: seekRate })
|
|
399
|
+
] }),
|
|
400
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
401
|
+
"Total: ",
|
|
402
|
+
/* @__PURE__ */ jsx("span", { className: "text-foreground font-mono", children: events.length })
|
|
403
|
+
] }),
|
|
404
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-1 flex-wrap ml-auto", children: activeKinds.map(([kind, count]) => /* @__PURE__ */ jsxs(Badge, { variant: "outline", className: cn("text-[10px] px-1 py-0", KIND_STYLES[kind]?.className), children: [
|
|
405
|
+
KIND_STYLES[kind]?.label ?? kind,
|
|
406
|
+
" ",
|
|
407
|
+
count
|
|
408
|
+
] }, kind)) }),
|
|
409
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", className: "h-6 w-6 ml-1", onClick: clear, children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" }) })
|
|
410
|
+
] }),
|
|
411
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 border-b border-border px-3 py-1.5", children: [
|
|
412
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground mr-1", children: "DEBUG_AUDIO:" }),
|
|
413
|
+
DEBUG_MODES.map((mode) => /* @__PURE__ */ jsx(
|
|
414
|
+
"button",
|
|
415
|
+
{
|
|
416
|
+
type: "button",
|
|
417
|
+
className: cn(
|
|
418
|
+
"text-[10px] px-2 py-0.5 rounded border transition-colors",
|
|
419
|
+
debugMode === mode ? "border-primary bg-primary/10 text-primary" : "border-border hover:bg-muted text-muted-foreground"
|
|
420
|
+
),
|
|
421
|
+
onClick: () => handleSetDebugAudio(mode),
|
|
422
|
+
children: mode
|
|
423
|
+
},
|
|
424
|
+
mode
|
|
425
|
+
))
|
|
426
|
+
] }),
|
|
427
|
+
/* @__PURE__ */ jsx(ScrollArea, { className: "flex-1", children: reversedEvents.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-10 text-muted-foreground", children: [
|
|
428
|
+
/* @__PURE__ */ jsx(Radio, { className: "h-8 w-8 mb-2 opacity-40" }),
|
|
429
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm", children: "No audio events yet" }),
|
|
430
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs mt-1", children: [
|
|
431
|
+
"Wire ",
|
|
432
|
+
/* @__PURE__ */ jsx("code", { className: "text-xs", children: "emitAudioEvent()" }),
|
|
433
|
+
" into your audio engine"
|
|
434
|
+
] })
|
|
435
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "divide-y divide-border/30", children: reversedEvents.map((entry) => {
|
|
436
|
+
const style = KIND_STYLES[entry.kind] ?? { label: entry.kind, className: "text-foreground" };
|
|
437
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 px-3 py-1 text-xs", children: [
|
|
438
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-muted-foreground shrink-0 w-[88px]", children: formatTs(entry.ts) }),
|
|
439
|
+
/* @__PURE__ */ jsx("span", { className: cn("shrink-0 w-12 font-medium", style.className), children: style.label }),
|
|
440
|
+
entry.trackId && /* @__PURE__ */ jsx("span", { className: "shrink-0 font-mono text-muted-foreground", children: entry.trackId.slice(0, 6) }),
|
|
441
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-foreground/80", children: entry.msg })
|
|
442
|
+
] }, entry.id);
|
|
443
|
+
}) }) })
|
|
444
|
+
] });
|
|
445
|
+
}
|
|
446
|
+
__name(AudioDebugPanel, "AudioDebugPanel");
|
|
447
|
+
var BUILTIN_TABS = [
|
|
448
|
+
{ id: "logs", label: "Logs", icon: ScrollText },
|
|
449
|
+
{ id: "audio", label: "Audio", icon: Radio }
|
|
450
|
+
];
|
|
451
|
+
var POSITION_CLASSES = {
|
|
452
|
+
"bottom-right": "bottom-4 right-4",
|
|
453
|
+
"bottom-left": "bottom-4 left-4",
|
|
454
|
+
"top-right": "top-4 right-4",
|
|
455
|
+
"top-left": "top-4 left-4"
|
|
456
|
+
};
|
|
457
|
+
function DebugPanel({
|
|
458
|
+
tabs: customTabs = [],
|
|
459
|
+
position = "bottom-left",
|
|
460
|
+
defaultHeight = 480,
|
|
461
|
+
defaultWidth = 560
|
|
462
|
+
}) {
|
|
463
|
+
const isOpen = useDebugStore((s) => s.isOpen);
|
|
464
|
+
const activeTab = useDebugStore((s) => s.tab);
|
|
465
|
+
const setTab = useDebugStore((s) => s.setTab);
|
|
466
|
+
const close = useDebugStore((s) => s.close);
|
|
467
|
+
const errorCount = useDebugErrorCount();
|
|
468
|
+
const [isMinimized, setIsMinimized] = useState(false);
|
|
469
|
+
useEffect(() => {
|
|
470
|
+
const unsub = installMonitorBridge();
|
|
471
|
+
return () => unsub?.();
|
|
472
|
+
}, []);
|
|
473
|
+
const allTabs = useMemo(() => [
|
|
474
|
+
...BUILTIN_TABS,
|
|
475
|
+
...customTabs.map((t) => ({ id: t.id, label: t.label, icon: t.icon }))
|
|
476
|
+
], [customTabs]);
|
|
477
|
+
const panelHeight = isMinimized ? 48 : defaultHeight;
|
|
478
|
+
if (!isOpen) return null;
|
|
479
|
+
return /* @__PURE__ */ jsxs(
|
|
480
|
+
"div",
|
|
481
|
+
{
|
|
482
|
+
className: cn(
|
|
483
|
+
"fixed z-[100] flex flex-col overflow-hidden rounded-lg border border-border bg-background shadow-xl",
|
|
484
|
+
POSITION_CLASSES[position]
|
|
485
|
+
),
|
|
486
|
+
style: { width: defaultWidth, height: panelHeight },
|
|
487
|
+
children: [
|
|
488
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-border bg-muted/50 px-3 py-2 shrink-0", children: [
|
|
489
|
+
/* @__PURE__ */ jsx(Bug, { className: "h-4 w-4 text-primary" }),
|
|
490
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Debug" }),
|
|
491
|
+
errorCount > 0 && /* @__PURE__ */ jsxs(Badge, { variant: "destructive", className: "ml-1 text-[10px]", children: [
|
|
492
|
+
errorCount,
|
|
493
|
+
" errors"
|
|
494
|
+
] }),
|
|
495
|
+
!isMinimized && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5 ml-3", children: allTabs.map((tab) => {
|
|
496
|
+
const Icon = tab.icon;
|
|
497
|
+
const isActive = activeTab === tab.id;
|
|
498
|
+
return /* @__PURE__ */ jsxs(
|
|
499
|
+
"button",
|
|
500
|
+
{
|
|
501
|
+
type: "button",
|
|
502
|
+
onClick: () => setTab(tab.id),
|
|
503
|
+
className: cn(
|
|
504
|
+
"flex items-center gap-1.5 px-2 py-0.5 rounded text-xs transition-colors",
|
|
505
|
+
isActive ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground hover:bg-muted"
|
|
506
|
+
),
|
|
507
|
+
children: [
|
|
508
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-3 w-3" }),
|
|
509
|
+
tab.label
|
|
510
|
+
]
|
|
511
|
+
},
|
|
512
|
+
tab.id
|
|
513
|
+
);
|
|
514
|
+
}) }),
|
|
515
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 ml-auto", children: [
|
|
516
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
517
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", className: "h-6 w-6", onClick: () => setIsMinimized((v) => !v), children: isMinimized ? /* @__PURE__ */ jsx(Maximize2, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Minimize2, { className: "h-3.5 w-3.5" }) }) }),
|
|
518
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: isMinimized ? "Expand" : "Minimize" })
|
|
519
|
+
] }),
|
|
520
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
521
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", className: "h-6 w-6", onClick: close, children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" }) }) }),
|
|
522
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: "Close" })
|
|
523
|
+
] })
|
|
524
|
+
] })
|
|
525
|
+
] }),
|
|
526
|
+
!isMinimized && /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-hidden", children: [
|
|
527
|
+
activeTab === "logs" && /* @__PURE__ */ jsx(LogsPanel, { isActive: activeTab === "logs" }),
|
|
528
|
+
activeTab === "audio" && /* @__PURE__ */ jsx(AudioDebugPanel, { isActive: activeTab === "audio" }),
|
|
529
|
+
customTabs.map((tab) => {
|
|
530
|
+
const Panel = tab.panel;
|
|
531
|
+
return /* @__PURE__ */ jsx("div", { className: cn("h-full", activeTab !== tab.id && "hidden"), children: /* @__PURE__ */ jsx(Panel, { isActive: activeTab === tab.id }) }, tab.id);
|
|
532
|
+
})
|
|
533
|
+
] })
|
|
534
|
+
]
|
|
535
|
+
}
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
__name(DebugPanel, "DebugPanel");
|
|
539
|
+
function useEasterEgg(onTriggered) {
|
|
540
|
+
const clicks = useRef([]);
|
|
541
|
+
return useCallback(() => {
|
|
542
|
+
const now = Date.now();
|
|
543
|
+
clicks.current = [...clicks.current.filter((t) => now - t < 2e3), now];
|
|
544
|
+
if (clicks.current.length >= 5) {
|
|
545
|
+
clicks.current = [];
|
|
546
|
+
onTriggered();
|
|
547
|
+
}
|
|
548
|
+
}, [onTriggered]);
|
|
549
|
+
}
|
|
550
|
+
__name(useEasterEgg, "useEasterEgg");
|
|
551
|
+
function DebugButton({ className, panel = {} }) {
|
|
552
|
+
const isOpen = useDebugStore((s) => s.isOpen);
|
|
553
|
+
const isUnlocked = useDebugStore((s) => s.isUnlocked);
|
|
554
|
+
const unlock = useDebugStore((s) => s.unlock);
|
|
555
|
+
const toggle = useDebugStore((s) => s.toggle);
|
|
556
|
+
const errorCount = useDebugErrorCount();
|
|
557
|
+
const isDev = typeof process !== "undefined" && true;
|
|
558
|
+
useDebugShortcut();
|
|
559
|
+
useEffect(() => {
|
|
560
|
+
if (typeof document === "undefined") return;
|
|
561
|
+
const apply = /* @__PURE__ */ __name(() => {
|
|
562
|
+
document.querySelectorAll("nextjs-portal").forEach((el) => {
|
|
563
|
+
el.style.display = isOpen ? "none" : "";
|
|
564
|
+
});
|
|
565
|
+
}, "apply");
|
|
566
|
+
apply();
|
|
567
|
+
const observer = new MutationObserver(apply);
|
|
568
|
+
observer.observe(document.body, { childList: true });
|
|
569
|
+
return () => observer.disconnect();
|
|
570
|
+
}, [isOpen]);
|
|
571
|
+
useEffect(() => {
|
|
572
|
+
if (typeof window === "undefined") return;
|
|
573
|
+
const params = new URLSearchParams(window.location.search);
|
|
574
|
+
const key = params.get("debug");
|
|
575
|
+
if (key) {
|
|
576
|
+
unlock(key);
|
|
577
|
+
params.delete("debug");
|
|
578
|
+
const newUrl = `${window.location.pathname}${params.toString() ? `?${params.toString()}` : ""}${window.location.hash}`;
|
|
579
|
+
window.history.replaceState(null, "", newUrl);
|
|
580
|
+
}
|
|
581
|
+
}, [unlock]);
|
|
582
|
+
const handleEasterEggClick = useCallback(() => {
|
|
583
|
+
unlock("1");
|
|
584
|
+
toggle();
|
|
585
|
+
}, [unlock, toggle]);
|
|
586
|
+
const handleEasterEggTrigger = useEasterEgg(handleEasterEggClick);
|
|
587
|
+
if (!isDev && !isUnlocked) {
|
|
588
|
+
return /* @__PURE__ */ jsx(
|
|
589
|
+
"div",
|
|
590
|
+
{
|
|
591
|
+
className: "fixed bottom-5 left-5 w-9 h-9 z-[99999] opacity-0",
|
|
592
|
+
onClick: handleEasterEggTrigger
|
|
593
|
+
}
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
597
|
+
!isOpen && /* @__PURE__ */ jsx("div", { className: "fixed bottom-5 left-16 z-[99999]", children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
598
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
599
|
+
"button",
|
|
600
|
+
{
|
|
601
|
+
type: "button",
|
|
602
|
+
className: cn(
|
|
603
|
+
"relative w-9 h-9 flex items-center justify-center rounded-full",
|
|
604
|
+
"bg-black/80 backdrop-blur-xl",
|
|
605
|
+
"shadow-[0_0_0_1px_#171717,inset_0_0_0_1px_hsla(0,0%,100%,0.14),0px_16px_32px_-8px_rgba(0,0,0,0.24)]",
|
|
606
|
+
"transition-all duration-150 hover:scale-105",
|
|
607
|
+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-white/50",
|
|
608
|
+
errorCount > 0 && "bg-red-600/90",
|
|
609
|
+
className
|
|
610
|
+
),
|
|
611
|
+
onClick: toggle,
|
|
612
|
+
children: [
|
|
613
|
+
/* @__PURE__ */ jsx(Bug, { className: cn("h-4 w-4", errorCount > 0 ? "text-white" : "text-white/80") }),
|
|
614
|
+
errorCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute -right-1 -top-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-white text-[10px] font-semibold text-red-600 shadow-sm", children: errorCount })
|
|
615
|
+
]
|
|
616
|
+
}
|
|
617
|
+
) }),
|
|
618
|
+
/* @__PURE__ */ jsxs(TooltipContent, { side: "top", children: [
|
|
619
|
+
"Debug Panel ",
|
|
620
|
+
/* @__PURE__ */ jsx("kbd", { className: "ml-1 text-[10px] opacity-60", children: "\u2318D" })
|
|
621
|
+
] })
|
|
622
|
+
] }) }),
|
|
623
|
+
/* @__PURE__ */ jsx(DebugPanel, { ...panel })
|
|
624
|
+
] });
|
|
625
|
+
}
|
|
626
|
+
__name(DebugButton, "DebugButton");
|
|
627
|
+
function shallowEqual(a, b) {
|
|
628
|
+
if (a === b) return true;
|
|
629
|
+
if (typeof a !== "object" || typeof b !== "object") return false;
|
|
630
|
+
if (a === null || b === null) return false;
|
|
631
|
+
const keysA = Object.keys(a);
|
|
632
|
+
const keysB = Object.keys(b);
|
|
633
|
+
if (keysA.length !== keysB.length) return false;
|
|
634
|
+
for (const key of keysA) {
|
|
635
|
+
if (a[key] !== b[key]) return false;
|
|
636
|
+
}
|
|
637
|
+
return true;
|
|
638
|
+
}
|
|
639
|
+
__name(shallowEqual, "shallowEqual");
|
|
640
|
+
function useStoreSnapshot(getState, intervalMs = 200, active = true) {
|
|
641
|
+
const getStateRef = useRef(getState);
|
|
642
|
+
getStateRef.current = getState;
|
|
643
|
+
const [snapshot, setSnapshot] = useState(() => getState());
|
|
644
|
+
useEffect(() => {
|
|
645
|
+
if (!active) return;
|
|
646
|
+
const id = setInterval(() => {
|
|
647
|
+
const next = getStateRef.current();
|
|
648
|
+
setSnapshot((prev) => shallowEqual(prev, next) ? prev : next);
|
|
649
|
+
}, intervalMs);
|
|
650
|
+
return () => clearInterval(id);
|
|
651
|
+
}, [active, intervalMs]);
|
|
652
|
+
return snapshot;
|
|
653
|
+
}
|
|
654
|
+
__name(useStoreSnapshot, "useStoreSnapshot");
|
|
655
|
+
function StorePanel({ label, getState, intervalMs = 200, isActive }) {
|
|
656
|
+
const snapshot = useStoreSnapshot(getState, intervalMs, isActive);
|
|
657
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
|
|
658
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-border px-3 py-2 shrink-0", children: [
|
|
659
|
+
/* @__PURE__ */ jsx(Database, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
660
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: label }),
|
|
661
|
+
/* @__PURE__ */ jsxs("span", { className: "ml-auto text-[10px] text-muted-foreground/60", children: [
|
|
662
|
+
"polling ",
|
|
663
|
+
intervalMs,
|
|
664
|
+
"ms"
|
|
665
|
+
] })
|
|
666
|
+
] }),
|
|
667
|
+
/* @__PURE__ */ jsx(ScrollArea, { className: "flex-1", children: /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsx(LazyJsonTree, { data: snapshot, mode: "full" }) }) })
|
|
668
|
+
] });
|
|
669
|
+
}
|
|
670
|
+
__name(StorePanel, "StorePanel");
|
|
671
|
+
var MAX_EVENTS2 = 300;
|
|
672
|
+
function useCustomEventLog(active, channel) {
|
|
673
|
+
const [events, setEvents] = useState([]);
|
|
674
|
+
const counterRef = useRef(0);
|
|
675
|
+
const clear = useCallback(() => setEvents([]), []);
|
|
676
|
+
useEffect(() => {
|
|
677
|
+
if (!active) return;
|
|
678
|
+
const unsubscribe = subscribeCustomEvents((event) => {
|
|
679
|
+
if (channel && event.channel !== channel) return;
|
|
680
|
+
const entry = { ...event, id: String(++counterRef.current) };
|
|
681
|
+
setEvents((prev) => {
|
|
682
|
+
const next = [...prev, entry];
|
|
683
|
+
return next.length > MAX_EVENTS2 ? next.slice(-MAX_EVENTS2) : next;
|
|
684
|
+
});
|
|
685
|
+
});
|
|
686
|
+
return unsubscribe;
|
|
687
|
+
}, [active, channel]);
|
|
688
|
+
return { events, clear };
|
|
689
|
+
}
|
|
690
|
+
__name(useCustomEventLog, "useCustomEventLog");
|
|
691
|
+
|
|
692
|
+
export { AudioDebugPanel, DebugButton, DebugPanel, LogsPanel, StorePanel, installMonitorBridge, useAudioEventLog, useCustomEventLog, useDebugShortcut, useDebugStore, useStoreSnapshot };
|
|
693
|
+
//# sourceMappingURL=index.mjs.map
|
|
694
|
+
//# sourceMappingURL=index.mjs.map
|