Skip to content

Commit 2ec6feb

Browse files
authored
Merge pull request #89 from GCWing/gcwing/dev
Gcwing/dev
2 parents aa28558 + 39e6e00 commit 2ec6feb

172 files changed

Lines changed: 13103 additions & 10701 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/apps/desktop/src/api/app_state.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,26 @@ impl AppState {
109109
uptime_seconds: 0,
110110
}));
111111

112+
let initial_workspace_path = workspace_service
113+
.get_current_workspace()
114+
.await
115+
.map(|workspace| workspace.root_path);
116+
117+
if let Some(workspace_path) = initial_workspace_path.clone() {
118+
miniapp_manager
119+
.set_workspace_path(Some(workspace_path.clone()))
120+
.await;
121+
if let Err(e) = ai_rules_service.set_workspace(workspace_path).await {
122+
log::warn!("Failed to restore AI rules workspace on startup: {}", e);
123+
}
124+
}
125+
112126
let app_state = Self {
113127
ai_client,
114128
ai_client_factory,
115129
tool_registry,
116130
workspace_service,
117-
workspace_path: Arc::new(RwLock::new(None)),
131+
workspace_path: Arc::new(RwLock::new(initial_workspace_path)),
118132
config_service,
119133
filesystem_service,
120134
ai_rules_service,

src/apps/desktop/src/api/commands.rs

Lines changed: 165 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::api::dto::WorkspaceInfoDto;
55
use bitfun_core::infrastructure::{file_watcher, FileOperationOptions, SearchMatchType};
66
use log::{debug, error, info, warn};
77
use serde::Deserialize;
8-
use tauri::State;
8+
use tauri::{AppHandle, State};
99

1010
#[derive(Debug, Deserialize)]
1111
pub 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)]
2234
pub 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]
132234
pub 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]
455557
pub 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]
531585
pub 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]
571648
pub 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]
595685
pub async fn scan_workspace_info(
596686
state: State<'_, AppState>,

src/apps/desktop/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,8 +540,10 @@ pub async fn run() {
540540
subscribe_config_updates,
541541
get_model_configs,
542542
get_recent_workspaces,
543+
get_opened_workspaces,
543544
open_workspace,
544545
close_workspace,
546+
set_active_workspace,
545547
get_current_workspace,
546548
scan_workspace_info,
547549
api::prompt_template_api::get_prompt_template_config,

0 commit comments

Comments
 (0)