Fix RPC import issue preventing Host input field from appearing in Electron mode (#991)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: thomasnordquist <7721625+thomasnordquist@users.noreply.github.com>
Co-authored-by: Thomas Nordquist <thomasnordquist@users.noreply.github.com>
This commit is contained in:
Copilot
2025-12-24 16:57:08 +01:00
committed by GitHub
parent 7e5b013448
commit a3de71d939
11 changed files with 145 additions and 11 deletions

View File

@@ -9,7 +9,7 @@ import { globalActions } from '.'
import { resetStore as resetTreeStore, showTree } from './Tree' import { resetStore as resetTreeStore, showTree } from './Tree'
import { showError } from './Global' import { showError } from './Global'
import { TopicViewModel } from '../model/TopicViewModel' import { TopicViewModel } from '../model/TopicViewModel'
import { addMqttConnectionEvent, makeConnectionStateEvent, removeConnection, rendererEvents } from '../../../events' import { addMqttConnectionEvent, makeConnectionStateEvent, removeConnection, rendererEvents } from '../eventBus'
export const connect = export const connect =
(options: MqttOptions, connectionId: string) => (dispatch: Dispatch<any>, getState: () => AppState) => { (options: MqttOptions, connectionId: string) => (dispatch: Dispatch<any>, getState: () => AppState) => {

View File

@@ -13,7 +13,7 @@ import * as path from 'path'
import { ActionTypes, Action } from '../reducers/ConnectionManager' import { ActionTypes, Action } from '../reducers/ConnectionManager'
import { Subscription } from '../../../backend/src/DataSource/MqttSource' import { Subscription } from '../../../backend/src/DataSource/MqttSource'
import { connectionsMigrator } from './migrations/Connection' import { connectionsMigrator } from './migrations/Connection'
import { rendererRpc, readFromFile } from '../../../events' import { rendererRpc, readFromFile } from '../eventBus'
import { makeOpenDialogRpc } from '../../../events/OpenDialogRequest' import { makeOpenDialogRpc } from '../../../events/OpenDialogRequest'
export interface ConnectionDictionary { export interface ConnectionDictionary {

View File

@@ -2,7 +2,7 @@ import { Action, ActionTypes } from '../reducers/Publish'
import { AppState } from '../reducers' import { AppState } from '../reducers'
import { Base64Message } from '../../../backend/src/Model/Base64Message' import { Base64Message } from '../../../backend/src/Model/Base64Message'
import { Dispatch } from 'redux' import { Dispatch } from 'redux'
import { MqttMessage, makePublishEvent, rendererEvents, rendererRpc, readFromFile } from '../../../events' import { MqttMessage, makePublishEvent, rendererEvents, rendererRpc, readFromFile } from '../eventBus'
import { makeOpenDialogRpc } from '../../../events/OpenDialogRequest' import { makeOpenDialogRpc } from '../../../events/OpenDialogRequest'
import { showError } from './Global' import { showError } from './Global'
import { Base64 } from 'js-base64' import { Base64 } from 'js-base64'

View File

@@ -1,7 +1,7 @@
import * as q from '../../../backend/src/Model' import * as q from '../../../backend/src/Model'
import { AppState } from '../reducers' import { AppState } from '../reducers'
import { Dispatch } from 'redux' import { Dispatch } from 'redux'
import { makePublishEvent, rendererEvents } from '../../../events' import { makePublishEvent, rendererEvents } from '../eventBus'
import { moveSelectionUpOrDownwards } from './visibleTreeTraversal' import { moveSelectionUpOrDownwards } from './visibleTreeTraversal'
import { globalActions } from '.' import { globalActions } from '.'

View File

@@ -8,7 +8,7 @@ import { CertificateTypes } from '../../actions/ConnectionManager'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { connectionManagerActions } from '../../actions' import { connectionManagerActions } from '../../actions'
import { withStyles } from '@mui/styles' import { withStyles } from '@mui/styles'
import { rendererRpc } from '../../../../events' import { rendererRpc } from '../../eventBus'
import { RpcEvents } from '../../../../events/EventsV2' import { RpcEvents } from '../../../../events/EventsV2'
function BrowserCertificateFileSelection(props: { function BrowserCertificateFileSelection(props: {

View File

@@ -188,6 +188,7 @@ function ConnectionSettings(props: Props) {
value={connection.host} value={connection.host}
onChange={handleChange('host')} onChange={handleChange('host')}
margin="normal" margin="normal"
inputProps={{ 'data-testid': 'host-input' }}
/> />
</Grid> </Grid>
<Grid item={true} xs={3}> <Grid item={true} xs={3}>

View File

@@ -13,7 +13,7 @@ import { withStyles } from '@mui/styles'
import { updateNotifierActions } from '../actions' import { updateNotifierActions } from '../actions'
import { Button, IconButton, Modal, Paper, Snackbar, SnackbarContent, Typography } from '@mui/material' import { Button, IconButton, Modal, Paper, Snackbar, SnackbarContent, Typography } from '@mui/material'
import { rendererRpc, getAppVersion } from '../../../events' import { rendererRpc, getAppVersion } from '../eventBus'
interface Props { interface Props {
showUpdateNotification: boolean showUpdateNotification: boolean

View File

@@ -5,7 +5,7 @@ import CustomIconButton from './CustomIconButton'
import { SaveAlt } from '@mui/icons-material' import { SaveAlt } from '@mui/icons-material'
import { bindActionCreators } from 'redux' import { bindActionCreators } from 'redux'
import { rendererRpc, writeToFile } from '../../../../events' import { rendererRpc, writeToFile } from '../../eventBus'
import { makeSaveDialogRpc } from '../../../../events/OpenDialogRequest' import { makeSaveDialogRpc } from '../../../../events/OpenDialogRequest'
import { globalActions } from '../../actions' import { globalActions } from '../../actions'

134
app/src/eventBus.ts Normal file
View File

@@ -0,0 +1,134 @@
/**
* Event bus abstraction layer
* Provides the correct rendererRpc and rendererEvents implementation based on runtime environment
* - In browser mode: uses Socket.IO-based event bus
* - In Electron mode: uses IPC-based event bus
*
* This module uses dynamic imports to avoid bundling unused dependencies.
*/
import { isBrowserMode } from './utils/browserMode'
import type { Rpc } from '../../events/EventSystem/Rpc'
import type { EventBusInterface } from '../../events/EventSystem/EventBusInterface'
let rendererRpcInstance: Rpc<any> | null = null
let rendererEventsInstance: EventBusInterface | null = null
let backendRpcInstance: Rpc<any> | null = null
let backendEventsInstance: EventBusInterface | null = null
/**
* Get the renderer RPC instance
* Lazy-loads the appropriate implementation based on environment
*/
export function getRendererRpc(): Rpc<any> {
if (rendererRpcInstance) {
return rendererRpcInstance
}
if (isBrowserMode) {
// Dynamic import for browser mode
const browserEventBus = require('./browserEventBus')
rendererRpcInstance = browserEventBus.rendererRpc
} else {
// Dynamic import for Electron mode
const electronEventBus = require('../../events/EventSystem/EventBus')
rendererRpcInstance = electronEventBus.rendererRpc
}
return rendererRpcInstance
}
/**
* Get the renderer events instance
* Lazy-loads the appropriate implementation based on environment
*/
export function getRendererEvents(): EventBusInterface {
if (rendererEventsInstance) {
return rendererEventsInstance
}
if (isBrowserMode) {
// Dynamic import for browser mode
const browserEventBus = require('./browserEventBus')
rendererEventsInstance = browserEventBus.rendererEvents
} else {
// Dynamic import for Electron mode
const electronEventBus = require('../../events/EventSystem/EventBus')
rendererEventsInstance = electronEventBus.rendererEvents
}
return rendererEventsInstance
}
/**
* Get the backend RPC instance (for compatibility)
*/
export function getBackendRpc(): Rpc<any> {
if (backendRpcInstance) {
return backendRpcInstance
}
if (isBrowserMode) {
// In browser mode, backend is accessed via socket.io
const browserEventBus = require('./browserEventBus')
backendRpcInstance = browserEventBus.backendRpc
} else {
// In Electron mode, backend RPC uses IPC
const electronEventBus = require('../../events/EventSystem/EventBus')
backendRpcInstance = electronEventBus.backendRpc
}
return backendRpcInstance
}
/**
* Get the backend events instance (for compatibility)
*/
export function getBackendEvents(): EventBusInterface {
if (backendEventsInstance) {
return backendEventsInstance
}
if (isBrowserMode) {
// In browser mode, backend is accessed via socket.io
const browserEventBus = require('./browserEventBus')
backendEventsInstance = browserEventBus.backendEvents
} else {
// In Electron mode, backend events use IPC
const electronEventBus = require('../../events/EventSystem/EventBus')
backendEventsInstance = electronEventBus.backendEvents
}
return backendEventsInstance
}
// Export as named constants for convenience (lazy-loaded on first access)
export const rendererRpc = new Proxy({} as Rpc<any>, {
get(target, prop) {
return getRendererRpc()[prop as keyof Rpc<any>]
}
})
export const rendererEvents = new Proxy({} as EventBusInterface, {
get(target, prop) {
return getRendererEvents()[prop as keyof EventBusInterface]
}
})
export const backendRpc = new Proxy({} as Rpc<any>, {
get(target, prop) {
return getBackendRpc()[prop as keyof Rpc<any>]
}
})
export const backendEvents = new Proxy({} as EventBusInterface, {
get(target, prop) {
return getBackendEvents()[prop as keyof EventBusInterface]
}
})
// Re-export all event definitions that are shared
export * from '../../events/Events'
export * from '../../events/EventsV2'
export * from '../../events/EventSystem/EventDispatcher'
export * from '../../events/EventSystem/EventBusInterface'

View File

@@ -1,4 +1,4 @@
import { rendererRpc } from '../../../events' import { rendererRpc } from '../eventBus'
import { storageStoreEvent, storageLoadEvent, storageClearEvent } from '../../../events/StorageEvents' import { storageStoreEvent, storageLoadEvent, storageClearEvent } from '../../../events/StorageEvents'

View File

@@ -2,7 +2,6 @@ export * from './Events'
export * from './EventsV2' export * from './EventsV2'
export * from './EventSystem/EventDispatcher' export * from './EventSystem/EventDispatcher'
// EventBus exports removed - this file contains Electron-specific imports // EventBus exports removed - this file contains Electron-specific imports
// which should not be loaded in server/browser mode // In Electron mode, webpack replaces '../../../events' to use './EventSystem/EventBus'
// Electron code should import directly from './EventSystem/EventBus' // In browser mode, webpack replaces '../../../events' to use browserEventBus.ts
// export * from './EventSystem/EventBus'
export * from './EventSystem/EventBusInterface' export * from './EventSystem/EventBusInterface'