11import type { Script } from "@App/app/repo/scripts" ;
2+ import { SCRIPT_STATUS_DISABLE , SCRIPT_STATUS_ENABLE } from "@App/app/repo/scripts" ;
23import { SCRIPT_TYPE_NORMAL , ScriptCodeDAO , ScriptDAO } from "@App/app/repo/scripts" ;
34import CodeEditor from "@App/pages/components/CodeEditor" ;
45import React , { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
@@ -25,6 +26,9 @@ import { IconDelete, IconSearch } from "@arco-design/web-react/icon";
2526import { lazyScriptName } from "@App/pkg/config/config" ;
2627import { makeBlobURL } from "@App/pkg/utils/utils" ;
2728import { VscLayoutSidebarLeft , VscLayoutSidebarLeftOff } from "react-icons/vsc" ;
29+ import type { TInstallScript , TDeleteScript , TEnableScript , TSortedScript } from "@App/app/service/queue" ;
30+ import { subscribeMessage } from "@App/pages/store/global" ;
31+ import { HookManager } from "@App/pkg/utils/hookManager" ;
2832
2933const { Row, Col } = Grid ;
3034
@@ -220,12 +224,131 @@ type EditorState = {
220224const scriptDAO = new ScriptDAO ( ) ;
221225const scriptCodeDAO = new ScriptCodeDAO ( ) ;
222226
227+ function useScriptList ( ) {
228+ const [ selectedScript , setSelectSciptButtonAndTab ] = useState < string > ( "" ) ;
229+ const [ editors , setEditors ] = useState < EditorState [ ] > ( [ ] ) ;
230+ const [ canLoadScript , setCanLoadScript ] = useState < boolean > ( false ) ;
231+ const [ scriptList , setScriptList ] = useState < Script [ ] > ( [ ] ) ;
232+ // 监听后台消息更新状态
233+ useEffect ( ( ) => {
234+ const pageApi = {
235+ async installScript ( data : TInstallScript ) {
236+ const latest = await scriptDAO . all ( ) ;
237+ const latestMap = new Map ( latest . map ( ( script ) => [ script . uuid , script ] ) ) ;
238+ setScriptList ( ( list ) => {
239+ const newList : Script [ ] = [ ] ;
240+ for ( const entry of list ) {
241+ if ( entry . uuid !== data . script . uuid ) {
242+ const latestScript = latestMap . get ( entry . uuid ) ;
243+ if ( latestScript ) {
244+ newList . push ( {
245+ ...entry ,
246+ sort : latestScript . sort ,
247+ name : latestScript . name ,
248+ updatetime : latestScript . updatetime ,
249+ status : latestScript . status ,
250+ } ) ;
251+ }
252+ }
253+ }
254+ const installedScript = latestMap . get ( data . script . uuid ) ;
255+ if ( installedScript ) {
256+ newList . push ( installedScript ) ;
257+ }
258+ newList . sort ( ( a , b ) => a . sort - b . sort ) ;
259+ return newList ;
260+ } ) ;
261+ } ,
262+ deleteScripts ( data : TDeleteScript [ ] ) {
263+ const dels = new Set ( data . map ( ( script ) => script . uuid ) ) ;
264+ setEditors ( ( prev ) => {
265+ const newList : EditorState [ ] = [ ] ;
266+ for ( const editor of prev ) {
267+ if ( ! dels . has ( editor . script . uuid ) ) {
268+ newList . push ( editor ) ;
269+ }
270+ }
271+ // 关键修复:确保关闭后仍有一个 Tab 是激活的
272+ if ( newList . length > 0 && ! newList . some ( ( e ) => e . active ) ) {
273+ newList [ 0 ] = { ...newList [ 0 ] , active : true } ;
274+ setSelectSciptButtonAndTab ( newList [ 0 ] . script . uuid ) ;
275+ }
276+ return newList ;
277+ } ) ;
278+ setScriptList ( ( list ) => {
279+ return list . filter ( ( script ) => ! dels . has ( script . uuid ) ) ;
280+ } ) ;
281+ } ,
282+ enableScripts ( data : TEnableScript [ ] ) {
283+ const enableMap = new Map ( data . map ( ( e ) => [ e . uuid , e . enable ] ) ) ;
284+ setScriptList ( ( list ) => {
285+ const newList : Script [ ] = [ ] ;
286+ for ( const script of list ) {
287+ const oldEnable = script . status !== SCRIPT_STATUS_DISABLE ;
288+ const newEnable = enableMap . get ( script . uuid ) ;
289+ if ( typeof newEnable === "boolean" && oldEnable !== newEnable ) {
290+ newList . push ( { ...script , status : newEnable ? SCRIPT_STATUS_ENABLE : SCRIPT_STATUS_DISABLE } ) ;
291+ } else {
292+ newList . push ( script ) ;
293+ }
294+ }
295+ return newList ;
296+ } ) ;
297+ } ,
298+ sortedScripts ( sorting : TSortedScript [ ] ) {
299+ const sortMap = new Map ( sorting . map ( ( s ) => [ s . uuid , s . sort ] ) ) ;
300+ setScriptList ( ( list ) => {
301+ const newList : Script [ ] = [ ] ;
302+ for ( const entry of list ) {
303+ const sort = sortMap . get ( entry . uuid ) ;
304+ if ( sort ! >= 0 ) {
305+ newList . push ( { ...entry , sort : sort ! } ) ;
306+ } else {
307+ newList . push ( entry ) ;
308+ }
309+ }
310+ newList . sort ( ( a , b ) => a . sort - b . sort ) ;
311+ return newList ;
312+ } ) ;
313+ } ,
314+ } as const ;
315+
316+ const hookMgr = new HookManager ( ) ;
317+ hookMgr . append (
318+ subscribeMessage < TInstallScript > ( "installScript" , pageApi . installScript ) ,
319+ subscribeMessage < TDeleteScript [ ] > ( "deleteScripts" , pageApi . deleteScripts ) ,
320+ subscribeMessage < TEnableScript [ ] > ( "enableScripts" , pageApi . enableScripts ) ,
321+ subscribeMessage < TSortedScript [ ] > ( "sortedScripts" , pageApi . sortedScripts )
322+ ) ;
323+ return hookMgr . unhook ;
324+ } , [ ] ) ;
325+ return {
326+ scriptList,
327+ setScriptList,
328+ canLoadScript,
329+ setCanLoadScript,
330+ editors,
331+ setEditors,
332+ selectedScript,
333+ setSelectSciptButtonAndTab,
334+ } ;
335+ }
336+
223337function ScriptEditor ( ) {
224338 const [ visible , setVisible ] = useState < { [ key : string ] : boolean } > ( { } ) ;
225339 const [ searchKeyword , setSearchKeyword ] = useState < string > ( "" ) ;
226340 const [ showSearchInput , setShowSearchInput ] = useState < boolean > ( false ) ;
227341 const [ modal , contextHolder ] = Modal . useModal ( ) ;
228- const [ editors , setEditors ] = useState < EditorState [ ] > ( [ ] ) ;
342+ const {
343+ scriptList,
344+ setScriptList,
345+ canLoadScript,
346+ setCanLoadScript,
347+ editors,
348+ setEditors,
349+ selectedScript,
350+ setSelectSciptButtonAndTab,
351+ } = useScriptList ( ) ;
229352 const editorsRef = useRef < EditorState [ ] > ( editors ) ; // 取出资料用
230353 // Sync during render (no useEffect needed)
231354 editorsRef . current = editors ;
@@ -245,16 +368,13 @@ function ScriptEditor() {
245368 setTimeout ( editor . focus . bind ( editor ) , delayMs ) ;
246369 }
247370 } ;
248- const [ scriptList , setScriptList ] = useState < Script [ ] > ( [ ] ) ;
249371 const [ currentScript , setCurrentScript ] = useState < Script > ( ) ;
250- const [ selectedScript , setSelectSciptButtonAndTab ] = useState < string > ( "" ) ;
251372 const [ rightOperationTab , setRightOperationTab ] = useState < {
252373 key : string ;
253374 uuid : string ;
254375 selectSciptButtonAndTab : string ;
255376 } > ( ) ;
256377 const cidRef = useRef < ReturnType < typeof setTimeout > > ( ) ;
257- const [ canLoadScript , setCanLoadScript ] = useState < boolean > ( false ) ;
258378 const [ hiddenScriptList , setHiddenScriptList ] = useState < boolean > ( ( ) => {
259379 return localStorage . getItem ( "hiddenEditorScriptList" ) === "true" ;
260380 } ) ;
@@ -1087,7 +1207,7 @@ function ScriptEditor() {
10871207 ) }
10881208 { filteredScriptList . map ( ( script ) => {
10891209 const editor = editorFindItem ( script . uuid ) ;
1090- const alpha = script . status === 2 ? 0.8 : 1.0 ;
1210+ const alpha = script . status === SCRIPT_STATUS_DISABLE ? 0.66 : 1.0 ;
10911211 return (
10921212 < div key = { `s_${ script . uuid } ` } className = "tw-relative tw-group" >
10931213 < Button
@@ -1097,7 +1217,6 @@ function ScriptEditor() {
10971217 overflow : "hidden" ,
10981218 textOverflow : "ellipsis" ,
10991219 whiteSpace : "nowrap" ,
1100- opacity : alpha ,
11011220 color : ! editor
11021221 ? "var(--color-text-3)"
11031222 : editor . isChanged
@@ -1115,7 +1234,14 @@ function ScriptEditor() {
11151234 openScript ( script . uuid ) ;
11161235 } }
11171236 >
1118- < span className = "tw-overflow-hidden tw-text-ellipsis" > { i18nName ( script ) } </ span >
1237+ < span
1238+ className = "tw-overflow-hidden tw-text-ellipsis"
1239+ style = { {
1240+ opacity : alpha ,
1241+ } }
1242+ >
1243+ { i18nName ( script ) }
1244+ </ span >
11191245 </ Button >
11201246 { /* 删除按钮,只在鼠标悬停时显示 */ }
11211247 < Button
0 commit comments