mirror of
https://github.com/infinilabs/coco-app.git
synced 2025-12-16 11:37:47 +01:00
fix: prevent window from hiding when moved on Windows (#748)
* fix: prevent window from hiding when moved on Windows * docs: update changelog * update
This commit is contained in:
@@ -17,8 +17,10 @@ Information about release notes of Coco Server is provided here.
|
||||
- feat: voice input support in both search and chat modes #732
|
||||
|
||||
### 🐛 Bug fix
|
||||
|
||||
- fix(file search): apply filters before from/size parameters #741
|
||||
- fix(file search): searching by name&content does not search file name #743
|
||||
- fix: prevent window from hiding when moved on Windows #748
|
||||
|
||||
### ✈️ Improvements
|
||||
|
||||
|
||||
@@ -145,7 +145,6 @@ pub fn run() {
|
||||
server::attachment::delete_attachment,
|
||||
server::transcription::transcription,
|
||||
server::system_settings::get_system_settings,
|
||||
simulate_mouse_click,
|
||||
extension::built_in::application::get_app_list,
|
||||
extension::built_in::application::get_app_search_path,
|
||||
extension::built_in::application::get_app_metadata,
|
||||
@@ -452,52 +451,6 @@ async fn hide_check(app_handle: AppHandle) {
|
||||
window.hide().unwrap();
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn simulate_mouse_click<R: Runtime>(window: WebviewWindow<R>, is_chat_mode: bool) {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use enigo::{Button, Coordinate, Direction, Enigo, Mouse, Settings};
|
||||
use std::{thread, time::Duration};
|
||||
|
||||
if let Ok(mut enigo) = Enigo::new(&Settings::default()) {
|
||||
// Save the current mouse position
|
||||
if let Ok((original_x, original_y)) = enigo.location() {
|
||||
// Retrieve the window's outer position (top-left corner)
|
||||
if let Ok(position) = window.outer_position() {
|
||||
// Retrieve the window's inner size (client area)
|
||||
if let Ok(size) = window.inner_size() {
|
||||
// Calculate the center position of the title bar
|
||||
let x = position.x + (size.width as i32 / 2);
|
||||
let y = if is_chat_mode {
|
||||
position.y + size.height as i32 - 50
|
||||
} else {
|
||||
position.y + 30
|
||||
};
|
||||
|
||||
// Move the mouse cursor to the calculated position
|
||||
if enigo.move_mouse(x, y, Coordinate::Abs).is_ok() {
|
||||
// // Simulate a left mouse click
|
||||
let _ = enigo.button(Button::Left, Direction::Click);
|
||||
// let _ = enigo.button(Button::Left, Direction::Release);
|
||||
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
|
||||
// Move the mouse cursor back to the original position
|
||||
let _ = enigo.move_mouse(original_x, original_y, Coordinate::Abs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let _ = window;
|
||||
let _ = is_chat_mode;
|
||||
}
|
||||
}
|
||||
|
||||
/// Log format:
|
||||
///
|
||||
/// ```text
|
||||
|
||||
@@ -18,6 +18,7 @@ import InputControls from "./InputControls";
|
||||
import { useExtensionsStore } from "@/stores/extensionsStore";
|
||||
import AudioRecording from "../AudioRecording";
|
||||
import { isDefaultServer } from "@/utils";
|
||||
import { useTauriFocus } from "@/hooks/useTauriFocus";
|
||||
|
||||
interface ChatInputProps {
|
||||
onSend: (message: string) => void;
|
||||
@@ -99,18 +100,12 @@ export default function ChatInput({
|
||||
const { setSearchValue, visibleExtensionStore, selectedExtension } =
|
||||
useSearchStore();
|
||||
|
||||
useEffect(() => {
|
||||
const handleFocus = () => {
|
||||
useTauriFocus({
|
||||
onFocus() {
|
||||
setBlurred(false);
|
||||
setModifierKeyPressed(false);
|
||||
};
|
||||
|
||||
window.addEventListener("focus", handleFocus);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("focus", handleFocus);
|
||||
};
|
||||
}, []);
|
||||
},
|
||||
});
|
||||
|
||||
const handleToggleFocus = useCallback(() => {
|
||||
textareaRef.current?.focus();
|
||||
|
||||
@@ -118,19 +118,6 @@ function SearchChat({
|
||||
const isWin10 = await platformAdapter.isWindows10();
|
||||
|
||||
setIsWin10(isWin10);
|
||||
|
||||
const unlisten = platformAdapter.listenEvent("show-coco", () => {
|
||||
//console.log("show-coco");
|
||||
|
||||
platformAdapter.invokeBackend("simulate_mouse_click", {
|
||||
isChatMode: isChatModeRef.current,
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
// Cleanup logic if needed
|
||||
unlisten.then((fn) => fn());
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
44
src/hooks/useTauriFocus.ts
Normal file
44
src/hooks/useTauriFocus.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { useRef } from "react";
|
||||
import { debounce, noop } from "lodash-es";
|
||||
import { useMount, useUnmount } from "ahooks";
|
||||
|
||||
import { useAppStore } from "@/stores/appStore";
|
||||
import { isMac } from "@/utils/platform";
|
||||
import platformAdapter from "@/utils/platformAdapter";
|
||||
|
||||
interface Props {
|
||||
onFocus?: () => void;
|
||||
onBlur?: () => void;
|
||||
}
|
||||
|
||||
export const useTauriFocus = (props: Props) => {
|
||||
const { onFocus, onBlur } = props;
|
||||
const { isTauri } = useAppStore();
|
||||
const unlistenRef = useRef(noop);
|
||||
|
||||
useMount(async () => {
|
||||
if (!isTauri) return;
|
||||
|
||||
const appWindow = await platformAdapter.getWebviewWindow();
|
||||
|
||||
const wait = isMac ? 0 : 100;
|
||||
|
||||
const debounced = debounce(({ payload }) => {
|
||||
if (payload) {
|
||||
console.log("Window focused");
|
||||
|
||||
onFocus?.();
|
||||
} else {
|
||||
console.log("Window blurred");
|
||||
|
||||
onBlur?.();
|
||||
}
|
||||
}, wait);
|
||||
|
||||
unlistenRef.current = await appWindow.onFocusChanged(debounced);
|
||||
});
|
||||
|
||||
useUnmount(() => {
|
||||
unlistenRef.current();
|
||||
});
|
||||
};
|
||||
@@ -1,29 +1,21 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { useAppStore } from "@/stores/appStore";
|
||||
import platformAdapter from "@/utils/platformAdapter";
|
||||
import { useTauriFocus } from "./useTauriFocus";
|
||||
|
||||
export function useWindowEvents() {
|
||||
const isPinned = useAppStore((state) => state.isPinned);
|
||||
const visible = useAppStore((state) => state.visible);
|
||||
const setBlurred = useAppStore((state) => state.setBlurred);
|
||||
|
||||
useEffect(() => {
|
||||
const handleBlur = async () => {
|
||||
console.log("Window blurred");
|
||||
useTauriFocus({
|
||||
onBlur() {
|
||||
if (isPinned || visible) {
|
||||
return setBlurred(true);
|
||||
}
|
||||
|
||||
await platformAdapter.hideWindow();
|
||||
platformAdapter.hideWindow();
|
||||
|
||||
console.log("Hide Coco");
|
||||
};
|
||||
|
||||
window.addEventListener("blur", handleBlur);
|
||||
|
||||
// Clean up event listeners on component unmount
|
||||
return () => {
|
||||
window.removeEventListener("blur", handleBlur);
|
||||
};
|
||||
}, [isPinned, visible]);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as commands from '@/commands';
|
||||
import * as commands from "@/commands";
|
||||
|
||||
// Window operations
|
||||
export const windowWrapper = {
|
||||
@@ -8,7 +8,9 @@ export const windowWrapper = {
|
||||
},
|
||||
|
||||
async getWebviewWindow() {
|
||||
const { getCurrentWebviewWindow } = await import("@tauri-apps/api/webviewWindow");
|
||||
const { getCurrentWebviewWindow } = await import(
|
||||
"@tauri-apps/api/webviewWindow"
|
||||
);
|
||||
return getCurrentWebviewWindow();
|
||||
},
|
||||
|
||||
@@ -37,16 +39,22 @@ export const eventWrapper = {
|
||||
// System functions
|
||||
export const systemWrapper = {
|
||||
async checkScreenPermission() {
|
||||
const { checkScreenRecordingPermission } = await import("tauri-plugin-macos-permissions-api");
|
||||
const { checkScreenRecordingPermission } = await import(
|
||||
"tauri-plugin-macos-permissions-api"
|
||||
);
|
||||
return checkScreenRecordingPermission();
|
||||
},
|
||||
|
||||
async captureScreen(id: number, type: 'monitor' | 'window') {
|
||||
if (type === 'monitor') {
|
||||
const { getMonitorScreenshot } = await import("tauri-plugin-screenshots-api");
|
||||
async captureScreen(id: number, type: "monitor" | "window") {
|
||||
if (type === "monitor") {
|
||||
const { getMonitorScreenshot } = await import(
|
||||
"tauri-plugin-screenshots-api"
|
||||
);
|
||||
return getMonitorScreenshot(id);
|
||||
} else {
|
||||
const { getWindowScreenshot } = await import("tauri-plugin-screenshots-api");
|
||||
const { getWindowScreenshot } = await import(
|
||||
"tauri-plugin-screenshots-api"
|
||||
);
|
||||
return getWindowScreenshot(id);
|
||||
}
|
||||
},
|
||||
@@ -60,5 +68,5 @@ export const commandWrapper = {
|
||||
return (commands as any)[commandName](...args);
|
||||
}
|
||||
throw new Error(`Command ${commandName} not found`);
|
||||
}
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user