Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { ComponentPropsWithoutRef } from 'react'

import { useCapabilities, useProject, useWindow } from '../../../../middleware/shared/providers'
Expand Down Expand Up @@ -78,7 +78,7 @@
clearAndClose()
return
case 'close-app':
if (capabilities.hasNativeWindowControls) {
if (capabilities.isNativeApplication) {
windowPort.quit()
}
return
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { useCapabilities } from '../../../../../middleware/shared/providers'
import { OpenPLCIcon } from '../../../../assets/icons/oplc'
import { useOpenPLCStore } from '../../../../store'
Expand All @@ -10,10 +10,10 @@
return (
<div
className={cn('flex flex-1 items-center justify-center gap-2', {
'oplc-titlebar-drag-region': caps.hasNativeTitleBar,
'oplc-titlebar-drag-region': caps.isNativeApplication,
})}
>
{caps.hasNativeMenu ? (
{caps.isNativeApplication ? (
<>
<OpenPLCIcon />
<span className='font-caption text-xs font-normal'>OpenPLC Editor</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { useCapabilities } from '../../../../../middleware/shared/providers'
import { OpenPLCIcon } from '../../../../assets/icons/oplc'
import { useOpenPLCStore } from '../../../../store'
Expand All @@ -6,8 +6,9 @@
export const TitleBarLeftSlot = () => {
const caps = useCapabilities()
const path = useOpenPLCStore((state) => state.project.meta.path)
const OS = useOpenPLCStore((state) => state.workspace.systemConfigs.OS)

if (caps.hasNativeMenu) {
if (caps.isNativeApplication && OS !== 'win32') {
return <div className='flex items-center justify-start gap-1 px-4 py-0.5' />
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { useCapabilities } from '../../../../../middleware/shared/providers'
import { useOpenPLCStore } from '../../../../store'
import { WindowControls } from '../../../_molecules/window-controls'

const TitleBarRightSlot = () => {
const caps = useCapabilities()
const OS = useOpenPLCStore((state) => state.workspace.systemConfigs.OS)

return (
<div id='title-bar-right-slot' className='flex items-center justify-end'>
{caps.hasNativeWindowControls && <WindowControls />}
{caps.isNativeApplication && OS === 'win32' && <WindowControls />}
</div>
)
}
Expand Down
20 changes: 10 additions & 10 deletions src/frontend/components/_templates/accelerator-handler.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { useCallback, useEffect, useRef, useState } from 'react'

import {
Expand Down Expand Up @@ -318,44 +318,44 @@
* Window lifecycle events (editor-only, gated by capabilities)
*/
useEffect(() => {
if (!capabilities.hasNativeWindowControls) return
if (!capabilities.isNativeApplication) return

const unsub = windowPort.enableAutoCloseHandshake?.()
return unsub
}, [capabilities.hasNativeWindowControls, windowPort])
}, [capabilities.isNativeApplication, windowPort])

useEffect(() => {
if (!capabilities.hasNativeWindowControls) return
if (!capabilities.isNativeApplication) return

const unsub = windowPort.onCloseRequested(() => {
setCloseWindow(true)
})
return unsub
}, [capabilities.hasNativeWindowControls, windowPort, setCloseWindow])
}, [capabilities.isNativeApplication, windowPort, setCloseWindow])

useEffect(() => {
if (!capabilities.hasNativeWindowControls) return
if (!capabilities.isNativeApplication) return

const unsub = windowPort.onDarwinAppQuitting?.(() => {
setCloseAppDarwin(true)
})
return unsub
}, [capabilities.hasNativeWindowControls, windowPort, setCloseAppDarwin])
}, [capabilities.isNativeApplication, windowPort, setCloseAppDarwin])

useEffect(() => {
if (!capabilities.hasNativeWindowControls) return
if (!capabilities.isNativeApplication) return

const unsub = windowPort.onMaximizedChanged?.(() => {
toggleMaximizedWindow()
})
return unsub
}, [capabilities.hasNativeWindowControls, windowPort, toggleMaximizedWindow])
}, [capabilities.isNativeApplication, windowPort, toggleMaximizedWindow])

/**
* beforeunload event (editor-only)
*/
useEffect(() => {
if (!capabilities.hasNativeWindowControls) return
if (!capabilities.isNativeApplication) return

const handler = (e: BeforeUnloadEvent) => {
if (capabilities.isDevMode) return
Expand All @@ -380,7 +380,7 @@
window.addEventListener('beforeunload', handler)
return () => window.removeEventListener('beforeunload', handler)
}, [
capabilities.hasNativeWindowControls,
capabilities.isNativeApplication,
close.window,
close.app,
close.appDarwin,
Expand Down
5 changes: 3 additions & 2 deletions src/frontend/components/_templates/app-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ComponentPropsWithoutRef, ReactNode, useCallback, useEffect, useState } from 'react'

import { useProject, useSystem } from '../../../middleware/shared/providers'
import { useCapabilities, useProject, useSystem } from '../../../middleware/shared/providers'
import { useOpenPLCStore } from '../../store'
import type { RungLadderState } from '../../store/slices/ladder'
import { cn } from '../../utils/cn'
Expand All @@ -25,6 +25,7 @@
const AppLayout = ({ children, ...rest }: AppLayoutProps): ReactNode => {
const system = useSystem()
const projectPort = useProject()
const caps = useCapabilities()
const [showComponent, setShowComponent] = useState(true)
const modals = useOpenPLCStore(useCallback((s) => s.modals, []))
const OS = useOpenPLCStore(useCallback((s) => s.workspace.systemConfigs.OS, []))
Expand Down Expand Up @@ -65,7 +66,7 @@
return () => window.removeEventListener('resize', handleResize)
}, [])

const showTitleBar = OS !== 'linux'
const showTitleBar = !caps.isNativeApplication || OS !== 'linux'

return (
<>
Expand Down
6 changes: 1 addition & 5 deletions src/middleware/editor-platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@ export function setRuntimeIpAddress(ip: string): void {
_runtimeIpAddress = ip
}

// macOS has a native application menu bar; Windows and Linux do not
// (the Electron menu is hidden on those platforms — see menu.ts buildDefaultTemplate).
const isMac = navigator.platform.startsWith('Mac')

/**
* Editor platform ports — all port interfaces wired to Electron IPC bridge.
*/
Expand All @@ -58,5 +54,5 @@ export const editorPorts: PlatformPorts = {
accelerator: createEditorAcceleratorAdapter(),
theme: createEditorThemeAdapter(),
versionControl: createEditorVersionControlAdapter(),
capabilities: { ...EDITOR_CAPABILITIES, isDevMode: process.env.NODE_ENV === 'development', hasNativeMenu: isMac },
capabilities: { ...EDITOR_CAPABILITIES, isDevMode: process.env.NODE_ENV === 'development' },
}
18 changes: 4 additions & 14 deletions src/middleware/shared/ports/platform-capabilities.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/**
* PlatformCapabilities — Feature toggles for platform-specific UI behavior.
*
Expand All @@ -13,14 +13,8 @@
export interface PlatformCapabilities {
// --- Window & Chrome ---

/** True if the app has native window controls (minimize, maximize, close buttons in title bar). */
hasNativeWindowControls: boolean

/** True if the app has a native application menu (Electron menu bar). */
hasNativeMenu: boolean

/** True if the app has a native title bar with drag-to-move support (Electron frameless window). */
hasNativeTitleBar: boolean
/** True if the app is a native desktop application (Electron editor). */
isNativeApplication: boolean

/** True if the app supports native file dialogs (open, save, pick directory). */
hasNativeFileDialogs: boolean
Expand Down Expand Up @@ -98,9 +92,7 @@
// ---------------------------------------------------------------------------

export const EDITOR_CAPABILITIES: PlatformCapabilities = {
hasNativeWindowControls: true,
hasNativeMenu: true,
hasNativeTitleBar: true,
isNativeApplication: true,
hasNativeFileDialogs: true,
hasAuthentication: false,
hasLocalSerialPorts: true,
Expand All @@ -121,9 +113,7 @@
}

export const WEB_CAPABILITIES: PlatformCapabilities = {
hasNativeWindowControls: false,
hasNativeMenu: false,
hasNativeTitleBar: false,
isNativeApplication: false,
hasNativeFileDialogs: false,
hasAuthentication: true,
hasLocalSerialPorts: false,
Expand Down
2 changes: 1 addition & 1 deletion src/middleware/shared/providers/platform-context.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { createContext, type ReactNode, useContext } from 'react'

import type { PlatformPorts } from './types'
Expand All @@ -9,7 +9,7 @@
* // In a component:
* const { compiler, runtime, capabilities } = usePlatform()
* await compiler.compileProgram(args, onProgress)
* if (capabilities.hasNativeWindowControls) { ... }
* if (capabilities.isNativeApplication) { ... }
*
* Wiring:
* // At the app root (editor):
Expand Down
Loading