@@ -22,9 +22,11 @@ import {
2222 type TreeChangeDiffParams ,
2323 type UpdateBranchNameParams ,
2424 type ApplyParams ,
25+ type ShowNativeMenuParams ,
2526 type UnapplyStackParams ,
2627 WatcherSubscribeParams ,
2728 WatcherUnsubscribeParams ,
29+ NativeMenuPopupItem ,
2830} from "./ipc.js" ;
2931import {
3032 absorb ,
@@ -55,14 +57,30 @@ import {
5557 updateBranchName ,
5658 BranchListingFilter ,
5759} from "@gitbutler/but-sdk" ;
58- import { app , BrowserWindow , ipcMain } from "electron" ;
60+ import { app , BrowserWindow , ipcMain , Menu , type MenuItemConstructorOptions } from "electron" ;
5961import { REACT_DEVELOPER_TOOLS , installExtension } from "electron-devtools-installer" ;
6062import path from "node:path" ;
6163import { fileURLToPath } from "node:url" ;
6264
6365const currentFilePath = fileURLToPath ( import . meta. url ) ;
6466const currentDirPath = path . dirname ( currentFilePath ) ;
6567
68+ const buildNativeMenuTemplate = (
69+ items : Array < NativeMenuPopupItem > ,
70+ onItem : ( itemId : string ) => void ,
71+ ) : Array < MenuItemConstructorOptions > =>
72+ items . map ( ( item ) : MenuItemConstructorOptions => {
73+ if ( item . _tag === "Separator" ) return { type : "separator" } ;
74+ const itemId = item . itemId ;
75+
76+ return {
77+ label : item . label ,
78+ enabled : item . enabled ,
79+ click : itemId !== undefined ? ( ) => onItem ( itemId ) : undefined ,
80+ submenu : item . submenu ? buildNativeMenuTemplate ( item . submenu , onItem ) : undefined ,
81+ } ;
82+ } ) ;
83+
6684function registerIpcHandlers ( ) : void {
6785 ipcMain . handle (
6886 liteIpcChannels . absorptionPlan ,
@@ -176,6 +194,31 @@ function registerIpcHandlers(): void {
176194 ( _e , { projectId, stackId, branch } : PushStackLegacyParams ) =>
177195 pushStackLegacy ( projectId , stackId , false , false , branch , true ) ,
178196 ) ;
197+ ipcMain . handle (
198+ liteIpcChannels . showNativeMenu ,
199+ async ( event , { items, position } : ShowNativeMenuParams ) => {
200+ const window = BrowserWindow . fromWebContents ( event . sender ) ;
201+ if ( ! window ) return null ;
202+
203+ let selectedItemId : string | null = null ;
204+ const menu = Menu . buildFromTemplate (
205+ buildNativeMenuTemplate ( items , ( itemId ) => {
206+ selectedItemId = itemId ;
207+ } ) ,
208+ ) ;
209+
210+ await new Promise < void > ( ( resolve ) => {
211+ menu . popup ( {
212+ window,
213+ x : Math . round ( position . x ) ,
214+ y : Math . round ( position . y ) ,
215+ callback : ( ) => resolve ( ) ,
216+ } ) ;
217+ } ) ;
218+
219+ return selectedItemId ;
220+ } ,
221+ ) ;
179222 ipcMain . handle (
180223 liteIpcChannels . treeChangeDiffs ,
181224 ( _e , { projectId, change } : TreeChangeDiffParams ) => treeChangeDiffs ( projectId , change ) ,
0 commit comments