@@ -5,7 +5,7 @@ use crate::api::dto::WorkspaceInfoDto;
55use bitfun_core:: infrastructure:: { file_watcher, FileOperationOptions , SearchMatchType } ;
66use log:: { debug, error, info, warn} ;
77use serde:: Deserialize ;
8- use tauri:: State ;
8+ use tauri:: { AppHandle , State } ;
99
1010#[ derive( Debug , Deserialize ) ]
1111pub struct OpenWorkspaceRequest {
@@ -18,6 +18,18 @@ pub struct ScanWorkspaceInfoRequest {
1818 pub workspace_path : String ,
1919}
2020
21+ #[ derive( Debug , Deserialize ) ]
22+ #[ serde( rename_all = "camelCase" ) ]
23+ pub struct CloseWorkspaceRequest {
24+ pub workspace_id : String ,
25+ }
26+
27+ #[ derive( Debug , Deserialize ) ]
28+ #[ serde( rename_all = "camelCase" ) ]
29+ pub struct SetActiveWorkspaceRequest {
30+ pub workspace_id : String ,
31+ }
32+
2133#[ derive( Debug , Deserialize ) ]
2234pub struct TestAIConfigConnectionRequest {
2335 pub config : bitfun_core:: service:: config:: types:: AIModelConfig ,
@@ -128,6 +140,96 @@ pub struct RevealInExplorerRequest {
128140 pub path : String ,
129141}
130142
143+ async fn clear_active_workspace_context ( state : & State < ' _ , AppState > , app : & AppHandle ) {
144+ #[ cfg( not( target_os = "macos" ) ) ]
145+ let _ = app;
146+
147+ * state. workspace_path . write ( ) . await = None ;
148+ state. miniapp_manager . set_workspace_path ( None ) . await ;
149+
150+ if let Some ( ref pool) = state. js_worker_pool {
151+ pool. stop_all ( ) . await ;
152+ }
153+
154+ state. ai_rules_service . clear_workspace ( ) . await ;
155+ state. agent_registry . clear_custom_subagents ( ) ;
156+
157+ #[ cfg( target_os = "macos" ) ]
158+ {
159+ let language = state
160+ . config_service
161+ . get_config :: < String > ( Some ( "app.language" ) )
162+ . await
163+ . unwrap_or_else ( |_| "zh-CN" . to_string ( ) ) ;
164+ let _ = crate :: macos_menubar:: set_macos_menubar_with_mode (
165+ app,
166+ & language,
167+ crate :: macos_menubar:: MenubarMode :: Startup ,
168+ ) ;
169+ }
170+ }
171+
172+ async fn apply_active_workspace_context (
173+ state : & State < ' _ , AppState > ,
174+ app : & AppHandle ,
175+ workspace_info : & bitfun_core:: service:: workspace:: manager:: WorkspaceInfo ,
176+ ) {
177+ #[ cfg( not( target_os = "macos" ) ) ]
178+ let _ = app;
179+
180+ clear_active_workspace_context ( state, app) . await ;
181+
182+ * state. workspace_path . write ( ) . await = Some ( workspace_info. root_path . clone ( ) ) ;
183+ state
184+ . miniapp_manager
185+ . set_workspace_path ( Some ( workspace_info. root_path . clone ( ) ) )
186+ . await ;
187+
188+ if let Err ( e) = bitfun_core:: service:: snapshot:: initialize_global_snapshot_manager (
189+ workspace_info. root_path . clone ( ) ,
190+ None ,
191+ )
192+ . await
193+ {
194+ warn ! (
195+ "Failed to initialize snapshot system: path={}, error={}" ,
196+ workspace_info. root_path. display( ) ,
197+ e
198+ ) ;
199+ }
200+
201+ state
202+ . agent_registry
203+ . load_custom_subagents ( & workspace_info. root_path )
204+ . await ;
205+
206+ if let Err ( e) = state
207+ . ai_rules_service
208+ . set_workspace ( workspace_info. root_path . clone ( ) )
209+ . await
210+ {
211+ warn ! (
212+ "Failed to set AI rules workspace: path={}, error={}" ,
213+ workspace_info. root_path. display( ) ,
214+ e
215+ ) ;
216+ }
217+
218+ #[ cfg( target_os = "macos" ) ]
219+ {
220+ let language = state
221+ . config_service
222+ . get_config :: < String > ( Some ( "app.language" ) )
223+ . await
224+ . unwrap_or_else ( |_| "zh-CN" . to_string ( ) ) ;
225+ let _ = crate :: macos_menubar:: set_macos_menubar_with_mode (
226+ app,
227+ & language,
228+ crate :: macos_menubar:: MenubarMode :: Workspace ,
229+ ) ;
230+ }
231+ }
232+
131233#[ tauri:: command]
132234pub async fn initialize_global_state ( _state : State < ' _ , AppState > ) -> Result < String , String > {
133235 Ok ( "Global state initialized successfully" . to_string ( ) )
@@ -454,7 +556,7 @@ pub async fn update_app_status(
454556#[ tauri:: command]
455557pub async fn open_workspace (
456558 state : State < ' _ , AppState > ,
457- _app : tauri:: AppHandle ,
559+ app : tauri:: AppHandle ,
458560 request : OpenWorkspaceRequest ,
459561) -> Result < WorkspaceInfoDto , String > {
460562 match state
@@ -463,55 +565,7 @@ pub async fn open_workspace(
463565 . await
464566 {
465567 Ok ( workspace_info) => {
466- * state. workspace_path . write ( ) . await = Some ( workspace_info. root_path . clone ( ) ) ;
467- state
468- . miniapp_manager
469- . set_workspace_path ( Some ( workspace_info. root_path . clone ( ) ) )
470- . await ;
471-
472- if let Err ( e) = bitfun_core:: service:: snapshot:: initialize_global_snapshot_manager (
473- workspace_info. root_path . clone ( ) ,
474- None ,
475- )
476- . await
477- {
478- warn ! (
479- "Failed to initialize snapshot system: path={}, error={}" ,
480- workspace_info. root_path. display( ) ,
481- e
482- ) ;
483- }
484-
485- state
486- . agent_registry
487- . load_custom_subagents ( & workspace_info. root_path )
488- . await ;
489-
490- if let Err ( e) = state
491- . ai_rules_service
492- . set_workspace ( workspace_info. root_path . clone ( ) )
493- . await
494- {
495- warn ! (
496- "Failed to set AI rules workspace: path={}, error={}" ,
497- workspace_info. root_path. display( ) ,
498- e
499- ) ;
500- }
501-
502- #[ cfg( target_os = "macos" ) ]
503- {
504- let language = state
505- . config_service
506- . get_config :: < String > ( Some ( "app.language" ) )
507- . await
508- . unwrap_or_else ( |_| "zh-CN" . to_string ( ) ) ;
509- let _ = crate :: macos_menubar:: set_macos_menubar_with_mode (
510- & _app,
511- & language,
512- crate :: macos_menubar:: MenubarMode :: Workspace ,
513- ) ;
514- }
568+ apply_active_workspace_context ( & state, & app, & workspace_info) . await ;
515569
516570 info ! (
517571 "Workspace opened: name={}, path={}" ,
@@ -530,34 +584,22 @@ pub async fn open_workspace(
530584#[ tauri:: command]
531585pub async fn close_workspace (
532586 state : State < ' _ , AppState > ,
533- _app : tauri:: AppHandle ,
587+ app : tauri:: AppHandle ,
588+ request : CloseWorkspaceRequest ,
534589) -> Result < ( ) , String > {
535- match state. workspace_service . close_workspace ( "default" ) . await {
590+ match state
591+ . workspace_service
592+ . close_workspace ( & request. workspace_id )
593+ . await
594+ {
536595 Ok ( _) => {
537- * state. workspace_path . write ( ) . await = None ;
538- state. miniapp_manager . set_workspace_path ( None ) . await ;
539- if let Some ( ref pool) = state. js_worker_pool {
540- pool. stop_all ( ) . await ;
541- }
542- state. ai_rules_service . clear_workspace ( ) . await ;
543-
544- state. agent_registry . clear_custom_subagents ( ) ;
545-
546- #[ cfg( target_os = "macos" ) ]
547- {
548- let language = state
549- . config_service
550- . get_config :: < String > ( Some ( "app.language" ) )
551- . await
552- . unwrap_or_else ( |_| "zh-CN" . to_string ( ) ) ;
553- let _ = crate :: macos_menubar:: set_macos_menubar_with_mode (
554- & _app,
555- & language,
556- crate :: macos_menubar:: MenubarMode :: Startup ,
557- ) ;
596+ if let Some ( workspace_info) = state. workspace_service . get_current_workspace ( ) . await {
597+ apply_active_workspace_context ( & state, & app, & workspace_info) . await ;
598+ } else {
599+ clear_active_workspace_context ( & state, & app) . await ;
558600 }
559601
560- info ! ( "Workspace closed" ) ;
602+ info ! ( "Workspace closed: workspace_id={}" , request . workspace_id ) ;
561603 Ok ( ( ) )
562604 }
563605 Err ( e) => {
@@ -567,6 +609,41 @@ pub async fn close_workspace(
567609 }
568610}
569611
612+ #[ tauri:: command]
613+ pub async fn set_active_workspace (
614+ state : State < ' _ , AppState > ,
615+ app : tauri:: AppHandle ,
616+ request : SetActiveWorkspaceRequest ,
617+ ) -> Result < WorkspaceInfoDto , String > {
618+ match state
619+ . workspace_service
620+ . set_active_workspace ( & request. workspace_id )
621+ . await
622+ {
623+ Ok ( _) => {
624+ let workspace_info = state
625+ . workspace_service
626+ . get_current_workspace ( )
627+ . await
628+ . ok_or_else ( || "Active workspace not found after switching" . to_string ( ) ) ?;
629+
630+ apply_active_workspace_context ( & state, & app, & workspace_info) . await ;
631+
632+ info ! (
633+ "Active workspace changed: workspace_id={}, path={}" ,
634+ workspace_info. id,
635+ workspace_info. root_path. display( )
636+ ) ;
637+
638+ Ok ( WorkspaceInfoDto :: from_workspace_info ( & workspace_info) )
639+ }
640+ Err ( e) => {
641+ error ! ( "Failed to set active workspace: {}" , e) ;
642+ Err ( format ! ( "Failed to set active workspace: {}" , e) )
643+ }
644+ }
645+ }
646+
570647#[ tauri:: command]
571648pub async fn get_current_workspace (
572649 state : State < ' _ , AppState > ,
@@ -591,6 +668,19 @@ pub async fn get_recent_workspaces(
591668 . collect ( ) )
592669}
593670
671+ #[ tauri:: command]
672+ pub async fn get_opened_workspaces (
673+ state : State < ' _ , AppState > ,
674+ ) -> Result < Vec < WorkspaceInfoDto > , String > {
675+ let workspace_service = & state. workspace_service ;
676+ Ok ( workspace_service
677+ . get_opened_workspaces ( )
678+ . await
679+ . into_iter ( )
680+ . map ( |info| WorkspaceInfoDto :: from_workspace_info ( & info) )
681+ . collect ( ) )
682+ }
683+
594684#[ tauri:: command]
595685pub async fn scan_workspace_info (
596686 state : State < ' _ , AppState > ,
0 commit comments