@@ -25,6 +25,11 @@ type TxUpdateScriptMenuCallback = (
2525 result : ScriptMenu [ ]
2626) => Promise < ScriptMenu [ ] | undefined > | ScriptMenu [ ] | undefined ;
2727
28+ const enum ScriptMenuRegisterType {
29+ REGISTER = 1 ,
30+ UNREGISTER = 2 ,
31+ }
32+
2833// 以 tabId 为 key 的「执行次数」快取(字串形式存放),供 badge 显示使用。
2934const runCountMap = new Map < number , string > ( ) ;
3035
@@ -173,101 +178,105 @@ export class PopupService {
173178 }
174179
175180 // 防止并发导致频繁更新菜单,将注册菜单的请求集中在一个队列中处理
176- registerMenuCommandMessages = new Map < string , TScriptMenuRegister [ ] > ( ) ;
177-
178- async registerMenuCommand ( message : TScriptMenuRegister ) {
179- const { tabId, uuid } = message ;
180- const mrKey = `${ tabId } .${ uuid } ` ;
181- if ( ! this . registerMenuCommandMessages . has ( mrKey ) ) {
182- this . registerMenuCommandMessages . set ( mrKey , [ ] ) ;
183- }
184- this . registerMenuCommandMessages . get ( mrKey ) ! . push ( message ) ;
185-
186- let retUpdated = false ;
187- // 给脚本添加菜单
188- await this . txUpdateScriptMenu ( tabId , async ( data ) => {
189- while ( true ) {
190- const message = this . registerMenuCommandMessages . get ( mrKey ) ?. shift ( ) ;
191- if ( ! message ) {
192- this . registerMenuCommandMessages . delete ( mrKey ) ;
193- return data ;
181+ updateMenuCommands = new Map < number , ( ( TScriptMenuRegister | TScriptMenuUnregister ) & { registerType : number } ) [ ] > ( ) ;
182+ isUpdateMenuDirty = false ;
183+
184+ // 此函数必须是同步执行的,避免updateMenuCommands并发问题
185+ updateMenuCommand ( tabId : number , data : ScriptMenu [ ] ) : string [ ] {
186+ const retUpdated = new Set < string > ( ) ;
187+ const list = this . updateMenuCommands . get ( tabId ) ;
188+ if ( ! list ) return [ ] ;
189+ const uuids = new Set ( list . map ( ( entry ) => entry . uuid ) ) ;
190+ const scripts = new Map ( data . filter ( ( item ) => uuids . has ( item . uuid ) ) . map ( ( item ) => [ item . uuid , item ] ) ) ;
191+ for ( const listEntry of list ) {
192+ const message = listEntry as TScriptMenuRegister ;
193+ // message.key是唯一的。 即使在同一tab里的mainframe subframe也是不一样
194+ const { uuid, key, name } = message ;
195+ const script = scripts . get ( uuid ) ;
196+ if ( ! script ) continue ;
197+
198+ if ( listEntry . registerType === ScriptMenuRegisterType . REGISTER ) {
199+ const menus = script . menus ;
200+ retUpdated . add ( script . uuid ) ;
201+ // 以 options+name 生成稳定 groupKey:相同语义项目在 UI 只呈现一次,但可同时触发多个来源(frame)。
202+ // groupKey 用来表示「相同性质的项目」,允许重叠。
203+ // 例如 subframe 和 mainframe 创建了相同的 menu item,显示时只会出现一个。
204+ // 但点击后,两边都会执行。
205+ // 目的是整理显示,实际上内部还是存有多笔 entry(分别记录不同的 frameId 和 id)。
206+ const groupKey = uuidv5 (
207+ message . options ?. inputType
208+ ? JSON . stringify ( { ...message . options , autoClose : undefined , id : undefined , name : name } )
209+ : `${ name } \n${ message . options ?. accessKey || "" } ` ,
210+ groupKeyNS
211+ ) ;
212+ const menu = menus . find ( ( item ) => item . key === key ) ;
213+ if ( ! menu ) {
214+ // 不存在新增
215+ menus . push ( {
216+ groupKey,
217+ key : key , // unique primary key
218+ name : name ,
219+ options : message . options ,
220+ tabId : tabId , // fix
221+ frameId : message . frameId , // fix with unique key
222+ documentId : message . documentId , // fix with unique key
223+ } ) ;
224+ } else {
225+ // 存在修改信息
226+ menu . name = message . name ;
227+ menu . options = message . options ;
228+ menu . groupKey = groupKey ;
194229 }
195- // message.key是唯一的。 即使在同一tab里的mainframe subframe也是不一样
196- const { key, name, uuid } = message ; // 唯一键, 项目显示名字, 脚本uuid
197- const script = data . find ( ( item ) => item . uuid === uuid ) ;
198- if ( script ) {
199- retUpdated = true ;
200- // 以 options+name 生成稳定 groupKey:相同语义项目在 UI 只呈现一次,但可同时触发多个来源(frame)。
201- // groupKey 用来表示「相同性质的项目」,允许重叠。
202- // 例如 subframe 和 mainframe 创建了相同的 menu item,显示时只会出现一个。
203- // 但点击后,两边都会执行。
204- // 目的是整理显示,实际上内部还是存有多笔 entry(分别记录不同的 frameId 和 id)。
205- const groupKey = uuidv5 (
206- message . options ?. inputType
207- ? JSON . stringify ( { ...message . options , autoClose : undefined , id : undefined , name : name } )
208- : `${ name } \n${ message . options ?. accessKey || "" } ` ,
209- groupKeyNS
210- ) ;
211- const menu = script . menus . find ( ( item ) => item . key === key ) ;
212- if ( ! menu ) {
213- // 不存在新增
214- script . menus . push ( {
215- groupKey,
216- key : key , // unique primary key
217- name : name ,
218- options : message . options ,
219- tabId : tabId , // fix
220- frameId : message . frameId , // fix with unique key
221- documentId : message . documentId , // fix with unique key
222- } ) ;
223- } else {
224- // 存在修改信息
225- menu . name = message . name ;
226- menu . options = message . options ;
227- menu . groupKey = groupKey ;
228- }
230+ } else if ( listEntry . registerType === ScriptMenuRegisterType . UNREGISTER ) {
231+ const menus = script . menus ;
232+ // 删除菜单
233+ const index = menus . findIndex ( ( item ) => item . key === key ) ;
234+ if ( index >= 0 ) {
235+ retUpdated . add ( uuid ) ;
236+ menus . splice ( index , 1 ) ;
229237 }
230238 }
231- } ) ;
232- if ( retUpdated ) {
233- this . mq . publish < TPopupScript > ( "popupMenuRecordUpdated" , { tabId, uuid } ) ;
234- // 更新数据后再更新菜单
235- await this . updateScriptMenu ( tabId ) ;
236239 }
240+ list . length = 0 ;
241+ this . updateMenuCommands . delete ( tabId ) ;
242+ return [ ...retUpdated ] ;
237243 }
238244
239- unregisterMenuCommandMessages = new Map < string , TScriptMenuUnregister [ ] > ( ) ;
240-
241- async unregisterMenuCommand ( { key, uuid, tabId } : TScriptMenuUnregister ) {
242- const mrKey = `${ tabId } .${ uuid } ` ;
243- if ( ! this . unregisterMenuCommandMessages . has ( mrKey ) ) {
244- this . unregisterMenuCommandMessages . set ( mrKey , [ ] ) ;
245+ updateRegisterMenuCommand (
246+ message : TScriptMenuRegister | TScriptMenuUnregister ,
247+ registerType : ScriptMenuRegisterType
248+ ) : Promise < void > {
249+ const { tabId } = message ;
250+ let list = this . updateMenuCommands . get ( tabId ) ;
251+ if ( ! list ) {
252+ this . updateMenuCommands . set ( tabId , ( list = [ ] ) ) ;
245253 }
246- this . unregisterMenuCommandMessages . get ( mrKey ) ! . push ( { key , uuid , tabId } ) ;
247-
248- let retUpdated = false ;
249- await this . txUpdateScriptMenu ( tabId , async ( data ) => {
250- while ( true ) {
251- const message = this . unregisterMenuCommandMessages . get ( mrKey ) ?. shift ( ) ;
252- if ( ! message ) {
253- this . unregisterMenuCommandMessages . delete ( mrKey ) ;
254- return data ;
254+ list . push ( { ... message , registerType } ) ;
255+ let retUpdated : string [ ] | undefined ;
256+ return Promise . resolve ( ) // 增加一个 await Promise.reslove() 转移微任务队列 再判断长度是否为0
257+ . then ( ( ) => {
258+ if ( list . length ) {
259+ return this . txUpdateScriptMenu ( tabId , ( data ) => {
260+ retUpdated = this . updateMenuCommand ( tabId , data ) ;
261+ return data ;
262+ } ) ;
255263 }
256- const script = data . find ( ( item ) => item . uuid === uuid ) ;
257- if ( script ) {
258- retUpdated = true ;
259- // 删除菜单
260- script . menus = script . menus . filter ( ( item ) => item . key !== key ) ;
264+ } )
265+ . then ( ( ) => {
266+ if ( retUpdated ?. length ) {
267+ this . mq . publish < TPopupScript > ( "popupMenuRecordUpdated" , { tabId, uuids : retUpdated } ) ;
268+ // 更新数据后再更新菜单
269+ this . updateScriptMenu ( tabId ) ;
261270 }
262- return data ;
263- }
264- } ) ;
271+ } ) ;
272+ }
265273
266- if ( retUpdated ) {
267- this . mq . publish < TPopupScript > ( "popupMenuRecordUpdated" , { tabId, uuid } ) ;
268- // 更新数据后再更新菜单
269- await this . updateScriptMenu ( tabId ) ;
270- }
274+ registerMenuCommand ( message : TScriptMenuRegister ) {
275+ this . updateRegisterMenuCommand ( message , ScriptMenuRegisterType . REGISTER ) ;
276+ }
277+
278+ unregisterMenuCommand ( { key, uuid, tabId } : TScriptMenuUnregister ) {
279+ this . updateRegisterMenuCommand ( { key, uuid, tabId } , ScriptMenuRegisterType . UNREGISTER ) ;
271280 }
272281
273282 async updateScriptMenu ( tabId : number ) {
0 commit comments