How the Floating Simulate Panel in law-chain-hot/websocket-devtools Enables WebSocket Message Editing and Simulation
The floating simulate panel provides a draggable, resizable React component that lets developers compose JSON payloads, persist editor state across sessions, and send messages as either incoming or outgoing WebSocket traffic through an imperative openPanel API.
The law-chain-hot/websocket-devtools repository is a browser extension that adds debugging capabilities to WebSocket connections. At its core, the floating simulate panel delivers an in-page sandbox where developers can craft, test, and reuse WebSocket messages without leaving the inspected tab. This self-contained UI widget combines a JSON editor, persistence layer, and simulation controls into a movable window that integrates seamlessly with the Chrome DevTools workflow.
Architecture of the Floating Simulate Panel
The panel follows a layered React architecture that separates UI presentation from state management and animation logic.
Component Hierarchy
The implementation spans four primary files in the src directory:
FloatingSimulate.jsx– A wrapper component that usesforwardRefanduseImperativeHandleto expose anopenPanelmethod to parent componentsSimulateMessagePanel.jsx– The core container handling the draggable window, tab navigation, and message transmission logicSimulateEditorTab.jsx– The editing interface featuring aJsonViewercomponent and simulation trigger buttonsusePanelManager.js– A custom hook encapsulating open/toggle/minimize logic andlocalStoragesynchronization
State Management with usePanelManager
The usePanelManager hook centralizes panel visibility and geometry state. When openPanel is invoked, it retrieves persisted dimensions from localStorage["simulateMessagePanel"], validates coordinates against viewport boundaries using useWindowConstraints, and triggers the opening animation.
// src/hooks/usePanelManager.js
const openPanel = useCallback(() => {
if (isWindowOpen || isAnimating) return;
const savedState = localStorage.getItem("simulateMessagePanel");
// Extract position & size, validate with useWindowConstraints
setIsWindowOpen(true);
animateWindowOpen(validatedPos);
}, [isWindowOpen, isAnimating, /* ... */]);
Editing WebSocket Messages
The editing workflow centers on the SimulateEditorTab, which provides a structured environment for payload composition.
The SimulateEditorTab Component
Located at src/components/SimulateEditorTab.jsx, this component renders a JsonViewer instance where developers can type or paste JSON payloads. The editor supports both structured data and plain text, storing the current value in the simulateMessage state variable. Two Simulate buttons (outgoing and incoming) flank the editor, allowing users to designate message direction before transmission.
Persisting Editor State
State persistence occurs through a debounced save mechanism in SimulateMessagePanel.jsx. On every change to simulateMessage, isPinned, windowPosition, or windowSize, the hook writes to localStorage["simulateMessagePanel"]. When the component mounts, it rehydrates the editor content and window geometry, restoring the previous editing session automatically.
Simulating Incoming and Outgoing Messages
Simulation transforms the edited payload into actual WebSocket traffic routed through the background script.
The handleSimulateMessage Flow
The handleSimulateMessage callback in SimulateMessagePanel.jsx validates the connection and payload before dispatching:
// src/components/SimulateMessagePanel.jsx
const handleSimulateMessage = useCallback(
async (direction, data = null) => {
const messageData = data || simulateMessage;
if (!connection || !messageData.trim() || isSending) return;
setIsSending(true);
await onSimulateMessage({
connectionId: connection.id,
message: messageData,
direction, // 'outgoing' or 'incoming'
});
setTimeout(() => setIsSending(false), 200);
},
[connection, simulateMessage, isSending, onSimulateMessage]
);
The direction parameter determines whether the message appears as client-sent or server-received traffic in the WebSocket inspector.
Programmatic Panel Control
Parent components can pre-populate the editor and open the panel imperatively using the exposed ref API:
import React, { useRef } from "react";
import FloatingSimulate from "./src/components/FloatingSimulate";
function DebugView({ connection }) {
const simulateRef = useRef(null);
const testConnection = () => {
simulateRef.current?.openPanel({
tab: "editor",
data: JSON.stringify({ type: "ping", timestamp: Date.now() }, null, 2),
});
};
return (
<>
<button onClick={testConnection}>Send Test Ping</button>
<FloatingSimulate
ref={simulateRef}
connection={connection}
onSimulateMessage={async ({ connectionId, message, direction }) => {
await chrome.runtime.sendMessage({
type: "simulate-message",
payload: { connectionId, message, direction },
});
}}
/>
</>
);
}
Draggable UI and Persistence
The panel leverages react-rnd to provide desktop-application-like windowing behavior within the browser viewport.
Window Constraints and Animation
The useWindowConstraints hook ensures the panel never leaves the visible screen area during drag operations, while useWindowAnimation provides smooth entrance transitions when openPanel triggers. Resize events update the windowSize state, which persists alongside position coordinates via the debounced save routine.
Saving Favorites and System Events
Beyond basic editing, the panel includes auxiliary tabs for workflow efficiency:
- Favorites: Uses
globalFavorites.jsandfavoritesServiceto store reusable message snippets. Users can save the current editor content to favorites viaglobalFavorites.addFromEditor. - System Events: Provides pre-defined WebSocket events (close, error, ping) that dispatch through
chrome.runtime.sendMessageto simulate connection-level scenarios without manual JSON composition.
The pin functionality (isPinned state) keeps the panel visible when clicking outside the window, while the minimize button (setIsWindowOpen(false)) hides the panel to a floating toggle button without destroying state.
Summary
- The floating simulate panel exposes an imperative
openPanelAPI viaFloatingSimulate.jsxfor programmatic control from parent components. SimulateEditorTab.jsxprovides the JSON editing interface, whileSimulateMessagePanel.jsxhandles message transmission and state persistence.- The
handleSimulateMessagefunction supports bidirectional simulation, sending payloads as either incoming or outgoing WebSocket traffic. - Window state (size, position, pin status) persists to
localStorage["simulateMessagePanel"]via debounced writes inusePanelManager. - Favorites and System Events tabs streamline reuse of common payloads and connection events.
Frequently Asked Questions
How do I open the floating simulate panel programmatically with pre-filled data?
Acquire a ref to the FloatingSimulate component and call ref.current.openPanel({ tab: "editor", data: yourJSONString }). This API is exposed through useImperativeHandle in src/components/FloatingSimulate.jsx, allowing host components to trigger the panel and populate the editor in a single command.
Where is the panel position and editor content saved between sessions?
The panel persists state to localStorage under the key "simulateMessagePanel". The usePanelManager hook writes window size, position, pin status, and current message content via a debounced save mechanism, then rehydrates these values when the component mounts.
Can the panel simulate both client-to-server and server-to-client messages?
Yes. The handleSimulateMessage function accepts a direction parameter ("outgoing" or "incoming") that determines how the message appears in the WebSocket traffic log. Both directions execute through the same validation and transmission pipeline in SimulateMessagePanel.jsx.
What prevents the draggable panel from disappearing off-screen?
The useWindowConstraints hook validates coordinates against the current viewport dimensions before and during drag operations. When openPanel restores saved geometry from localStorage, it recalculates position to ensure the panel remains fully visible even if the browser window has resized since the last session.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →