@lombard.finance/sdk-devtools 0.1.0-canary-2.10
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/LICENSE +21 -0
- package/README.md +348 -0
- package/dist/index.d.ts +830 -0
- package/dist/index.js +1644 -0
- package/dist/index.js.map +1 -0
- package/package.json +70 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1644 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var DEFAULT_DEVTOOLS_CONFIG = {
|
|
3
|
+
position: "bottom-right",
|
|
4
|
+
defaultOpen: false,
|
|
5
|
+
theme: "dark",
|
|
6
|
+
showEnvironment: true,
|
|
7
|
+
consoleLogging: false,
|
|
8
|
+
maxEvents: 100,
|
|
9
|
+
maxReducerLogs: 50
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/provider/DevToolsProvider.tsx
|
|
13
|
+
import {
|
|
14
|
+
createContext,
|
|
15
|
+
useCallback,
|
|
16
|
+
useContext,
|
|
17
|
+
useEffect,
|
|
18
|
+
useMemo,
|
|
19
|
+
useState
|
|
20
|
+
} from "react";
|
|
21
|
+
|
|
22
|
+
// src/bridge/DevToolsBridge.ts
|
|
23
|
+
var DevToolsBridge = class {
|
|
24
|
+
constructor(config) {
|
|
25
|
+
this.events = [];
|
|
26
|
+
this.actions = /* @__PURE__ */ new Map();
|
|
27
|
+
this.unsubscribers = /* @__PURE__ */ new Map();
|
|
28
|
+
this.eventListeners = /* @__PURE__ */ new Set();
|
|
29
|
+
this.stateListeners = /* @__PURE__ */ new Set();
|
|
30
|
+
this.eventIdCounter = 0;
|
|
31
|
+
// Network logging
|
|
32
|
+
this.networkLog = /* @__PURE__ */ new Map();
|
|
33
|
+
this.networkListeners = /* @__PURE__ */ new Set();
|
|
34
|
+
this.requestIdCounter = 0;
|
|
35
|
+
this.config = { ...DEFAULT_DEVTOOLS_CONFIG, ...config };
|
|
36
|
+
}
|
|
37
|
+
// ─────────────────────────────────────────────────────────────────
|
|
38
|
+
// Action Registration
|
|
39
|
+
// ─────────────────────────────────────────────────────────────────
|
|
40
|
+
/**
|
|
41
|
+
* Register an SDK action for monitoring
|
|
42
|
+
*
|
|
43
|
+
* Automatically subscribes to all standard events:
|
|
44
|
+
* - status-change
|
|
45
|
+
* - error
|
|
46
|
+
* - completed
|
|
47
|
+
* - failed
|
|
48
|
+
* - loading
|
|
49
|
+
* - progress
|
|
50
|
+
*/
|
|
51
|
+
registerAction(name, action, options) {
|
|
52
|
+
this.actions.set(name, {
|
|
53
|
+
name,
|
|
54
|
+
action,
|
|
55
|
+
registeredAt: Date.now(),
|
|
56
|
+
category: options?.category
|
|
57
|
+
});
|
|
58
|
+
const eventTypes = [
|
|
59
|
+
"status-change",
|
|
60
|
+
"error",
|
|
61
|
+
"completed",
|
|
62
|
+
"failed",
|
|
63
|
+
"loading",
|
|
64
|
+
"progress"
|
|
65
|
+
];
|
|
66
|
+
const unsubscribers = [];
|
|
67
|
+
for (const eventType of eventTypes) {
|
|
68
|
+
try {
|
|
69
|
+
const unsubscribe = action.on(eventType, (...args) => {
|
|
70
|
+
this.handleEvent(name, eventType, args[0]);
|
|
71
|
+
});
|
|
72
|
+
if (typeof unsubscribe === "function") {
|
|
73
|
+
unsubscribers.push(unsubscribe);
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
this.unsubscribers.set(name, unsubscribers);
|
|
79
|
+
this.notifyStateListeners();
|
|
80
|
+
this.handleEvent(name, "registered", { status: action.status });
|
|
81
|
+
return () => {
|
|
82
|
+
this.unregisterAction(name);
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Unregister an action
|
|
87
|
+
*/
|
|
88
|
+
unregisterAction(name) {
|
|
89
|
+
const unsubs = this.unsubscribers.get(name);
|
|
90
|
+
if (unsubs) {
|
|
91
|
+
unsubs.forEach((unsub) => {
|
|
92
|
+
unsub();
|
|
93
|
+
});
|
|
94
|
+
this.unsubscribers.delete(name);
|
|
95
|
+
}
|
|
96
|
+
this.actions.delete(name);
|
|
97
|
+
this.notifyStateListeners();
|
|
98
|
+
this.handleEvent(name, "unregistered", null);
|
|
99
|
+
}
|
|
100
|
+
// ─────────────────────────────────────────────────────────────────
|
|
101
|
+
// Event Handling
|
|
102
|
+
// ─────────────────────────────────────────────────────────────────
|
|
103
|
+
handleEvent(source, type, data) {
|
|
104
|
+
const event = {
|
|
105
|
+
id: `${++this.eventIdCounter}`,
|
|
106
|
+
type,
|
|
107
|
+
timestamp: Date.now(),
|
|
108
|
+
data,
|
|
109
|
+
source,
|
|
110
|
+
isSDKEvent: true
|
|
111
|
+
};
|
|
112
|
+
this.events.push(event);
|
|
113
|
+
if (this.events.length > this.config.maxEvents) {
|
|
114
|
+
this.events.shift();
|
|
115
|
+
}
|
|
116
|
+
if (this.config.consoleLogging) {
|
|
117
|
+
console.log(`[DevTools] ${source} \u2192 ${type}:`, data);
|
|
118
|
+
}
|
|
119
|
+
this.notifyEventListeners(event);
|
|
120
|
+
}
|
|
121
|
+
notifyEventListeners(event) {
|
|
122
|
+
this.eventListeners.forEach((listener) => {
|
|
123
|
+
try {
|
|
124
|
+
listener(event);
|
|
125
|
+
} catch (err) {
|
|
126
|
+
console.error("[DevTools] Event listener error:", err);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
notifyStateListeners() {
|
|
131
|
+
this.stateListeners.forEach((listener) => {
|
|
132
|
+
try {
|
|
133
|
+
listener(new Map(this.actions));
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.error("[DevTools] State listener error:", err);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
// ─────────────────────────────────────────────────────────────────
|
|
140
|
+
// Subscriptions
|
|
141
|
+
// ─────────────────────────────────────────────────────────────────
|
|
142
|
+
/**
|
|
143
|
+
* Subscribe to new events
|
|
144
|
+
*/
|
|
145
|
+
onEvent(callback) {
|
|
146
|
+
this.eventListeners.add(callback);
|
|
147
|
+
return () => this.eventListeners.delete(callback);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Subscribe to action state changes
|
|
151
|
+
*/
|
|
152
|
+
onStateChange(callback) {
|
|
153
|
+
this.stateListeners.add(callback);
|
|
154
|
+
return () => this.stateListeners.delete(callback);
|
|
155
|
+
}
|
|
156
|
+
// ─────────────────────────────────────────────────────────────────
|
|
157
|
+
// Getters
|
|
158
|
+
// ─────────────────────────────────────────────────────────────────
|
|
159
|
+
/**
|
|
160
|
+
* Get all collected events
|
|
161
|
+
*/
|
|
162
|
+
getEvents() {
|
|
163
|
+
return [...this.events];
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Get all registered actions
|
|
167
|
+
*/
|
|
168
|
+
getActions() {
|
|
169
|
+
return new Map(this.actions);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get current state summary for all actions
|
|
173
|
+
*/
|
|
174
|
+
getStateSummary() {
|
|
175
|
+
const summary = {};
|
|
176
|
+
this.actions.forEach((reg, name) => {
|
|
177
|
+
summary[name] = {
|
|
178
|
+
status: reg.action.status,
|
|
179
|
+
isLoading: reg.action.isLoading,
|
|
180
|
+
hasError: reg.action.isFailed
|
|
181
|
+
};
|
|
182
|
+
});
|
|
183
|
+
return summary;
|
|
184
|
+
}
|
|
185
|
+
// ─────────────────────────────────────────────────────────────────
|
|
186
|
+
// Network Logging (API Requests/Responses)
|
|
187
|
+
// ─────────────────────────────────────────────────────────────────
|
|
188
|
+
/**
|
|
189
|
+
* Log an API request
|
|
190
|
+
*
|
|
191
|
+
* Call this when an HTTP request is initiated.
|
|
192
|
+
* Returns a request ID to correlate with the response.
|
|
193
|
+
*/
|
|
194
|
+
logApiRequest(params) {
|
|
195
|
+
const requestId = `req-${++this.requestIdCounter}`;
|
|
196
|
+
const request = {
|
|
197
|
+
id: requestId,
|
|
198
|
+
type: "api-request",
|
|
199
|
+
method: params.method,
|
|
200
|
+
url: params.url,
|
|
201
|
+
payload: params.payload,
|
|
202
|
+
headers: params.headers,
|
|
203
|
+
timestamp: Date.now(),
|
|
204
|
+
source: params.source
|
|
205
|
+
};
|
|
206
|
+
const entry = {
|
|
207
|
+
request,
|
|
208
|
+
response: null,
|
|
209
|
+
isPending: true,
|
|
210
|
+
isFailed: false
|
|
211
|
+
};
|
|
212
|
+
this.networkLog.set(requestId, entry);
|
|
213
|
+
this.handleEvent(params.source || "api", "api-request", {
|
|
214
|
+
method: params.method,
|
|
215
|
+
url: params.url,
|
|
216
|
+
payload: params.payload
|
|
217
|
+
});
|
|
218
|
+
if (this.config.consoleLogging) {
|
|
219
|
+
console.log(
|
|
220
|
+
`[DevTools] API ${params.method} ${params.url}`,
|
|
221
|
+
params.payload
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
this.notifyNetworkListeners();
|
|
225
|
+
return requestId;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Log an API response
|
|
229
|
+
*
|
|
230
|
+
* Call this when an HTTP response is received.
|
|
231
|
+
* Pass the requestId returned from logApiRequest.
|
|
232
|
+
*/
|
|
233
|
+
logApiResponse(params) {
|
|
234
|
+
const entry = this.networkLog.get(params.requestId);
|
|
235
|
+
if (!entry) {
|
|
236
|
+
console.warn(`[DevTools] No request found for ID: ${params.requestId}`);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const response = {
|
|
240
|
+
id: `res-${params.requestId}`,
|
|
241
|
+
type: "api-response",
|
|
242
|
+
requestId: params.requestId,
|
|
243
|
+
status: params.status,
|
|
244
|
+
statusText: params.statusText,
|
|
245
|
+
data: params.data,
|
|
246
|
+
error: params.error,
|
|
247
|
+
duration: Date.now() - entry.request.timestamp,
|
|
248
|
+
timestamp: Date.now()
|
|
249
|
+
};
|
|
250
|
+
entry.response = response;
|
|
251
|
+
entry.isPending = false;
|
|
252
|
+
entry.isFailed = params.status >= 400 || !!params.error;
|
|
253
|
+
this.handleEvent(entry.request.source || "api", "api-response", {
|
|
254
|
+
status: params.status,
|
|
255
|
+
duration: response.duration,
|
|
256
|
+
url: entry.request.url,
|
|
257
|
+
error: params.error
|
|
258
|
+
});
|
|
259
|
+
if (this.config.consoleLogging) {
|
|
260
|
+
const logFn = entry.isFailed ? console.error : console.log;
|
|
261
|
+
logFn(
|
|
262
|
+
`[DevTools] API Response ${params.status} (${response.duration}ms) ${entry.request.url}`,
|
|
263
|
+
params.data || params.error
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
this.notifyNetworkListeners();
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Subscribe to network log changes
|
|
270
|
+
*/
|
|
271
|
+
onNetworkChange(callback) {
|
|
272
|
+
this.networkListeners.add(callback);
|
|
273
|
+
return () => this.networkListeners.delete(callback);
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Get all network log entries
|
|
277
|
+
*/
|
|
278
|
+
getNetworkLog() {
|
|
279
|
+
return Array.from(this.networkLog.values()).sort(
|
|
280
|
+
(a, b) => b.request.timestamp - a.request.timestamp
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Clear network log
|
|
285
|
+
*/
|
|
286
|
+
clearNetworkLog() {
|
|
287
|
+
this.networkLog.clear();
|
|
288
|
+
this.notifyNetworkListeners();
|
|
289
|
+
}
|
|
290
|
+
notifyNetworkListeners() {
|
|
291
|
+
const entries = this.getNetworkLog();
|
|
292
|
+
this.networkListeners.forEach((listener) => {
|
|
293
|
+
try {
|
|
294
|
+
listener(entries);
|
|
295
|
+
} catch (err) {
|
|
296
|
+
console.error("[DevTools] Network listener error:", err);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
// ─────────────────────────────────────────────────────────────────
|
|
301
|
+
// Utilities
|
|
302
|
+
// ─────────────────────────────────────────────────────────────────
|
|
303
|
+
/**
|
|
304
|
+
* Clear all events
|
|
305
|
+
*/
|
|
306
|
+
clearEvents() {
|
|
307
|
+
this.events = [];
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Clear all registered actions
|
|
311
|
+
*/
|
|
312
|
+
clearActions() {
|
|
313
|
+
this.actions.forEach((_, name) => {
|
|
314
|
+
this.unregisterAction(name);
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Destroy the bridge
|
|
319
|
+
*/
|
|
320
|
+
destroy() {
|
|
321
|
+
this.clearActions();
|
|
322
|
+
this.clearNetworkLog();
|
|
323
|
+
this.eventListeners.clear();
|
|
324
|
+
this.stateListeners.clear();
|
|
325
|
+
this.networkListeners.clear();
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
var globalBridge = null;
|
|
329
|
+
function getDevToolsBridge(config) {
|
|
330
|
+
if (!globalBridge) {
|
|
331
|
+
globalBridge = new DevToolsBridge(config);
|
|
332
|
+
}
|
|
333
|
+
return globalBridge;
|
|
334
|
+
}
|
|
335
|
+
function resetDevToolsBridge() {
|
|
336
|
+
if (globalBridge) {
|
|
337
|
+
globalBridge.destroy();
|
|
338
|
+
globalBridge = null;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/provider/DevToolsProvider.tsx
|
|
343
|
+
import { jsx } from "react/jsx-runtime";
|
|
344
|
+
var DevToolsContext = createContext(null);
|
|
345
|
+
function DevToolsProvider({
|
|
346
|
+
children,
|
|
347
|
+
config,
|
|
348
|
+
enabled = process.env.NODE_ENV !== "production"
|
|
349
|
+
}) {
|
|
350
|
+
const [bridge] = useState(() => new DevToolsBridge(config));
|
|
351
|
+
const [events, setEvents] = useState([]);
|
|
352
|
+
const [actions, setActions] = useState(
|
|
353
|
+
/* @__PURE__ */ new Map()
|
|
354
|
+
);
|
|
355
|
+
const [networkLog, setNetworkLog] = useState([]);
|
|
356
|
+
const [mountId, setMountId] = useState(0);
|
|
357
|
+
useEffect(() => {
|
|
358
|
+
setMountId((prev) => prev + 1);
|
|
359
|
+
}, []);
|
|
360
|
+
useEffect(() => {
|
|
361
|
+
if (!enabled || mountId === 0) return;
|
|
362
|
+
console.log(
|
|
363
|
+
"[DevToolsProvider] Setting up event subscriptions, mountId:",
|
|
364
|
+
mountId
|
|
365
|
+
);
|
|
366
|
+
const existingEvents = bridge.getEvents();
|
|
367
|
+
const existingActions = bridge.getActions();
|
|
368
|
+
if (existingEvents.length > 0) {
|
|
369
|
+
console.log(
|
|
370
|
+
"[DevToolsProvider] Syncing existing events:",
|
|
371
|
+
existingEvents.length
|
|
372
|
+
);
|
|
373
|
+
setEvents(existingEvents);
|
|
374
|
+
}
|
|
375
|
+
if (existingActions.size > 0) {
|
|
376
|
+
console.log(
|
|
377
|
+
"[DevToolsProvider] Syncing existing actions:",
|
|
378
|
+
existingActions.size
|
|
379
|
+
);
|
|
380
|
+
setActions(existingActions);
|
|
381
|
+
}
|
|
382
|
+
const unsubEvent = bridge.onEvent((event) => {
|
|
383
|
+
console.log("[DevToolsProvider] Event received:", event);
|
|
384
|
+
const newEvents = bridge.getEvents();
|
|
385
|
+
console.log("[DevToolsProvider] Setting events:", newEvents.length);
|
|
386
|
+
setEvents(newEvents);
|
|
387
|
+
});
|
|
388
|
+
const unsubState = bridge.onStateChange((newActions) => {
|
|
389
|
+
console.log(
|
|
390
|
+
"[DevToolsProvider] State changed:",
|
|
391
|
+
newActions.size,
|
|
392
|
+
"actions"
|
|
393
|
+
);
|
|
394
|
+
setActions(newActions);
|
|
395
|
+
});
|
|
396
|
+
const unsubNetwork = bridge.onNetworkChange((entries) => {
|
|
397
|
+
console.log(
|
|
398
|
+
"[DevToolsProvider] Network log updated:",
|
|
399
|
+
entries.length,
|
|
400
|
+
"entries"
|
|
401
|
+
);
|
|
402
|
+
setNetworkLog(entries);
|
|
403
|
+
});
|
|
404
|
+
const existingNetworkLog = bridge.getNetworkLog();
|
|
405
|
+
if (existingNetworkLog.length > 0) {
|
|
406
|
+
console.log(
|
|
407
|
+
"[DevToolsProvider] Syncing existing network log:",
|
|
408
|
+
existingNetworkLog.length
|
|
409
|
+
);
|
|
410
|
+
setNetworkLog(existingNetworkLog);
|
|
411
|
+
}
|
|
412
|
+
return () => {
|
|
413
|
+
console.log("[DevToolsProvider] Cleaning up subscriptions");
|
|
414
|
+
unsubEvent();
|
|
415
|
+
unsubState();
|
|
416
|
+
unsubNetwork();
|
|
417
|
+
};
|
|
418
|
+
}, [enabled, bridge, mountId]);
|
|
419
|
+
useEffect(() => {
|
|
420
|
+
return () => {
|
|
421
|
+
console.log("[DevToolsProvider] Destroying bridge");
|
|
422
|
+
bridge.destroy();
|
|
423
|
+
};
|
|
424
|
+
}, [bridge]);
|
|
425
|
+
const registerAction = useCallback(
|
|
426
|
+
(name, action, options) => {
|
|
427
|
+
if (!enabled) return () => {
|
|
428
|
+
};
|
|
429
|
+
return bridge.registerAction(name, action, options);
|
|
430
|
+
},
|
|
431
|
+
[bridge, enabled]
|
|
432
|
+
);
|
|
433
|
+
const unregisterAction = useCallback(
|
|
434
|
+
(name) => {
|
|
435
|
+
if (!enabled) return;
|
|
436
|
+
bridge.unregisterAction(name);
|
|
437
|
+
},
|
|
438
|
+
[bridge, enabled]
|
|
439
|
+
);
|
|
440
|
+
const clearEvents = useCallback(() => {
|
|
441
|
+
bridge.clearEvents();
|
|
442
|
+
setEvents([]);
|
|
443
|
+
}, [bridge]);
|
|
444
|
+
const clearNetworkLog = useCallback(() => {
|
|
445
|
+
bridge.clearNetworkLog();
|
|
446
|
+
setNetworkLog([]);
|
|
447
|
+
}, [bridge]);
|
|
448
|
+
const getStateSummary = useCallback(() => {
|
|
449
|
+
return bridge.getStateSummary();
|
|
450
|
+
}, [bridge]);
|
|
451
|
+
const value = useMemo(
|
|
452
|
+
() => ({
|
|
453
|
+
events,
|
|
454
|
+
actions,
|
|
455
|
+
networkLog,
|
|
456
|
+
registerAction,
|
|
457
|
+
unregisterAction,
|
|
458
|
+
clearEvents,
|
|
459
|
+
clearNetworkLog,
|
|
460
|
+
getStateSummary,
|
|
461
|
+
bridge,
|
|
462
|
+
isEnabled: enabled
|
|
463
|
+
}),
|
|
464
|
+
[
|
|
465
|
+
events,
|
|
466
|
+
actions,
|
|
467
|
+
networkLog,
|
|
468
|
+
registerAction,
|
|
469
|
+
unregisterAction,
|
|
470
|
+
clearEvents,
|
|
471
|
+
clearNetworkLog,
|
|
472
|
+
getStateSummary,
|
|
473
|
+
bridge,
|
|
474
|
+
enabled
|
|
475
|
+
]
|
|
476
|
+
);
|
|
477
|
+
return /* @__PURE__ */ jsx(DevToolsContext.Provider, { value, children });
|
|
478
|
+
}
|
|
479
|
+
function useDevToolsContext() {
|
|
480
|
+
const context = useContext(DevToolsContext);
|
|
481
|
+
if (!context) {
|
|
482
|
+
throw new Error(
|
|
483
|
+
"useDevToolsContext must be used within a DevToolsProvider"
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
return context;
|
|
487
|
+
}
|
|
488
|
+
function useRegisterAction(name, action, category) {
|
|
489
|
+
const { registerAction } = useDevToolsContext();
|
|
490
|
+
useEffect(() => {
|
|
491
|
+
if (!action) return;
|
|
492
|
+
return registerAction(name, action, category ? { category } : void 0);
|
|
493
|
+
}, [name, action, registerAction]);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// src/components/DevToolsPanel.tsx
|
|
497
|
+
import {
|
|
498
|
+
Activity as Activity2,
|
|
499
|
+
Bug,
|
|
500
|
+
Database,
|
|
501
|
+
Maximize2,
|
|
502
|
+
Minimize2,
|
|
503
|
+
Terminal as Terminal2,
|
|
504
|
+
Wifi
|
|
505
|
+
} from "lucide-react";
|
|
506
|
+
import { useMemo as useMemo2, useState as useState5 } from "react";
|
|
507
|
+
|
|
508
|
+
// src/components/EventLog.tsx
|
|
509
|
+
import { Terminal, Trash2, Zap } from "lucide-react";
|
|
510
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
511
|
+
function EventLog({
|
|
512
|
+
events,
|
|
513
|
+
onClear,
|
|
514
|
+
title = "SDK Event Log",
|
|
515
|
+
showInfoBanner = true,
|
|
516
|
+
className = ""
|
|
517
|
+
}) {
|
|
518
|
+
const sdkEventCount = events.filter((e) => e.isSDKEvent).length;
|
|
519
|
+
return /* @__PURE__ */ jsxs("div", { className: `h-full flex flex-col ${className}`, children: [
|
|
520
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-1.5 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800", children: [
|
|
521
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-gray-600 dark:text-gray-400", children: [
|
|
522
|
+
/* @__PURE__ */ jsx2(Terminal, { className: "w-3.5 h-3.5" }),
|
|
523
|
+
/* @__PURE__ */ jsx2("span", { children: title }),
|
|
524
|
+
events.length > 0 && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 px-1.5 py-0.5 bg-cyan-100 dark:bg-cyan-900/50 border border-cyan-200 dark:border-cyan-700/50 rounded text-[10px] text-cyan-600 dark:text-cyan-400", children: [
|
|
525
|
+
/* @__PURE__ */ jsx2(Zap, { className: "w-2.5 h-2.5" }),
|
|
526
|
+
sdkEventCount,
|
|
527
|
+
" SDK events"
|
|
528
|
+
] })
|
|
529
|
+
] }),
|
|
530
|
+
events.length > 0 && /* @__PURE__ */ jsxs(
|
|
531
|
+
"button",
|
|
532
|
+
{
|
|
533
|
+
onClick: onClear,
|
|
534
|
+
className: "flex items-center gap-1 text-[10px] text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition-colors",
|
|
535
|
+
children: [
|
|
536
|
+
/* @__PURE__ */ jsx2(Trash2, { className: "w-3 h-3" }),
|
|
537
|
+
"Clear"
|
|
538
|
+
]
|
|
539
|
+
}
|
|
540
|
+
)
|
|
541
|
+
] }),
|
|
542
|
+
showInfoBanner && /* @__PURE__ */ jsxs("div", { className: "px-3 py-1 bg-gray-100 dark:bg-gray-800/50 border-b border-gray-200 dark:border-gray-700/50 text-[10px] text-gray-500", children: [
|
|
543
|
+
"\u{1F4A1} These events are emitted by the SDK's",
|
|
544
|
+
" ",
|
|
545
|
+
/* @__PURE__ */ jsx2("code", { className: "text-gray-900 dark:text-white font-semibold", children: "action.on()" }),
|
|
546
|
+
" ",
|
|
547
|
+
"method"
|
|
548
|
+
] }),
|
|
549
|
+
/* @__PURE__ */ jsx2("div", { className: "flex-1 overflow-y-auto p-2 font-mono text-[11px] leading-relaxed", children: events.length === 0 ? /* @__PURE__ */ jsx2("div", { className: "text-gray-400 dark:text-gray-600 text-center py-4", children: /* @__PURE__ */ jsx2("p", { children: "SDK events will appear here..." }) }) : /* @__PURE__ */ jsx2("div", { className: "space-y-0.5", children: events.map((event) => /* @__PURE__ */ jsx2(EventRow, { event }, event.id)) }) })
|
|
550
|
+
] });
|
|
551
|
+
}
|
|
552
|
+
function EventRow({ event }) {
|
|
553
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex gap-2 hover:bg-gray-100 dark:hover:bg-gray-800/50 px-1 py-0.5 rounded", children: [
|
|
554
|
+
/* @__PURE__ */ jsx2("span", { className: "text-gray-400 dark:text-gray-600 flex-shrink-0", children: formatTime(event.timestamp) }),
|
|
555
|
+
event.isSDKEvent && /* @__PURE__ */ jsx2("span", { className: "flex-shrink-0 text-[9px] px-1 py-0.5 bg-cyan-100 dark:bg-cyan-900/30 text-cyan-600 dark:text-cyan-500 rounded border border-cyan-200 dark:border-cyan-800/50", children: "SDK" }),
|
|
556
|
+
event.source && /* @__PURE__ */ jsx2("span", { className: "flex-shrink-0 text-[9px] px-1 py-0.5 bg-purple-100 dark:bg-purple-900/30 text-purple-600 dark:text-purple-400 rounded border border-purple-200 dark:border-purple-800/50", children: event.source }),
|
|
557
|
+
/* @__PURE__ */ jsx2("span", { className: `flex-shrink-0 ${getEventColor(event.type)}`, children: event.type }),
|
|
558
|
+
/* @__PURE__ */ jsx2("span", { className: "text-gray-400 dark:text-gray-500", children: "\u2192" }),
|
|
559
|
+
/* @__PURE__ */ jsx2("span", { className: "text-gray-600 dark:text-gray-400 truncate", children: formatEventData(event.data) })
|
|
560
|
+
] });
|
|
561
|
+
}
|
|
562
|
+
function formatTime(timestamp) {
|
|
563
|
+
const date = new Date(timestamp);
|
|
564
|
+
const time = date.toLocaleTimeString("en-US", {
|
|
565
|
+
hour12: false,
|
|
566
|
+
hour: "2-digit",
|
|
567
|
+
minute: "2-digit",
|
|
568
|
+
second: "2-digit"
|
|
569
|
+
});
|
|
570
|
+
const ms = String(date.getMilliseconds()).padStart(3, "0");
|
|
571
|
+
return `${time}.${ms}`;
|
|
572
|
+
}
|
|
573
|
+
function getEventColor(type) {
|
|
574
|
+
if (type === "error" || type === "failed") return "text-red-400";
|
|
575
|
+
if (type === "completed") return "text-green-400";
|
|
576
|
+
if (type === "status-change") return "text-cyan-400";
|
|
577
|
+
if (type === "progress") return "text-blue-400";
|
|
578
|
+
if (type === "loading") return "text-yellow-400";
|
|
579
|
+
return "text-gray-400";
|
|
580
|
+
}
|
|
581
|
+
function formatEventData(data) {
|
|
582
|
+
if (data === null || data === void 0) return "\u2014";
|
|
583
|
+
if (typeof data === "string") return data;
|
|
584
|
+
if (typeof data === "boolean") return data ? "true" : "false";
|
|
585
|
+
if (typeof data === "object") {
|
|
586
|
+
try {
|
|
587
|
+
return JSON.stringify(data);
|
|
588
|
+
} catch {
|
|
589
|
+
return String(data);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return String(data);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// src/components/NetworkLog.tsx
|
|
596
|
+
import {
|
|
597
|
+
AlertCircle,
|
|
598
|
+
CheckCircle,
|
|
599
|
+
ChevronDown,
|
|
600
|
+
ChevronRight,
|
|
601
|
+
Clock,
|
|
602
|
+
Loader2
|
|
603
|
+
} from "lucide-react";
|
|
604
|
+
import { useState as useState2 } from "react";
|
|
605
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
606
|
+
function NetworkLog({
|
|
607
|
+
entries,
|
|
608
|
+
onClear,
|
|
609
|
+
maxHeight = "400px"
|
|
610
|
+
}) {
|
|
611
|
+
const [expandedIds, setExpandedIds] = useState2(/* @__PURE__ */ new Set());
|
|
612
|
+
const toggleExpand = (id) => {
|
|
613
|
+
setExpandedIds((prev) => {
|
|
614
|
+
const next = new Set(prev);
|
|
615
|
+
if (next.has(id)) {
|
|
616
|
+
next.delete(id);
|
|
617
|
+
} else {
|
|
618
|
+
next.add(id);
|
|
619
|
+
}
|
|
620
|
+
return next;
|
|
621
|
+
});
|
|
622
|
+
};
|
|
623
|
+
if (entries.length === 0) {
|
|
624
|
+
return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center py-8 text-gray-500 dark:text-gray-400", children: [
|
|
625
|
+
/* @__PURE__ */ jsx3(Clock, { className: "w-8 h-8 mb-2 opacity-50" }),
|
|
626
|
+
/* @__PURE__ */ jsx3("p", { className: "text-sm", children: "No API requests yet" }),
|
|
627
|
+
/* @__PURE__ */ jsx3("p", { className: "text-xs opacity-75 mt-1", children: "Requests will appear here when you use SDK methods" })
|
|
628
|
+
] });
|
|
629
|
+
}
|
|
630
|
+
return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col h-full", children: [
|
|
631
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between px-3 py-2 border-b border-gray-200 dark:border-gray-700", children: [
|
|
632
|
+
/* @__PURE__ */ jsxs2("span", { className: "text-xs font-medium text-gray-600 dark:text-gray-400", children: [
|
|
633
|
+
entries.length,
|
|
634
|
+
" request",
|
|
635
|
+
entries.length !== 1 ? "s" : ""
|
|
636
|
+
] }),
|
|
637
|
+
/* @__PURE__ */ jsx3(
|
|
638
|
+
"button",
|
|
639
|
+
{
|
|
640
|
+
onClick: onClear,
|
|
641
|
+
className: "text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200",
|
|
642
|
+
children: "Clear"
|
|
643
|
+
}
|
|
644
|
+
)
|
|
645
|
+
] }),
|
|
646
|
+
/* @__PURE__ */ jsx3("div", { className: "flex-1 overflow-y-auto", style: { maxHeight }, children: entries.map((entry) => /* @__PURE__ */ jsx3(
|
|
647
|
+
NetworkEntry,
|
|
648
|
+
{
|
|
649
|
+
entry,
|
|
650
|
+
isExpanded: expandedIds.has(entry.request.id),
|
|
651
|
+
onToggle: () => {
|
|
652
|
+
toggleExpand(entry.request.id);
|
|
653
|
+
}
|
|
654
|
+
},
|
|
655
|
+
entry.request.id
|
|
656
|
+
)) })
|
|
657
|
+
] });
|
|
658
|
+
}
|
|
659
|
+
function NetworkEntry({ entry, isExpanded, onToggle }) {
|
|
660
|
+
const { request, response, isPending, isFailed } = entry;
|
|
661
|
+
const methodColor = {
|
|
662
|
+
GET: "text-emerald-600 dark:text-emerald-400",
|
|
663
|
+
POST: "text-blue-600 dark:text-blue-400",
|
|
664
|
+
PUT: "text-amber-600 dark:text-amber-400",
|
|
665
|
+
DELETE: "text-red-600 dark:text-red-400",
|
|
666
|
+
PATCH: "text-purple-600 dark:text-purple-400"
|
|
667
|
+
}[request.method] || "text-gray-600 dark:text-gray-400";
|
|
668
|
+
const StatusIcon = isPending ? () => /* @__PURE__ */ jsx3(Loader2, { className: "w-3.5 h-3.5 animate-spin text-blue-500" }) : isFailed ? () => /* @__PURE__ */ jsx3(AlertCircle, { className: "w-3.5 h-3.5 text-red-500" }) : () => /* @__PURE__ */ jsx3(CheckCircle, { className: "w-3.5 h-3.5 text-emerald-500" });
|
|
669
|
+
const urlPath = (() => {
|
|
670
|
+
try {
|
|
671
|
+
const url = new URL(request.url, "http://localhost");
|
|
672
|
+
return url.pathname + url.search;
|
|
673
|
+
} catch {
|
|
674
|
+
return request.url;
|
|
675
|
+
}
|
|
676
|
+
})();
|
|
677
|
+
return /* @__PURE__ */ jsxs2(
|
|
678
|
+
"div",
|
|
679
|
+
{
|
|
680
|
+
className: `border-b border-gray-100 dark:border-gray-700/50 ${isFailed ? "bg-red-50/50 dark:bg-red-900/10" : ""}`,
|
|
681
|
+
children: [
|
|
682
|
+
/* @__PURE__ */ jsxs2(
|
|
683
|
+
"button",
|
|
684
|
+
{
|
|
685
|
+
onClick: onToggle,
|
|
686
|
+
className: "w-full flex items-center gap-2 px-3 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700/30 transition-colors",
|
|
687
|
+
children: [
|
|
688
|
+
isExpanded ? /* @__PURE__ */ jsx3(ChevronDown, { className: "w-3.5 h-3.5 text-gray-400 flex-shrink-0" }) : /* @__PURE__ */ jsx3(ChevronRight, { className: "w-3.5 h-3.5 text-gray-400 flex-shrink-0" }),
|
|
689
|
+
/* @__PURE__ */ jsx3(StatusIcon, {}),
|
|
690
|
+
/* @__PURE__ */ jsx3(
|
|
691
|
+
"span",
|
|
692
|
+
{
|
|
693
|
+
className: `text-xs font-mono font-bold ${methodColor} w-10 flex-shrink-0`,
|
|
694
|
+
children: request.method
|
|
695
|
+
}
|
|
696
|
+
),
|
|
697
|
+
/* @__PURE__ */ jsx3("span", { className: "flex-1 text-xs font-mono text-gray-700 dark:text-gray-300 truncate", children: urlPath }),
|
|
698
|
+
response && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
699
|
+
/* @__PURE__ */ jsx3(
|
|
700
|
+
"span",
|
|
701
|
+
{
|
|
702
|
+
className: `text-xs font-mono ${isFailed ? "text-red-600 dark:text-red-400" : "text-gray-500 dark:text-gray-400"}`,
|
|
703
|
+
children: response.status
|
|
704
|
+
}
|
|
705
|
+
),
|
|
706
|
+
/* @__PURE__ */ jsxs2("span", { className: "text-xs text-gray-400 dark:text-gray-500", children: [
|
|
707
|
+
response.duration,
|
|
708
|
+
"ms"
|
|
709
|
+
] })
|
|
710
|
+
] }),
|
|
711
|
+
request.source && /* @__PURE__ */ jsx3("span", { className: "text-[10px] px-1.5 py-0.5 bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 rounded", children: request.source })
|
|
712
|
+
]
|
|
713
|
+
}
|
|
714
|
+
),
|
|
715
|
+
isExpanded && /* @__PURE__ */ jsxs2("div", { className: "px-3 pb-3 space-y-2", children: [
|
|
716
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
717
|
+
/* @__PURE__ */ jsx3(
|
|
718
|
+
"h4",
|
|
719
|
+
{
|
|
720
|
+
style: { fontSize: "10px", lineHeight: "14px" },
|
|
721
|
+
className: "font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1",
|
|
722
|
+
children: "Request"
|
|
723
|
+
}
|
|
724
|
+
),
|
|
725
|
+
/* @__PURE__ */ jsx3("div", { className: "bg-gray-50 dark:bg-gray-800 rounded p-2 overflow-x-auto max-w-full", children: request.payload ? /* @__PURE__ */ jsx3(
|
|
726
|
+
"pre",
|
|
727
|
+
{
|
|
728
|
+
style: { fontSize: "11px", lineHeight: "16px" },
|
|
729
|
+
className: "text-gray-700 dark:text-gray-300 whitespace-pre-wrap break-all font-mono m-0",
|
|
730
|
+
children: JSON.stringify(request.payload, null, 2)
|
|
731
|
+
}
|
|
732
|
+
) : /* @__PURE__ */ jsx3(
|
|
733
|
+
"span",
|
|
734
|
+
{
|
|
735
|
+
style: { fontSize: "11px" },
|
|
736
|
+
className: "text-gray-400 italic",
|
|
737
|
+
children: "No payload"
|
|
738
|
+
}
|
|
739
|
+
) })
|
|
740
|
+
] }),
|
|
741
|
+
response && /* @__PURE__ */ jsxs2("div", { children: [
|
|
742
|
+
/* @__PURE__ */ jsxs2(
|
|
743
|
+
"h4",
|
|
744
|
+
{
|
|
745
|
+
style: { fontSize: "10px", lineHeight: "14px" },
|
|
746
|
+
className: "font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-1",
|
|
747
|
+
children: [
|
|
748
|
+
"Response",
|
|
749
|
+
response.error && /* @__PURE__ */ jsx3("span", { className: "text-red-500 ml-2", children: "Error" })
|
|
750
|
+
]
|
|
751
|
+
}
|
|
752
|
+
),
|
|
753
|
+
/* @__PURE__ */ jsx3(
|
|
754
|
+
"div",
|
|
755
|
+
{
|
|
756
|
+
className: `rounded p-2 overflow-x-auto max-w-full ${isFailed ? "bg-red-50 dark:bg-red-900/20" : "bg-gray-50 dark:bg-gray-800"}`,
|
|
757
|
+
children: response.error ? /* @__PURE__ */ jsx3(
|
|
758
|
+
"pre",
|
|
759
|
+
{
|
|
760
|
+
style: { fontSize: "11px", lineHeight: "16px" },
|
|
761
|
+
className: "text-red-600 dark:text-red-400 whitespace-pre-wrap break-all font-mono m-0",
|
|
762
|
+
children: response.error
|
|
763
|
+
}
|
|
764
|
+
) : response.data ? /* @__PURE__ */ jsx3(
|
|
765
|
+
"pre",
|
|
766
|
+
{
|
|
767
|
+
style: { fontSize: "11px", lineHeight: "16px" },
|
|
768
|
+
className: "text-gray-700 dark:text-gray-300 whitespace-pre-wrap break-all font-mono m-0",
|
|
769
|
+
children: JSON.stringify(response.data, null, 2)
|
|
770
|
+
}
|
|
771
|
+
) : /* @__PURE__ */ jsx3(
|
|
772
|
+
"span",
|
|
773
|
+
{
|
|
774
|
+
style: { fontSize: "11px" },
|
|
775
|
+
className: "text-gray-400 italic",
|
|
776
|
+
children: "No data"
|
|
777
|
+
}
|
|
778
|
+
)
|
|
779
|
+
}
|
|
780
|
+
)
|
|
781
|
+
] }),
|
|
782
|
+
isPending && /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 text-xs text-blue-600 dark:text-blue-400", children: [
|
|
783
|
+
/* @__PURE__ */ jsx3(Loader2, { className: "w-3 h-3 animate-spin" }),
|
|
784
|
+
/* @__PURE__ */ jsx3("span", { children: "Waiting for response..." })
|
|
785
|
+
] })
|
|
786
|
+
] })
|
|
787
|
+
]
|
|
788
|
+
}
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// src/components/ReducerLog.tsx
|
|
793
|
+
import { Activity, ChevronDown as ChevronDown2, ChevronRight as ChevronRight2, Trash2 as Trash22 } from "lucide-react";
|
|
794
|
+
import { useState as useState3 } from "react";
|
|
795
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
796
|
+
function ReducerLog({ logs, onClear, className = "" }) {
|
|
797
|
+
return /* @__PURE__ */ jsxs3("div", { className: `h-full flex flex-col ${className}`, children: [
|
|
798
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between px-3 py-1.5 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800", children: [
|
|
799
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 text-xs text-gray-600 dark:text-gray-400", children: [
|
|
800
|
+
/* @__PURE__ */ jsx4(Activity, { className: "w-3.5 h-3.5" }),
|
|
801
|
+
/* @__PURE__ */ jsx4("span", { children: "Action Log" }),
|
|
802
|
+
logs.length > 0 && /* @__PURE__ */ jsxs3("span", { className: "px-1.5 py-0.5 bg-gray-200 dark:bg-gray-700 rounded text-[10px]", children: [
|
|
803
|
+
logs.length,
|
|
804
|
+
" actions"
|
|
805
|
+
] })
|
|
806
|
+
] }),
|
|
807
|
+
logs.length > 0 && /* @__PURE__ */ jsxs3(
|
|
808
|
+
"button",
|
|
809
|
+
{
|
|
810
|
+
onClick: onClear,
|
|
811
|
+
className: "flex items-center gap-1 text-[10px] text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition-colors",
|
|
812
|
+
children: [
|
|
813
|
+
/* @__PURE__ */ jsx4(Trash22, { className: "w-3 h-3" }),
|
|
814
|
+
"Clear"
|
|
815
|
+
]
|
|
816
|
+
}
|
|
817
|
+
)
|
|
818
|
+
] }),
|
|
819
|
+
/* @__PURE__ */ jsx4("div", { className: "flex-1 overflow-y-auto", children: logs.length === 0 ? /* @__PURE__ */ jsx4("div", { className: "text-gray-400 dark:text-gray-600 text-center py-4 text-xs", children: /* @__PURE__ */ jsx4("p", { children: "Reducer actions will appear here..." }) }) : /* @__PURE__ */ jsx4("div", { className: "divide-y divide-gray-200 dark:divide-gray-800", children: logs.map((log) => /* @__PURE__ */ jsx4(LogEntry, { log }, log.id)) }) })
|
|
820
|
+
] });
|
|
821
|
+
}
|
|
822
|
+
function LogEntry({ log }) {
|
|
823
|
+
const [isExpanded, setIsExpanded] = useState3(false);
|
|
824
|
+
return /* @__PURE__ */ jsxs3("div", { className: "text-xs", children: [
|
|
825
|
+
/* @__PURE__ */ jsxs3(
|
|
826
|
+
"button",
|
|
827
|
+
{
|
|
828
|
+
onClick: () => {
|
|
829
|
+
setIsExpanded(!isExpanded);
|
|
830
|
+
},
|
|
831
|
+
className: "w-full flex items-center gap-2 px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-800/50 transition-colors",
|
|
832
|
+
children: [
|
|
833
|
+
isExpanded ? /* @__PURE__ */ jsx4(ChevronDown2, { className: "w-3 h-3 text-gray-400 dark:text-gray-500" }) : /* @__PURE__ */ jsx4(ChevronRight2, { className: "w-3 h-3 text-gray-400 dark:text-gray-500" }),
|
|
834
|
+
/* @__PURE__ */ jsx4("span", { className: "text-gray-400 dark:text-gray-600 flex-shrink-0 font-mono", children: formatTime2(log.timestamp) }),
|
|
835
|
+
/* @__PURE__ */ jsx4("span", { className: "text-cyan-600 dark:text-cyan-400 font-medium", children: log.action.type }),
|
|
836
|
+
log.action.payload !== void 0 && /* @__PURE__ */ jsx4("span", { className: "text-gray-500 truncate", children: formatPayload(log.action.payload) })
|
|
837
|
+
]
|
|
838
|
+
}
|
|
839
|
+
),
|
|
840
|
+
isExpanded && /* @__PURE__ */ jsxs3("div", { className: "px-3 pb-3 space-y-2", children: [
|
|
841
|
+
log.action.payload !== void 0 && /* @__PURE__ */ jsxs3("div", { children: [
|
|
842
|
+
/* @__PURE__ */ jsx4("div", { className: "text-[10px] text-gray-500 mb-1", children: "Payload" }),
|
|
843
|
+
/* @__PURE__ */ jsx4("pre", { className: "p-2 bg-gray-100 dark:bg-gray-800 rounded text-[10px] font-mono text-amber-600 dark:text-yellow-300 overflow-x-auto", children: JSON.stringify(log.action.payload, null, 2) })
|
|
844
|
+
] }),
|
|
845
|
+
/* @__PURE__ */ jsxs3("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
846
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
847
|
+
/* @__PURE__ */ jsx4("div", { className: "text-[10px] text-gray-500 mb-1", children: "Before" }),
|
|
848
|
+
/* @__PURE__ */ jsx4("pre", { className: "p-2 bg-gray-100 dark:bg-gray-800 rounded text-[10px] font-mono text-gray-600 dark:text-gray-400 overflow-x-auto max-h-32 overflow-y-auto", children: JSON.stringify(log.prevState, null, 2) })
|
|
849
|
+
] }),
|
|
850
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
851
|
+
/* @__PURE__ */ jsx4("div", { className: "text-[10px] text-gray-500 mb-1", children: "After" }),
|
|
852
|
+
/* @__PURE__ */ jsx4("pre", { className: "p-2 bg-gray-100 dark:bg-gray-800 rounded text-[10px] font-mono text-green-600 dark:text-green-400 overflow-x-auto max-h-32 overflow-y-auto", children: JSON.stringify(log.nextState, null, 2) })
|
|
853
|
+
] })
|
|
854
|
+
] })
|
|
855
|
+
] })
|
|
856
|
+
] });
|
|
857
|
+
}
|
|
858
|
+
function formatTime2(timestamp) {
|
|
859
|
+
const date = new Date(timestamp);
|
|
860
|
+
return date.toLocaleTimeString("en-US", {
|
|
861
|
+
hour12: false,
|
|
862
|
+
hour: "2-digit",
|
|
863
|
+
minute: "2-digit",
|
|
864
|
+
second: "2-digit"
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
function formatPayload(payload) {
|
|
868
|
+
if (payload === null || payload === void 0) return "";
|
|
869
|
+
if (typeof payload === "string") return `"${payload}"`;
|
|
870
|
+
if (typeof payload === "object") {
|
|
871
|
+
try {
|
|
872
|
+
const str = JSON.stringify(payload);
|
|
873
|
+
return str.length > 50 ? str.slice(0, 50) + "..." : str;
|
|
874
|
+
} catch {
|
|
875
|
+
return String(payload);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
return String(payload);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
// src/components/StateInspector.tsx
|
|
882
|
+
import { Check, ChevronDown as ChevronDown3, ChevronRight as ChevronRight3, Copy } from "lucide-react";
|
|
883
|
+
import { useCallback as useCallback2, useState as useState4 } from "react";
|
|
884
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
885
|
+
function StateInspector({
|
|
886
|
+
state,
|
|
887
|
+
title = "State",
|
|
888
|
+
defaultExpandDepth = 2,
|
|
889
|
+
className = ""
|
|
890
|
+
}) {
|
|
891
|
+
const [isExpanded, setIsExpanded] = useState4(true);
|
|
892
|
+
const [copied, setCopied] = useState4(false);
|
|
893
|
+
const handleCopy = useCallback2(async () => {
|
|
894
|
+
try {
|
|
895
|
+
await navigator.clipboard.writeText(JSON.stringify(state, null, 2));
|
|
896
|
+
setCopied(true);
|
|
897
|
+
setTimeout(() => {
|
|
898
|
+
setCopied(false);
|
|
899
|
+
}, 2e3);
|
|
900
|
+
} catch (err) {
|
|
901
|
+
console.error("Failed to copy:", err);
|
|
902
|
+
}
|
|
903
|
+
}, [state]);
|
|
904
|
+
return /* @__PURE__ */ jsxs4(
|
|
905
|
+
"div",
|
|
906
|
+
{
|
|
907
|
+
className: `bg-gray-50 dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden h-full flex flex-col ${className}`,
|
|
908
|
+
children: [
|
|
909
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex-shrink-0 flex items-center justify-between px-3 py-2 bg-gray-100 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700", children: [
|
|
910
|
+
/* @__PURE__ */ jsxs4(
|
|
911
|
+
"button",
|
|
912
|
+
{
|
|
913
|
+
onClick: () => {
|
|
914
|
+
setIsExpanded(!isExpanded);
|
|
915
|
+
},
|
|
916
|
+
className: "flex items-center gap-2 text-sm font-medium text-gray-700 dark:text-gray-200 hover:text-gray-900 dark:hover:text-white",
|
|
917
|
+
children: [
|
|
918
|
+
isExpanded ? /* @__PURE__ */ jsx5(ChevronDown3, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx5(ChevronRight3, { className: "w-4 h-4" }),
|
|
919
|
+
title
|
|
920
|
+
]
|
|
921
|
+
}
|
|
922
|
+
),
|
|
923
|
+
/* @__PURE__ */ jsx5(
|
|
924
|
+
"button",
|
|
925
|
+
{
|
|
926
|
+
onClick: () => {
|
|
927
|
+
void handleCopy();
|
|
928
|
+
},
|
|
929
|
+
className: "flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors",
|
|
930
|
+
title: "Copy state as JSON",
|
|
931
|
+
children: copied ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
932
|
+
/* @__PURE__ */ jsx5(Check, { className: "w-3 h-3 text-green-500 dark:text-green-400" }),
|
|
933
|
+
/* @__PURE__ */ jsx5("span", { className: "text-green-500 dark:text-green-400", children: "Copied" })
|
|
934
|
+
] }) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
935
|
+
/* @__PURE__ */ jsx5(Copy, { className: "w-3 h-3" }),
|
|
936
|
+
"Copy"
|
|
937
|
+
] })
|
|
938
|
+
}
|
|
939
|
+
)
|
|
940
|
+
] }),
|
|
941
|
+
isExpanded && /* @__PURE__ */ jsx5("div", { className: "p-3 flex-1 overflow-auto", children: /* @__PURE__ */ jsx5(StateTree, { value: state, defaultExpandDepth }) })
|
|
942
|
+
]
|
|
943
|
+
}
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
function StateTree({
|
|
947
|
+
value,
|
|
948
|
+
depth = 0,
|
|
949
|
+
keyName,
|
|
950
|
+
defaultExpandDepth = 2
|
|
951
|
+
}) {
|
|
952
|
+
const [isOpen, setIsOpen] = useState4(depth < defaultExpandDepth);
|
|
953
|
+
const indent = depth * 12;
|
|
954
|
+
const isExpandable = value !== null && typeof value === "object";
|
|
955
|
+
if (!isExpandable) {
|
|
956
|
+
return /* @__PURE__ */ jsxs4(
|
|
957
|
+
"div",
|
|
958
|
+
{
|
|
959
|
+
className: "flex items-baseline gap-1 font-mono text-xs",
|
|
960
|
+
style: { marginLeft: indent },
|
|
961
|
+
children: [
|
|
962
|
+
keyName && /* @__PURE__ */ jsxs4("span", { className: "text-purple-600 dark:text-purple-400", children: [
|
|
963
|
+
keyName,
|
|
964
|
+
":"
|
|
965
|
+
] }),
|
|
966
|
+
/* @__PURE__ */ jsx5(ValueDisplay, { value })
|
|
967
|
+
]
|
|
968
|
+
}
|
|
969
|
+
);
|
|
970
|
+
}
|
|
971
|
+
if (Array.isArray(value)) {
|
|
972
|
+
if (value.length === 0) {
|
|
973
|
+
return /* @__PURE__ */ jsxs4("div", { className: "font-mono text-xs", style: { marginLeft: indent }, children: [
|
|
974
|
+
keyName && /* @__PURE__ */ jsxs4("span", { className: "text-purple-600 dark:text-purple-400", children: [
|
|
975
|
+
keyName,
|
|
976
|
+
":",
|
|
977
|
+
" "
|
|
978
|
+
] }),
|
|
979
|
+
/* @__PURE__ */ jsx5("span", { className: "text-gray-400 dark:text-gray-500", children: "[]" })
|
|
980
|
+
] });
|
|
981
|
+
}
|
|
982
|
+
return /* @__PURE__ */ jsxs4("div", { style: { marginLeft: indent }, children: [
|
|
983
|
+
/* @__PURE__ */ jsxs4(
|
|
984
|
+
"button",
|
|
985
|
+
{
|
|
986
|
+
onClick: () => {
|
|
987
|
+
setIsOpen(!isOpen);
|
|
988
|
+
},
|
|
989
|
+
className: "flex items-center gap-1 font-mono text-xs hover:bg-gray-200 dark:hover:bg-gray-800 rounded px-1 -ml-1",
|
|
990
|
+
children: [
|
|
991
|
+
isOpen ? /* @__PURE__ */ jsx5(ChevronDown3, { className: "w-3 h-3 text-gray-400 dark:text-gray-500" }) : /* @__PURE__ */ jsx5(ChevronRight3, { className: "w-3 h-3 text-gray-400 dark:text-gray-500" }),
|
|
992
|
+
keyName && /* @__PURE__ */ jsxs4("span", { className: "text-purple-600 dark:text-purple-400", children: [
|
|
993
|
+
keyName,
|
|
994
|
+
":"
|
|
995
|
+
] }),
|
|
996
|
+
/* @__PURE__ */ jsxs4("span", { className: "text-gray-500", children: [
|
|
997
|
+
"[",
|
|
998
|
+
value.length,
|
|
999
|
+
"]"
|
|
1000
|
+
] })
|
|
1001
|
+
]
|
|
1002
|
+
}
|
|
1003
|
+
),
|
|
1004
|
+
isOpen && /* @__PURE__ */ jsx5("div", { className: "ml-2", children: value.map((item, i) => /* @__PURE__ */ jsx5(
|
|
1005
|
+
StateTree,
|
|
1006
|
+
{
|
|
1007
|
+
value: item,
|
|
1008
|
+
depth: depth + 1,
|
|
1009
|
+
keyName: String(i),
|
|
1010
|
+
defaultExpandDepth
|
|
1011
|
+
},
|
|
1012
|
+
i
|
|
1013
|
+
)) })
|
|
1014
|
+
] });
|
|
1015
|
+
}
|
|
1016
|
+
const entries = Object.entries(value);
|
|
1017
|
+
if (entries.length === 0) {
|
|
1018
|
+
return /* @__PURE__ */ jsxs4("div", { className: "font-mono text-xs", style: { marginLeft: indent }, children: [
|
|
1019
|
+
keyName && /* @__PURE__ */ jsxs4("span", { className: "text-purple-600 dark:text-purple-400", children: [
|
|
1020
|
+
keyName,
|
|
1021
|
+
":",
|
|
1022
|
+
" "
|
|
1023
|
+
] }),
|
|
1024
|
+
/* @__PURE__ */ jsx5("span", { className: "text-gray-400 dark:text-gray-500", children: "{}" })
|
|
1025
|
+
] });
|
|
1026
|
+
}
|
|
1027
|
+
return /* @__PURE__ */ jsxs4("div", { style: { marginLeft: indent }, children: [
|
|
1028
|
+
/* @__PURE__ */ jsxs4(
|
|
1029
|
+
"button",
|
|
1030
|
+
{
|
|
1031
|
+
onClick: () => {
|
|
1032
|
+
setIsOpen(!isOpen);
|
|
1033
|
+
},
|
|
1034
|
+
className: "flex items-center gap-1 font-mono text-xs hover:bg-gray-200 dark:hover:bg-gray-800 rounded px-1 -ml-1",
|
|
1035
|
+
children: [
|
|
1036
|
+
isOpen ? /* @__PURE__ */ jsx5(ChevronDown3, { className: "w-3 h-3 text-gray-400 dark:text-gray-500" }) : /* @__PURE__ */ jsx5(ChevronRight3, { className: "w-3 h-3 text-gray-400 dark:text-gray-500" }),
|
|
1037
|
+
keyName && /* @__PURE__ */ jsxs4("span", { className: "text-purple-600 dark:text-purple-400", children: [
|
|
1038
|
+
keyName,
|
|
1039
|
+
":"
|
|
1040
|
+
] }),
|
|
1041
|
+
/* @__PURE__ */ jsx5("span", { className: "text-gray-500", children: `{${entries.length}}` })
|
|
1042
|
+
]
|
|
1043
|
+
}
|
|
1044
|
+
),
|
|
1045
|
+
isOpen && /* @__PURE__ */ jsx5("div", { className: "ml-2", children: entries.map(([key, val]) => /* @__PURE__ */ jsx5(
|
|
1046
|
+
StateTree,
|
|
1047
|
+
{
|
|
1048
|
+
value: val,
|
|
1049
|
+
depth: depth + 1,
|
|
1050
|
+
keyName: key,
|
|
1051
|
+
defaultExpandDepth
|
|
1052
|
+
},
|
|
1053
|
+
key
|
|
1054
|
+
)) })
|
|
1055
|
+
] });
|
|
1056
|
+
}
|
|
1057
|
+
function ValueDisplay({ value }) {
|
|
1058
|
+
if (value === null) {
|
|
1059
|
+
return /* @__PURE__ */ jsx5("span", { className: "text-gray-400 dark:text-gray-500", children: "null" });
|
|
1060
|
+
}
|
|
1061
|
+
if (value === void 0) {
|
|
1062
|
+
return /* @__PURE__ */ jsx5("span", { className: "text-gray-400 dark:text-gray-500", children: "undefined" });
|
|
1063
|
+
}
|
|
1064
|
+
if (typeof value === "boolean") {
|
|
1065
|
+
return /* @__PURE__ */ jsx5("span", { className: "text-blue-600 dark:text-blue-400", children: String(value) });
|
|
1066
|
+
}
|
|
1067
|
+
if (typeof value === "number") {
|
|
1068
|
+
return /* @__PURE__ */ jsx5("span", { className: "text-green-600 dark:text-green-400", children: value });
|
|
1069
|
+
}
|
|
1070
|
+
if (typeof value === "string") {
|
|
1071
|
+
const display = value.length > 50 ? `${value.slice(0, 50)}...` : value;
|
|
1072
|
+
return /* @__PURE__ */ jsxs4("span", { className: "text-amber-600 dark:text-yellow-300", children: [
|
|
1073
|
+
'"',
|
|
1074
|
+
display,
|
|
1075
|
+
'"'
|
|
1076
|
+
] });
|
|
1077
|
+
}
|
|
1078
|
+
return /* @__PURE__ */ jsx5("span", { className: "text-gray-600 dark:text-gray-400", children: String(value) });
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// src/components/DevToolsPanel.tsx
|
|
1082
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1083
|
+
function DevToolsPanel({
|
|
1084
|
+
events,
|
|
1085
|
+
onClearEvents,
|
|
1086
|
+
reducerLogs = [],
|
|
1087
|
+
onClearReducerLogs,
|
|
1088
|
+
state = {},
|
|
1089
|
+
networkLog = [],
|
|
1090
|
+
onClearNetworkLog,
|
|
1091
|
+
initialTab = "events",
|
|
1092
|
+
defaultMinimized = false,
|
|
1093
|
+
className = "",
|
|
1094
|
+
title = "DevTools",
|
|
1095
|
+
onMinimize
|
|
1096
|
+
}) {
|
|
1097
|
+
const [activeTab, setActiveTab] = useState5(initialTab);
|
|
1098
|
+
const [isMinimized, setIsMinimized] = useState5(defaultMinimized);
|
|
1099
|
+
const handleMinimize = () => {
|
|
1100
|
+
if (onMinimize) {
|
|
1101
|
+
onMinimize();
|
|
1102
|
+
} else {
|
|
1103
|
+
setIsMinimized(true);
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
1106
|
+
const tabs = useMemo2(
|
|
1107
|
+
() => [
|
|
1108
|
+
{
|
|
1109
|
+
id: "events",
|
|
1110
|
+
label: "Events",
|
|
1111
|
+
icon: Terminal2,
|
|
1112
|
+
count: events.length
|
|
1113
|
+
},
|
|
1114
|
+
{
|
|
1115
|
+
id: "network",
|
|
1116
|
+
label: "Network",
|
|
1117
|
+
icon: Wifi,
|
|
1118
|
+
count: networkLog.length
|
|
1119
|
+
},
|
|
1120
|
+
{
|
|
1121
|
+
id: "reducer",
|
|
1122
|
+
label: "Actions",
|
|
1123
|
+
icon: Activity2,
|
|
1124
|
+
count: reducerLogs.length
|
|
1125
|
+
},
|
|
1126
|
+
{
|
|
1127
|
+
id: "state",
|
|
1128
|
+
label: "State",
|
|
1129
|
+
icon: Database,
|
|
1130
|
+
count: null
|
|
1131
|
+
}
|
|
1132
|
+
],
|
|
1133
|
+
[events.length, reducerLogs.length, networkLog.length]
|
|
1134
|
+
);
|
|
1135
|
+
if (isMinimized) {
|
|
1136
|
+
return /* @__PURE__ */ jsxs5(
|
|
1137
|
+
"button",
|
|
1138
|
+
{
|
|
1139
|
+
onClick: () => {
|
|
1140
|
+
setIsMinimized(false);
|
|
1141
|
+
},
|
|
1142
|
+
className: `
|
|
1143
|
+
flex items-center gap-2 px-3 py-2 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg
|
|
1144
|
+
text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors
|
|
1145
|
+
${className}
|
|
1146
|
+
`,
|
|
1147
|
+
children: [
|
|
1148
|
+
/* @__PURE__ */ jsx6(Bug, { className: "w-4 h-4" }),
|
|
1149
|
+
/* @__PURE__ */ jsx6("span", { className: "text-xs font-medium", children: title }),
|
|
1150
|
+
events.length > 0 && /* @__PURE__ */ jsx6("span", { className: "text-[10px] px-1.5 py-0.5 bg-cyan-100 dark:bg-cyan-900/50 text-cyan-600 dark:text-cyan-400 rounded", children: events.length }),
|
|
1151
|
+
/* @__PURE__ */ jsx6(Maximize2, { className: "w-3 h-3 ml-1" })
|
|
1152
|
+
]
|
|
1153
|
+
}
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
return /* @__PURE__ */ jsxs5(
|
|
1157
|
+
"div",
|
|
1158
|
+
{
|
|
1159
|
+
className: `h-full flex flex-col bg-white dark:bg-gray-900 ${className}`,
|
|
1160
|
+
children: [
|
|
1161
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800", children: [
|
|
1162
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1 px-2", children: [
|
|
1163
|
+
/* @__PURE__ */ jsx6(Bug, { className: "w-3.5 h-3.5 text-gray-400 dark:text-gray-500" }),
|
|
1164
|
+
/* @__PURE__ */ jsx6("span", { className: "text-[10px] text-gray-500 font-medium uppercase tracking-wider", children: title })
|
|
1165
|
+
] }),
|
|
1166
|
+
/* @__PURE__ */ jsx6("div", { className: "flex-1 flex", children: tabs.map((tab) => /* @__PURE__ */ jsxs5(
|
|
1167
|
+
"button",
|
|
1168
|
+
{
|
|
1169
|
+
onClick: () => {
|
|
1170
|
+
setActiveTab(tab.id);
|
|
1171
|
+
},
|
|
1172
|
+
className: `
|
|
1173
|
+
flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium border-b-2 transition-colors
|
|
1174
|
+
${activeTab === tab.id ? "text-cyan-600 dark:text-cyan-400 border-cyan-500 dark:border-cyan-400 bg-white dark:bg-gray-900/50" : "text-gray-500 dark:text-gray-400 border-transparent hover:text-gray-700 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700/30"}
|
|
1175
|
+
`,
|
|
1176
|
+
children: [
|
|
1177
|
+
/* @__PURE__ */ jsx6(tab.icon, { className: "w-3.5 h-3.5" }),
|
|
1178
|
+
tab.label,
|
|
1179
|
+
tab.count !== null && tab.count > 0 && /* @__PURE__ */ jsx6("span", { className: "ml-1 px-1 py-0.5 bg-gray-200 dark:bg-gray-700 rounded text-[9px]", children: tab.count })
|
|
1180
|
+
]
|
|
1181
|
+
},
|
|
1182
|
+
tab.id
|
|
1183
|
+
)) }),
|
|
1184
|
+
/* @__PURE__ */ jsx6(
|
|
1185
|
+
"button",
|
|
1186
|
+
{
|
|
1187
|
+
onClick: handleMinimize,
|
|
1188
|
+
className: "px-2 py-1 text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300 transition-colors",
|
|
1189
|
+
title: "Minimize",
|
|
1190
|
+
children: /* @__PURE__ */ jsx6(Minimize2, { className: "w-3.5 h-3.5" })
|
|
1191
|
+
}
|
|
1192
|
+
)
|
|
1193
|
+
] }),
|
|
1194
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex-1 min-h-0 overflow-hidden", children: [
|
|
1195
|
+
activeTab === "events" && /* @__PURE__ */ jsx6(EventLog, { events, onClear: onClearEvents }),
|
|
1196
|
+
activeTab === "network" && /* @__PURE__ */ jsx6(
|
|
1197
|
+
NetworkLog,
|
|
1198
|
+
{
|
|
1199
|
+
entries: networkLog,
|
|
1200
|
+
onClear: onClearNetworkLog ?? (() => {
|
|
1201
|
+
})
|
|
1202
|
+
}
|
|
1203
|
+
),
|
|
1204
|
+
activeTab === "reducer" && /* @__PURE__ */ jsx6(
|
|
1205
|
+
ReducerLog,
|
|
1206
|
+
{
|
|
1207
|
+
logs: reducerLogs,
|
|
1208
|
+
onClear: onClearReducerLogs ?? (() => {
|
|
1209
|
+
})
|
|
1210
|
+
}
|
|
1211
|
+
),
|
|
1212
|
+
activeTab === "state" && /* @__PURE__ */ jsx6("div", { className: "h-full overflow-auto p-2", children: /* @__PURE__ */ jsx6(StateInspector, { state, title: "Current State" }) })
|
|
1213
|
+
] })
|
|
1214
|
+
]
|
|
1215
|
+
}
|
|
1216
|
+
);
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
// src/components/StatusBadge.tsx
|
|
1220
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1221
|
+
function StatusBadge({ status, className = "" }) {
|
|
1222
|
+
const safeStatus = typeof status === "string" ? status : String(status ?? "idle");
|
|
1223
|
+
return /* @__PURE__ */ jsx7(
|
|
1224
|
+
"span",
|
|
1225
|
+
{
|
|
1226
|
+
className: `
|
|
1227
|
+
inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium
|
|
1228
|
+
${getStatusColor(safeStatus)}
|
|
1229
|
+
${className}
|
|
1230
|
+
`,
|
|
1231
|
+
children: formatStatus(safeStatus)
|
|
1232
|
+
}
|
|
1233
|
+
);
|
|
1234
|
+
}
|
|
1235
|
+
function getStatusColor(status) {
|
|
1236
|
+
const lower = status.toLowerCase();
|
|
1237
|
+
if (lower === "idle" || lower === "not_created") {
|
|
1238
|
+
return "bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300";
|
|
1239
|
+
}
|
|
1240
|
+
if (lower.includes("address_ready") || lower === "completed") {
|
|
1241
|
+
return "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300";
|
|
1242
|
+
}
|
|
1243
|
+
if (lower.includes("needs") || lower.includes("authorization") || lower.includes("approval")) {
|
|
1244
|
+
return "bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-300";
|
|
1245
|
+
}
|
|
1246
|
+
if (lower === "confirming") {
|
|
1247
|
+
return "bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300";
|
|
1248
|
+
}
|
|
1249
|
+
if (lower === "ready") {
|
|
1250
|
+
return "bg-teal-100 text-teal-700 dark:bg-teal-900/50 dark:text-teal-300";
|
|
1251
|
+
}
|
|
1252
|
+
return "bg-teal-100 text-teal-700 dark:bg-teal-900/50 dark:text-teal-300";
|
|
1253
|
+
}
|
|
1254
|
+
function formatStatus(status) {
|
|
1255
|
+
return status.replace(/[_-]/g, " ").split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// src/components/StepIndicator.tsx
|
|
1259
|
+
import { Check as Check2 } from "lucide-react";
|
|
1260
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1261
|
+
function StepIndicator({
|
|
1262
|
+
steps,
|
|
1263
|
+
currentStep,
|
|
1264
|
+
className = ""
|
|
1265
|
+
}) {
|
|
1266
|
+
return /* @__PURE__ */ jsx8(
|
|
1267
|
+
"div",
|
|
1268
|
+
{
|
|
1269
|
+
className: `flex ${className}`,
|
|
1270
|
+
style: {
|
|
1271
|
+
width: "100%"
|
|
1272
|
+
},
|
|
1273
|
+
children: steps.map((step, index) => {
|
|
1274
|
+
const isCompleted = index < currentStep;
|
|
1275
|
+
const isCurrent = index === currentStep;
|
|
1276
|
+
const isLast = index === steps.length - 1;
|
|
1277
|
+
const circleStyles = {
|
|
1278
|
+
width: "28px",
|
|
1279
|
+
height: "28px",
|
|
1280
|
+
minWidth: "28px",
|
|
1281
|
+
minHeight: "28px",
|
|
1282
|
+
borderRadius: "6px"
|
|
1283
|
+
// Slightly rounded corners
|
|
1284
|
+
};
|
|
1285
|
+
if (isCompleted) {
|
|
1286
|
+
circleStyles.backgroundColor = "#22c55e";
|
|
1287
|
+
circleStyles.color = "white";
|
|
1288
|
+
} else if (isCurrent) {
|
|
1289
|
+
circleStyles.backgroundColor = "var(--lombard-green, #00E676)";
|
|
1290
|
+
circleStyles.color = "#111827";
|
|
1291
|
+
circleStyles.fontWeight = "bold";
|
|
1292
|
+
} else {
|
|
1293
|
+
circleStyles.backgroundColor = "#e5e7eb";
|
|
1294
|
+
circleStyles.color = "#6b7280";
|
|
1295
|
+
}
|
|
1296
|
+
return /* @__PURE__ */ jsxs6(
|
|
1297
|
+
"div",
|
|
1298
|
+
{
|
|
1299
|
+
style: {
|
|
1300
|
+
flex: 1,
|
|
1301
|
+
display: "flex",
|
|
1302
|
+
flexDirection: "column",
|
|
1303
|
+
alignItems: "center",
|
|
1304
|
+
position: "relative"
|
|
1305
|
+
},
|
|
1306
|
+
children: [
|
|
1307
|
+
!isLast && /* @__PURE__ */ jsx8(
|
|
1308
|
+
"div",
|
|
1309
|
+
{
|
|
1310
|
+
style: {
|
|
1311
|
+
position: "absolute",
|
|
1312
|
+
top: "14px",
|
|
1313
|
+
// Half of circle height (28/2)
|
|
1314
|
+
left: "50%",
|
|
1315
|
+
right: "-50%",
|
|
1316
|
+
height: "2px",
|
|
1317
|
+
backgroundColor: isCompleted ? "#22c55e" : "#e5e7eb",
|
|
1318
|
+
zIndex: 0
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
),
|
|
1322
|
+
/* @__PURE__ */ jsx8(
|
|
1323
|
+
"div",
|
|
1324
|
+
{
|
|
1325
|
+
className: "flex items-center justify-center text-sm font-medium transition-colors",
|
|
1326
|
+
style: {
|
|
1327
|
+
...circleStyles,
|
|
1328
|
+
position: "relative",
|
|
1329
|
+
zIndex: 1
|
|
1330
|
+
},
|
|
1331
|
+
children: isCompleted ? /* @__PURE__ */ jsx8(Check2, { style: { width: "16px", height: "16px" } }) : index + 1
|
|
1332
|
+
}
|
|
1333
|
+
),
|
|
1334
|
+
/* @__PURE__ */ jsx8(
|
|
1335
|
+
"span",
|
|
1336
|
+
{
|
|
1337
|
+
className: "text-xs text-center",
|
|
1338
|
+
style: {
|
|
1339
|
+
marginTop: "6px",
|
|
1340
|
+
color: isCompleted ? "#16a34a" : isCurrent ? "#111827" : "#9ca3af",
|
|
1341
|
+
// gray-400
|
|
1342
|
+
fontWeight: isCompleted || isCurrent ? 500 : 400
|
|
1343
|
+
},
|
|
1344
|
+
children: step.label
|
|
1345
|
+
}
|
|
1346
|
+
)
|
|
1347
|
+
]
|
|
1348
|
+
},
|
|
1349
|
+
step.id
|
|
1350
|
+
);
|
|
1351
|
+
})
|
|
1352
|
+
}
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
function createSteps(configs, currentStep) {
|
|
1356
|
+
return configs.map((config, index) => ({
|
|
1357
|
+
...config,
|
|
1358
|
+
status: index < currentStep ? "completed" : index === currentStep ? "current" : "pending"
|
|
1359
|
+
}));
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// src/hooks/useDevTools.ts
|
|
1363
|
+
import { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo3, useRef, useState as useState6 } from "react";
|
|
1364
|
+
function useDevTools(config) {
|
|
1365
|
+
const bridgeRef = useRef(null);
|
|
1366
|
+
if (!bridgeRef.current) {
|
|
1367
|
+
bridgeRef.current = config ? new DevToolsBridge(config) : getDevToolsBridge();
|
|
1368
|
+
}
|
|
1369
|
+
const bridge = bridgeRef.current;
|
|
1370
|
+
const [events, setEvents] = useState6(
|
|
1371
|
+
() => bridge.getEvents()
|
|
1372
|
+
);
|
|
1373
|
+
const [actions, setActions] = useState6(
|
|
1374
|
+
() => bridge.getActions()
|
|
1375
|
+
);
|
|
1376
|
+
useEffect2(() => {
|
|
1377
|
+
const unsubEvent = bridge.onEvent(() => {
|
|
1378
|
+
setEvents(bridge.getEvents());
|
|
1379
|
+
});
|
|
1380
|
+
const unsubState = bridge.onStateChange((newActions) => {
|
|
1381
|
+
setActions(newActions);
|
|
1382
|
+
});
|
|
1383
|
+
return () => {
|
|
1384
|
+
unsubEvent();
|
|
1385
|
+
unsubState();
|
|
1386
|
+
};
|
|
1387
|
+
}, [bridge]);
|
|
1388
|
+
const registerAction = useCallback3(
|
|
1389
|
+
(name, action, options) => {
|
|
1390
|
+
return bridge.registerAction(name, action, options);
|
|
1391
|
+
},
|
|
1392
|
+
[bridge]
|
|
1393
|
+
);
|
|
1394
|
+
const unregisterAction = useCallback3(
|
|
1395
|
+
(name) => {
|
|
1396
|
+
bridge.unregisterAction(name);
|
|
1397
|
+
},
|
|
1398
|
+
[bridge]
|
|
1399
|
+
);
|
|
1400
|
+
const clearEvents = useCallback3(() => {
|
|
1401
|
+
bridge.clearEvents();
|
|
1402
|
+
setEvents([]);
|
|
1403
|
+
}, [bridge]);
|
|
1404
|
+
const clearActions = useCallback3(() => {
|
|
1405
|
+
bridge.clearActions();
|
|
1406
|
+
setActions(/* @__PURE__ */ new Map());
|
|
1407
|
+
}, [bridge]);
|
|
1408
|
+
const stateSummary = useMemo3(() => bridge.getStateSummary(), [actions]);
|
|
1409
|
+
return {
|
|
1410
|
+
events,
|
|
1411
|
+
actions,
|
|
1412
|
+
stateSummary,
|
|
1413
|
+
registerAction,
|
|
1414
|
+
unregisterAction,
|
|
1415
|
+
clearEvents,
|
|
1416
|
+
clearActions,
|
|
1417
|
+
bridge
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
function useMonitoredAction(name, createAction, deps) {
|
|
1421
|
+
const { registerAction } = useDevTools();
|
|
1422
|
+
const [action, setAction] = useState6(null);
|
|
1423
|
+
useEffect2(() => {
|
|
1424
|
+
const newAction = createAction();
|
|
1425
|
+
setAction(newAction);
|
|
1426
|
+
const unregister = registerAction(name, newAction);
|
|
1427
|
+
return () => {
|
|
1428
|
+
unregister();
|
|
1429
|
+
setAction(null);
|
|
1430
|
+
};
|
|
1431
|
+
}, deps);
|
|
1432
|
+
return action;
|
|
1433
|
+
}
|
|
1434
|
+
function useActionEvents(action) {
|
|
1435
|
+
const [events, setEvents] = useState6([]);
|
|
1436
|
+
const [status, setStatus] = useState6(action?.status ?? "idle");
|
|
1437
|
+
const [isLoading, setIsLoading] = useState6(action?.isLoading ?? false);
|
|
1438
|
+
const [error, setError] = useState6(action?.error ?? null);
|
|
1439
|
+
const eventIdRef = useRef(0);
|
|
1440
|
+
useEffect2(() => {
|
|
1441
|
+
if (!action) return;
|
|
1442
|
+
const unsubscribers = [];
|
|
1443
|
+
unsubscribers.push(
|
|
1444
|
+
action.on("status-change", (newStatus) => {
|
|
1445
|
+
setStatus(String(newStatus));
|
|
1446
|
+
setEvents((prev) => [
|
|
1447
|
+
...prev,
|
|
1448
|
+
{
|
|
1449
|
+
id: String(++eventIdRef.current),
|
|
1450
|
+
type: "status-change",
|
|
1451
|
+
timestamp: Date.now(),
|
|
1452
|
+
data: newStatus,
|
|
1453
|
+
isSDKEvent: true
|
|
1454
|
+
}
|
|
1455
|
+
]);
|
|
1456
|
+
})
|
|
1457
|
+
);
|
|
1458
|
+
unsubscribers.push(
|
|
1459
|
+
action.on("loading", (loading) => {
|
|
1460
|
+
setIsLoading(Boolean(loading));
|
|
1461
|
+
setEvents((prev) => [
|
|
1462
|
+
...prev,
|
|
1463
|
+
{
|
|
1464
|
+
id: String(++eventIdRef.current),
|
|
1465
|
+
type: "loading",
|
|
1466
|
+
timestamp: Date.now(),
|
|
1467
|
+
data: loading,
|
|
1468
|
+
isSDKEvent: true
|
|
1469
|
+
}
|
|
1470
|
+
]);
|
|
1471
|
+
})
|
|
1472
|
+
);
|
|
1473
|
+
unsubscribers.push(
|
|
1474
|
+
action.on("error", (err) => {
|
|
1475
|
+
setError(err);
|
|
1476
|
+
setEvents((prev) => [
|
|
1477
|
+
...prev,
|
|
1478
|
+
{
|
|
1479
|
+
id: String(++eventIdRef.current),
|
|
1480
|
+
type: "error",
|
|
1481
|
+
timestamp: Date.now(),
|
|
1482
|
+
data: err,
|
|
1483
|
+
isSDKEvent: true
|
|
1484
|
+
}
|
|
1485
|
+
]);
|
|
1486
|
+
})
|
|
1487
|
+
);
|
|
1488
|
+
unsubscribers.push(
|
|
1489
|
+
action.on("completed", () => {
|
|
1490
|
+
setEvents((prev) => [
|
|
1491
|
+
...prev,
|
|
1492
|
+
{
|
|
1493
|
+
id: String(++eventIdRef.current),
|
|
1494
|
+
type: "completed",
|
|
1495
|
+
timestamp: Date.now(),
|
|
1496
|
+
data: null,
|
|
1497
|
+
isSDKEvent: true
|
|
1498
|
+
}
|
|
1499
|
+
]);
|
|
1500
|
+
})
|
|
1501
|
+
);
|
|
1502
|
+
unsubscribers.push(
|
|
1503
|
+
action.on("failed", () => {
|
|
1504
|
+
setEvents((prev) => [
|
|
1505
|
+
...prev,
|
|
1506
|
+
{
|
|
1507
|
+
id: String(++eventIdRef.current),
|
|
1508
|
+
type: "failed",
|
|
1509
|
+
timestamp: Date.now(),
|
|
1510
|
+
data: null,
|
|
1511
|
+
isSDKEvent: true
|
|
1512
|
+
}
|
|
1513
|
+
]);
|
|
1514
|
+
})
|
|
1515
|
+
);
|
|
1516
|
+
return () => {
|
|
1517
|
+
unsubscribers.forEach((unsub) => {
|
|
1518
|
+
unsub();
|
|
1519
|
+
});
|
|
1520
|
+
};
|
|
1521
|
+
}, [action]);
|
|
1522
|
+
const clearEvents = useCallback3(() => {
|
|
1523
|
+
setEvents([]);
|
|
1524
|
+
}, []);
|
|
1525
|
+
return {
|
|
1526
|
+
events,
|
|
1527
|
+
status,
|
|
1528
|
+
isLoading,
|
|
1529
|
+
error,
|
|
1530
|
+
isFailed: error !== null,
|
|
1531
|
+
clearEvents
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
// src/hooks/useMockWallet.ts
|
|
1536
|
+
import { useCallback as useCallback4, useMemo as useMemo4, useState as useState7 } from "react";
|
|
1537
|
+
var MOCK_ADDRESSES = {
|
|
1538
|
+
// Vitalik's address (properly checksummed)
|
|
1539
|
+
evm: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
|
|
1540
|
+
// Valid mainnet bitcoin address
|
|
1541
|
+
bitcoin: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
|
|
1542
|
+
// Valid Solana address
|
|
1543
|
+
solana: "DRpbCBMxVnDK7maPMxTm9dRYNLGhPEYALmJY9VvUdWTm",
|
|
1544
|
+
// Valid Sui address
|
|
1545
|
+
sui: "0x7d20dcdb2bca4f508ea9613994683eb4e76e9cc555c8e2d4f8362e10e4eca31b",
|
|
1546
|
+
// Valid Starknet address
|
|
1547
|
+
starknet: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"
|
|
1548
|
+
};
|
|
1549
|
+
var MOCK_WALLET_LIMITATIONS = {
|
|
1550
|
+
/** Cannot sign transactions */
|
|
1551
|
+
cannotSign: true,
|
|
1552
|
+
/** Steps that work with mock wallet */
|
|
1553
|
+
supportedSteps: ["Create", "Prepare"],
|
|
1554
|
+
/** Steps that require real wallet */
|
|
1555
|
+
unsupportedSteps: ["Authorize", "Sign", "Execute"],
|
|
1556
|
+
/** User-friendly message */
|
|
1557
|
+
message: "Mock wallet cannot sign transactions. Connect a real wallet for full flow.",
|
|
1558
|
+
/** Short message for badges */
|
|
1559
|
+
shortMessage: "Cannot sign - connect real wallet"
|
|
1560
|
+
};
|
|
1561
|
+
var STORAGE_KEY = "lombard-devtools-mock-wallet";
|
|
1562
|
+
function useMockWallet() {
|
|
1563
|
+
const [isEnabled, setIsEnabled] = useState7(() => {
|
|
1564
|
+
if (typeof window === "undefined") return false;
|
|
1565
|
+
return localStorage.getItem(STORAGE_KEY) === "true";
|
|
1566
|
+
});
|
|
1567
|
+
const [chainId, setChainId] = useState7(1);
|
|
1568
|
+
const enable = useCallback4(() => {
|
|
1569
|
+
setIsEnabled(true);
|
|
1570
|
+
if (typeof window !== "undefined") {
|
|
1571
|
+
localStorage.setItem(STORAGE_KEY, "true");
|
|
1572
|
+
}
|
|
1573
|
+
}, []);
|
|
1574
|
+
const disable = useCallback4(() => {
|
|
1575
|
+
setIsEnabled(false);
|
|
1576
|
+
if (typeof window !== "undefined") {
|
|
1577
|
+
localStorage.setItem(STORAGE_KEY, "false");
|
|
1578
|
+
}
|
|
1579
|
+
}, []);
|
|
1580
|
+
const toggle = useCallback4(() => {
|
|
1581
|
+
setIsEnabled((prev) => {
|
|
1582
|
+
const newValue = !prev;
|
|
1583
|
+
if (typeof window !== "undefined") {
|
|
1584
|
+
localStorage.setItem(STORAGE_KEY, String(newValue));
|
|
1585
|
+
}
|
|
1586
|
+
return newValue;
|
|
1587
|
+
});
|
|
1588
|
+
}, []);
|
|
1589
|
+
const setChain = useCallback4((newChainId) => {
|
|
1590
|
+
setChainId(newChainId);
|
|
1591
|
+
}, []);
|
|
1592
|
+
const getAddress = useCallback4((chainType) => {
|
|
1593
|
+
return MOCK_ADDRESSES[chainType];
|
|
1594
|
+
}, []);
|
|
1595
|
+
return useMemo4(
|
|
1596
|
+
() => ({
|
|
1597
|
+
isEnabled,
|
|
1598
|
+
address: MOCK_ADDRESSES.evm,
|
|
1599
|
+
// Default to EVM
|
|
1600
|
+
chainId,
|
|
1601
|
+
canSign: false,
|
|
1602
|
+
// Mock wallet cannot sign - always false
|
|
1603
|
+
enable,
|
|
1604
|
+
disable,
|
|
1605
|
+
toggle,
|
|
1606
|
+
setChain,
|
|
1607
|
+
getAddress
|
|
1608
|
+
}),
|
|
1609
|
+
[isEnabled, chainId, enable, disable, toggle, setChain, getAddress]
|
|
1610
|
+
);
|
|
1611
|
+
}
|
|
1612
|
+
function getMockAddress(chainType) {
|
|
1613
|
+
return MOCK_ADDRESSES[chainType];
|
|
1614
|
+
}
|
|
1615
|
+
function requiresRealWallet(stepLabel) {
|
|
1616
|
+
const lower = stepLabel.toLowerCase();
|
|
1617
|
+
return lower.includes("authorize") || lower.includes("sign") || lower.includes("execute");
|
|
1618
|
+
}
|
|
1619
|
+
export {
|
|
1620
|
+
DEFAULT_DEVTOOLS_CONFIG,
|
|
1621
|
+
DevToolsBridge,
|
|
1622
|
+
DevToolsPanel,
|
|
1623
|
+
DevToolsProvider,
|
|
1624
|
+
EventLog,
|
|
1625
|
+
MOCK_ADDRESSES,
|
|
1626
|
+
MOCK_WALLET_LIMITATIONS,
|
|
1627
|
+
NetworkLog,
|
|
1628
|
+
ReducerLog,
|
|
1629
|
+
StateInspector,
|
|
1630
|
+
StatusBadge,
|
|
1631
|
+
StepIndicator,
|
|
1632
|
+
createSteps,
|
|
1633
|
+
getDevToolsBridge,
|
|
1634
|
+
getMockAddress,
|
|
1635
|
+
requiresRealWallet,
|
|
1636
|
+
resetDevToolsBridge,
|
|
1637
|
+
useActionEvents,
|
|
1638
|
+
useDevTools,
|
|
1639
|
+
useDevToolsContext,
|
|
1640
|
+
useMockWallet,
|
|
1641
|
+
useMonitoredAction,
|
|
1642
|
+
useRegisterAction
|
|
1643
|
+
};
|
|
1644
|
+
//# sourceMappingURL=index.js.map
|