@versini/ui-debug-overlay 2.0.1 → 2.1.1
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.d.ts +133 -133
- package/dist/index.js +544 -18
- package/package.json +9 -9
- package/dist/components/DebugOverlay/DebugOverlay.js +0 -350
package/dist/index.d.ts
CHANGED
|
@@ -1,133 +1,133 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
declare const LOG_GREEN: "LOG_GREEN";
|
|
93
|
-
|
|
94
|
-
declare const LOG_MAGENTA: "LOG_MAGENTA";
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
*
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* debug
|
|
114
|
-
* debug("
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
* -
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
* `targetOverlays`
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
declare
|
|
132
|
-
|
|
133
|
-
export {
|
|
1
|
+
import { JSX } from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test-only helper (not part of public API). Enables isolation across tests.
|
|
5
|
+
* Exported unconditionally; consumer code should not rely on it.
|
|
6
|
+
*/
|
|
7
|
+
export declare function __resetDebugOverlayTestState(): void;
|
|
8
|
+
|
|
9
|
+
export declare interface DebugFnOptions {
|
|
10
|
+
color?: LogColor;
|
|
11
|
+
targetOverlays?: string[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export declare const DebugOverlay: ({ appState, title, initialCollapsed, overlayId, position, maxBodyHeight, indent, maxSnapshots, maxVisibleSnapshots, snapshotOrder, appendSnapshotCountInTitle, }: DebugOverlayProps) => JSX.Element;
|
|
15
|
+
|
|
16
|
+
export declare type DebugOverlayProps = {
|
|
17
|
+
/**
|
|
18
|
+
* Optional application state to display. If omitted, only manual snapshots
|
|
19
|
+
* appear.
|
|
20
|
+
*/
|
|
21
|
+
appState?: unknown;
|
|
22
|
+
/**
|
|
23
|
+
* Short label shown in the header.
|
|
24
|
+
* @default "AppState"
|
|
25
|
+
*/
|
|
26
|
+
title?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Start collapsed (header only).
|
|
29
|
+
* @default false
|
|
30
|
+
*/
|
|
31
|
+
initialCollapsed?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Identifier for manual snapshot targeting..
|
|
34
|
+
* @default "default"
|
|
35
|
+
*/
|
|
36
|
+
overlayId?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Panel positioning.
|
|
39
|
+
*
|
|
40
|
+
* You can either provide a partial style object with any of the four edges
|
|
41
|
+
* (top/right/bottom/left) OR use a shorthand keyword. Shorthand avoids having
|
|
42
|
+
* to pass an object and also prevents the default (top/right) edges from being
|
|
43
|
+
* applied when the intent is a left anchor.
|
|
44
|
+
*
|
|
45
|
+
* Supported shorthands:
|
|
46
|
+
* - "left" (alias of "top-left")
|
|
47
|
+
* - "right" (alias of "top-right")
|
|
48
|
+
* - "top", "bottom"
|
|
49
|
+
* - "top-left", "top-right", "bottom-left", "bottom-right"
|
|
50
|
+
*
|
|
51
|
+
*/
|
|
52
|
+
position?: Partial<Pick<CSSStyleDeclaration, "top" | "right" | "bottom" | "left">> | "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
|
53
|
+
/**
|
|
54
|
+
* Maximum body height before scrolling (px number or CSS length e.g. "100vh").
|
|
55
|
+
* @default "50svh"
|
|
56
|
+
*/
|
|
57
|
+
maxBodyHeight?: number | string;
|
|
58
|
+
/**
|
|
59
|
+
* Pretty print spacing for JSON output.
|
|
60
|
+
* @default 2
|
|
61
|
+
*/
|
|
62
|
+
indent?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Maximum snapshots retained in memory (FIFO)..
|
|
65
|
+
* @default 50
|
|
66
|
+
*/
|
|
67
|
+
maxSnapshots?: number;
|
|
68
|
+
/**
|
|
69
|
+
* Maximum snapshots shown without scrolling.
|
|
70
|
+
* @default 10
|
|
71
|
+
*/
|
|
72
|
+
maxVisibleSnapshots?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Visual ordering of snapshots in the panel.
|
|
75
|
+
* - "desc": newest snapshot at the top.
|
|
76
|
+
* - "asc": oldest snapshot at the top.
|
|
77
|
+
* Numbering ALWAYS reflects chronological order with #1 being the oldest
|
|
78
|
+
* snapshot regardless of display order.
|
|
79
|
+
* @default "desc"
|
|
80
|
+
*/
|
|
81
|
+
snapshotOrder?: "asc" | "desc";
|
|
82
|
+
/**
|
|
83
|
+
* When true, appends the current snapshot count to the title (e.g. "AppState
|
|
84
|
+
* (5)").
|
|
85
|
+
* @default false
|
|
86
|
+
*/
|
|
87
|
+
appendSnapshotCountInTitle?: boolean;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export declare const LOG_BLUE: "LOG_BLUE";
|
|
91
|
+
|
|
92
|
+
export declare const LOG_GREEN: "LOG_GREEN";
|
|
93
|
+
|
|
94
|
+
export declare const LOG_MAGENTA: "LOG_MAGENTA";
|
|
95
|
+
|
|
96
|
+
export declare const LOG_RED: "LOG_RED";
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Manual snapshot input now requires an explicit label for clarity; we remove
|
|
100
|
+
* the previous flexible single-argument signature to keep debug calls
|
|
101
|
+
* unambiguous (always: label, data).
|
|
102
|
+
*/
|
|
103
|
+
export declare const LOG_YELLOW: "LOG_YELLOW";
|
|
104
|
+
|
|
105
|
+
export declare type LogColor = typeof LOG_YELLOW | typeof LOG_GREEN | typeof LOG_BLUE | typeof LOG_MAGENTA | typeof LOG_RED;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* React hook returning a function to push a manual snapshot into all (or
|
|
109
|
+
* targeted) mounted `DebugOverlay` instances.
|
|
110
|
+
*
|
|
111
|
+
* Usage:
|
|
112
|
+
* ```js
|
|
113
|
+
* const debug = useDebugOverlay();
|
|
114
|
+
* debug("loaded", { user, meta }, LOG_GREEN);
|
|
115
|
+
* debug("error", errorObj, { color: LOG_RED, targetOverlays: ["errors"] });
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
118
|
+
* Parameters when invoking the returned function:
|
|
119
|
+
* - label: short string identifying the snapshot category.
|
|
120
|
+
* - data: any serializable (or even cyclic) value; cyclic structures are
|
|
121
|
+
* replaced with "[Circular]" markers.
|
|
122
|
+
* - options: either a LogColor constant OR an object with `color` and
|
|
123
|
+
* optional `targetOverlays` (array of overlayId strings). When
|
|
124
|
+
* `targetOverlays` is omitted or empty, the snapshot broadcasts to
|
|
125
|
+
* every mounted overlay.
|
|
126
|
+
*
|
|
127
|
+
* The hook itself has no dependencies and returns a stable callback identity
|
|
128
|
+
* for the component lifetime, keeping re-renders minimal.
|
|
129
|
+
*
|
|
130
|
+
*/
|
|
131
|
+
export declare function useDebugOverlay(): (label: string, data: unknown, options?: LogColor | DebugFnOptions) => void;
|
|
132
|
+
|
|
133
|
+
export { }
|
package/dist/index.js
CHANGED
|
@@ -1,24 +1,550 @@
|
|
|
1
|
-
import { DebugOverlay as r, LOG_BLUE as i, LOG_GREEN as t, LOG_MAGENTA as O, LOG_RED as o, LOG_YELLOW as L, __resetDebugOverlayTestState as G, useDebugOverlay as s } from "./components/DebugOverlay/DebugOverlay.js";
|
|
2
1
|
/*!
|
|
3
|
-
@versini/ui-debug-overlay v2.
|
|
2
|
+
@versini/ui-debug-overlay v2.1.1
|
|
4
3
|
© 2025 gizmette.com
|
|
5
4
|
*/
|
|
6
5
|
try {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
6
|
+
if (!window.__VERSINI_UI_DEBUG_OVERLAY__) {
|
|
7
|
+
window.__VERSINI_UI_DEBUG_OVERLAY__ = {
|
|
8
|
+
version: "2.1.1",
|
|
9
|
+
buildTime: "12/24/2025 09:21 AM EST",
|
|
10
|
+
homepage: "https://www.npmjs.com/package/@versini/ui-debug-overlay",
|
|
11
|
+
license: "MIT",
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
} catch (error) {
|
|
15
|
+
// nothing to declare officer
|
|
14
16
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
|
|
18
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
19
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
20
|
+
|
|
21
|
+
;// CONCATENATED MODULE: external "react/jsx-runtime"
|
|
22
|
+
|
|
23
|
+
;// CONCATENATED MODULE: external "react"
|
|
24
|
+
|
|
25
|
+
;// CONCATENATED MODULE: ./src/components/DebugOverlay/DebugOverlay.tsx
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Manual snapshot input now requires an explicit label for clarity; we remove
|
|
30
|
+
* the previous flexible single-argument signature to keep debug calls
|
|
31
|
+
* unambiguous (always: label, data).
|
|
32
|
+
*/ const LOG_YELLOW = "LOG_YELLOW";
|
|
33
|
+
const LOG_GREEN = "LOG_GREEN";
|
|
34
|
+
const LOG_BLUE = "LOG_BLUE";
|
|
35
|
+
const LOG_MAGENTA = "LOG_MAGENTA";
|
|
36
|
+
const LOG_RED = "LOG_RED";
|
|
37
|
+
const MANUAL_BUFFER_LIMIT = 200;
|
|
38
|
+
let manualSeqCounter = 0; // shared seq for manual + state via helper
|
|
39
|
+
const manualBuffer = [];
|
|
40
|
+
const manualSubscribers = new Set();
|
|
41
|
+
/**
|
|
42
|
+
* Test-only helper (not part of public API). Enables isolation across tests.
|
|
43
|
+
* Exported unconditionally; consumer code should not rely on it.
|
|
44
|
+
*/ function __resetDebugOverlayTestState() {
|
|
45
|
+
manualBuffer.length = 0;
|
|
46
|
+
manualSeqCounter = 0;
|
|
47
|
+
}
|
|
48
|
+
function nextSeq() {
|
|
49
|
+
return manualSeqCounter++;
|
|
50
|
+
}
|
|
51
|
+
function pushDebugSnapshot(input) {
|
|
52
|
+
const snap = {
|
|
53
|
+
kind: "manual",
|
|
54
|
+
ts: Date.now(),
|
|
55
|
+
seq: nextSeq(),
|
|
56
|
+
label: input.label,
|
|
57
|
+
data: input.data,
|
|
58
|
+
color: input.color,
|
|
59
|
+
targetOverlays: input.targetOverlays && input.targetOverlays.length ? [
|
|
60
|
+
...input.targetOverlays
|
|
61
|
+
] : undefined
|
|
62
|
+
};
|
|
63
|
+
manualBuffer.push(snap);
|
|
64
|
+
/* v8 ignore start - buffer overflow edge case */ if (manualBuffer.length > MANUAL_BUFFER_LIMIT) {
|
|
65
|
+
manualBuffer.shift();
|
|
66
|
+
}
|
|
67
|
+
/* v8 ignore stop */ manualSubscribers.forEach((cb)=>cb(snap));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* React hook returning a function to push a manual snapshot into all (or
|
|
71
|
+
* targeted) mounted `DebugOverlay` instances.
|
|
72
|
+
*
|
|
73
|
+
* Usage:
|
|
74
|
+
* ```js
|
|
75
|
+
* const debug = useDebugOverlay();
|
|
76
|
+
* debug("loaded", { user, meta }, LOG_GREEN);
|
|
77
|
+
* debug("error", errorObj, { color: LOG_RED, targetOverlays: ["errors"] });
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* Parameters when invoking the returned function:
|
|
81
|
+
* - label: short string identifying the snapshot category.
|
|
82
|
+
* - data: any serializable (or even cyclic) value; cyclic structures are
|
|
83
|
+
* replaced with "[Circular]" markers.
|
|
84
|
+
* - options: either a LogColor constant OR an object with `color` and
|
|
85
|
+
* optional `targetOverlays` (array of overlayId strings). When
|
|
86
|
+
* `targetOverlays` is omitted or empty, the snapshot broadcasts to
|
|
87
|
+
* every mounted overlay.
|
|
88
|
+
*
|
|
89
|
+
* The hook itself has no dependencies and returns a stable callback identity
|
|
90
|
+
* for the component lifetime, keeping re-renders minimal.
|
|
91
|
+
*
|
|
92
|
+
*/ function useDebugOverlay() {
|
|
93
|
+
return useCallback((label, data, options)=>{
|
|
94
|
+
if (typeof options === "string") {
|
|
95
|
+
pushDebugSnapshot({
|
|
96
|
+
label,
|
|
97
|
+
data,
|
|
98
|
+
color: options
|
|
99
|
+
});
|
|
100
|
+
} else {
|
|
101
|
+
pushDebugSnapshot({
|
|
102
|
+
label,
|
|
103
|
+
data,
|
|
104
|
+
color: options?.color,
|
|
105
|
+
targetOverlays: options?.targetOverlays
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}, []);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Cycle‑safe, deterministic JSON stringify (orders object keys).
|
|
112
|
+
*/ function safeStringify(value, space = 2) {
|
|
113
|
+
const seen = new WeakSet();
|
|
114
|
+
const sortKeys = (val)=>{
|
|
115
|
+
/* v8 ignore start - edge cases for special object types */ if (!val || typeof val !== "object" || val instanceof Date || val instanceof RegExp) {
|
|
116
|
+
return val;
|
|
117
|
+
}
|
|
118
|
+
if (seen.has(val)) {
|
|
119
|
+
return "[Circular]";
|
|
120
|
+
}
|
|
121
|
+
/* v8 ignore stop */ seen.add(val);
|
|
122
|
+
/* v8 ignore start - array handling edge case */ if (Array.isArray(val)) {
|
|
123
|
+
return val.map(sortKeys);
|
|
124
|
+
}
|
|
125
|
+
/* v8 ignore stop */ const out = {};
|
|
126
|
+
for (const k of Object.keys(val).sort()){
|
|
127
|
+
out[k] = sortKeys(val[k]);
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
};
|
|
131
|
+
try {
|
|
132
|
+
return JSON.stringify(sortKeys(value), null, space);
|
|
133
|
+
/* v8 ignore start - error handling for unserializable values */ } catch (err) {
|
|
134
|
+
return `<<unserializable: ${err.message}>>`;
|
|
135
|
+
}
|
|
136
|
+
/* v8 ignore stop */ }
|
|
137
|
+
const DebugOverlay = ({ appState, title = "AppState", initialCollapsed = false, overlayId = "default", position, maxBodyHeight = "50svh", indent = 2, maxSnapshots = 50, maxVisibleSnapshots = 10, snapshotOrder = "desc", appendSnapshotCountInTitle = false })=>{
|
|
138
|
+
const [collapsed, setCollapsed] = useState(initialCollapsed);
|
|
139
|
+
const [copied, setCopied] = useState(false);
|
|
140
|
+
const containerRef = useRef(null);
|
|
141
|
+
const [snapshots, setSnapshots] = useState([]);
|
|
142
|
+
/* v8 ignore start - initial buffer filter edge case */ const [manualSnaps, setManualSnaps] = useState(()=>// Apply same targeting filter to initial buffered snapshots as we do for
|
|
143
|
+
// live subscription events so targeted snapshots don't leak to unrelated
|
|
144
|
+
// overlays.
|
|
145
|
+
manualBuffer.filter((m)=>!m.targetOverlays || m.targetOverlays.length === 0 || m.targetOverlays.includes(overlayId)));
|
|
146
|
+
/* v8 ignore stop */ // Serialize state only if provided; overlay can operate in manual-only mode.
|
|
147
|
+
const json = useMemo(()=>appState !== undefined ? safeStringify(appState, indent) : null, [
|
|
148
|
+
appState,
|
|
149
|
+
indent
|
|
150
|
+
]);
|
|
151
|
+
// Capture snapshot when appState changes (referential).
|
|
152
|
+
useEffect(()=>{
|
|
153
|
+
if (json == null) {
|
|
154
|
+
return; // no appState provided: skip automatic state snapshotting
|
|
155
|
+
}
|
|
156
|
+
setSnapshots((prev)=>[
|
|
157
|
+
{
|
|
158
|
+
ts: Date.now(),
|
|
159
|
+
json
|
|
160
|
+
},
|
|
161
|
+
...prev
|
|
162
|
+
].slice(0, maxSnapshots));
|
|
163
|
+
}, [
|
|
164
|
+
json,
|
|
165
|
+
maxSnapshots
|
|
166
|
+
]);
|
|
167
|
+
// Subscribe to manual snapshots (external pushes).
|
|
168
|
+
useEffect(()=>{
|
|
169
|
+
const handler = (s)=>{
|
|
170
|
+
/**
|
|
171
|
+
* Filter by overlay target: include if broadcast (no targetOverlays) or if
|
|
172
|
+
* overlayId present in targetOverlays.
|
|
173
|
+
*/ if (!s.targetOverlays || s.targetOverlays.length === 0 || s.targetOverlays.includes(overlayId)) {
|
|
174
|
+
setManualSnaps((prev)=>[
|
|
175
|
+
...prev,
|
|
176
|
+
s
|
|
177
|
+
]);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
manualSubscribers.add(handler);
|
|
181
|
+
return ()=>{
|
|
182
|
+
manualSubscribers.delete(handler);
|
|
183
|
+
};
|
|
184
|
+
}, [
|
|
185
|
+
overlayId
|
|
186
|
+
]);
|
|
187
|
+
// Build unified snapshot list.
|
|
188
|
+
const unifiedAsc = useMemo(()=>{
|
|
189
|
+
/**
|
|
190
|
+
* State snapshots: `snapshots` is newest-first; convert to ascending & add
|
|
191
|
+
* seq.
|
|
192
|
+
*/ const stateAsc = [
|
|
193
|
+
...snapshots
|
|
194
|
+
].reverse().map((s)=>({
|
|
195
|
+
kind: "state",
|
|
196
|
+
ts: s.ts,
|
|
197
|
+
json: s.json,
|
|
198
|
+
seq: nextSeq()
|
|
199
|
+
}));
|
|
200
|
+
/**
|
|
201
|
+
* Manual snapshots already oldest-first in manualBuffer/ manualSnaps; but we
|
|
202
|
+
* rely on local state manualSnaps (which reflects buffer at mount + pushes).
|
|
203
|
+
*/ const manualAsc = manualSnaps.map((m)=>({
|
|
204
|
+
kind: "manual",
|
|
205
|
+
ts: m.ts,
|
|
206
|
+
seq: m.seq,
|
|
207
|
+
label: m.label,
|
|
208
|
+
color: m.color,
|
|
209
|
+
json: safeStringify(m.data, indent)
|
|
210
|
+
}));
|
|
211
|
+
/**
|
|
212
|
+
* Merge two ascending arrays by time then seq. Since sizes are small, a
|
|
213
|
+
* simple concat + sort is fine (keeps code simpler than manual merge);
|
|
214
|
+
* optimize if needed.
|
|
215
|
+
*/ const merged = [
|
|
216
|
+
...stateAsc,
|
|
217
|
+
...manualAsc
|
|
218
|
+
];
|
|
219
|
+
merged.sort((a, b)=>a.ts === b.ts ? a.seq - b.seq : a.ts - b.ts);
|
|
220
|
+
return merged;
|
|
221
|
+
}, [
|
|
222
|
+
snapshots,
|
|
223
|
+
manualSnaps,
|
|
224
|
+
indent
|
|
225
|
+
]);
|
|
226
|
+
const displayList = useMemo(()=>snapshotOrder === "asc" ? unifiedAsc : [
|
|
227
|
+
...unifiedAsc
|
|
228
|
+
].reverse(), [
|
|
229
|
+
snapshotOrder,
|
|
230
|
+
unifiedAsc
|
|
231
|
+
]);
|
|
232
|
+
const copy = useCallback(()=>{
|
|
233
|
+
const lines = [];
|
|
234
|
+
lines.push(`${title} (total snapshots: ${unifiedAsc.length})`);
|
|
235
|
+
unifiedAsc.forEach((s, idx)=>{
|
|
236
|
+
const d = new Date(s.ts);
|
|
237
|
+
const ts = d.toLocaleTimeString(undefined, {
|
|
238
|
+
hour: "2-digit",
|
|
239
|
+
minute: "2-digit",
|
|
240
|
+
second: "2-digit",
|
|
241
|
+
hour12: false
|
|
242
|
+
});
|
|
243
|
+
const labelPart = s.kind === "manual" && s.label ? ` [${s.label}]` : "";
|
|
244
|
+
lines.push(`--- ${ts} (#${idx + 1})${labelPart} ---`);
|
|
245
|
+
lines.push(s.json);
|
|
246
|
+
});
|
|
247
|
+
const text = lines.join("\n");
|
|
248
|
+
if (navigator?.clipboard?.writeText) {
|
|
249
|
+
navigator.clipboard.writeText(text).then(()=>{
|
|
250
|
+
setCopied(true);
|
|
251
|
+
setTimeout(()=>setCopied(false), 1300);
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}, [
|
|
255
|
+
title,
|
|
256
|
+
unifiedAsc
|
|
257
|
+
]);
|
|
258
|
+
const clear = useCallback(()=>{
|
|
259
|
+
// Clear all snapshots (state + manual) and purge global manual buffer.
|
|
260
|
+
setSnapshots([]);
|
|
261
|
+
setManualSnaps([]);
|
|
262
|
+
manualBuffer.length = 0;
|
|
263
|
+
}, []);
|
|
264
|
+
// Normalize position (object or shorthand) into edge declarations.
|
|
265
|
+
const normalizedEdges = useMemo(()=>{
|
|
266
|
+
if (!position) {
|
|
267
|
+
return {};
|
|
268
|
+
}
|
|
269
|
+
if (typeof position === "string") {
|
|
270
|
+
switch(position){
|
|
271
|
+
case "left":
|
|
272
|
+
case "top-left":
|
|
273
|
+
return {
|
|
274
|
+
top: "0",
|
|
275
|
+
left: "0"
|
|
276
|
+
};
|
|
277
|
+
case "right":
|
|
278
|
+
case "top-right":
|
|
279
|
+
return {
|
|
280
|
+
top: "0",
|
|
281
|
+
right: "0"
|
|
282
|
+
};
|
|
283
|
+
case "bottom-left":
|
|
284
|
+
return {
|
|
285
|
+
bottom: "0",
|
|
286
|
+
left: "0"
|
|
287
|
+
};
|
|
288
|
+
case "bottom-right":
|
|
289
|
+
return {
|
|
290
|
+
bottom: "0",
|
|
291
|
+
right: "0"
|
|
292
|
+
};
|
|
293
|
+
case "bottom":
|
|
294
|
+
return {
|
|
295
|
+
bottom: "0",
|
|
296
|
+
right: "0"
|
|
297
|
+
};
|
|
298
|
+
case "top":
|
|
299
|
+
default:
|
|
300
|
+
return {
|
|
301
|
+
top: "0",
|
|
302
|
+
right: "0"
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/* v8 ignore start - custom position object handling */ // Shallow copy to avoid mutating caller object.
|
|
307
|
+
const obj = {};
|
|
308
|
+
for (const k of [
|
|
309
|
+
"top",
|
|
310
|
+
"right",
|
|
311
|
+
"bottom",
|
|
312
|
+
"left"
|
|
313
|
+
]){
|
|
314
|
+
const v = position[k];
|
|
315
|
+
if (v !== undefined) {
|
|
316
|
+
obj[k] = String(v);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return obj;
|
|
320
|
+
/* v8 ignore stop */ }, [
|
|
321
|
+
position
|
|
322
|
+
]);
|
|
323
|
+
/**
|
|
324
|
+
* Final edges: if neither vertical edge provided, default to top:0. If neither
|
|
325
|
+
* horizontal edge provided, default to right:0. This preserves previous
|
|
326
|
+
* behavior while respecting left-anchored shorthands (they supply left so we
|
|
327
|
+
* don't inject right).
|
|
328
|
+
*/ const finalEdges = useMemo(()=>{
|
|
329
|
+
const out = {
|
|
330
|
+
...normalizedEdges
|
|
331
|
+
};
|
|
332
|
+
if (!("top" in out) && !("bottom" in out)) {
|
|
333
|
+
out.top = "0";
|
|
334
|
+
}
|
|
335
|
+
if (!("left" in out) && !("right" in out)) {
|
|
336
|
+
out.right = "0";
|
|
337
|
+
}
|
|
338
|
+
return out;
|
|
339
|
+
}, [
|
|
340
|
+
normalizedEdges
|
|
341
|
+
]);
|
|
342
|
+
/* v8 ignore start - visualViewport browser-specific API for mobile keyboard handling */ // Keep overlay inside visual viewport when software keyboard shifts layout.
|
|
343
|
+
useEffect(()=>{
|
|
344
|
+
const vv = typeof window !== "undefined" ? window.visualViewport : null;
|
|
345
|
+
if (!vv) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const update = ()=>{
|
|
349
|
+
if (!containerRef.current) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
// Adjust dynamic top only if caller didn't set top/bottom explicitly.
|
|
353
|
+
if (!("top" in finalEdges) && !("bottom" in finalEdges)) {
|
|
354
|
+
containerRef.current.style.top = `${vv.offsetTop}px`;
|
|
355
|
+
}
|
|
356
|
+
// Adjust dynamic right only if neither left nor right explicitly set.
|
|
357
|
+
if (!("right" in finalEdges) && !("left" in finalEdges)) {
|
|
358
|
+
const rightGap = Math.max(0, Math.round(window.innerWidth - (vv.offsetLeft + vv.width)));
|
|
359
|
+
containerRef.current.style.right = `${rightGap}px`;
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
vv.addEventListener("resize", update);
|
|
363
|
+
vv.addEventListener("scroll", update);
|
|
364
|
+
update();
|
|
365
|
+
return ()=>{
|
|
366
|
+
vv.removeEventListener("resize", update);
|
|
367
|
+
vv.removeEventListener("scroll", update);
|
|
368
|
+
};
|
|
369
|
+
}, [
|
|
370
|
+
finalEdges
|
|
371
|
+
]);
|
|
372
|
+
/* v8 ignore stop */ const snapshotCount = unifiedAsc.length;
|
|
373
|
+
const headerTitle = appendSnapshotCountInTitle ? `${title} (${snapshotCount})` : title;
|
|
374
|
+
// Base style for all snapshots; manual ones get color variants.
|
|
375
|
+
const statePreStyle = {
|
|
376
|
+
margin: 0,
|
|
377
|
+
padding: 0
|
|
378
|
+
};
|
|
379
|
+
const manualBase = {
|
|
380
|
+
margin: 0,
|
|
381
|
+
padding: 0,
|
|
382
|
+
paddingLeft: 4
|
|
383
|
+
};
|
|
384
|
+
const manualColorStyles = {
|
|
385
|
+
LOG_YELLOW: {
|
|
386
|
+
color: "#ffcf7f",
|
|
387
|
+
background: "rgba(255,200,0,0.08)",
|
|
388
|
+
border: "2px solid #cc9a00"
|
|
389
|
+
},
|
|
390
|
+
LOG_GREEN: {
|
|
391
|
+
color: "#b6ffb6",
|
|
392
|
+
background: "rgba(0,255,0,0.08)",
|
|
393
|
+
border: "2px solid #00aa00"
|
|
394
|
+
},
|
|
395
|
+
LOG_BLUE: {
|
|
396
|
+
color: "#b6d9ff",
|
|
397
|
+
background: "rgba(0,136,255,0.08)",
|
|
398
|
+
border: "2px solid #0066cc"
|
|
399
|
+
},
|
|
400
|
+
LOG_MAGENTA: {
|
|
401
|
+
color: "#ffb6ef",
|
|
402
|
+
background: "rgba(255,0,200,0.08)",
|
|
403
|
+
border: "2px solid #cc0088"
|
|
404
|
+
},
|
|
405
|
+
LOG_RED: {
|
|
406
|
+
color: "#ffb6b6",
|
|
407
|
+
background: "rgba(255,0,0,0.08)",
|
|
408
|
+
border: "2px solid #cc0000"
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
function styleForManual(color) {
|
|
412
|
+
const variant = color && manualColorStyles[color] ? manualColorStyles[color] : manualColorStyles.LOG_YELLOW;
|
|
413
|
+
return {
|
|
414
|
+
...manualBase,
|
|
415
|
+
color: variant.color,
|
|
416
|
+
background: variant.background,
|
|
417
|
+
borderLeft: variant.border
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
421
|
+
ref: containerRef,
|
|
422
|
+
"aria-label": "Debug overlay",
|
|
423
|
+
style: {
|
|
424
|
+
position: "fixed",
|
|
425
|
+
fontFamily: "monospace",
|
|
426
|
+
zIndex: 9999,
|
|
427
|
+
fontSize: 12,
|
|
428
|
+
color: "#d1faff",
|
|
429
|
+
background: "rgba(0,0,0,0.78)",
|
|
430
|
+
border: "1px solid #055",
|
|
431
|
+
borderTop: "none",
|
|
432
|
+
borderRight: "none",
|
|
433
|
+
borderBottomLeftRadius: 6,
|
|
434
|
+
maxWidth: 360,
|
|
435
|
+
boxShadow: "0 2px 6px rgba(0,0,0,0.4)",
|
|
436
|
+
...finalEdges
|
|
437
|
+
},
|
|
438
|
+
children: [
|
|
439
|
+
/*#__PURE__*/ jsxs("div", {
|
|
440
|
+
style: {
|
|
441
|
+
display: "flex",
|
|
442
|
+
alignItems: "center",
|
|
443
|
+
gap: 6,
|
|
444
|
+
padding: "4px 6px 4px 8px",
|
|
445
|
+
borderBottom: collapsed ? "none" : "1px solid #044",
|
|
446
|
+
background: "#022",
|
|
447
|
+
borderBottomLeftRadius: collapsed ? 6 : 0,
|
|
448
|
+
userSelect: "none"
|
|
449
|
+
},
|
|
450
|
+
children: [
|
|
451
|
+
/*#__PURE__*/ jsx("strong", {
|
|
452
|
+
style: {
|
|
453
|
+
fontWeight: 600,
|
|
454
|
+
fontSize: 11
|
|
455
|
+
},
|
|
456
|
+
children: headerTitle
|
|
457
|
+
}),
|
|
458
|
+
/*#__PURE__*/ jsxs("div", {
|
|
459
|
+
style: {
|
|
460
|
+
marginLeft: "auto",
|
|
461
|
+
display: "flex",
|
|
462
|
+
gap: 4
|
|
463
|
+
},
|
|
464
|
+
children: [
|
|
465
|
+
/*#__PURE__*/ jsx("button", {
|
|
466
|
+
type: "button",
|
|
467
|
+
onClick: ()=>setCollapsed((c)=>!c),
|
|
468
|
+
"aria-label": collapsed ? "Expand debug overlay" : "Collapse debug overlay",
|
|
469
|
+
style: buttonStyle,
|
|
470
|
+
children: collapsed ? "+" : "−"
|
|
471
|
+
}),
|
|
472
|
+
/*#__PURE__*/ jsx("button", {
|
|
473
|
+
type: "button",
|
|
474
|
+
onClick: copy,
|
|
475
|
+
"aria-label": "Copy debug JSON",
|
|
476
|
+
style: {
|
|
477
|
+
...buttonStyle,
|
|
478
|
+
color: copied ? "#0f0" : buttonStyle.color
|
|
479
|
+
},
|
|
480
|
+
children: copied ? "Copied" : "Copy"
|
|
481
|
+
}),
|
|
482
|
+
unifiedAsc.length > 1 && /*#__PURE__*/ jsx("button", {
|
|
483
|
+
type: "button",
|
|
484
|
+
onClick: clear,
|
|
485
|
+
"aria-label": "Clear stored snapshots",
|
|
486
|
+
style: buttonStyle,
|
|
487
|
+
children: "Clear"
|
|
488
|
+
})
|
|
489
|
+
]
|
|
490
|
+
})
|
|
491
|
+
]
|
|
492
|
+
}),
|
|
493
|
+
!collapsed && /*#__PURE__*/ jsxs("div", {
|
|
494
|
+
style: {
|
|
495
|
+
margin: 0,
|
|
496
|
+
padding: "6px 8px 8px 8px",
|
|
497
|
+
maxHeight: maxBodyHeight,
|
|
498
|
+
overflow: "auto",
|
|
499
|
+
lineHeight: 1.25,
|
|
500
|
+
fontSize: 11,
|
|
501
|
+
fontFamily: "monospace",
|
|
502
|
+
whiteSpace: "pre-wrap",
|
|
503
|
+
wordBreak: "break-word"
|
|
504
|
+
},
|
|
505
|
+
children: [
|
|
506
|
+
displayList.slice(0, maxVisibleSnapshots).map((s)=>{
|
|
507
|
+
/**
|
|
508
|
+
* Need global chronological index irrespective of current order. Find in
|
|
509
|
+
* unifiedAsc (ascending) by seq (unique) for numbering.
|
|
510
|
+
*/ const ascIndex = unifiedAsc.findIndex((u)=>u.seq === s.seq);
|
|
511
|
+
const d = new Date(s.ts);
|
|
512
|
+
const ts = d.toLocaleTimeString(undefined, {
|
|
513
|
+
hour: "2-digit",
|
|
514
|
+
minute: "2-digit",
|
|
515
|
+
second: "2-digit",
|
|
516
|
+
hour12: false
|
|
517
|
+
});
|
|
518
|
+
const labelPart = s.kind === "manual" && s.label ? ` [${s.label}]` : "";
|
|
519
|
+
return /*#__PURE__*/ jsx("pre", {
|
|
520
|
+
style: s.kind === "manual" ? styleForManual(s.color) : statePreStyle,
|
|
521
|
+
children: `--- ${ts} (#${ascIndex + 1})${labelPart} ---\n${s.json}`
|
|
522
|
+
}, `${s.kind}-${s.seq}`);
|
|
523
|
+
}),
|
|
524
|
+
displayList.length > maxVisibleSnapshots && /*#__PURE__*/ jsx("div", {
|
|
525
|
+
style: {
|
|
526
|
+
opacity: 0.7,
|
|
527
|
+
marginTop: 4
|
|
528
|
+
},
|
|
529
|
+
children: `(+${displayList.length - maxVisibleSnapshots} snapshots not shown)`
|
|
530
|
+
})
|
|
531
|
+
]
|
|
532
|
+
})
|
|
533
|
+
]
|
|
534
|
+
});
|
|
535
|
+
};
|
|
536
|
+
const buttonStyle = {
|
|
537
|
+
background: "#033",
|
|
538
|
+
color: "#0ff",
|
|
539
|
+
border: "1px solid #055",
|
|
540
|
+
borderRadius: 4,
|
|
541
|
+
fontSize: 10,
|
|
542
|
+
padding: "2px 6px",
|
|
543
|
+
cursor: "pointer",
|
|
544
|
+
fontFamily: "inherit"
|
|
24
545
|
};
|
|
546
|
+
|
|
547
|
+
;// CONCATENATED MODULE: ./src/components/index.ts
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
export { DebugOverlay, LOG_BLUE, LOG_GREEN, LOG_MAGENTA, LOG_RED, LOG_YELLOW, __resetDebugOverlayTestState, useDebugOverlay };
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@versini/ui-debug-overlay",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Arno Versini",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
8
8
|
},
|
|
9
|
-
"homepage": "https://
|
|
9
|
+
"homepage": "https://www.npmjs.com/package/@versini/ui-debug-overlay",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
12
12
|
"url": "git@github.com:aversini/ui-components.git"
|
|
@@ -20,13 +20,13 @@
|
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
22
|
"build:check": "tsc",
|
|
23
|
-
"build:js": "
|
|
24
|
-
"build:types": "
|
|
25
|
-
"build": "npm-run-all --serial clean build:check build:js
|
|
23
|
+
"build:js": "rslib build",
|
|
24
|
+
"build:types": "echo 'Types now built with rslib'",
|
|
25
|
+
"build": "npm-run-all --serial clean build:check build:js",
|
|
26
26
|
"clean": "rimraf dist tmp",
|
|
27
|
-
"dev:js": "
|
|
28
|
-
"dev:types": "
|
|
29
|
-
"dev": "
|
|
27
|
+
"dev:js": "rslib build --watch",
|
|
28
|
+
"dev:types": "echo 'Types now watched with rslib'",
|
|
29
|
+
"dev": "rslib build --watch",
|
|
30
30
|
"lint": "biome lint src",
|
|
31
31
|
"lint:fix": "biome check src --write --no-errors-on-unmatched",
|
|
32
32
|
"prettier": "biome check --write --no-errors-on-unmatched",
|
|
@@ -39,5 +39,5 @@
|
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@testing-library/jest-dom": "6.9.1"
|
|
41
41
|
},
|
|
42
|
-
"gitHead": "
|
|
42
|
+
"gitHead": "7b2640a0650a4c3aa6ca078888f765cb400f9f13"
|
|
43
43
|
}
|
|
@@ -1,350 +0,0 @@
|
|
|
1
|
-
import { jsxs as L, jsx as g } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback as _, useState as x, useRef as V, useMemo as h, useEffect as S } from "react";
|
|
3
|
-
const re = "LOG_YELLOW", ne = "LOG_GREEN", se = "LOG_BLUE", ae = "LOG_MAGENTA", le = "LOG_RED", ee = 200;
|
|
4
|
-
let W = 0;
|
|
5
|
-
const p = [], $ = /* @__PURE__ */ new Set();
|
|
6
|
-
function ie() {
|
|
7
|
-
p.length = 0, W = 0;
|
|
8
|
-
}
|
|
9
|
-
function F() {
|
|
10
|
-
return W++;
|
|
11
|
-
}
|
|
12
|
-
function z(s) {
|
|
13
|
-
const a = {
|
|
14
|
-
kind: "manual",
|
|
15
|
-
ts: Date.now(),
|
|
16
|
-
seq: F(),
|
|
17
|
-
label: s.label,
|
|
18
|
-
data: s.data,
|
|
19
|
-
color: s.color,
|
|
20
|
-
targetOverlays: s.targetOverlays && s.targetOverlays.length ? [...s.targetOverlays] : void 0
|
|
21
|
-
};
|
|
22
|
-
p.push(a), p.length > ee && p.shift(), $.forEach((l) => l(a));
|
|
23
|
-
}
|
|
24
|
-
function ce() {
|
|
25
|
-
return _(
|
|
26
|
-
(s, a, l) => {
|
|
27
|
-
z(typeof l == "string" ? { label: s, data: a, color: l } : {
|
|
28
|
-
label: s,
|
|
29
|
-
data: a,
|
|
30
|
-
color: l?.color,
|
|
31
|
-
targetOverlays: l?.targetOverlays
|
|
32
|
-
});
|
|
33
|
-
},
|
|
34
|
-
[]
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
function N(s, a = 2) {
|
|
38
|
-
const l = /* @__PURE__ */ new WeakSet(), c = (o) => {
|
|
39
|
-
if (!o || typeof o != "object" || o instanceof Date || o instanceof RegExp)
|
|
40
|
-
return o;
|
|
41
|
-
if (l.has(o))
|
|
42
|
-
return "[Circular]";
|
|
43
|
-
if (l.add(o), Array.isArray(o))
|
|
44
|
-
return o.map(c);
|
|
45
|
-
const y = {};
|
|
46
|
-
for (const d of Object.keys(o).sort())
|
|
47
|
-
y[d] = c(o[d]);
|
|
48
|
-
return y;
|
|
49
|
-
};
|
|
50
|
-
try {
|
|
51
|
-
return JSON.stringify(c(s), null, a);
|
|
52
|
-
} catch (o) {
|
|
53
|
-
return `<<unserializable: ${o.message}>>`;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
const de = ({
|
|
57
|
-
appState: s,
|
|
58
|
-
title: a = "AppState",
|
|
59
|
-
initialCollapsed: l = !1,
|
|
60
|
-
overlayId: c = "default",
|
|
61
|
-
position: o,
|
|
62
|
-
maxBodyHeight: y = "50svh",
|
|
63
|
-
indent: d = 2,
|
|
64
|
-
maxSnapshots: C = 50,
|
|
65
|
-
maxVisibleSnapshots: v = 10,
|
|
66
|
-
snapshotOrder: A = "desc",
|
|
67
|
-
appendSnapshotCountInTitle: U = !1
|
|
68
|
-
}) => {
|
|
69
|
-
const [b, I] = x(l), [R, T] = x(!1), m = V(null), [D, j] = x([]), [q, B] = x(
|
|
70
|
-
() => (
|
|
71
|
-
// Apply same targeting filter to initial buffered snapshots as we do for
|
|
72
|
-
// live subscription events so targeted snapshots don't leak to unrelated
|
|
73
|
-
// overlays.
|
|
74
|
-
p.filter(
|
|
75
|
-
(e) => !e.targetOverlays || e.targetOverlays.length === 0 || e.targetOverlays.includes(c)
|
|
76
|
-
)
|
|
77
|
-
)
|
|
78
|
-
), E = h(
|
|
79
|
-
() => s !== void 0 ? N(s, d) : null,
|
|
80
|
-
[s, d]
|
|
81
|
-
);
|
|
82
|
-
S(() => {
|
|
83
|
-
E != null && j(
|
|
84
|
-
(e) => [{ ts: Date.now(), json: E }, ...e].slice(0, C)
|
|
85
|
-
);
|
|
86
|
-
}, [E, C]), S(() => {
|
|
87
|
-
const e = (t) => {
|
|
88
|
-
(!t.targetOverlays || t.targetOverlays.length === 0 || t.targetOverlays.includes(c)) && B((r) => [...r, t]);
|
|
89
|
-
};
|
|
90
|
-
return $.add(e), () => {
|
|
91
|
-
$.delete(e);
|
|
92
|
-
};
|
|
93
|
-
}, [c]);
|
|
94
|
-
const i = h(() => {
|
|
95
|
-
const e = [...D].reverse().map((n) => ({
|
|
96
|
-
kind: "state",
|
|
97
|
-
ts: n.ts,
|
|
98
|
-
json: n.json,
|
|
99
|
-
seq: F()
|
|
100
|
-
})), t = q.map((n) => ({
|
|
101
|
-
kind: "manual",
|
|
102
|
-
ts: n.ts,
|
|
103
|
-
seq: n.seq,
|
|
104
|
-
label: n.label,
|
|
105
|
-
color: n.color,
|
|
106
|
-
json: N(n.data, d)
|
|
107
|
-
})), r = [...e, ...t];
|
|
108
|
-
return r.sort((n, f) => n.ts === f.ts ? n.seq - f.seq : n.ts - f.ts), r;
|
|
109
|
-
}, [D, q, d]), k = h(
|
|
110
|
-
() => A === "asc" ? i : [...i].reverse(),
|
|
111
|
-
[A, i]
|
|
112
|
-
), Y = _(() => {
|
|
113
|
-
const e = [];
|
|
114
|
-
e.push(`${a} (total snapshots: ${i.length})`), i.forEach((r, n) => {
|
|
115
|
-
const G = new Date(r.ts).toLocaleTimeString(void 0, {
|
|
116
|
-
hour: "2-digit",
|
|
117
|
-
minute: "2-digit",
|
|
118
|
-
second: "2-digit",
|
|
119
|
-
hour12: !1
|
|
120
|
-
}), Z = r.kind === "manual" && r.label ? ` [${r.label}]` : "";
|
|
121
|
-
e.push(`--- ${G} (#${n + 1})${Z} ---`), e.push(r.json);
|
|
122
|
-
});
|
|
123
|
-
const t = e.join(`
|
|
124
|
-
`);
|
|
125
|
-
navigator?.clipboard?.writeText && navigator.clipboard.writeText(t).then(() => {
|
|
126
|
-
T(!0), setTimeout(() => T(!1), 1300);
|
|
127
|
-
});
|
|
128
|
-
}, [a, i]), P = _(() => {
|
|
129
|
-
j([]), B([]), p.length = 0;
|
|
130
|
-
}, []), M = h(() => {
|
|
131
|
-
if (!o)
|
|
132
|
-
return {};
|
|
133
|
-
if (typeof o == "string")
|
|
134
|
-
switch (o) {
|
|
135
|
-
case "left":
|
|
136
|
-
case "top-left":
|
|
137
|
-
return { top: "0", left: "0" };
|
|
138
|
-
case "right":
|
|
139
|
-
case "top-right":
|
|
140
|
-
return { top: "0", right: "0" };
|
|
141
|
-
case "bottom-left":
|
|
142
|
-
return { bottom: "0", left: "0" };
|
|
143
|
-
case "bottom-right":
|
|
144
|
-
return { bottom: "0", right: "0" };
|
|
145
|
-
case "bottom":
|
|
146
|
-
return { bottom: "0", right: "0" };
|
|
147
|
-
case "top":
|
|
148
|
-
default:
|
|
149
|
-
return { top: "0", right: "0" };
|
|
150
|
-
}
|
|
151
|
-
const e = {};
|
|
152
|
-
for (const t of ["top", "right", "bottom", "left"]) {
|
|
153
|
-
const r = o[t];
|
|
154
|
-
r !== void 0 && (e[t] = String(r));
|
|
155
|
-
}
|
|
156
|
-
return e;
|
|
157
|
-
}, [o]), u = h(() => {
|
|
158
|
-
const e = { ...M };
|
|
159
|
-
return !("top" in e) && !("bottom" in e) && (e.top = "0"), !("left" in e) && !("right" in e) && (e.right = "0"), e;
|
|
160
|
-
}, [M]);
|
|
161
|
-
S(() => {
|
|
162
|
-
const e = typeof window < "u" ? window.visualViewport : null;
|
|
163
|
-
if (!e)
|
|
164
|
-
return;
|
|
165
|
-
const t = () => {
|
|
166
|
-
if (m.current && (!("top" in u) && !("bottom" in u) && (m.current.style.top = `${e.offsetTop}px`), !("right" in u) && !("left" in u))) {
|
|
167
|
-
const r = Math.max(
|
|
168
|
-
0,
|
|
169
|
-
Math.round(window.innerWidth - (e.offsetLeft + e.width))
|
|
170
|
-
);
|
|
171
|
-
m.current.style.right = `${r}px`;
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
return e.addEventListener("resize", t), e.addEventListener("scroll", t), t(), () => {
|
|
175
|
-
e.removeEventListener("resize", t), e.removeEventListener("scroll", t);
|
|
176
|
-
};
|
|
177
|
-
}, [u]);
|
|
178
|
-
const H = i.length, J = U ? `${a} (${H})` : a, K = { margin: 0, padding: 0 }, Q = {
|
|
179
|
-
margin: 0,
|
|
180
|
-
padding: 0,
|
|
181
|
-
paddingLeft: 4
|
|
182
|
-
}, w = {
|
|
183
|
-
LOG_YELLOW: {
|
|
184
|
-
color: "#ffcf7f",
|
|
185
|
-
background: "rgba(255,200,0,0.08)",
|
|
186
|
-
border: "2px solid #cc9a00"
|
|
187
|
-
},
|
|
188
|
-
LOG_GREEN: {
|
|
189
|
-
color: "#b6ffb6",
|
|
190
|
-
background: "rgba(0,255,0,0.08)",
|
|
191
|
-
border: "2px solid #00aa00"
|
|
192
|
-
},
|
|
193
|
-
LOG_BLUE: {
|
|
194
|
-
color: "#b6d9ff",
|
|
195
|
-
background: "rgba(0,136,255,0.08)",
|
|
196
|
-
border: "2px solid #0066cc"
|
|
197
|
-
},
|
|
198
|
-
LOG_MAGENTA: {
|
|
199
|
-
color: "#ffb6ef",
|
|
200
|
-
background: "rgba(255,0,200,0.08)",
|
|
201
|
-
border: "2px solid #cc0088"
|
|
202
|
-
},
|
|
203
|
-
LOG_RED: {
|
|
204
|
-
color: "#ffb6b6",
|
|
205
|
-
background: "rgba(255,0,0,0.08)",
|
|
206
|
-
border: "2px solid #cc0000"
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
function X(e) {
|
|
210
|
-
const t = e && w[e] ? w[e] : w.LOG_YELLOW;
|
|
211
|
-
return {
|
|
212
|
-
...Q,
|
|
213
|
-
color: t.color,
|
|
214
|
-
background: t.background,
|
|
215
|
-
borderLeft: t.border
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
return /* @__PURE__ */ L(
|
|
219
|
-
"div",
|
|
220
|
-
{
|
|
221
|
-
ref: m,
|
|
222
|
-
"aria-label": "Debug overlay",
|
|
223
|
-
style: {
|
|
224
|
-
position: "fixed",
|
|
225
|
-
fontFamily: "monospace",
|
|
226
|
-
zIndex: 9999,
|
|
227
|
-
fontSize: 12,
|
|
228
|
-
color: "#d1faff",
|
|
229
|
-
background: "rgba(0,0,0,0.78)",
|
|
230
|
-
border: "1px solid #055",
|
|
231
|
-
borderTop: "none",
|
|
232
|
-
borderRight: "none",
|
|
233
|
-
borderBottomLeftRadius: 6,
|
|
234
|
-
maxWidth: 360,
|
|
235
|
-
boxShadow: "0 2px 6px rgba(0,0,0,0.4)",
|
|
236
|
-
...u
|
|
237
|
-
},
|
|
238
|
-
children: [
|
|
239
|
-
/* @__PURE__ */ L(
|
|
240
|
-
"div",
|
|
241
|
-
{
|
|
242
|
-
style: {
|
|
243
|
-
display: "flex",
|
|
244
|
-
alignItems: "center",
|
|
245
|
-
gap: 6,
|
|
246
|
-
padding: "4px 6px 4px 8px",
|
|
247
|
-
borderBottom: b ? "none" : "1px solid #044",
|
|
248
|
-
background: "#022",
|
|
249
|
-
borderBottomLeftRadius: b ? 6 : 0,
|
|
250
|
-
userSelect: "none"
|
|
251
|
-
},
|
|
252
|
-
children: [
|
|
253
|
-
/* @__PURE__ */ g("strong", { style: { fontWeight: 600, fontSize: 11 }, children: J }),
|
|
254
|
-
/* @__PURE__ */ L("div", { style: { marginLeft: "auto", display: "flex", gap: 4 }, children: [
|
|
255
|
-
/* @__PURE__ */ g(
|
|
256
|
-
"button",
|
|
257
|
-
{
|
|
258
|
-
type: "button",
|
|
259
|
-
onClick: () => I((e) => !e),
|
|
260
|
-
"aria-label": b ? "Expand debug overlay" : "Collapse debug overlay",
|
|
261
|
-
style: O,
|
|
262
|
-
children: b ? "+" : "−"
|
|
263
|
-
}
|
|
264
|
-
),
|
|
265
|
-
/* @__PURE__ */ g(
|
|
266
|
-
"button",
|
|
267
|
-
{
|
|
268
|
-
type: "button",
|
|
269
|
-
onClick: Y,
|
|
270
|
-
"aria-label": "Copy debug JSON",
|
|
271
|
-
style: {
|
|
272
|
-
...O,
|
|
273
|
-
color: R ? "#0f0" : O.color
|
|
274
|
-
},
|
|
275
|
-
children: R ? "Copied" : "Copy"
|
|
276
|
-
}
|
|
277
|
-
),
|
|
278
|
-
i.length > 1 && /* @__PURE__ */ g(
|
|
279
|
-
"button",
|
|
280
|
-
{
|
|
281
|
-
type: "button",
|
|
282
|
-
onClick: P,
|
|
283
|
-
"aria-label": "Clear stored snapshots",
|
|
284
|
-
style: O,
|
|
285
|
-
children: "Clear"
|
|
286
|
-
}
|
|
287
|
-
)
|
|
288
|
-
] })
|
|
289
|
-
]
|
|
290
|
-
}
|
|
291
|
-
),
|
|
292
|
-
!b && /* @__PURE__ */ L(
|
|
293
|
-
"div",
|
|
294
|
-
{
|
|
295
|
-
style: {
|
|
296
|
-
margin: 0,
|
|
297
|
-
padding: "6px 8px 8px 8px",
|
|
298
|
-
maxHeight: y,
|
|
299
|
-
overflow: "auto",
|
|
300
|
-
lineHeight: 1.25,
|
|
301
|
-
fontSize: 11,
|
|
302
|
-
fontFamily: "monospace",
|
|
303
|
-
whiteSpace: "pre-wrap",
|
|
304
|
-
wordBreak: "break-word"
|
|
305
|
-
},
|
|
306
|
-
children: [
|
|
307
|
-
k.slice(0, v).map((e) => {
|
|
308
|
-
const t = i.findIndex((G) => G.seq === e.seq), n = new Date(e.ts).toLocaleTimeString(void 0, {
|
|
309
|
-
hour: "2-digit",
|
|
310
|
-
minute: "2-digit",
|
|
311
|
-
second: "2-digit",
|
|
312
|
-
hour12: !1
|
|
313
|
-
}), f = e.kind === "manual" && e.label ? ` [${e.label}]` : "";
|
|
314
|
-
return /* @__PURE__ */ g(
|
|
315
|
-
"pre",
|
|
316
|
-
{
|
|
317
|
-
style: e.kind === "manual" ? X(e.color) : K,
|
|
318
|
-
children: `--- ${n} (#${t + 1})${f} ---
|
|
319
|
-
${e.json}`
|
|
320
|
-
},
|
|
321
|
-
`${e.kind}-${e.seq}`
|
|
322
|
-
);
|
|
323
|
-
}),
|
|
324
|
-
k.length > v && /* @__PURE__ */ g("div", { style: { opacity: 0.7, marginTop: 4 }, children: `(+${k.length - v} snapshots not shown)` })
|
|
325
|
-
]
|
|
326
|
-
}
|
|
327
|
-
)
|
|
328
|
-
]
|
|
329
|
-
}
|
|
330
|
-
);
|
|
331
|
-
}, O = {
|
|
332
|
-
background: "#033",
|
|
333
|
-
color: "#0ff",
|
|
334
|
-
border: "1px solid #055",
|
|
335
|
-
borderRadius: 4,
|
|
336
|
-
fontSize: 10,
|
|
337
|
-
padding: "2px 6px",
|
|
338
|
-
cursor: "pointer",
|
|
339
|
-
fontFamily: "inherit"
|
|
340
|
-
};
|
|
341
|
-
export {
|
|
342
|
-
de as DebugOverlay,
|
|
343
|
-
se as LOG_BLUE,
|
|
344
|
-
ne as LOG_GREEN,
|
|
345
|
-
ae as LOG_MAGENTA,
|
|
346
|
-
le as LOG_RED,
|
|
347
|
-
re as LOG_YELLOW,
|
|
348
|
-
ie as __resetDebugOverlayTestState,
|
|
349
|
-
ce as useDebugOverlay
|
|
350
|
-
};
|