diff --git a/.tool-versions b/.tool-versions
deleted file mode 100644
index 9a0a73528..000000000
--- a/.tool-versions
+++ /dev/null
@@ -1,2 +0,0 @@
-nodejs 18.14.0
-yarn 1.22.19
diff --git a/.yarn/patches/babel-plugin-minify-mangle-names-npm-0.5.1-fec77ddad5.patch b/.yarn/patches/babel-plugin-minify-mangle-names-npm-0.5.1-fec77ddad5.patch
new file mode 100644
index 000000000..6747911f5
--- /dev/null
+++ b/.yarn/patches/babel-plugin-minify-mangle-names-npm-0.5.1-fec77ddad5.patch
@@ -0,0 +1,241 @@
+diff --git a/lib/scope-tracker.js b/lib/scope-tracker.js
+index ac51afdf1e1adbd3ece02dfeba43aba0651667ab..c85383ed65e140e50d2c6039cfd61f1d6008d818 100644
+--- a/lib/scope-tracker.js
++++ b/lib/scope-tracker.js
+@@ -21,7 +21,6 @@ module.exports = class ScopeTracker {
+ * @param {Scope} scope
+ */
+
+-
+ addScope(scope) {
+ if (!this.references.has(scope)) {
+ this.references.set(scope, new CountedSet());
+@@ -39,19 +38,23 @@ module.exports = class ScopeTracker {
+ * @param {String} name
+ */
+
+-
+ addReference(scope, binding, name) {
+ let parent = scope;
+
+ do {
+- this.references.get(parent).add(name);
++ (this.references.get(parent) || this.references.get(parent.parent)).add(
++ name
++ );
+
+ if (!binding) {
+- throw new Error(`Binding Not Found for ${name} during scopeTracker.addRefernce. ` + `Please report at ${newIssueUrl}`);
++ throw new Error(
++ `Binding Not Found for ${name} during scopeTracker.addRefernce. ` +
++ `Please report at ${newIssueUrl}`
++ );
+ }
+
+ if (binding.scope === parent) break;
+- } while (parent = parent.parent);
++ } while ((parent = parent.parent));
+ }
+ /**
+ * has a Reference in the given {Scope} or a child Scope
+@@ -63,9 +66,10 @@ module.exports = class ScopeTracker {
+ * @param {String} name
+ */
+
+-
+ hasReference(scope, name) {
+- return this.references.get(scope).has(name);
++ return (
++ this.references.get(scope) || this.references.get(scope.parent)
++ ).has(name);
+ }
+ /**
+ * Update reference count in all scopes between and including the
+@@ -77,22 +81,26 @@ module.exports = class ScopeTracker {
+ * @param {String} newName
+ */
+
+-
+ updateReference(scope, binding, oldName, newName) {
+ let parent = scope;
+
+ do {
+- const ref = this.references.get(parent);
++ const ref =
++ this.references.get(parent) || this.references.get(parent.parent);
+ ref.delete(oldName);
+ ref.add(newName);
+
+ if (!binding) {
+ // Something went wrong - panic
+- throw new Error("Binding Not Found during scopeTracker.updateRefernce " + `while updating "${oldName}" to "${newName}". ` + `Please report at ${newIssueUrl}`);
++ throw new Error(
++ "Binding Not Found during scopeTracker.updateRefernce " +
++ `while updating "${oldName}" to "${newName}". ` +
++ `Please report at ${newIssueUrl}`
++ );
+ }
+
+ if (binding.scope === parent) break;
+- } while (parent = parent.parent);
++ } while ((parent = parent.parent));
+ }
+ /**
+ * has either a Binding or a Reference
+@@ -101,7 +109,6 @@ module.exports = class ScopeTracker {
+ * @param {String} name
+ */
+
+-
+ hasBindingOrReference(scope, binding, name) {
+ return this.hasReference(scope, name) || this.hasBinding(scope, name);
+ }
+@@ -117,7 +124,6 @@ module.exports = class ScopeTracker {
+ * @param {String} next
+ */
+
+-
+ canUseInReferencedScopes(binding, next) {
+ const tracker = this;
+
+@@ -129,32 +135,40 @@ module.exports = class ScopeTracker {
+ // https://bugs.webkit.org/show_bug.cgi?id=171041
+ // https://trac.webkit.org/changeset/217200/webkit/trunk/Source
+
+-
+ const maybeDecl = binding.path.parentPath;
+- const isBlockScoped = maybeDecl.isVariableDeclaration({
+- kind: "let"
+- }) || maybeDecl.isVariableDeclaration({
+- kind: "const"
+- });
++ const isBlockScoped =
++ maybeDecl.isVariableDeclaration({
++ kind: "let",
++ }) ||
++ maybeDecl.isVariableDeclaration({
++ kind: "const",
++ });
+
+ if (isBlockScoped) {
+ const maybeFor = maybeDecl.parentPath;
+- const isForLoopBinding = maybeFor.isForStatement({
+- init: maybeDecl.node
+- }) || maybeFor.isForXStatement({
+- left: maybeDecl.node
+- });
++ const isForLoopBinding =
++ maybeFor.isForStatement({
++ init: maybeDecl.node,
++ }) ||
++ maybeFor.isForXStatement({
++ left: maybeDecl.node,
++ });
+
+ if (isForLoopBinding) {
+ const fnParent = getFunctionParent(maybeFor);
+
+- if (fnParent.isFunction({
+- body: maybeFor.parent
+- })) {
+- const parentFunctionBinding = this.bindings.get(fnParent.scope).get(next);
++ if (
++ fnParent.isFunction({
++ body: maybeFor.parent,
++ })
++ ) {
++ const parentFunctionBinding = this.bindings
++ .get(fnParent.scope)
++ .get(next);
+
+ if (parentFunctionBinding) {
+- const parentFunctionHasParamBinding = parentFunctionBinding.kind === "param";
++ const parentFunctionHasParamBinding =
++ parentFunctionBinding.kind === "param";
+
+ if (parentFunctionHasParamBinding) {
+ return false;
+@@ -184,8 +198,7 @@ module.exports = class ScopeTracker {
+ if (tracker.hasBindingOrReference(path.scope, binding, next)) {
+ canUse = false;
+ }
+- }
+-
++ },
+ });
+
+ if (!canUse) {
+@@ -205,17 +218,22 @@ module.exports = class ScopeTracker {
+ * @param {Binding} binding
+ */
+
+-
+ addBinding(binding) {
+ if (!binding) {
+ return;
+ }
+
+- const bindings = this.bindings.get(binding.scope);
++ const bindings =
++ this.bindings.get(binding.scope) ||
++ this.bindings.get(binding.scope.parent);
+ const existingBinding = bindings.get(binding.identifier.name);
+
+ if (existingBinding && existingBinding !== binding) {
+- throw new Error(`scopeTracker.addBinding: ` + `Binding "${existingBinding.identifier.name}" already exists. ` + `Trying to add "${binding.identifier.name}" again.`);
++ throw new Error(
++ `scopeTracker.addBinding: ` +
++ `Binding "${existingBinding.identifier.name}" already exists. ` +
++ `Trying to add "${binding.identifier.name}" again.`
++ );
+ }
+
+ bindings.set(binding.identifier.name, binding);
+@@ -229,10 +247,15 @@ module.exports = class ScopeTracker {
+ * @param {Scope} toScope
+ */
+
+-
+ moveBinding(binding, toScope) {
+- this.bindings.get(binding.scope).delete(binding.identifier.name);
+- this.bindings.get(toScope).set(binding.identifier.name, binding);
++ (
++ this.bindings.get(binding.scope) ||
++ this.bindings.get(binding.scope.parent)
++ ).delete(binding.identifier.name);
++ (this.bindings.get(toScope) || this.bindings.get(toScope.parent)).set(
++ binding.identifier.name,
++ binding
++ );
+ }
+ /**
+ * has a Binding in the current {Scope}
+@@ -240,9 +263,10 @@ module.exports = class ScopeTracker {
+ * @param {String} name
+ */
+
+-
+ hasBinding(scope, name) {
+- return this.bindings.get(scope).has(name);
++ return (this.bindings.get(scope) || this.bindings.get(scope.parent)).has(
++ name
++ );
+ }
+ /**
+ * Update the ScopeTracker on rename
+@@ -251,13 +275,12 @@ module.exports = class ScopeTracker {
+ * @param {String} newName
+ */
+
+-
+ renameBinding(scope, oldName, newName) {
+- const bindings = this.bindings.get(scope);
++ const bindings =
++ this.bindings.get(scope) || this.bindings.get(scope.parent);
+ bindings.set(newName, bindings.get(oldName));
+ bindings.delete(oldName);
+ }
+-
+ };
+ /**
+ * Babel-7 returns null if there is no function parent
diff --git a/apps/reactotron-app/package.json b/apps/reactotron-app/package.json
index 9bdd4e9e0..37d07a45e 100644
--- a/apps/reactotron-app/package.json
+++ b/apps/reactotron-app/package.json
@@ -40,7 +40,10 @@
"whiteListedModules": [
"reactotron-core-ui",
"react-router-dom",
- "styled-components"
+ "styled-components",
+ "mobx-react-lite",
+ "mobx-state-tree",
+ "mobx"
],
"renderer": {
"webpackConfig": "webpack.config.js",
@@ -56,6 +59,9 @@
"electron-updater": "^6.1.7",
"electron-window-state": "^5.0.3",
"immer": "^10.0.3",
+ "mobx": "^6.12.0",
+ "mobx-react-lite": "^4.0.5",
+ "mobx-state-tree": "^5.4.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hotkeys": "^2.0.0",
@@ -68,19 +74,19 @@
"reactotron-core-server": "workspace:*",
"reactotron-core-ui": "workspace:*",
"source-map-support": "^0.5.21",
- "styled-components": "^6.1.0",
+ "styled-components": "^6.1.1",
"v8-compile-cache": "^2.4.0"
},
"devDependencies": {
- "@babel/core": "^7.23.2",
- "@babel/preset-react": "^7.22.15",
- "@babel/preset-typescript": "^7.23.2",
+ "@babel/core": "^7.23.6",
+ "@babel/preset-react": "^7.23.3",
+ "@babel/preset-typescript": "^7.23.3",
"@electron/notarize": "^2.1.0",
- "@storybook/addon-actions": "^5.2.8",
- "@storybook/addon-knobs": "^5.2.8",
- "@storybook/addon-links": "^5.2.8",
- "@storybook/addons": "^5.2.8",
- "@storybook/react": "^5.2.8",
+ "@storybook/addon-actions": "6.5.16",
+ "@storybook/addon-knobs": "6.4.0",
+ "@storybook/addon-links": "6.5.16",
+ "@storybook/addons": "6.5.16",
+ "@storybook/react": "6.5.16",
"@testing-library/react-hooks": "^8.0.1",
"@types/jest": "^29.5.7",
"@types/react": "18.2.35",
diff --git a/apps/reactotron-app/src/main/index.ts b/apps/reactotron-app/src/main/index.ts
index a63591489..800da22f4 100644
--- a/apps/reactotron-app/src/main/index.ts
+++ b/apps/reactotron-app/src/main/index.ts
@@ -102,3 +102,18 @@ app.on("ready", () => {
// Sets up the electron IPC commands for android functionality on the Help screen.
setupAndroidDeviceIPCCommands(mainWindow)
})
+
+// // Add the React DevTools if in development mode.
+// // This doesn't seem to work; here's the issue: https://github.com/electron/electron/issues/32133
+// if (isDevelopment) {
+// const reactDevToolsPath = path.join(
+// os.homedir(),
+// "/Library/Application Support/Google/Chrome/Default/Extensions/fmkadmapgofadopljbjfkapdkoienihi/5.0.0_0"
+// )
+
+// console.error("LOADING REACT DEV TOOLS FROM", reactDevToolsPath)
+
+// app.whenReady().then(async () => {
+// await session.defaultSession.loadExtension(reactDevToolsPath)
+// })
+// }
diff --git a/apps/reactotron-app/src/main/menu.ts b/apps/reactotron-app/src/main/menu.ts
index 6095fcb2f..1321305c1 100644
--- a/apps/reactotron-app/src/main/menu.ts
+++ b/apps/reactotron-app/src/main/menu.ts
@@ -1,7 +1,7 @@
import { Menu, app, shell } from "electron"
-import Store from "electron-store"
+import ElectronStore from "electron-store"
-const configStore = new Store()
+const configStore = new ElectronStore()
const isDarwin = process.platform === "darwin"
@@ -88,15 +88,29 @@ function buildViewMenu(window: Electron.BrowserWindow, isDevelopment: boolean) {
window.setFullScreen(!window.isFullScreen())
},
},
+ {
+ label: "Toggle Sidebar",
+ accelerator: "Shift+Ctrl+S",
+ click: () => {
+ window.webContents.send("sidebar:toggle")
+ },
+ },
{
label: "Toggle Developer Tools",
accelerator: "Alt+Command+I",
click: () => {
;(window as any).toggleDevTools()
},
- }
+ },
+ { role: "separator" },
+ { role: "resetZoom" },
+ { role: "zoomIn" },
+ { role: "zoomOut" },
+ { role: "separator" }
)
+ // add Toggle Sidebar
+
if (isDevelopment) {
viewMenu.submenu.push({
label: isDarwin ? "Reload" : "&Reload",
@@ -159,6 +173,6 @@ export default function createMenu(window: Electron.BrowserWindow, isDevelopment
buildHelpMenu(),
]
- const menu = Menu.buildFromTemplate(template.filter(t => !!t) as any)
+ const menu = Menu.buildFromTemplate(template.filter((t) => !!t) as any)
Menu.setApplicationMenu(menu)
}
diff --git a/apps/reactotron-app/src/renderer/App.tsx b/apps/reactotron-app/src/renderer/App.tsx
index 9657b992b..32e4c60a3 100644
--- a/apps/reactotron-app/src/renderer/App.tsx
+++ b/apps/reactotron-app/src/renderer/App.tsx
@@ -4,7 +4,6 @@ import styled from "styled-components"
import SideBar from "./components/SideBar"
import Footer from "./components/Footer"
-import RootContextProvider from "./contexts"
import RootModals from "./RootModals"
import Home from "./pages/home"
@@ -15,6 +14,7 @@ import Overlay from "./pages/reactNative/Overlay"
import Storybook from "./pages/reactNative/Storybook"
import CustomCommands from "./pages/customCommands"
import Help from "./pages/help"
+import ReactotronBrain from "./ReactotronBrain"
const AppContainer = styled.div`
position: absolute;
@@ -44,11 +44,10 @@ const MainContainer = styled.div`
function App() {
return (
-
+
-
{/* Home */}
@@ -76,7 +75,7 @@ function App() {
-
+
)
}
diff --git a/apps/reactotron-app/src/renderer/KeybindHandler.tsx b/apps/reactotron-app/src/renderer/KeybindHandler.tsx
index c66f3696b..dd33d05a8 100644
--- a/apps/reactotron-app/src/renderer/KeybindHandler.tsx
+++ b/apps/reactotron-app/src/renderer/KeybindHandler.tsx
@@ -1,16 +1,16 @@
import React, { useContext } from "react"
import { GlobalHotKeys, KeyEventName } from "react-hotkeys"
import { ReactotronContext, StateContext } from "reactotron-core-ui"
-import LayoutContext from "./contexts/Layout"
+import { useStore } from "./models/RootStore"
const keyMap = {
// Application wide
- ToggleSidebar: {
- name: "Toggle Sidebar",
- group: "Application",
- sequences: ["command+shift+s", "ctrl+shift+s"],
- action: "keyup" as KeyEventName,
- },
+ // ToggleSidebar: {
+ // name: "Toggle Sidebar",
+ // group: "Application",
+ // sequences: ["command+shift+s", "ctrl+shift+s"],
+ // action: "keyup" as KeyEventName,
+ // },
// Tab Navigation
OpenHomeTab: {
name: "Home tab",
@@ -81,10 +81,16 @@ const keyMap = {
sequences: ["command+s", "ctrl+s"],
action: "keyup" as KeyEventName,
},
+ LogDiagnostic: {
+ name: "Log Reactotron diagnostic info",
+ group: "Application",
+ sequences: ["command+shift+i", "ctrl+shift+i"],
+ action: "keyup" as KeyEventName,
+ },
}
function KeybindHandler({ children }) {
- const { toggleSideBar } = useContext(LayoutContext)
+ const store = useStore()
const { openDispatchModal, openSubscriptionModal, clearCommands } = useContext(ReactotronContext)
const { createSnapshot } = useContext(StateContext)
@@ -125,11 +131,14 @@ function KeybindHandler({ children }) {
// Miscellaneous
ToggleSidebar: () => {
- toggleSideBar()
+ store.toggleSidebar()
},
ClearTimeline: () => {
clearCommands()
},
+ LogDiagnostic: () => {
+ store.logDiagnostic()
+ },
}
return (
diff --git a/apps/reactotron-app/src/renderer/ReactotronBrain.tsx b/apps/reactotron-app/src/renderer/ReactotronBrain.tsx
index 170a73741..4cdc8ef42 100644
--- a/apps/reactotron-app/src/renderer/ReactotronBrain.tsx
+++ b/apps/reactotron-app/src/renderer/ReactotronBrain.tsx
@@ -1,6 +1,5 @@
// TODO: Name this better...
-import React, { FunctionComponent, PropsWithChildren } from "react"
-import type { Command } from "reactotron-core-contract"
+import React, { PropsWithChildren, useCallback, useEffect, useRef } from "react"
import {
ReactotronProvider,
CustomCommandsProvider,
@@ -10,28 +9,71 @@ import {
} from "reactotron-core-ui"
import KeybindHandler from "./KeybindHandler"
-
-interface Props {
- commands: Command[]
- sendCommand: (type: string, payload: any, clientId?: string) => void
- clearCommands: () => void
- addCommandListener: (callback: (command: Command) => void) => void
-}
+import { useStore } from "./models/RootStore"
+import Server, { createServer } from "reactotron-core-server"
+import config from "./config"
+import { observer } from "mobx-react-lite"
/** Wrapper for Reactotron context providers */
-const ReactotronBrain: FunctionComponent> = ({
- commands,
- sendCommand,
- clearCommands,
- addCommandListener,
- children,
-}) => {
+const ReactotronBrain = ({ children }: PropsWithChildren) => {
+ const reactotronServer = useRef(null)
+ const store = useStore()
+
+ useEffect(() => {
+ // Creates a server and starts listening for connections
+ reactotronServer.current = createServer({ port: config.get("serverPort") as number })
+
+ reactotronServer.current.on("start", () => {
+ store.setProp("serverStatus", "started")
+ })
+
+ reactotronServer.current.on("stop", () => {
+ store.setProp("serverStatus", "stopped")
+ })
+
+ // @ts-expect-error need to sync these types between reactotron-core-server and reactotron-app
+ reactotronServer.current.on("connectionEstablished", (connection: Connection) => {
+ store.addConnection(connection)
+ })
+
+ // @ts-expect-error need to sync these types between reactotron-core-server and reactotron-app
+ reactotronServer.current.on("disconnect", (connection: Connection) => {
+ store.removeConnection(connection)
+ })
+
+ reactotronServer.current.on("command", (command: any) => {
+ store.commandReceived(command)
+ })
+
+ reactotronServer.current.on("portUnavailable", () => {
+ store.setProp("serverStatus", "portUnavailable")
+ })
+
+ // Start the server
+ reactotronServer.current.start()
+
+ // Stop the server when the app closes
+ return () => {
+ reactotronServer.current.stop()
+ }
+ }, [store])
+
+ const sendCommand = useCallback(
+ (type: string, payload: any, clientId?: string) => {
+ // TODO: Do better then just throwing these away...
+ if (!reactotronServer.current) return
+
+ reactotronServer.current.send(type, payload, clientId)
+ },
+ [reactotronServer]
+ )
+
return (
@@ -46,4 +88,4 @@ const ReactotronBrain: FunctionComponent> = ({
)
}
-export default ReactotronBrain
+export default observer(ReactotronBrain)
diff --git a/apps/reactotron-app/src/renderer/components/ConnectionSelector/index.tsx b/apps/reactotron-app/src/renderer/components/ConnectionSelector/index.tsx
index 44b270c16..b5876c455 100644
--- a/apps/reactotron-app/src/renderer/components/ConnectionSelector/index.tsx
+++ b/apps/reactotron-app/src/renderer/components/ConnectionSelector/index.tsx
@@ -3,7 +3,7 @@ import styled from "styled-components"
import { MdCheckCircle as Checkmark } from "react-icons/md"
import { getIcon, getPlatformName, getPlatformDetails } from "../../util/connectionHelpers"
-import { Connection } from "../../contexts/Standalone/useStandalone"
+import { type Connection } from "../../models/Connection"
const Container = styled.div`
display: flex;
diff --git a/apps/reactotron-app/src/renderer/components/Footer/Footer.story.tsx b/apps/reactotron-app/src/renderer/components/Footer/Footer.story.tsx
index 4119bb035..0ea88bf4b 100644
--- a/apps/reactotron-app/src/renderer/components/Footer/Footer.story.tsx
+++ b/apps/reactotron-app/src/renderer/components/Footer/Footer.story.tsx
@@ -2,12 +2,11 @@
import React from "react"
import { storiesOf } from "@storybook/react"
-import { Connection } from "../../contexts/Standalone/useStandalone"
-
import Footer from "./Stateless"
+import { Connection, ConnectionModel } from "../../models/Connection"
const testConnections: Connection[] = [
- {
+ ConnectionModel.create({
id: 0,
clientId: "1",
name: "Test 1",
@@ -15,8 +14,8 @@ const testConnections: Connection[] = [
platform: "ios",
platformVersion: "12",
connected: true,
- },
- {
+ }),
+ ConnectionModel.create({
id: 0,
clientId: "1",
name: "Test 2",
@@ -24,8 +23,8 @@ const testConnections: Connection[] = [
platform: "ios",
platformVersion: "12",
connected: true,
- },
- {
+ }),
+ ConnectionModel.create({
id: 0,
clientId: "1",
name: "Test 3",
@@ -33,8 +32,8 @@ const testConnections: Connection[] = [
platform: "ios",
platformVersion: "12",
connected: true,
- },
- {
+ }),
+ ConnectionModel.create({
id: 0,
clientId: "1",
name: "Test 4",
@@ -42,8 +41,8 @@ const testConnections: Connection[] = [
platform: "ios",
platformVersion: "12",
connected: true,
- },
- {
+ }),
+ ConnectionModel.create({
id: 0,
clientId: "1",
name: "Test 5",
@@ -51,7 +50,7 @@ const testConnections: Connection[] = [
platform: "ios",
platformVersion: "12",
connected: true,
- },
+ }),
]
storiesOf("components/Footer", module)
diff --git a/apps/reactotron-app/src/renderer/components/Footer/Stateless.tsx b/apps/reactotron-app/src/renderer/components/Footer/Stateless.tsx
index 879a1a8dc..38bfeaf39 100644
--- a/apps/reactotron-app/src/renderer/components/Footer/Stateless.tsx
+++ b/apps/reactotron-app/src/renderer/components/Footer/Stateless.tsx
@@ -4,8 +4,9 @@ import { MdSwapVert as ExpandIcon } from "react-icons/md"
import config from "../../config"
import { getPlatformName, getPlatformDetails } from "../../util/connectionHelpers"
-import { Connection, ServerStatus } from "../../contexts/Standalone/useStandalone"
import ConnectionSelector from "../ConnectionSelector"
+import { type ServerStatus } from "../../models/RootStore"
+import { type Connection } from "../../models/Connection"
const Container = styled.div`
border-top: 1px solid ${(props) => props.theme.chromeLine};
diff --git a/apps/reactotron-app/src/renderer/components/Footer/index.tsx b/apps/reactotron-app/src/renderer/components/Footer/index.tsx
index 1b1c15dc8..07368031c 100644
--- a/apps/reactotron-app/src/renderer/components/Footer/index.tsx
+++ b/apps/reactotron-app/src/renderer/components/Footer/index.tsx
@@ -1,22 +1,19 @@
-import React, { useContext, useState } from "react"
-
-import StandaloneContext from "../../contexts/Standalone"
-
+import React from "react"
import Footer from "./Stateless"
+import { useStore } from "../../models/RootStore"
+import { observer } from "mobx-react-lite"
-export default function ConnectedFooter() {
- const { serverStatus, connections, selectedConnection, selectConnection } =
- useContext(StandaloneContext)
- const [isOpen, setIsOpen] = useState(false)
+export default observer(function ConnectedFooter() {
+ const store = useStore()
return (