diff --git a/bun.lockb b/bun.lockb
index 3c12cc6..17cabe7 100644
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/scripts/debugger.mjs b/scripts/debugger.mjs
index 78e51b5..72c362e 100644
--- a/scripts/debugger.mjs
+++ b/scripts/debugger.mjs
@@ -1,11 +1,12 @@
#!/usr/bin/env node
import * as repl from 'node:repl'
import { WebSocketServer } from 'ws'
+import os from 'os'
import chalk from 'chalk'
import clipboardy from 'clipboardy'
-import { join, resolve } from 'node:path'
-import { existsSync } from 'node:fs'
-import { mkdir, writeFile } from 'node:fs/promises'
+import { join, resolve } from 'path'
+import { existsSync } from 'fs'
+import { mkdir, writeFile } from 'fs/promises'
const debuggerHistoryPath = resolve(join('node_modules', 'debugger'))
@@ -32,12 +33,12 @@ const logAsClient = message => console.info(clientColorify(null, message))
const logAsClientWarn = message => console.warn(clientColorify('warn', message))
const logAsClientError = message => console.error(clientColorify('error', message))
-const copyPrompt = ' --copy'
-const clearHistoryPrompt = '--ch'
+const copyPrompt = '--copy'
+const clearHistoryPrompt = '--clear'
export function serve() {
let websocketOpen = false
- let awaitingReply
+ let nextReply
const wss = new WebSocketServer({
port: 9090,
@@ -46,25 +47,25 @@ export function serve() {
if (websocketOpen) return
websocketOpen = true
- logAsDebugger('Starting debugger session')
+ logAsDebugger('Client connected')
ws.on('message', data => {
try {
/** @type {{ level: "info" | "warn" | "error", message: string, nonce?: string }} */
const json = JSON.parse(data.toString())
- if (awaitingReply?.cb && awaitingReply?.nonce && awaitingReply.nonce === json.nonce) {
- if (json.level === 'info' && awaitingReply.toCopy) {
+ if (nextReply?.cb && nextReply?.nonce && nextReply.nonce === json.nonce) {
+ if (json.level === 'info' && nextReply.toCopy) {
clipboardy.write(json.message)
- awaitingReply.cb(null, debuggerColorify('Copied result to clipboard'))
+ nextReply.cb(null, debuggerColorify('Copied result to clipboard'))
} else
- awaitingReply.cb(
+ nextReply.cb(
null,
json.level === 'error'
? clientColorify('error', json.message)
: clientColorify(null, json.message),
)
- awaitingReply = null
+ nextReply = null
isPrompting = true
} else {
if (json.level === 'error') logAsClientError(json.message)
@@ -88,11 +89,11 @@ export function serve() {
const code = input.trim()
if (code === clearHistoryPrompt) {
writeFile(join(debuggerHistoryPath, 'history.txt'), '')
- logAsDebugger('Cleared repl history')
+ logAsDebugger('Cleared REPL history')
return cb()
}
- awaitingReply = {
+ nextReply = {
nonce: crypto.randomUUID(),
cb,
toCopy: code.endsWith(copyPrompt),
@@ -100,7 +101,7 @@ export function serve() {
ws.send(
JSON.stringify({
code: code.endsWith(copyPrompt) ? code.slice(0, -copyPrompt.length) : code,
- nonce: awaitingReply.nonce,
+ nonce: nextReply.nonce,
}),
)
} catch (e) {
@@ -116,19 +117,27 @@ export function serve() {
rl.on('close', () => {
isPrompting = false
ws.close()
- logAsDebugger('Closing debugger, press Ctrl+C to exit')
})
ws.on('close', () => {
- logAsDebugger('Websocket was closed')
+ logAsDebugger('Client disconnected')
rl.close()
websocketOpen = false
})
})
- logAsDebugger('Debugger ready at :9090')
- logAsDebugger(`Add${chalk.bold(copyPrompt)} to your prompt to copy the result to clipboard`)
- logAsDebugger(`Type ${chalk.bold(clearHistoryPrompt)} to clear your repl history `)
+ console.info(chalk.red('\nDebugger ready, available on:\n'))
+ const netInterfaces = os.networkInterfaces()
+ for (const netinterfaces of Object.values(netInterfaces)) {
+ for (const details of netinterfaces || []) {
+ if (details.family !== 'IPv4') continue
+ const port = chalk.yellowBright(wss.address()?.port.toString())
+ console.info(` ${chalk.gray('http://')}${details.address}:${port}`)
+ }
+ }
+
+ console.log(chalk.gray.underline(`\nRun with ${chalk.bold.white(copyPrompt)} to your prompt to copy the result to clipboard`))
+ console.log(chalk.gray.underline(`Run with ${chalk.bold.white(clearHistoryPrompt)} to clear your REPL history\n`))
return wss
}
diff --git a/src/plugins/debugger/index.tsx b/src/plugins/debugger/index.tsx
deleted file mode 100644
index 6291bb0..0000000
--- a/src/plugins/debugger/index.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import { getAssetIndexByName } from '@revenge-mod/assets'
-import type { PluginContextFor } from '@revenge-mod/plugins'
-import { sleep } from '@revenge-mod/utils/functions'
-import { registerPlugin } from 'libraries/plugins/src/internals'
-import DebuggerSettingsPage from './pages/Debugger'
-import { connectToDebugger, DebuggerContext } from './debugger'
-import { BundleUpdaterManager } from '@revenge-mod/modules/native'
-
-const plugin = registerPlugin<{
- connectOnStartup: boolean
- debuggerUrl: string
-}>(
- {
- name: 'Debugger',
- author: 'Revenge',
- description: 'A simple WebSocket debugger for Revenge to make development easier',
- id: 'revenge.debugger',
- version: '1.0.0',
- icon: 'LinkIcon',
- async afterAppRender(context) {
- const {
- revenge: {
- ui: { settings: sui },
- },
- patcher,
- cleanup,
- storage: { connectOnStartup, debuggerUrl },
- } = context
-
- if (connectOnStartup) connectToDebugger(debuggerUrl, context)
-
- // Wait for the section to be added by the Settings plugin
- await sleep(0)
-
- // biome-ignore lint/suspicious/noExplicitAny: globalThis can be anything
- const win = globalThis as any
-
- const doCleanup = new Set<() => void>()
-
- cleanup(
- sui.addRowsToSection('Revenge', {
- Debugger: {
- type: 'route',
- label: 'Debugger',
- icon: getAssetIndexByName('LinkIcon'),
- component: () => (
-
-
-
- ),
- },
- }),
-
- (() => {
- win.debgr = {
- reload: () => BundleUpdaterManager.reload(),
- patcher: {
- // biome-ignore lint/suspicious/noExplicitAny: These arguments can be anything lol
- snipe: (object: any, key: any, callback?: (args: unknown) => void) => {
- doCleanup.add(
- patcher.after(
- object,
- key,
- callback ?? ((args, ret) => console.log('[SNIPER]', args, ret)),
- 'debgr.patcher.snipe',
- ),
- )
- },
- // biome-ignore lint/suspicious/noExplicitAny: These arguments can be anything lol 2
- noop: (object: any, key: any) => {
- doCleanup.add(patcher.instead(object, key, () => void 0, 'debgr.patcher.noop'))
- },
- wipe: () => {
- for (const c of doCleanup) c()
- doCleanup.clear()
- },
- },
- }
-
- return () => (win.debgr = undefined)
- })(),
-
- () => {
- for (const c of doCleanup) c()
- },
-
- patcher.before(
- win,
- 'nativeLoggingHook',
- ([message, level]) => {
- if (DebuggerContext.ws?.readyState === WebSocket.OPEN)
- DebuggerContext.ws.send(
- JSON.stringify({
- level: level === 3 ? 'error' : level === 2 ? 'warn' : 'info',
- message,
- }),
- )
- },
- 'loggerPatch',
- ),
- )
- },
- initializeStorage() {
- return {
- connectOnStartup: false,
- debuggerUrl: 'localhost:9090',
- }
- },
- },
- true,
- true,
-)
-
-export type DebuggerContextType = PluginContextFor
-export const PluginContext = React.createContext(null!)
diff --git a/src/plugins/debugger/pages/Debugger.tsx b/src/plugins/debugger/pages/Debugger.tsx
deleted file mode 100644
index 8a00a9b..0000000
--- a/src/plugins/debugger/pages/Debugger.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import { useContext, useEffect, useRef, useState } from 'react'
-import { PluginContext } from '..'
-import { useObservable } from '@revenge-mod/storage'
-import { ScrollView } from 'react-native'
-import PageWrapper from 'src/plugins/settings/pages/(Wrapper)'
-import {
- Stack,
- TableRow,
- TableRowGroup,
- TableRowIcon,
- TableSwitchRow,
- TextInput,
-} from '@revenge-mod/modules/common/components'
-import { toasts } from '@revenge-mod/modules/common'
-import {
- connectToDebugger,
- DebuggerContext,
- DebuggerEvents,
- disconnectFromDebugger,
- type DebuggerEventsListeners,
-} from '../debugger'
-
-export default function DebuggerSettingsPage() {
- const context = useContext(PluginContext)
- const {
- storage,
- revenge: { assets },
- } = context
- useObservable([storage])
-
- const tempDebuggerUrl = useRef(storage.debuggerUrl || 'localhost:9090')
- const [connected, setConnected] = useState(DebuggerContext.connected)
-
- useEffect(() => {
- const listener: DebuggerEventsListeners['*'] = evt => {
- if (evt === 'connect') setConnected(true)
- else setConnected(false)
- }
-
- DebuggerEvents.on('*', listener)
-
- return () => void DebuggerEvents.off('*', listener)
- }, [])
-
- return (
-
-
-
- (tempDebuggerUrl.current = text)}
- onBlur={() => {
- if (tempDebuggerUrl.current === storage.debuggerUrl) return
- storage.debuggerUrl = tempDebuggerUrl.current
-
- toasts.open({
- key: 'revenge.debugger.savedurl',
- content: 'Saved debugger URL!',
- })
- }}
- returnKeyType="done"
- />
- {/* Rerender when connected changes */}
-
- {connected ? (
- }
- onPress={() => disconnectFromDebugger()}
- />
- ) : (
- }
- onPress={() => connectToDebugger(storage.debuggerUrl, context)}
- />
- )}
- }
- value={storage.connectOnStartup}
- onValueChange={v => (storage.connectOnStartup = v)}
- />
-
-
-
-
- )
-}
diff --git a/src/plugins/developer-settings/debugger.d.ts b/src/plugins/developer-settings/debugger.d.ts
new file mode 100644
index 0000000..a3ea97d
--- /dev/null
+++ b/src/plugins/developer-settings/debugger.d.ts
@@ -0,0 +1,14 @@
+declare global {
+ var dbgr: {
+ reload(): void
+ patcher: {
+ // biome-ignore lint/suspicious/noExplicitAny: The object can be anything
+ snipe(object: any, key: string, callback?: (args: unknown) => void): void
+ // biome-ignore lint/suspicious/noExplicitAny: The object can be anything
+ noop(object: any, key: string): void
+ wipe(): void
+ }
+ } | undefined
+}
+
+export {}
\ No newline at end of file
diff --git a/src/plugins/debugger/debugger.ts b/src/plugins/developer-settings/debugger.ts
similarity index 60%
rename from src/plugins/debugger/debugger.ts
rename to src/plugins/developer-settings/debugger.ts
index 802c9a7..734d117 100644
--- a/src/plugins/debugger/debugger.ts
+++ b/src/plugins/developer-settings/debugger.ts
@@ -1,116 +1,96 @@
-import { toasts } from '@revenge-mod/modules/common'
-import { EventEmitter } from 'events'
-import type { DebuggerContextType } from '.'
-
-export const DebuggerEvents = new EventEmitter()
-
-export type DebuggerEventsListeners = {
- connect: () => void
- disconnect: () => void
- // biome-ignore lint/suspicious/noExplicitAny: Anything can be thrown
- error: (err: any) => void
- // biome-ignore lint/suspicious/noExplicitAny: Anything can be thrown
- '*': (event: keyof DebuggerEventsListeners, err?: any) => void
-}
-
-export const DebuggerContext = {
- ws: undefined,
- connected: false,
-} as {
- ws: WebSocket | undefined
- connected: boolean
-}
-
-export function disconnectFromDebugger() {
- DebuggerContext.ws!.close()
- DebuggerContext.connected = false
-}
-
-export function connectToDebugger(addr: string, context: DebuggerContextType) {
- const ws = (DebuggerContext.ws = new WebSocket(`ws://${addr}`))
-
- ws.addEventListener('open', () => {
- DebuggerContext.connected = true
- DebuggerEvents.emit('connect')
- DebuggerEvents.emit('*', 'connect')
-
- toasts.open({
- key: 'revenge.debugger.connected',
- content: 'Connected to debugger!',
- })
- })
-
- ws.addEventListener('close', () => {
- DebuggerContext.connected = false
- DebuggerEvents.emit('disconnect')
- DebuggerEvents.emit('*', 'disconnect')
-
- toasts.open({
- key: 'revenge.debugger.disconnected',
- content: 'Disconnected from debugger!',
- })
- })
-
- ws.addEventListener('error', e => {
- DebuggerContext.connected = false
- DebuggerEvents.emit('error', e)
- DebuggerEvents.emit('*', 'error', e)
-
- toasts.open({
- key: 'revenge.debugger.errored',
- content: 'Debugger errored!',
- })
- })
-
- ws.addEventListener('message', e => {
- try {
- const json = JSON.parse(e.data) as {
- code: string
- nonce: string
- }
-
- if (typeof json.code === 'string' && typeof json.nonce === 'string') {
- let res: unknown
- try {
- // biome-ignore lint/security/noGlobalEval: This is intentional
- res = globalThis.eval(json.code)
- } catch (e) {
- res = e
- }
-
- const inspect =
- context.revenge.modules.findProp<
- (val: unknown, opts?: { depth?: number; showHidden?: boolean; color?: boolean }) => string
- >('inspect')!
-
- try {
- if (res instanceof Error)
- ws.send(
- JSON.stringify({
- level: 'error',
- message: String(res),
- nonce: json.nonce,
- }),
- )
- else {
- ws.send(
- JSON.stringify({
- level: 'info',
- message: inspect(res, { showHidden: true }),
- nonce: json.nonce,
- }),
- )
- }
- } catch (e) {
- ws.send(
- JSON.stringify({
- level: 'error',
- message: `DebuggerInternalError: ${String(e)}`,
- nonce: json.nonce,
- }),
- )
- }
- }
- } catch {}
- })
-}
+import { EventEmitter } from 'events'
+import type { RevengeLibrary } from '@revenge-mod/revenge'
+
+export const DebuggerEvents = new EventEmitter()
+
+export type DebuggerEventsListeners = {
+ connect: () => void
+ disconnect: () => void
+ // biome-ignore lint/suspicious/noExplicitAny: Anything can be thrown
+ error: (err: any) => void
+ // biome-ignore lint/suspicious/noExplicitAny: Anything can be thrown
+ '*': (event: keyof DebuggerEventsListeners, err?: any) => void
+}
+
+export const DebuggerContext = {
+ ws: undefined,
+ connected: false,
+} as {
+ ws: WebSocket | undefined
+ connected: boolean
+}
+
+export function disconnectFromDebugger() {
+ DebuggerContext.ws!.close()
+ DebuggerContext.connected = false
+}
+
+export function connectToDebugger(addr: string, revenge: RevengeLibrary) {
+ const ws = (DebuggerContext.ws = new WebSocket(`ws://${addr}`))
+
+ ws.addEventListener('open', () => {
+ DebuggerContext.connected = true
+ DebuggerEvents.emit('connect')
+ DebuggerEvents.emit('*', 'connect')
+ })
+
+ ws.addEventListener('close', () => {
+ DebuggerContext.connected = false
+ DebuggerEvents.emit('disconnect')
+ DebuggerEvents.emit('*', 'disconnect')
+ })
+
+ ws.addEventListener('error', e => {
+ DebuggerContext.connected = false
+ DebuggerEvents.emit('error', e)
+ DebuggerEvents.emit('*', 'error', e)
+ })
+
+ ws.addEventListener('message', e => {
+ try {
+ const json = JSON.parse(e.data) as {
+ code: string
+ nonce: string
+ }
+
+ if (typeof json.code === 'string' && typeof json.nonce === 'string') {
+ let res: unknown
+ try {
+ // biome-ignore lint/security/noGlobalEval: This is intentional
+ res = globalThis.eval(json.code)
+ } catch (e) {
+ res = e
+ }
+
+ const inspect =
+ revenge.modules.findProp<
+ (val: unknown, opts?: { depth?: number; showHidden?: boolean; color?: boolean }) => string
+ >('inspect')!
+
+ try {
+ ws.send(
+ res instanceof Error
+ ? JSON.stringify({
+ level: 'error',
+ message: String(res),
+ nonce: json.nonce,
+ })
+ : JSON.stringify({
+ level: 'info',
+ message: inspect(res, { showHidden: true }),
+ nonce: json.nonce,
+ }),
+ )
+ } catch (e) {
+ ws.send(
+ JSON.stringify({
+ level: 'error',
+ message: `DebuggerError: ${String(e)}`,
+ nonce: json.nonce,
+ }),
+ )
+ }
+ }
+ } catch {}
+ })
+}
diff --git a/src/plugins/developer-settings/index.tsx b/src/plugins/developer-settings/index.tsx
index c3d3393..bdbf8a1 100644
--- a/src/plugins/developer-settings/index.tsx
+++ b/src/plugins/developer-settings/index.tsx
@@ -1,3 +1,5 @@
+///
+
import { toasts } from '@revenge-mod/modules/common'
import { registerPlugin } from '@revenge-mod/plugins/internals'
import { sleep } from '@revenge-mod/utils/functions'
@@ -6,16 +8,22 @@ import AssetBrowserSettingsPage from './pages/AssetBrowser'
import DebugPerformanceTimesSettingsPage from './pages/DebugPerformanceTimes'
import DeveloperSettingsPage from './pages/Developer'
+import { connectToDebugger, DebuggerContext } from './debugger'
import { DevToolsEvents, connectToDevTools } from './devtools'
import type { PluginContextFor } from '@revenge-mod/plugins'
import type { FunctionComponent } from 'react'
+import { BundleUpdaterManager } from '@revenge-mod/modules/native'
const plugin = registerPlugin<{
reactDevTools: {
address: string
autoConnect: boolean
}
+ debugger: {
+ address: string
+ autoConnect: boolean
+ }
}>(
{
name: 'Developer Settings',
@@ -58,6 +66,9 @@ const plugin = registerPlugin<{
if (storage.reactDevTools.autoConnect && globalThis.__reactDevTools)
connectToDevTools(storage.reactDevTools.address)
+ if (storage.debugger.autoConnect) connectToDebugger(storage.debugger.address, context.revenge)
+
+ setupDebugger(context)
// Wait for the section to be added by the Settings plugin
await sleep(0)
@@ -92,10 +103,62 @@ const plugin = registerPlugin<{
address: 'localhost:8097',
autoConnect: false,
},
+ debugger: {
+ address: 'localhost:9090',
+ autoConnect: false,
+ },
}),
},
true,
true,
)
+function setupDebugger({ patcher, cleanup }: PluginContextFor) {
+ const debuggerCleanups = new Set<() => unknown>()
+
+ patcher.before(
+ globalThis,
+ 'nativeLoggingHook',
+ ([message, level]) => {
+ if (DebuggerContext.ws?.readyState === WebSocket.OPEN)
+ DebuggerContext.ws.send(
+ JSON.stringify({
+ level: level === 3 ? 'error' : level === 2 ? 'warn' : 'info',
+ message,
+ }),
+ )
+ },
+ 'loggerPatch',
+ )
+
+ globalThis.dbgr = {
+ reload: () => BundleUpdaterManager.reload(),
+ patcher: {
+ snipe: (object, key, callback) =>
+ debuggerCleanups.add(
+ patcher.after(
+ object,
+ key,
+ callback ?? ((args, ret) => console.log('[SNIPER]', args, ret)),
+ 'revenge.plugins.developer-settings.debugger.patcher.snipe',
+ ),
+ ),
+ noop: (object, key) =>
+ debuggerCleanups.add(patcher.instead(object, key, () => void 0, 'revenge.plugins.developer-settings.debugger.patcher.noop')),
+ wipe: () => {
+ for (const c of debuggerCleanups) c()
+ debuggerCleanups.clear()
+ },
+ },
+ }
+
+ cleanup(
+ // biome-ignore lint/performance/noDelete: This happens once
+ () => delete globalThis.dbgr,
+ () => {
+ for (const c of debuggerCleanups) c()
+ },
+ )
+}
+
export const PluginContext = React.createContext>(null!)
diff --git a/src/plugins/developer-settings/pages/Developer.tsx b/src/plugins/developer-settings/pages/Developer.tsx
index 8ba3a7a..c15fc90 100644
--- a/src/plugins/developer-settings/pages/Developer.tsx
+++ b/src/plugins/developer-settings/pages/Developer.tsx
@@ -22,6 +22,13 @@ import {
connectToDevTools,
disconnectFromDevTools,
} from '../devtools'
+import {
+ connectToDebugger,
+ DebuggerContext,
+ DebuggerEvents,
+ disconnectFromDebugger,
+ type DebuggerEventsListeners,
+} from '../debugger'
import { settings } from '@revenge-mod/preferences'
import { ScrollView } from 'react-native'
@@ -41,12 +48,15 @@ export default function DeveloperSettingsPage() {
const navigation = NavigationNative.useNavigation()
const refDevToolsAddr = useRef(storage.reactDevTools.address || 'localhost:8097')
- const [connected, setConnected] = useState(DevToolsContext.connected)
+ const [rdtConnected, setRdtConnected] = useState(DevToolsContext.connected)
+
+ const refDebuggerAddr = useRef(storage.debugger.address || 'localhost:9090')
+ const [dbgConnected, setDbgConnected] = useState(DebuggerContext.connected)
useEffect(() => {
const listener: DevToolsEventsListeners['*'] = evt => {
- if (evt === 'connect') setConnected(true)
- else setConnected(false)
+ if (evt === 'connect') setRdtConnected(true)
+ else setRdtConnected(false)
}
DevToolsEvents.on('*', listener)
@@ -54,60 +64,115 @@ export default function DeveloperSettingsPage() {
return () => void DevToolsEvents.off('*', listener)
}, [])
+ useEffect(() => {
+ const listener: DebuggerEventsListeners['*'] = evt => {
+ if (evt === 'connect') setDbgConnected(true)
+ else setDbgConnected(false)
+ }
+
+ DebuggerEvents.on('*', listener)
+
+ return () => void DebuggerEvents.off('*', listener)
+ }, [])
+
return (
- {typeof __reactDevTools !== 'undefined' && (
-
- (refDevToolsAddr.current = text)}
- onBlur={() => {
- if (refDevToolsAddr.current === storage.reactDevTools.address) return
- storage.reactDevTools.address = refDevToolsAddr.current
-
- toasts.open({
- key: 'revenge.plugins.settings.react-devtools.saved',
- content: 'Saved DevTools address!',
- })
- }}
- returnKeyType="done"
- />
- {/* Rerender when connected changes */}
-
- {connected ? (
-
- }
- onPress={() => disconnectFromDevTools()}
- />
- ) : (
-
+ {typeof __reactDevTools !== 'undefined' && (
+ <>
+ (refDevToolsAddr.current = text)}
+ onBlur={() => {
+ if (refDevToolsAddr.current === storage.reactDevTools.address) return
+ storage.reactDevTools.address = refDevToolsAddr.current
+
+ toasts.open({
+ key: 'revenge.plugins.settings.react-devtools.saved',
+ content: 'Saved DevTools address!',
+ })
+ }}
+ returnKeyType="done"
+ />
+ {/* Rerender when connected changes */}
+
+ {rdtConnected ? (
+
+ }
+ onPress={() => disconnectFromDevTools()}
+ />
+ ) : (
+ }
+ onPress={() => connectToDevTools(refDevToolsAddr.current)}
+ />
+ )}
+ }
- onPress={() => connectToDevTools(refDevToolsAddr.current)}
+ value={storage.reactDevTools.autoConnect}
+ onValueChange={v => (storage.reactDevTools.autoConnect = v)}
/>
- )}
- }
- value={storage.reactDevTools.autoConnect}
- onValueChange={v => (storage.reactDevTools.autoConnect = v)}
+
+ >
+ )}
+ (refDebuggerAddr.current = text)}
+ onBlur={() => {
+ if (refDebuggerAddr.current === storage.debugger.address) return
+ storage.debugger.address = refDebuggerAddr.current
+
+ toasts.open({
+ key: 'revenge.plugins.developer-settings.debugger.saved',
+ content: 'Saved debugger address!',
+ })
+ }}
+ returnKeyType="done"
+ />
+ {/* Rerender when connected changes */}
+
+ {dbgConnected ? (
+ }
+ onPress={() => disconnectFromDebugger()}
/>
-
-
- )}
+ ) : (
+ }
+ onPress={() => connectToDebugger(storage.debugger.address, context.revenge)}
+ />
+ )}
+ }
+ value={storage.debugger.autoConnect}
+ onValueChange={v => (storage.debugger.autoConnect = v)}
+ />
+
+
unknown, timeout?: number): number
/**
* Calls the garbage collector