@@ -125,6 +125,34 @@ const DESKTOP_UPDATE_CHANNEL = "latest";
125125const DESKTOP_UPDATE_ALLOW_PRERELEASE = false ;
126126const DESKTOP_LOOPBACK_HOST = "127.0.0.1" ;
127127const DESKTOP_REQUIRED_PORT_PROBE_HOSTS = [ "0.0.0.0" , "::" ] as const ;
128+ function normalizeContextMenuItems ( source : readonly ContextMenuItem [ ] ) : ContextMenuItem [ ] {
129+ const normalizedItems : ContextMenuItem [ ] = [ ] ;
130+
131+ for ( const sourceItem of source ) {
132+ if ( typeof sourceItem . id !== "string" || typeof sourceItem . label !== "string" ) {
133+ continue ;
134+ }
135+
136+ const normalizedItem : ContextMenuItem = {
137+ id : sourceItem . id ,
138+ label : sourceItem . label ,
139+ destructive : sourceItem . destructive === true ,
140+ disabled : sourceItem . disabled === true ,
141+ } ;
142+
143+ if ( sourceItem . children ) {
144+ const normalizedChildren = normalizeContextMenuItems ( sourceItem . children ) ;
145+ if ( normalizedChildren . length === 0 ) {
146+ continue ;
147+ }
148+ normalizedItem . children = normalizedChildren ;
149+ }
150+
151+ normalizedItems . push ( normalizedItem ) ;
152+ }
153+
154+ return normalizedItems ;
155+ }
128156
129157type DesktopUpdateErrorContext = DesktopUpdateState [ "errorContext" ] ;
130158type LinuxDesktopNamedApp = Electron . App & {
@@ -1537,14 +1565,7 @@ function registerIpcHandlers(): void {
15371565 ipcMain . handle (
15381566 CONTEXT_MENU_CHANNEL ,
15391567 async ( _event , items : ContextMenuItem [ ] , position ?: { x : number ; y : number } ) => {
1540- const normalizedItems = items
1541- . filter ( ( item ) => typeof item . id === "string" && typeof item . label === "string" )
1542- . map ( ( item ) => ( {
1543- id : item . id ,
1544- label : item . label ,
1545- destructive : item . destructive === true ,
1546- disabled : item . disabled === true ,
1547- } ) ) ;
1568+ const normalizedItems = normalizeContextMenuItems ( items ) ;
15481569 if ( normalizedItems . length === 0 ) {
15491570 return null ;
15501571 }
@@ -1565,28 +1586,37 @@ function registerIpcHandlers(): void {
15651586 if ( ! window ) return null ;
15661587
15671588 return new Promise < string | null > ( ( resolve ) => {
1568- const template : MenuItemConstructorOptions [ ] = [ ] ;
1569- let hasInsertedDestructiveSeparator = false ;
1570- for ( const item of normalizedItems ) {
1571- if ( item . destructive && ! hasInsertedDestructiveSeparator && template . length > 0 ) {
1572- template . push ( { type : "separator" } ) ;
1573- hasInsertedDestructiveSeparator = true ;
1574- }
1575- const itemOption : MenuItemConstructorOptions = {
1576- label : item . label ,
1577- enabled : ! item . disabled ,
1578- click : ( ) => resolve ( item . id ) ,
1579- } ;
1580- if ( item . destructive ) {
1581- const destructiveIcon = getDestructiveMenuIcon ( ) ;
1582- if ( destructiveIcon ) {
1583- itemOption . icon = destructiveIcon ;
1589+ const buildTemplate = (
1590+ entries : readonly ContextMenuItem [ ] ,
1591+ ) : MenuItemConstructorOptions [ ] => {
1592+ const template : MenuItemConstructorOptions [ ] = [ ] ;
1593+ let hasInsertedDestructiveSeparator = false ;
1594+ for ( const item of entries ) {
1595+ if ( item . destructive && ! hasInsertedDestructiveSeparator && template . length > 0 ) {
1596+ template . push ( { type : "separator" } ) ;
1597+ hasInsertedDestructiveSeparator = true ;
15841598 }
1599+ const itemOption : MenuItemConstructorOptions = {
1600+ label : item . label ,
1601+ enabled : ! item . disabled ,
1602+ } ;
1603+ if ( item . children && item . children . length > 0 ) {
1604+ itemOption . submenu = buildTemplate ( item . children ) ;
1605+ } else {
1606+ itemOption . click = ( ) => resolve ( item . id ) ;
1607+ }
1608+ if ( item . destructive && ( ! item . children || item . children . length === 0 ) ) {
1609+ const destructiveIcon = getDestructiveMenuIcon ( ) ;
1610+ if ( destructiveIcon ) {
1611+ itemOption . icon = destructiveIcon ;
1612+ }
1613+ }
1614+ template . push ( itemOption ) ;
15851615 }
1586- template . push ( itemOption ) ;
1587- }
1616+ return template ;
1617+ } ;
15881618
1589- const menu = Menu . buildFromTemplate ( template ) ;
1619+ const menu = Menu . buildFromTemplate ( buildTemplate ( normalizedItems ) ) ;
15901620 menu . popup ( {
15911621 window,
15921622 ...popupPosition ,
0 commit comments