diff --git a/src/apps/desktop/src/api/commands.rs b/src/apps/desktop/src/api/commands.rs index 5a4eac21e..fb2278899 100644 --- a/src/apps/desktop/src/api/commands.rs +++ b/src/apps/desktop/src/api/commands.rs @@ -2212,6 +2212,12 @@ pub async fn reveal_in_explorer( )) } }; + #[cfg(target_env = "ohos")] + { + use crate::ohos::ohos_file_system::reveal_in_oh_explorer; + let _ = reveal_in_oh_explorer(path.to_string_lossy().to_string()); + return Ok(()); + } if !path.exists() { return Err(format!("Path does not exist: {}", request.path)); } diff --git a/src/apps/desktop/src/api/ohos/ohos_file_system.rs b/src/apps/desktop/src/api/ohos/ohos_file_system.rs index 64d7d33e7..20e147f3e 100644 --- a/src/apps/desktop/src/api/ohos/ohos_file_system.rs +++ b/src/apps/desktop/src/api/ohos/ohos_file_system.rs @@ -1,5 +1,32 @@ -use bitfun_core::util::open_dialog_file; +use bitfun_core::util::{JS_THREADSAFE_FUNCTION, open_dialog_file}; +use napi_ohos::threadsafe_function::ThreadsafeFunctionCallMode; #[tauri::command] pub async fn open_oh_file_dialog() -> Result { open_dialog_file().await +} + +#[tauri::command] +pub async fn set_theme_mode(theme: String) -> Result<(), String> { + let function = { + let lock = JS_THREADSAFE_FUNCTION.read(); + lock.get("set_theme_mode").cloned() + }; + let Some(function) = function else { + return Err("The Arkts has not register the function".to_owned()); + }; + function.call(Ok(theme),ThreadsafeFunctionCallMode::NonBlocking); + Ok(()) +} + +#[tauri::command] +pub fn reveal_in_oh_explorer(path: String) -> Result<(), String> { + let function = { + let lock = JS_THREADSAFE_FUNCTION.read(); + lock.get("reveal_in_explorer").cloned() + }; + let Some(function) = function else { + return Err("The Arkts has not register the function".to_owned()); + }; + function.call(Ok(path),ThreadsafeFunctionCallMode::NonBlocking); + Ok(()) } \ No newline at end of file diff --git a/src/apps/desktop/src/api/ohos/window.rs b/src/apps/desktop/src/api/ohos/window.rs index 1a52e0563..8cc7e2c7a 100644 --- a/src/apps/desktop/src/api/ohos/window.rs +++ b/src/apps/desktop/src/api/ohos/window.rs @@ -1,9 +1,5 @@ -use crate::AppState; use bitfun_core::util::JS_THREADSAFE_FUNCTION; -use log::error; use napi_ohos::threadsafe_function::ThreadsafeFunctionCallMode; -use std::sync::mpsc::channel; -use tauri::State; #[tauri::command] pub fn handle_min_window() -> Result<(), String> { diff --git a/src/apps/desktop/src/lib.rs b/src/apps/desktop/src/lib.rs index 1b4922f77..536dcc56a 100644 --- a/src/apps/desktop/src/lib.rs +++ b/src/apps/desktop/src/lib.rs @@ -24,7 +24,7 @@ use tauri::Manager; // Re-export API pub use api::*; -use crate::ohos::ohos_file_system::open_oh_file_dialog; +use crate::ohos::ohos_file_system::{open_oh_file_dialog, set_theme_mode}; use crate::ohos::window::{ close_window,handle_max_window,handle_min_window,handle_restore_window,window_is_maximized, window_is_minimized, window_start_dragging @@ -792,6 +792,7 @@ pub async fn _run() { window_is_minimized, window_start_dragging, close_window, + set_theme_mode, ]) .run(tauri::generate_context!()); diff --git a/src/apps/vcoder/entry/src/main/ets/entryability/EntryAbility.ets b/src/apps/vcoder/entry/src/main/ets/entryability/EntryAbility.ets index 2ba31f66e..55d7e7191 100644 --- a/src/apps/vcoder/entry/src/main/ets/entryability/EntryAbility.ets +++ b/src/apps/vcoder/entry/src/main/ets/entryability/EntryAbility.ets @@ -1,5 +1,10 @@ import { - abilityAccessCtrl, AbilityConstant, common, Permissions, Want + abilityAccessCtrl, + AbilityConstant, + common, + ConfigurationConstant, + Permissions, + Want } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { window } from '@kit.ArkUI'; @@ -119,6 +124,37 @@ export default class EntryAbility extends RustAbility { }); return ''; }); + setTimeout(() => { + windowStage.getMainWindow((err, data) => { + data.setWindowDecorHeight(32) + data.setWindowDecorVisible(false); + }) + }, 40) + RustModule.registerArktsFunction('reveal_in_explorer', async (err: Error, arg: string): Promise => { + const path = arg.replace('file://', 'file://docs'); + const want: Want = { + bundleName: 'com.huawei.hmos.filemanager', + abilityName: 'MainAbility', + parameters: { + fileUri: path + } + }; + const context = getContext(this) as common.UIAbilityContext; + try { + await context.startAbility(want) + } catch (err) { + hilog.error(201, 'vnext', `reveal in explorer failed ${JSON.stringify(err)}`) + } + return ''; + }); + RustModule.registerArktsFunction('set_theme_mode', async (err: Error, arg: string): Promise => { + if (arg === 'bitfun-light' || arg === 'bitfun-china-style') { + this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT) + } else { + this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_DARK) + } + return ''; + }); return super.onWindowStageCreate(windowStage); } @@ -129,16 +165,15 @@ export default class EntryAbility extends RustAbility { private sendOnlyCallback = (sharableTable: harmonyShare.SharableTarget) => { if (this.remote_url.length == 0) { - let content = this.remote_url; - let shareData: systemShare.SharedData = new systemShare.SharedData({ - utd: uniformTypeDescriptor.UniformDataType.HYPERLINK, - content, - title: "Bitfun", - description: "Phone", - }); - sharableTable.share(shareData) - } - else { + let content = this.remote_url; + let shareData: systemShare.SharedData = new systemShare.SharedData({ + utd: uniformTypeDescriptor.UniformDataType.HYPERLINK, + content, + title: "Bitfun", + description: "Phone", + }); + sharableTable.share(shareData) + } else { hilog.error(DOMAIN, 'vnext', 'sendOnlyCallback error: remote url is empty'); } } diff --git a/src/web-ui/src/app/components/NavBar/NavBar.tsx b/src/web-ui/src/app/components/NavBar/NavBar.tsx index cbebee436..a9e08cffe 100644 --- a/src/web-ui/src/app/components/NavBar/NavBar.tsx +++ b/src/web-ui/src/app/components/NavBar/NavBar.tsx @@ -18,6 +18,7 @@ import { PanelLeftIcon } from '../TitleBar/PanelIcons'; import { createLogger } from '@/shared/utils/logger'; import { isMacOSDesktopRuntime, supportsNativeWindowDragging } from '@/infrastructure/runtime'; import './NavBar.scss'; +import { workspaceAPI } from '@/infrastructure'; const log = createLogger('NavBar'); @@ -65,8 +66,7 @@ const NavBar: React.FC = ({ void (async () => { try { - const { getCurrentWindow } = await import('@tauri-apps/api/window'); - await getCurrentWindow().startDragging(); + await workspaceAPI.window_start_dragging(); } catch (error) { log.debug('startDragging failed', error); } diff --git a/src/web-ui/src/app/components/SceneBar/SceneBar.tsx b/src/web-ui/src/app/components/SceneBar/SceneBar.tsx index ae6603465..1cb0b6d34 100644 --- a/src/web-ui/src/app/components/SceneBar/SceneBar.tsx +++ b/src/web-ui/src/app/components/SceneBar/SceneBar.tsx @@ -15,6 +15,7 @@ import { useI18n } from '@/infrastructure/i18n/hooks/useI18n'; import { createLogger } from '@/shared/utils/logger'; import { supportsNativeWindowDragging } from '@/infrastructure/runtime'; import './SceneBar.scss'; +import { workspaceAPI } from '@/infrastructure'; const log = createLogger('SceneBar'); @@ -66,8 +67,7 @@ const SceneBar: React.FC = ({ void (async () => { try { - const { getCurrentWindow } = await import('@tauri-apps/api/window'); - await getCurrentWindow().startDragging(); + await workspaceAPI.window_start_dragging(); } catch (error) { log.debug('startDragging failed', error); } diff --git a/src/web-ui/src/component-library/components/WindowControls/WindowControls.tsx b/src/web-ui/src/component-library/components/WindowControls/WindowControls.tsx index aa8652352..479ebdd9a 100644 --- a/src/web-ui/src/component-library/components/WindowControls/WindowControls.tsx +++ b/src/web-ui/src/component-library/components/WindowControls/WindowControls.tsx @@ -3,8 +3,6 @@ */ import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { Tooltip } from '../Tooltip'; import './WindowControls.scss'; export interface WindowControlsProps extends React.HTMLAttributes { @@ -29,122 +27,7 @@ export interface WindowControlsProps extends React.HTMLAttributes = ({ - onMinimize, - onMaximize, - onClose, - showMinimize = true, - showMaximize = true, - showClose = true, - disabled = false, - isMaximized = false, - minimizeIcon, - maximizeIcon, - restoreIcon, - closeIcon, - className = '', - 'data-testid-minimize': testIdMinimize, - 'data-testid-maximize': testIdMaximize, - 'data-testid-close': testIdClose, - ...props -}) => { - const { t } = useTranslation('common'); - const defaultMinimizeIcon = ( - - - - ); - - const defaultMaximizeIcon = ( - - - - ); - - const defaultRestoreIcon = ( - - - - - ); - - const defaultCloseIcon = ( - - - - - ); - - return ( -
- {showMinimize && ( - - - - )} - - {showMaximize && ( - - - - )} - - {showClose && ( - - - - )} -
- ); +export const WindowControls: React.FC = ( +) => { + return (
); }; diff --git a/src/web-ui/src/infrastructure/api/service-api/WorkspaceAPI.ts b/src/web-ui/src/infrastructure/api/service-api/WorkspaceAPI.ts index 91cafc584..183658d59 100644 --- a/src/web-ui/src/infrastructure/api/service-api/WorkspaceAPI.ts +++ b/src/web-ui/src/infrastructure/api/service-api/WorkspaceAPI.ts @@ -1,4 +1,4 @@ - + import { api } from './ApiClient'; import { createTauriCommandError } from '../errors/TauriCommandError'; @@ -65,77 +65,77 @@ function groupSearchResultsByFile(results: FileSearchResult[]): FileSearchResult } export class WorkspaceAPI { - + async openWorkspace(path: string): Promise { try { - return await api.invoke('open_workspace', { - request: { path } + return await api.invoke('open_workspace', { + request: { path } }); } catch (error) { throw createTauriCommandError('open_workspace', error, { path }); } } - + async closeWorkspace(): Promise { try { - await api.invoke('close_workspace', { - request: {} + await api.invoke('close_workspace', { + request: {} }); } catch (error) { throw createTauriCommandError('close_workspace', error); } } - + async getWorkspaceInfo(): Promise { try { - return await api.invoke('get_workspace_info', { - request: {} + return await api.invoke('get_workspace_info', { + request: {} }); } catch (error) { throw createTauriCommandError('get_workspace_info', error); } } - + async listFiles(path: string): Promise { try { - return await api.invoke('list_files', { - request: { path } + return await api.invoke('list_files', { + request: { path } }); } catch (error) { throw createTauriCommandError('list_files', error, { path }); } } - + async readFile(path: string): Promise { try { - return await api.invoke('read_file', { - request: { path } + return await api.invoke('read_file', { + request: { path } }); } catch (error) { throw createTauriCommandError('read_file', error, { path }); } } - + async writeFile(path: string, content: string): Promise { try { - await api.invoke('write_file', { - request: { path, content } + await api.invoke('write_file', { + request: { path, content } }); } catch (error) { throw createTauriCommandError('write_file', error, { path, content }); } } - + async writeFileContent(workspacePath: string, filePath: string, content: string): Promise { try { - - + + await api.invoke('write_file_content', { request: { workspacePath, filePath, content } }); @@ -154,81 +154,81 @@ export class WorkspaceAPI { } } - + async createFile(path: string): Promise { try { - await api.invoke('create_file', { - request: { path } + await api.invoke('create_file', { + request: { path } }); } catch (error) { throw createTauriCommandError('create_file', error, { path }); } } - + async deleteFile(path: string): Promise { try { - await api.invoke('delete_file', { - request: { path } + await api.invoke('delete_file', { + request: { path } }); } catch (error) { throw createTauriCommandError('delete_file', error, { path }); } } - + async createDirectory(path: string): Promise { try { - await api.invoke('create_directory', { - request: { path } + await api.invoke('create_directory', { + request: { path } }); } catch (error) { throw createTauriCommandError('create_directory', error, { path }); } } - + async deleteDirectory(path: string, recursive: boolean = true): Promise { try { - await api.invoke('delete_directory', { - request: { path, recursive } + await api.invoke('delete_directory', { + request: { path, recursive } }); } catch (error) { throw createTauriCommandError('delete_directory', error, { path, recursive }); } } - + async getFileTree(path: string, maxDepth?: number): Promise { try { - return await api.invoke('get_file_tree', { - request: { path, maxDepth } + return await api.invoke('get_file_tree', { + request: { path, maxDepth } }); } catch (error) { throw createTauriCommandError('get_file_tree', error, { path, maxDepth }); } } - + async getDirectoryChildren(path: string): Promise { try { - return await api.invoke('get_directory_children', { - request: { path } + return await api.invoke('get_directory_children', { + request: { path } }); } catch (error) { throw createTauriCommandError('get_directory_children', error, { path }); } } - + async getDirectoryChildrenPaginated( - path: string, - offset: number = 0, + path: string, + offset: number = 0, limit: number = 100 ): Promise { try { - return await api.invoke('get_directory_children_paginated', { - request: { path, offset, limit } + return await api.invoke('get_directory_children_paginated', { + request: { path, offset, limit } }); } catch (error) { throw createTauriCommandError('get_directory_children_paginated', error, { path, offset, limit }); @@ -245,11 +245,11 @@ export class WorkspaceAPI { } } - + async readFileContent(filePath: string, encoding?: string): Promise { try { - return await api.invoke('read_file_content', { - request: { filePath, encoding } + return await api.invoke('read_file_content', { + request: { filePath, encoding } }); } catch (error) { throw createTauriCommandError('read_file_content', error, { filePath, encoding }); @@ -446,8 +446,8 @@ export class WorkspaceAPI { } async searchFiles( - rootPath: string, - pattern: string, + rootPath: string, + pattern: string, searchContent: boolean = true, caseSensitive: boolean = false, useRegex: boolean = false, @@ -460,10 +460,10 @@ export class WorkspaceAPI { const effectiveSearchId = searchId ?? this.createSearchId(searchContent ? 'legacy-content' : 'legacy-filenames'); try { - const resultPromise = api.invoke('search_files', { - request: { - rootPath, - pattern, + const resultPromise = api.invoke('search_files', { + request: { + rootPath, + pattern, searchContent, searchId: effectiveSearchId, caseSensitive, @@ -471,7 +471,7 @@ export class WorkspaceAPI { wholeWord, maxResults, includeDirectories, - } + } }); return await this.raceCancelable('search_files', resultPromise, effectiveSearchId, signal); @@ -494,8 +494,8 @@ export class WorkspaceAPI { } async searchFilenamesOnly( - rootPath: string, - pattern: string, + rootPath: string, + pattern: string, caseSensitive: boolean = false, useRegex: boolean = false, wholeWord: boolean = false, @@ -631,8 +631,8 @@ export class WorkspaceAPI { } async searchContentOnly( - rootPath: string, - pattern: string, + rootPath: string, + pattern: string, caseSensitive: boolean = false, useRegex: boolean = false, wholeWord: boolean = false, @@ -668,16 +668,16 @@ export class WorkspaceAPI { typeof searchIdOrSignal === 'string' ? searchIdOrSignal : this.createSearchId('content'); try { - const resultPromise = api.invoke('search_file_contents', { - request: { - rootPath, - pattern, + const resultPromise = api.invoke('search_file_contents', { + request: { + rootPath, + pattern, searchId: effectiveSearchId, caseSensitive, useRegex, wholeWord, maxResults, - } + } }); return await this.raceCancelable('search_file_contents', resultPromise, effectiveSearchId, effectiveSignal); @@ -759,11 +759,11 @@ export class WorkspaceAPI { ); } - + async renameFile(oldPath: string, newPath: string): Promise { try { - await api.invoke('rename_file', { - request: { oldPath, newPath } + await api.invoke('rename_file', { + request: { oldPath, newPath } }); } catch (error) { throw createTauriCommandError('rename_file', error, { oldPath, newPath }); @@ -789,70 +789,70 @@ export class WorkspaceAPI { async open_oh_file_dialog(): Promise { try { return await api.invoke("open_oh_file_dialog") - }catch (error){ - throw createTauriCommandError('open_oh_file_dialog',error) + } catch (error) { + throw createTauriCommandError('open_oh_file_dialog', error) } } async window_is_minimized(): Promise { try { return await api.invoke("window_is_minimized") - }catch (error){ - throw createTauriCommandError('window_is_minimized',error) + } catch (error) { + throw createTauriCommandError('window_is_minimized', error) } } async window_start_dragging(): Promise { try { return await api.invoke("window_start_dragging") - }catch (error){ - throw createTauriCommandError('window_start_dragging',error) + } catch (error) { + throw createTauriCommandError('window_start_dragging', error) } } async close_window(): Promise { try { return await api.invoke("close_window") - }catch (error){ - throw createTauriCommandError('close_window',error) + } catch (error) { + throw createTauriCommandError('close_window', error) } } async window_is_maximized(): Promise { try { return await api.invoke("window_is_maximized") - }catch (error){ - throw createTauriCommandError('window_is_maximized',error) + } catch (error) { + throw createTauriCommandError('window_is_maximized', error) } } async handle_min_window(): Promise { try { return await api.invoke("handle_min_window") - }catch (error){ - throw createTauriCommandError('handle_min_window',error) + } catch (error) { + throw createTauriCommandError('handle_min_window', error) } } async handle_max_window(): Promise { try { return await api.invoke("handle_max_window") - }catch (error){ - throw createTauriCommandError('handle_max_window',error) + } catch (error) { + throw createTauriCommandError('handle_max_window', error) } } async handle_restore_window(): Promise { try { return await api.invoke("handle_restore_window") - }catch (error){ - throw createTauriCommandError('handle_restore_window',error) + } catch (error) { + throw createTauriCommandError('handle_restore_window', error) } } async revealInExplorer(path: string): Promise { try { - await api.invoke('reveal_in_explorer', { + await api.invoke('reveal_in_explorer', { request: { path } }); } catch (error) { @@ -860,10 +860,20 @@ export class WorkspaceAPI { } } - + async setThemeMode(theme: string): Promise { + try { + await api.invoke('set_theme_mode', { + theme + }); + } catch (error) { + throw createTauriCommandError('set_theme_mode', error, { theme }); + } + } + + async startFileWatch(path: string, recursive?: boolean): Promise { try { - await api.invoke('start_file_watch', { + await api.invoke('start_file_watch', { path, recursive }); @@ -873,10 +883,10 @@ export class WorkspaceAPI { } } - + async stopFileWatch(path: string): Promise { try { - await api.invoke('stop_file_watch', { + await api.invoke('stop_file_watch', { path }); } catch (error) { @@ -885,7 +895,7 @@ export class WorkspaceAPI { } } - + async getWatchedPaths(): Promise { try { return await api.invoke('get_watched_paths', {}); @@ -894,7 +904,7 @@ export class WorkspaceAPI { } } - + async getClipboardFiles(): Promise<{ files: string[]; isCut: boolean }> { try { return await api.invoke('get_clipboard_files'); @@ -903,7 +913,7 @@ export class WorkspaceAPI { } } - + async pasteFiles( sourcePaths: string[], targetDirectory: string, diff --git a/src/web-ui/src/infrastructure/theme/core/ThemeService.ts b/src/web-ui/src/infrastructure/theme/core/ThemeService.ts index dff0a194e..25ff4a810 100644 --- a/src/web-ui/src/infrastructure/theme/core/ThemeService.ts +++ b/src/web-ui/src/infrastructure/theme/core/ThemeService.ts @@ -14,7 +14,7 @@ import { ThemeSelectionId, } from '../types'; import { builtinThemes, getSystemPreferredDefaultThemeId } from '../presets'; -import { configAPI } from '@/infrastructure/api'; +import { configAPI, workspaceAPI } from '@/infrastructure/api'; import { monacoThemeSync } from '../integrations/MonacoThemeSync'; import { createLogger } from '@/shared/utils/logger'; @@ -258,6 +258,8 @@ export class ThemeService { this.injectCSSVariables(theme); + await workspaceAPI.setThemeMode(resolvedId); + try { monacoThemeSync.syncTheme(theme); } catch (error) {