From e0f2acefa2b729971a31ba7b3b2ef384d47938a7 Mon Sep 17 00:00:00 2001 From: Amirhossein Alibakhshi Date: Mon, 14 Jul 2025 13:57:20 +0330 Subject: [PATCH 1/6] feat(core): add hideDevtool and showDevtool methods --- packages/core/src/ClientSocketManager.test.ts | 18 ++++++++++++++++-- packages/core/src/ClientSocketManager.ts | 17 ++++++++++++++++- packages/core/src/ClientSocketManagerStub.ts | 10 ++++++++++ packages/core/src/devtool/devtool.ts | 5 +++-- 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/packages/core/src/ClientSocketManager.test.ts b/packages/core/src/ClientSocketManager.test.ts index c44ae7f..ed4582b 100644 --- a/packages/core/src/ClientSocketManager.test.ts +++ b/packages/core/src/ClientSocketManager.test.ts @@ -328,9 +328,8 @@ describe("ClientSocketManager: unit tests", () => { }, }); - // at first the devtool should be in the dom but because no we have lo logs or channels, these sections shouldn't exist. + // at first the devtool should be in the dom but because no we have no logs or channels, these sections shouldn't exist. expect(devtool.getDevtoolElement()).not.toBeNull(); - expect(devtool.getDevtoolLogSectionElement()).toBeNull(); expect(devtool.getDevtoolChannelsElement()).toBeNull(); expect(devtool.getDevtoolInfoElement()).not.toBeNull(); expect(devtool.getDevtoolStatusElement()?.innerHTML).not.toEqual( @@ -372,6 +371,21 @@ describe("ClientSocketManager: unit tests", () => { ); expect(devtool.getDevtoolChannelsElement()).toBeNull(); + // hiding devtool hide the devtool from the browser... + socketManager.hideDevtool(); + expect(devtool.getDevtoolElement()).toBeNull(); + // ... but the devtool state should remain the same + expect(socketManager.connected).toBe(true); + expect(socketManager.disposed).toBe(false); + + // the devtool can be visible again + socketManager.showDevtool(); + expect(devtool.getDevtoolElement()).not.toBeNull(); + expect(devtool.getDevtoolStatusElement()?.innerHTML).toEqual( + devtool.Status.CONNECTED, + ); + expect(devtool.getDevtoolLogSectionElement()!.children).toHaveLength(3); + socketManager.dispose(); await diposeResolver.promise; diff --git a/packages/core/src/ClientSocketManager.ts b/packages/core/src/ClientSocketManager.ts index d81b6b8..1913280 100644 --- a/packages/core/src/ClientSocketManager.ts +++ b/packages/core/src/ClientSocketManager.ts @@ -50,7 +50,7 @@ class ClientSocketManager< this._inputListeners.onInit?.call(this); if (devtoolOpt) { - devtool.init(); + this.showDevtool(); } } catch (err) { // eslint-disable-next-line no-console @@ -437,6 +437,21 @@ class ClientSocketManager< devtool.dispose(); } + + /** + * Show devtool in the browser programmatically. + */ + public showDevtool(): void { + devtool.init(); + devtool.render(); + } + + /** + * Show devtool in the browser programmatically. + */ + public hideDevtool(): void { + devtool.dispose(); + } } export default ClientSocketManager; diff --git a/packages/core/src/ClientSocketManagerStub.ts b/packages/core/src/ClientSocketManagerStub.ts index ff2de33..e96fbf6 100644 --- a/packages/core/src/ClientSocketManagerStub.ts +++ b/packages/core/src/ClientSocketManagerStub.ts @@ -137,6 +137,16 @@ class ClientSocketManagerStub { this._disposed = true; this._inputListeners = {}; } + + /** + * Show devtool in the browser programmatically. + */ + public showDevtool(): void {} + + /** + * Show devtool in the browser programmatically. + */ + public hideDevtool(): void {} } export default ClientSocketManagerStub; diff --git a/packages/core/src/devtool/devtool.ts b/packages/core/src/devtool/devtool.ts index 3a1a72f..70009c7 100644 --- a/packages/core/src/devtool/devtool.ts +++ b/packages/core/src/devtool/devtool.ts @@ -304,6 +304,7 @@ export const dispose = () => { getDevtoolWrapperElement()?.remove(); active = false; + expanded = false; }; const updateUi = () => { @@ -330,10 +331,10 @@ const toggle = () => { ); }; -export const render = (cb: (s: typeof devtool) => void) => { +export const render = (cb?: (s: typeof devtool) => void) => { if (!active) return; - cb(devtool); + cb?.(devtool); updateUi(); }; From 982eb2b9e6e3540fa150f895616d9e64cf0b7ee8 Mon Sep 17 00:00:00 2001 From: Amirhossein Alibakhshi Date: Mon, 14 Jul 2025 13:57:52 +0330 Subject: [PATCH 2/6] chore(playground): add buttons for hiding and showing the devtool --- playground/client/index.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/playground/client/index.ts b/playground/client/index.ts index 6e9ca4a..156bdc9 100644 --- a/playground/client/index.ts +++ b/playground/client/index.ts @@ -130,9 +130,35 @@ const createUnsubBtn = () => { return button; }; +const createShowDevtoolBtn = () => { + const button = createButton("show devtool"); + + button.addEventListener("click", () => { + socketManager.showDevtool(); + }); + + document.body.append(button); + + return button; +}; + +const createHideDevtoolBtn = () => { + const button = createButton("hide devtool"); + + button.addEventListener("click", () => { + socketManager.hideDevtool(); + }); + + document.body.append(button); + + return button; +}; + createDisconnectBtn(); createReconnectBtn(); createDisposeBtn(); createSendMessageBtn(); createSubBtn(); createUnsubBtn(); +createShowDevtoolBtn(); +createHideDevtoolBtn(); From 8cc37458d2b61be49961ca19edc2b0d9877f1d47 Mon Sep 17 00:00:00 2001 From: Amirhossein Alibakhshi Date: Mon, 14 Jul 2025 13:58:32 +0330 Subject: [PATCH 3/6] docs: update changeset --- .changeset/ninety-friends-hug.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/ninety-friends-hug.md diff --git a/.changeset/ninety-friends-hug.md b/.changeset/ninety-friends-hug.md new file mode 100644 index 0000000..fe41417 --- /dev/null +++ b/.changeset/ninety-friends-hug.md @@ -0,0 +1,6 @@ +--- +"@tapsioss/client-socket-manager": minor +--- + +Add `hideDevtool` and `showDevtool` methods to the `ClientSocketManager`. + \ No newline at end of file From 1b88c6a56892cd2439d827b72771e299d5a38c05 Mon Sep 17 00:00:00 2001 From: Amirhossein Alibakhshi Date: Mon, 14 Jul 2025 14:38:52 +0330 Subject: [PATCH 4/6] docs: update documentations --- packages/core/README.md | 44 ++++++++++++++++-------- packages/core/src/ClientSocketManager.ts | 2 +- packages/react/README.md | 10 +++--- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/packages/core/README.md b/packages/core/README.md index ff9d0a1..fde0365 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -11,7 +11,7 @@ more abstracted and opinionated manner. -
+--- `ClientSocketManager` is a flexible and robust manager for handling socket connections using `socket.io-client`. It provides easy setup and management of @@ -67,12 +67,12 @@ socketManager.subscribe("message", msg => { constructor(uri: string, options?: ClientSocketManagerOptions) ``` -#### Parameters: +#### Parameters - `uri`: The URI of the socket server. - `options`: (optional): Configuration options for the socket connection. -##### Options: +##### Options We have extended [socket-io's options](https://socket.io/docs/v4/client-options/) to include @@ -99,7 +99,7 @@ additional options: useful for development and debugging purposes. In production environments, it's recommended to leave this disabled. -### Properties: +### Properties #### `id: string | null` @@ -122,9 +122,9 @@ Whether the connection state was recovered after a temporary disconnection. Whether the Socket will try to reconnect when its Manager connects or reconnects. -### Methods: +### Methods -#### `emit`: +#### `emit` ```ts emit>( @@ -135,12 +135,12 @@ emit>( Emits an event to the socket identified by the channel name. -##### Parameters: +##### Parameters - `channel`: The name of the channel to emit the event to. - `args`: The arguments to pass with the event. -#### `subscribe`: +#### `subscribe` ```ts subscribe>( @@ -156,7 +156,7 @@ subscribe>( Subscribes to a specified channel with a callback function. Ensures that only one listener exists per channel. -##### Parameters: +##### Parameters - `channel`: The name of the channel to subscribe to. - `cb`: The callback function to invoke when a message is received on the @@ -166,7 +166,7 @@ one listener exists per channel. subscription is complete. - `signal`: The `AbortSignal` to unsubscribe the listener upon abortion. -#### `unsubscribe`: +#### `unsubscribe` ```ts unsubscribe>( @@ -178,12 +178,12 @@ unsubscribe>( Removes the listener for the specified channel. If no callback is provided, it removes all listeners for that channel. -##### Parameters: +##### Parameters - `channel`: The name of the channel whose listener should be deleted. - `cb` (optional): The subscriber callback function to remove. -#### `connect`: +#### `connect` ```ts connect(): void; @@ -191,7 +191,7 @@ connect(): void; Manually connects/reconnects the socket. -#### `disconnect`: +#### `disconnect` ```ts disconnect(): void; @@ -201,7 +201,7 @@ Manually disconnects the socket. In that case, the socket will not try to reconnect. If this is the last active Socket instance of the Manager, the low-level connection will be closed. -#### `dispose`: +#### `dispose` ```ts dispose(): void; @@ -210,6 +210,22 @@ dispose(): void; Disposes of the socket, manager, and engine, ensuring all connections are closed and cleaned up. +#### `showDevtool` + +```ts +showDevtool(): void; +``` + +Show devtool in the browser programmatically. + +#### `hideDevtool` + +```ts +hideDevtool(): void; +``` + +Hide devtool in the browser programmatically. + ## `ClientSocketManagerStub` The package also exports a stubbed version of the socket manager for use in diff --git a/packages/core/src/ClientSocketManager.ts b/packages/core/src/ClientSocketManager.ts index 1913280..5bc8252 100644 --- a/packages/core/src/ClientSocketManager.ts +++ b/packages/core/src/ClientSocketManager.ts @@ -447,7 +447,7 @@ class ClientSocketManager< } /** - * Show devtool in the browser programmatically. + * Hide devtool in the browser programmatically. */ public hideDevtool(): void { devtool.dispose(); diff --git a/packages/react/README.md b/packages/react/README.md index fa7a40f..d0c70a9 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -11,7 +11,7 @@ seamless management of socket connections with `socket.io-client`. -
+--- `ClientSocketManager` is a flexible and robust manager for handling socket connections using `socket.io-client`. It provides easy setup and management of @@ -83,7 +83,7 @@ const App = () => { ## API Reference -### `SocketClientProvider` Component: +### `SocketClientProvider` Component ```ts const SocketClientProvider: (props: SocketClientProviderProps) => JSX.Element; @@ -91,7 +91,7 @@ const SocketClientProvider: (props: SocketClientProviderProps) => JSX.Element; Wraps your application to provide `ClientSocketManager` client. -#### Parameters: +#### Parameters - `children`: The React tree to provide the socket client for. - `uri`: The URI of the socket server. @@ -101,7 +101,7 @@ Wraps your application to provide `ClientSocketManager` client. scenarios. - `options`: (optional): Configuration options for the socket connection. -##### Options: +##### Options We have extended [socket-io's options](https://socket.io/docs/v4/client-options/) to include @@ -128,7 +128,7 @@ additional options: useful for development and debugging purposes. In production environments, it's recommended to leave this disabled. -### `useSocketClient` Hook: +### `useSocketClient` Hook ```ts type ConnectionStatusValues = "connected" | "disconnected" | "reconnecting"; From 0e20449ec7d440429ca53e0925276e53cb292fee Mon Sep 17 00:00:00 2001 From: Amirhossein Alibakhshi Date: Sat, 19 Jul 2025 14:37:12 +0330 Subject: [PATCH 5/6] fix(core/devtool): update the API of the render function --- packages/core/src/ClientSocketManager.ts | 147 ++++++++++++---------- packages/core/src/devtool/FixedQueue.ts | 7 ++ packages/core/src/devtool/devtool.test.ts | 55 +++++--- packages/core/src/devtool/devtool.ts | 33 +++-- 4 files changed, 146 insertions(+), 96 deletions(-) diff --git a/packages/core/src/ClientSocketManager.ts b/packages/core/src/ClientSocketManager.ts index 5bc8252..1df2b45 100644 --- a/packages/core/src/ClientSocketManager.ts +++ b/packages/core/src/ClientSocketManager.ts @@ -74,8 +74,10 @@ class ClientSocketManager< this._socket.on(SocketReservedEvents.CONNECTION, () => { this._inputListeners.onSocketConnection?.call(this); - devtool.render(s => { - s.status = devtool.Status.CONNECTED; + devtool.render({ + action: s => { + s.status = devtool.Status.CONNECTED; + }, }); }); @@ -89,8 +91,10 @@ class ClientSocketManager< this._socket.on(SocketReservedEvents.DISCONNECTION, (reason, details) => { this._inputListeners.onSocketDisconnection?.call(this, reason, details); - devtool.render(s => { - s.status = devtool.Status.DISCONNECTED; + devtool.render({ + action: s => { + s.status = devtool.Status.DISCONNECTED; + }, }); if (!this.autoReconnectable) { @@ -117,12 +121,14 @@ class ClientSocketManager< manager.on(ManagerReservedEvents.CONNECTION_ERROR, error => { onConnectionError?.call(this, error); - devtool.render(s => { - s.logs.enqueue({ - type: devtool.LogType.CONNECTION_ERROR, - date: new Date(), - detail: error.message, - }); + devtool.render({ + action: s => { + s.logs.enqueue({ + type: devtool.LogType.CONNECTION_ERROR, + date: new Date(), + detail: error.message, + }); + }, }); }); @@ -132,46 +138,54 @@ class ClientSocketManager< manager.on(ManagerReservedEvents.RECONNECTING, attempt => { onReconnecting?.call(this, attempt); - devtool.render(s => { - s.status = devtool.Status.RECONNECTING; - s.logs.enqueue({ - type: devtool.LogType.RECONNECTING, - date: new Date(), - detail: `Reconnecting... (${attempt} attempt(s))`, - }); + devtool.render({ + action: s => { + s.status = devtool.Status.RECONNECTING; + s.logs.enqueue({ + type: devtool.LogType.RECONNECTING, + date: new Date(), + detail: `Reconnecting... (${attempt} attempt(s))`, + }); + }, }); }); manager.on(ManagerReservedEvents.RECONNECTING_ERROR, error => { onReconnectingError?.call(this, error); - devtool.render(s => { - s.logs.enqueue({ - type: devtool.LogType.RECONNECTING_ERROR, - date: new Date(), - detail: error.message, - }); + devtool.render({ + action: s => { + s.logs.enqueue({ + type: devtool.LogType.RECONNECTING_ERROR, + date: new Date(), + detail: error.message, + }); + }, }); }); manager.on(ManagerReservedEvents.RECONNECTION_FAILURE, () => { onReconnectionFailure?.call(this); - devtool.render(s => { - s.logs.enqueue({ - type: devtool.LogType.RECONNECTION_FAILURE, - date: new Date(), - detail: `Failed to reconnect.`, - }); + devtool.render({ + action: s => { + s.logs.enqueue({ + type: devtool.LogType.RECONNECTION_FAILURE, + date: new Date(), + detail: `Failed to reconnect.`, + }); + }, }); }); manager.on(ManagerReservedEvents.SUCCESSFUL_RECONNECTION, attempt => { onSuccessfulReconnection?.call(this, attempt); - devtool.render(s => { - s.logs.enqueue({ - type: devtool.LogType.SUCCESSFUL_RECONNECTION, - date: new Date(), - detail: `Successfully connected after ${attempt} attempt(s)`, - }); + devtool.render({ + action: s => { + s.logs.enqueue({ + type: devtool.LogType.SUCCESSFUL_RECONNECTION, + date: new Date(), + detail: `Successfully connected after ${attempt} attempt(s)`, + }); + }, }); }); } @@ -336,13 +350,15 @@ class ClientSocketManager< onSubscriptionComplete?.call(this, channel); - devtool.render(s => { - s.channels.add(channel); - s.logs.enqueue({ - type: devtool.LogType.SUBSCRIBED, - date: new Date(), - detail: `subscribed to \`${channel}\` channel`, - }); + devtool.render({ + action: s => { + s.channels.add(channel); + s.logs.enqueue({ + type: devtool.LogType.SUBSCRIBED, + date: new Date(), + detail: `subscribed to \`${channel}\` channel`, + }); + }, }); } @@ -367,13 +383,15 @@ class ClientSocketManager< if (cb) this._socket.off(channel, cb); else this._socket.off(channel); - devtool.render(s => { - s.channels.delete(channel); - s.logs.enqueue({ - type: devtool.LogType.UNSUBSCRIBED, - date: new Date(), - detail: `unsubscribed from \`${channel}\` channel`, - }); + devtool.render({ + action: s => { + s.channels.delete(channel); + s.logs.enqueue({ + type: devtool.LogType.UNSUBSCRIBED, + date: new Date(), + detail: `unsubscribed from \`${channel}\` channel`, + }); + }, }); } @@ -385,12 +403,14 @@ class ClientSocketManager< this._socket?.connect(); - devtool.render(s => { - s.logs.enqueue({ - type: devtool.LogType.CONNECTED, - date: new Date(), - detail: `socket was conneced manually`, - }); + devtool.render({ + action: s => { + s.logs.enqueue({ + type: devtool.LogType.CONNECTED, + date: new Date(), + detail: `socket was conneced manually`, + }); + }, }); } @@ -406,12 +426,14 @@ class ClientSocketManager< this._socket?.disconnect(); - devtool.render(s => { - s.logs.enqueue({ - type: devtool.LogType.DISCONNECTED, - date: new Date(), - detail: `socket was disconneced manually`, - }); + devtool.render({ + action: s => { + s.logs.enqueue({ + type: devtool.LogType.DISCONNECTED, + date: new Date(), + detail: `socket was disconneced manually`, + }); + }, }); } @@ -442,8 +464,7 @@ class ClientSocketManager< * Show devtool in the browser programmatically. */ public showDevtool(): void { - devtool.init(); - devtool.render(); + devtool.render({ force: true }); } /** diff --git a/packages/core/src/devtool/FixedQueue.ts b/packages/core/src/devtool/FixedQueue.ts index 928c4df..e795632 100644 --- a/packages/core/src/devtool/FixedQueue.ts +++ b/packages/core/src/devtool/FixedQueue.ts @@ -62,4 +62,11 @@ export class FixedQueue { get length(): number { return this._queue.length; } + + /** + * Clears the queue. + */ + clear(): void { + this._queue = []; + } } diff --git a/packages/core/src/devtool/devtool.test.ts b/packages/core/src/devtool/devtool.test.ts index 972ddb0..c9c159b 100644 --- a/packages/core/src/devtool/devtool.test.ts +++ b/packages/core/src/devtool/devtool.test.ts @@ -10,7 +10,14 @@ import * as devtool from "./devtool.ts"; describe("Devtool", () => { beforeEach(() => { - devtool.init(); + devtool.render({ + force: true, + action: s => { + s.channels.clear(); + s.status = devtool.Status.UNKNOWN; + s.logs.clear(); + }, + }); }); afterEach(() => { @@ -28,7 +35,7 @@ describe("Devtool", () => { it("should not double-init", () => { const originalWrapper = devtool.getDevtoolWrapperElement(); - devtool.init(); // re-init should do nothing + devtool.render(); // re-init should do nothing expect(document.querySelectorAll(`#${DEVTOOL_WRAPPER_ID}`)).toHaveLength(1); expect(devtool.getDevtoolWrapperElement()).toBe(originalWrapper); }); @@ -40,8 +47,10 @@ describe("Devtool", () => { it("should be able to update status of the socket using render", () => { Object.values(Status).forEach(status => { - devtool.render(state => { - state.status = status; + devtool.render({ + action: state => { + state.status = status; + }, }); expect(devtool.getDevtoolInfoElement()!.innerHTML).toContain(status); @@ -55,10 +64,12 @@ describe("Devtool", () => { date: new Date(), }; - devtool.render(state => { - state.logs.enqueue(mockLog); - state.channels.add("my-channel"); - state.status = Status.DISCONNECTED; + devtool.render({ + action: state => { + state.logs.enqueue(mockLog); + state.channels.add("my-channel"); + state.status = Status.DISCONNECTED; + }, }); const info = devtool.getDevtoolInfoElement(); @@ -85,12 +96,14 @@ describe("Devtool", () => { // Fill the logs for (let i = 0; i < LOG_CAPACITY; i++) { - devtool.render(state => { - state.logs.enqueue({ - type: LogType.CONNECTION_ERROR, - detail: `log-${i}`, - date: new Date(), - }); + devtool.render({ + action: state => { + state.logs.enqueue({ + type: LogType.CONNECTION_ERROR, + detail: `log-${i}`, + date: new Date(), + }); + }, }); } @@ -104,12 +117,14 @@ describe("Devtool", () => { expect(devtool.getDevtoolLogSectionElement()!.innerHTML).toContain(`log-0`); // after adding new log, the first element will not be available and the new log will append to the queue. - devtool.render(state => { - state.logs.enqueue({ - type: LogType.CONNECTION_ERROR, - detail: `log-${LOG_CAPACITY}`, - date: new Date(), - }); + devtool.render({ + action: state => { + state.logs.enqueue({ + type: LogType.CONNECTION_ERROR, + detail: `log-${LOG_CAPACITY}`, + date: new Date(), + }); + }, }); expect(devtool.getDevtoolLogSectionElement()!.innerHTML).not.toContain( diff --git a/packages/core/src/devtool/devtool.ts b/packages/core/src/devtool/devtool.ts index 70009c7..83be4f5 100644 --- a/packages/core/src/devtool/devtool.ts +++ b/packages/core/src/devtool/devtool.ts @@ -267,7 +267,7 @@ export const updateInfoSection = () => { return infoSection; }; -export const init = () => { +const init = () => { if (active) return; active = true; @@ -307,14 +307,6 @@ export const dispose = () => { expanded = false; }; -const updateUi = () => { - const devtoolElement = getDevtoolElement(); - - if (!devtoolElement) init(); - - updateInfoSection(); -}; - const toggle = () => { const socketIcon = getDevtoolSocketIconElement()!; const closeIcon = getDevtoolCloseIconElement()!; @@ -331,11 +323,26 @@ const toggle = () => { ); }; -export const render = (cb?: (s: typeof devtool) => void) => { - if (!active) return; +type RenderOptions = { + action?: (s: typeof devtool) => void; + force?: boolean; +}; - cb?.(devtool); - updateUi(); +export const render = (options?: RenderOptions) => { + const { action, force = false } = options ?? {}; + + if (force) { + init(); + } else { + if (!active) return; + + const devtoolElement = getDevtoolElement(); + + if (!devtoolElement) init(); + } + + action?.(devtool); + updateInfoSection(); }; export { LogType, Status }; From a8dcfae1927aa5004351c930012dd1a8e387e4c5 Mon Sep 17 00:00:00 2001 From: Amirhossein Alibakhshi Date: Sat, 19 Jul 2025 18:49:33 +0330 Subject: [PATCH 6/6] fix: update ui --- packages/core/src/devtool/devtool.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/core/src/devtool/devtool.ts b/packages/core/src/devtool/devtool.ts index 83be4f5..154648f 100644 --- a/packages/core/src/devtool/devtool.ts +++ b/packages/core/src/devtool/devtool.ts @@ -65,6 +65,9 @@ export const renderChipGroup = (items: string[]) => { "border-radius": "999px", padding: "0 0.5rem", "list-style-type": "none", + overflow: "hidden", + "white-space": "nowrap", + "text-overflow": "ellipsis", }); const chipGroupStyle = generateInlineStyle({ @@ -73,6 +76,9 @@ export const renderChipGroup = (items: string[]) => { gap: "0.5rem", padding: "0", "flex-wrap": "wrap", + "overflow-x": "hidden", + "overflow-y": "auto", + "max-height": "6rem", }); return `
    ${items.map(item => `
  • ${item}
  • `).join("")}
`; @@ -212,7 +218,7 @@ export const renderDevtoolInfo = () => { transform: "scale(0)", "transform-origin": "0 0", transition: "opacity 0.2s, transform 0.2s", - width: "12rem", + width: "14rem", }), }); @@ -275,6 +281,8 @@ const init = () => { const devtoolWrapper = document.createElement("div"); devtoolWrapper.style.position = "fixed"; + devtoolWrapper.style.top = "8px"; + devtoolWrapper.style.left = "8px"; devtoolWrapper.id = DEVTOOL_WRAPPER_ID; devtoolWrapper.innerHTML = renderDevtool();