@djangocfg/debuger 2.1.234 → 2.1.235
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/dist/index.cjs +20 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +21 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
- package/src/DebugButton.tsx +44 -49
package/src/DebugButton.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
TooltipContent,
|
|
8
8
|
TooltipTrigger,
|
|
9
9
|
} from '@djangocfg/ui-core/components';
|
|
10
|
-
import { cn, isDev
|
|
10
|
+
import { cn, isDev } from '@djangocfg/ui-core/lib';
|
|
11
11
|
import { useDebugMode } from '@djangocfg/monitor/client';
|
|
12
12
|
import { useDebugStore } from './store/debugStore';
|
|
13
13
|
import { useDebugShortcut } from './hooks/useDebugShortcut';
|
|
@@ -23,90 +23,86 @@ export interface DebugButtonProps {
|
|
|
23
23
|
className?: string;
|
|
24
24
|
/** Props forwarded to DebugPanel */
|
|
25
25
|
panel?: Omit<DebugPanelProps, never>;
|
|
26
|
-
/**
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// ============================================================================
|
|
31
|
-
// Easter-egg: 5 clicks in 2 seconds → unlock
|
|
32
|
-
// ============================================================================
|
|
33
|
-
|
|
34
|
-
function useEasterEgg(onTriggered: () => void) {
|
|
35
|
-
const clicks = useRef<number[]>([]);
|
|
36
|
-
|
|
37
|
-
return useCallback(() => {
|
|
38
|
-
const now = Date.now();
|
|
39
|
-
clicks.current = [...clicks.current.filter((t) => now - t < 2000), now];
|
|
40
|
-
if (clicks.current.length >= 5) {
|
|
41
|
-
clicks.current = [];
|
|
42
|
-
onTriggered();
|
|
43
|
-
}
|
|
44
|
-
}, [onTriggered]);
|
|
26
|
+
/** Explicitly disable. Default: undefined (auto — visible in dev, hidden in prod, ?debug=1 always works) */
|
|
27
|
+
enabled?: boolean;
|
|
45
28
|
}
|
|
46
29
|
|
|
47
30
|
// ============================================================================
|
|
48
31
|
// DebugButton
|
|
32
|
+
//
|
|
33
|
+
// Visibility rules (simple):
|
|
34
|
+
// 1. enabled === false AND no ?debug=1 → hidden completely
|
|
35
|
+
// 2. isDev OR ?debug=1 (localStorage) → visible (unlocked)
|
|
36
|
+
// 3. prod without ?debug=1 → invisible click trap (easter egg: 5 clicks)
|
|
49
37
|
// ============================================================================
|
|
50
38
|
|
|
51
|
-
export function DebugButton({ className, panel = {},
|
|
39
|
+
export function DebugButton({ className, panel = {}, enabled }: DebugButtonProps) {
|
|
40
|
+
// All hooks called unconditionally (React rules of hooks)
|
|
52
41
|
const isOpen = useDebugStore((s) => s.isOpen);
|
|
53
42
|
const isUnlocked = useDebugStore((s) => s.isUnlocked);
|
|
54
43
|
const unlock = useDebugStore((s) => s.unlock);
|
|
55
44
|
const toggle = useDebugStore((s) => s.toggle);
|
|
56
|
-
|
|
57
45
|
const isDebugMode = useDebugMode();
|
|
58
|
-
|
|
59
|
-
// Auto-unlock when defaultUnlocked or debug mode is active
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
if (defaultUnlocked || isDebugMode) unlock('1');
|
|
62
|
-
}, [defaultUnlocked, isDebugMode, unlock]);
|
|
63
46
|
const errorCount = useDebugErrorCount();
|
|
47
|
+
useDebugShortcut();
|
|
64
48
|
|
|
65
|
-
//
|
|
66
|
-
|
|
49
|
+
// Should this button be visible (unlocked)?
|
|
50
|
+
const shouldUnlock = isDev || isDebugMode;
|
|
67
51
|
|
|
68
|
-
//
|
|
69
|
-
|
|
52
|
+
// eslint-disable-next-line no-console
|
|
53
|
+
if (typeof window !== 'undefined') console.info('[DebugButton]', { isDev, isDebugMode, shouldUnlock, isUnlocked, enabled });
|
|
70
54
|
|
|
71
|
-
//
|
|
72
|
-
// Uses MutationObserver because <nextjs-portal> is mounted asynchronously after hydration.
|
|
55
|
+
// Auto-unlock in dev or when ?debug=1
|
|
73
56
|
useEffect(() => {
|
|
74
|
-
if (
|
|
57
|
+
if (shouldUnlock) unlock('1');
|
|
58
|
+
}, [shouldUnlock, unlock]);
|
|
75
59
|
|
|
60
|
+
// Hide Next.js dev indicator while panel is open
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (typeof document === 'undefined') return;
|
|
76
63
|
const apply = () => {
|
|
77
64
|
document.querySelectorAll<HTMLElement>('nextjs-portal').forEach((el) => {
|
|
78
65
|
el.style.display = isOpen ? 'none' : '';
|
|
79
66
|
});
|
|
80
67
|
};
|
|
81
|
-
|
|
82
|
-
apply(); // handle already-mounted elements
|
|
83
|
-
|
|
68
|
+
apply();
|
|
84
69
|
const observer = new MutationObserver(apply);
|
|
85
70
|
observer.observe(document.body, { childList: true });
|
|
86
|
-
|
|
87
71
|
return () => observer.disconnect();
|
|
88
72
|
}, [isOpen]);
|
|
89
73
|
|
|
90
|
-
// Easter
|
|
91
|
-
const
|
|
92
|
-
const
|
|
74
|
+
// Easter egg: 5 clicks in 2s → unlock
|
|
75
|
+
const clicks = useRef<number[]>([]);
|
|
76
|
+
const handleEasterEgg = useCallback(() => {
|
|
77
|
+
const now = Date.now();
|
|
78
|
+
clicks.current = [...clicks.current.filter((t) => now - t < 2000), now];
|
|
79
|
+
if (clicks.current.length >= 5) {
|
|
80
|
+
clicks.current = [];
|
|
81
|
+
unlock('1');
|
|
82
|
+
toggle();
|
|
83
|
+
}
|
|
84
|
+
}, [unlock, toggle]);
|
|
85
|
+
|
|
86
|
+
// === Render decision (after all hooks) ===
|
|
93
87
|
|
|
94
|
-
//
|
|
95
|
-
|
|
88
|
+
// Explicitly disabled AND no debug override → don't render at all
|
|
89
|
+
if (enabled === false && !isDebugMode) return null;
|
|
90
|
+
|
|
91
|
+
// Not unlocked → invisible click trap for easter egg
|
|
96
92
|
if (!isUnlocked) {
|
|
97
93
|
return (
|
|
98
94
|
<div
|
|
99
|
-
className="fixed bottom-5 left-5 w-9 h-9 z-[99999] opacity-0"
|
|
100
|
-
onClick={
|
|
95
|
+
className="fixed bottom-5 left-5 w-9 h-9 z-[99999] opacity-0 cursor-default"
|
|
96
|
+
onClick={handleEasterEgg}
|
|
101
97
|
/>
|
|
102
98
|
);
|
|
103
99
|
}
|
|
104
100
|
|
|
101
|
+
// Unlocked → visible debug button
|
|
105
102
|
return (
|
|
106
103
|
<>
|
|
107
|
-
{/* Trigger button — hidden when panel is open */}
|
|
108
104
|
{!isOpen && (
|
|
109
|
-
<div className=
|
|
105
|
+
<div className={cn('fixed bottom-5 z-[99999]', isDev ? 'left-16' : 'left-5')}>
|
|
110
106
|
<Tooltip>
|
|
111
107
|
<TooltipTrigger asChild>
|
|
112
108
|
<button
|
|
@@ -118,7 +114,7 @@ export function DebugButton({ className, panel = {}, defaultUnlocked = isDev }:
|
|
|
118
114
|
'transition-all duration-150 hover:scale-105',
|
|
119
115
|
'focus:outline-none focus-visible:ring-2 focus-visible:ring-white/50',
|
|
120
116
|
errorCount > 0 && 'bg-red-600/90',
|
|
121
|
-
className
|
|
117
|
+
className,
|
|
122
118
|
)}
|
|
123
119
|
onClick={toggle}
|
|
124
120
|
>
|
|
@@ -134,7 +130,6 @@ export function DebugButton({ className, panel = {}, defaultUnlocked = isDev }:
|
|
|
134
130
|
</Tooltip>
|
|
135
131
|
</div>
|
|
136
132
|
)}
|
|
137
|
-
|
|
138
133
|
<DebugPanel {...panel} />
|
|
139
134
|
</>
|
|
140
135
|
);
|