Tom Butcher d16aa373be
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
Implement Navigation Tabs and Dashboard Enhancements
- Introduced NavigationTabsContext to manage tab state and navigation within the dashboard.
- Added DashboardTabs component for improved tabbed navigation, allowing users to add, close, and reorder tabs.
- Enhanced Dashboard layout to conditionally render tab panes based on the active tab, improving user experience.
- Updated various components to utilize navigation tab context, ensuring consistent title management and tab interactions.
- Implemented drag-and-drop functionality for tabs, enabling users to rearrange their workspace effectively.
- Refactored CSS styles for dashboard tabs and panes to ensure proper layout and responsiveness across devices.
2026-09-17 20:39:50 +01:00

178 lines
5.3 KiB
JavaScript

import { BrowserView } from 'electrobun/bun'
import {
checkForCompletedUpdate,
getRunningAppVersion,
startAppUpdate
} from './appupdate.js'
import {
checkForDuplicateInstallations,
removeDuplicateInstallations
} from './check-duplicate-installations.js'
import { setSidebarViewMenu } from './menu.js'
import { sendToAllWindows, sendToRenderer, sendToWindow } from './notify.js'
import { resizeSpotlightWindow } from './spotlight.js'
import {
clearAuthSession,
getAppSettings,
getAuthSession,
setAppSettings,
setAuthSession
} from './store.js'
import {
createAppWindow,
getMainWindow,
getWindowSession,
getWindowState,
handleWindowControl,
openExternalUrl,
openInternalUrl,
syncWindowTabs,
beginTabDrag,
completeTabDrop,
cancelTabDrag
} from './window.js'
export function createAppRpc() {
const requestHandlers = {
getOsInfo: async () => ({
platform: process.platform
}),
getWindowState: async ({ windowId } = {}) => getWindowState(windowId),
windowControl: async ({ action, windowId } = {}) => {
handleWindowControl(action, windowId)
return { ok: true }
},
syncWindowTabs: async ({ windowId, activeTabId, tabs } = {}) => ({
ok: syncWindowTabs(windowId, { activeTabId, tabs })
}),
getWindowSession: async ({ windowId } = {}) => getWindowSession(windowId),
createAppWindow: async ({
tabs,
activeTabId,
windowId
} = {}) => {
await createAppWindow({
tabs,
activeTabId,
sourceWindowId: windowId
})
return { ok: true }
},
beginTabDrag: async ({ windowId, tab } = {}) =>
beginTabDrag({ windowId, tab }),
completeTabDrop: async ({ windowId, beforeTabId, insertBefore } = {}) =>
completeTabDrop({ windowId, beforeTabId, insertBefore }),
cancelTabDrag: async ({ windowId } = {}) => cancelTabDrag({ windowId }),
openExternalUrl: async ({ url }) => {
openExternalUrl(url)
return { ok: true }
},
openInternalUrl: async ({ url }) => ({
ok: openInternalUrl(url)
}),
getAuthSession: async () => getAuthSession(),
setAuthSession: async ({ session }) => ({
ok: await setAuthSession(session)
}),
clearAuthSession: async () => ({
ok: await clearAuthSession()
}),
getAppSettings: async () => getAppSettings(),
setAppSettings: async ({ settings }) => ({
ok: await setAppSettings(settings)
}),
startAppUpdate: async ({ update }) => {
const mainWindow = getMainWindow()
const sendProgress = (payload) => {
sendToAllWindows('appUpdateProgress', {
timestamp: new Date().toISOString(),
...payload
})
}
// Updates can take several minutes; return immediately and report
// progress via appUpdateProgress messages instead of blocking RPC.
void startAppUpdate(mainWindow, update, sendProgress).catch((error) => {
console.error('App update failed:', error)
})
return { ok: true }
},
resizeSpotlightWindow: async ({ height }) => ({
ok: resizeSpotlightWindow(height)
}),
setSidebarViewMenu: async ({ sections }) => ({
ok: setSidebarViewMenu(sections)
}),
checkAppUpdateResult: async () =>
checkForCompletedUpdate(getMainWindow()),
checkDuplicateInstallations: async () => checkForDuplicateInstallations(),
removeDuplicateInstallations: async () => {
// Removal waits on an admin/UAC prompt which can outlive the RPC
// timeout; report the outcome via a push message instead.
void removeDuplicateInstallations()
.catch((error) => ({
ok: false,
error:
error?.message || 'Failed to remove the duplicate installation.'
}))
.then((result) => {
sendToAllWindows('duplicateInstallationsRemoved', result)
})
return { ok: true }
},
getAppVersion: async () => getRunningAppVersion() || 'desktop',
getAppEngine: async () => {
const mainWindow = getMainWindow()
const renderer = mainWindow?.renderer || 'native'
return renderer === 'cef' ? 'chromium' : 'native'
}
}
return BrowserView.defineRPC({
maxRequestTime: 30000,
handlers: {
requests: requestHandlers,
messages: {
rendererRequest: ({ id, method, params, windowId }) => {
const requestParams = params || {}
const requestWindowId = windowId || requestParams.windowId
const respond = (payload) => {
if (requestWindowId) {
sendToWindow(requestWindowId, 'rpcResponse', payload)
return
}
sendToRenderer('rpcResponse', payload)
}
const handler = requestHandlers[method]
if (!handler) {
respond({
id,
success: false,
error: `Unknown desktop RPC method: ${String(method)}`
})
return
}
void Promise.resolve()
.then(() => handler(requestParams))
.then((result) => {
respond({
id,
success: true,
result
})
})
.catch((error) => {
respond({
id,
success: false,
error: error?.message || String(error)
})
})
}
}
}
})
}