mirror of
https://github.com/infinilabs/coco-app.git
synced 2025-12-15 19:17:42 +01:00
fix: resolve app show/hide behavior and tray icon handling (#129)
This commit is contained in:
@@ -11,16 +11,17 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^2.1.10",
|
||||
"@react-oauth/google": "^0.12.1",
|
||||
"@tauri-apps/api": "^2.2.0",
|
||||
"@tauri-apps/plugin-autostart": "~2",
|
||||
"@tauri-apps/plugin-deep-link": "^2.2.0",
|
||||
"@tauri-apps/plugin-dialog": "^2.2.0",
|
||||
"@tauri-apps/plugin-global-shortcut": "~2.0.0",
|
||||
"@tauri-apps/plugin-http": "~2.0.1",
|
||||
"@tauri-apps/plugin-os": "^2.2.0",
|
||||
"@tauri-apps/plugin-shell": ">=2.0.0",
|
||||
"@tauri-apps/plugin-websocket": "~2",
|
||||
"@tauri-apps/plugin-window": "2.0.0-alpha.1",
|
||||
"@tauri-apps/plugin-updater": "^2.3.0",
|
||||
"ahooks": "^3.8.4",
|
||||
"axios": "^1.7.7",
|
||||
"clsx": "^2.1.1",
|
||||
|
||||
47
pnpm-lock.yaml
generated
47
pnpm-lock.yaml
generated
@@ -11,9 +11,6 @@ importers:
|
||||
'@headlessui/react':
|
||||
specifier: ^2.1.10
|
||||
version: 2.1.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@react-oauth/google':
|
||||
specifier: ^0.12.1
|
||||
version: 0.12.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@tauri-apps/api':
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0
|
||||
@@ -23,6 +20,9 @@ importers:
|
||||
'@tauri-apps/plugin-deep-link':
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0
|
||||
'@tauri-apps/plugin-dialog':
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0
|
||||
'@tauri-apps/plugin-global-shortcut':
|
||||
specifier: ~2.0.0
|
||||
version: 2.0.0
|
||||
@@ -35,6 +35,9 @@ importers:
|
||||
'@tauri-apps/plugin-shell':
|
||||
specifier: '>=2.0.0'
|
||||
version: 2.0.0
|
||||
'@tauri-apps/plugin-updater':
|
||||
specifier: ^2.3.0
|
||||
version: 2.5.0
|
||||
'@tauri-apps/plugin-websocket':
|
||||
specifier: ~2
|
||||
version: 2.0.0
|
||||
@@ -513,12 +516,6 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@react-oauth/google@0.12.1':
|
||||
resolution: {integrity: sha512-qagsy22t+7UdkYAiT5ZhfM4StXi9PPNvw0zuwNmabrWyMKddczMtBIOARflbaIj+wHiQjnMAsZmzsUYuXeyoSg==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
|
||||
'@react-stately/utils@3.10.4':
|
||||
resolution: {integrity: sha512-gBEQEIMRh5f60KCm7QKQ2WfvhB2gLUr9b72sqUdIZ2EG+xuPgaIlCBeSicvjmjBvYZwOjoOEnmIkcx2GHp/HWw==}
|
||||
peerDependencies:
|
||||
@@ -557,55 +554,46 @@ packages:
|
||||
resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.24.0':
|
||||
resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.24.0':
|
||||
resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.24.0':
|
||||
resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-powerpc64le-gnu@4.24.0':
|
||||
resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.24.0':
|
||||
resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.24.0':
|
||||
resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.24.0':
|
||||
resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.24.0':
|
||||
resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.24.0':
|
||||
resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==}
|
||||
@@ -664,28 +652,24 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@tauri-apps/cli-linux-arm64-musl@2.2.7':
|
||||
resolution: {integrity: sha512-+8HZ+txff/Y3YjAh80XcLXcX8kpGXVdr1P8AfjLHxHdS6QD4Md+acSxGTTNbplmHuBaSHJvuTvZf9tU1eDCTDg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@tauri-apps/cli-linux-x64-gnu@2.2.7':
|
||||
resolution: {integrity: sha512-ahlSnuCnUntblp9dG7/w5ZWZOdzRFi3zl0oScgt7GF4KNAOEa7duADsxPA4/FT2hLRa0SvpqtD4IYFvCxoVv3Q==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@tauri-apps/cli-linux-x64-musl@2.2.7':
|
||||
resolution: {integrity: sha512-+qKAWnJRSX+pjjRbKAQgTdFY8ecdcu8UdJ69i7wn3ZcRn2nMMzOO2LOMOTQV42B7/Q64D1pIpmZj9yblTMvadA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@tauri-apps/cli-win32-arm64-msvc@2.2.7':
|
||||
resolution: {integrity: sha512-aa86nRnrwT04u9D9fhf5JVssuAZlUCCc8AjqQjqODQjMd4BMA2+d4K9qBMpEG/1kVh95vZaNsLogjEaqSTTw4A==}
|
||||
@@ -716,6 +700,9 @@ packages:
|
||||
'@tauri-apps/plugin-deep-link@2.2.0':
|
||||
resolution: {integrity: sha512-H6mkxr2KZ3XJcKL44tiq6cOjCw9DL8OgU1xjn3j26Qsn+H/roPFiyhR7CHuB8Ar+sQFj4YVlfmJwtBajK2FETQ==}
|
||||
|
||||
'@tauri-apps/plugin-dialog@2.2.0':
|
||||
resolution: {integrity: sha512-6bLkYK68zyK31418AK5fNccCdVuRnNpbxquCl8IqgFByOgWFivbiIlvb79wpSXi0O+8k8RCSsIpOquebusRVSg==}
|
||||
|
||||
'@tauri-apps/plugin-global-shortcut@2.0.0':
|
||||
resolution: {integrity: sha512-pnB4CUwFVjg4twtBSxoLJ4uLFTYxsvOdC1zIbG581pYzhYatOl6mjB+ijD5SSXgiS/jNoqMcfkOF9PWAisurew==}
|
||||
|
||||
@@ -728,6 +715,9 @@ packages:
|
||||
'@tauri-apps/plugin-shell@2.0.0':
|
||||
resolution: {integrity: sha512-OpW2+ycgJLrEoZityWeWYk+6ZWP9VyiAfbO+N/O8VfLkqyOym8kXh7odKDfINx9RAotkSGBtQM4abyKfJDkcUg==}
|
||||
|
||||
'@tauri-apps/plugin-updater@2.5.0':
|
||||
resolution: {integrity: sha512-CWpwrkgpMESDPgJ0EuXgQTI/U9zeZQ9NLUvMyuWVrsuRez7tJ/3c7y73LAkvkI6+ekUxVaRJrxYrSfd8W+DRvQ==}
|
||||
|
||||
'@tauri-apps/plugin-websocket@2.0.0':
|
||||
resolution: {integrity: sha512-O2qRxZCljd4g+ceJhW7LfgQr+fg0fBBiAaLiMopoKL6TXKMnhBHOenp4nZ5/MoVTr77OQIDNO6Jp/c1YwiRVtQ==}
|
||||
|
||||
@@ -2649,11 +2639,6 @@ snapshots:
|
||||
clsx: 2.1.1
|
||||
react: 18.3.1
|
||||
|
||||
'@react-oauth/google@0.12.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
|
||||
'@react-stately/utils@3.10.4(react@18.3.1)':
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.13
|
||||
@@ -2780,6 +2765,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
'@tauri-apps/plugin-dialog@2.2.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
'@tauri-apps/plugin-global-shortcut@2.0.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.2.0
|
||||
@@ -2796,6 +2785,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
'@tauri-apps/plugin-updater@2.5.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
'@tauri-apps/plugin-websocket@2.0.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.2.0
|
||||
|
||||
597
src-tauri/Cargo.lock
generated
597
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -14,23 +14,31 @@ name = "coco_lib"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.0.0", features = [] }
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
pizza-common = { git = "https://github.com/infinilabs/pizza-common", branch = "main" }
|
||||
|
||||
tauri = { version = "2.0.6", features = ["macos-private-api", "tray-icon", "image-png", "unstable"] }
|
||||
tauri-plugin-shell = "2.0.0"
|
||||
tauri = { version = "2", features = ["protocol-asset", "macos-private-api", "tray-icon", "image-ico", "image-png", "unstable"] }
|
||||
tauri-plugin-shell = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
tauri-plugin-http = "2"
|
||||
|
||||
tauri-plugin-websocket = "2"
|
||||
tauri-plugin-theme = "2.1.2"
|
||||
tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" }
|
||||
tauri-plugin-deep-link = "2.0.0"
|
||||
tauri-plugin-single-instance = "2.0.0"
|
||||
tauri-plugin-store = "2.2.0"
|
||||
tauri-plugin-os = "2"
|
||||
tauri-plugin-dialog = "2"
|
||||
tauri-plugin-fs = "2"
|
||||
tauri-plugin-updater = "2"
|
||||
tauri-plugin-process = "2"
|
||||
tauri-plugin-drag = "2"
|
||||
|
||||
[target."cfg(target_os = \"macos\")".dependencies]
|
||||
tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" }
|
||||
|
||||
reqwest = "0.12.12"
|
||||
futures = "0.3.31"
|
||||
ordered-float = { version = "4.6.0", default-features = false }
|
||||
|
||||
8
src-tauri/Coco.desktop
Normal file
8
src-tauri/Coco.desktop
Normal file
@@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name={{{name}}}
|
||||
Exec={{{exec}}}
|
||||
Icon={{{icon}}}
|
||||
Categories={{{categories}}}
|
||||
Comment={{{comment}}}
|
||||
Terminal=false
|
||||
BIN
src-tauri/assets/drag-icon.png
Normal file
BIN
src-tauri/assets/drag-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.1 KiB |
BIN
src-tauri/assets/logo-mac.png
Normal file
BIN
src-tauri/assets/logo-mac.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
BIN
src-tauri/assets/logo.png
Normal file
BIN
src-tauri/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
src-tauri/assets/tray-mac.ico
Normal file
BIN
src-tauri/assets/tray-mac.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
src-tauri/assets/tray.ico
Normal file
BIN
src-tauri/assets/tray.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
@@ -55,9 +55,6 @@
|
||||
{
|
||||
"identifier": "http:default",
|
||||
"allow": [
|
||||
{
|
||||
"url": "http://localhost:9000"
|
||||
},
|
||||
{
|
||||
"url": "https://coco.infini.cloud"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ use reqwest::Client;
|
||||
use std::path::PathBuf;
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::ActivationPolicy;
|
||||
use tauri::{AppHandle, Emitter, Listener, Manager, Runtime, WebviewWindow};
|
||||
use tauri::{AppHandle, Emitter, Listener, Manager, Runtime, WebviewWindow, WindowEvent};
|
||||
use tauri_plugin_autostart::MacosLauncher;
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
use tokio::runtime::Runtime as RT;
|
||||
@@ -73,7 +73,7 @@ struct Payload {
|
||||
pub fn run() {
|
||||
let mut ctx = tauri::generate_context!();
|
||||
|
||||
tauri::Builder::default()
|
||||
let app = tauri::Builder::default()
|
||||
.plugin(tauri_nspanel::init())
|
||||
.plugin(tauri_plugin_http::init())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
@@ -96,7 +96,7 @@ pub fn run() {
|
||||
shortcut::get_current_shortcut,
|
||||
change_autostart,
|
||||
hide_coco,
|
||||
switch_tray_icon,
|
||||
// switch_tray_icon,
|
||||
server::servers::add_coco_server,
|
||||
server::servers::remove_coco_server,
|
||||
server::servers::list_coco_servers,
|
||||
@@ -158,9 +158,31 @@ pub fn run() {
|
||||
setup::default(app, main_window.clone(), settings_window.clone());
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.run(ctx)
|
||||
}).on_window_event(|window, event| match event {
|
||||
WindowEvent::CloseRequested { api, .. } => {
|
||||
dbg!("Close requested event received");
|
||||
window.hide().unwrap();
|
||||
api.prevent_close();
|
||||
}
|
||||
_ => {}
|
||||
}).build(ctx)
|
||||
.expect("error while running tauri application");
|
||||
|
||||
app.run(|app_handle, event| match event {
|
||||
#[cfg(target_os = "macos")]
|
||||
tauri::RunEvent::Reopen {
|
||||
has_visible_windows,
|
||||
..
|
||||
} => {
|
||||
dbg!("Reopen event received: has_visible_windows = {}", has_visible_windows);
|
||||
if has_visible_windows {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let _ = app_handle;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn init<R: Runtime>(app_handle: &AppHandle<R>) {
|
||||
@@ -253,7 +275,7 @@ fn hide_coco(app: tauri::AppHandle) {
|
||||
}
|
||||
|
||||
fn handle_open_coco(app: &AppHandle) {
|
||||
// println!("Open Coco menu clicked!");
|
||||
println!("Open Coco menu clicked!");
|
||||
|
||||
if let Some(window) = app.get_window(MAIN_WINDOW_LABEL) {
|
||||
window.show().unwrap();
|
||||
@@ -279,36 +301,36 @@ fn handle_hide_coco(app: &AppHandle) {
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn switch_tray_icon(app: tauri::AppHandle, is_dark_mode: bool) {
|
||||
let app_handle = app.app_handle();
|
||||
|
||||
// println!("is_dark_mode: {}", is_dark_mode);
|
||||
|
||||
const DARK_ICON_PATH: &[u8] = include_bytes!("../icons/dark@2x.png");
|
||||
const LIGHT_ICON_PATH: &[u8] = include_bytes!("../icons/light@2x.png");
|
||||
|
||||
let icon_path: &[u8] = if is_dark_mode {
|
||||
DARK_ICON_PATH
|
||||
} else {
|
||||
LIGHT_ICON_PATH
|
||||
};
|
||||
|
||||
let tray = match app_handle.tray_by_id("tray") {
|
||||
Some(tray) => tray,
|
||||
None => {
|
||||
eprintln!("Tray with ID 'tray' not found");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = tray.set_icon(Some(
|
||||
tauri::image::Image::from_bytes(icon_path)
|
||||
.unwrap_or_else(|e| panic!("Failed to load icon from bytes: {}", e)),
|
||||
)) {
|
||||
eprintln!("Failed to set tray icon: {}", e);
|
||||
}
|
||||
}
|
||||
// #[tauri::command]
|
||||
// fn switch_tray_icon(app: tauri::AppHandle, is_dark_mode: bool) {
|
||||
// let app_handle = app.app_handle();
|
||||
//
|
||||
// // println!("is_dark_mode: {}", is_dark_mode);
|
||||
//
|
||||
// const DARK_ICON_PATH: &[u8] = include_bytes!("../icons/dark@2x.png");
|
||||
// const LIGHT_ICON_PATH: &[u8] = include_bytes!("../icons/light@2x.png");
|
||||
//
|
||||
// let icon_path: &[u8] = if is_dark_mode {
|
||||
// DARK_ICON_PATH
|
||||
// } else {
|
||||
// LIGHT_ICON_PATH
|
||||
// };
|
||||
//
|
||||
// let tray = match app_handle.tray_by_id("tray") {
|
||||
// Some(tray) => tray,
|
||||
// None => {
|
||||
// eprintln!("Tray with ID 'tray' not found");
|
||||
// return;
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// if let Err(e) = tray.set_icon(Some(
|
||||
// tauri::image::Image::from_bytes(icon_path)
|
||||
// .unwrap_or_else(|e| panic!("Failed to load icon from bytes: {}", e)),
|
||||
// )) {
|
||||
// eprintln!("Failed to set tray icon: {}", e);
|
||||
// }
|
||||
// }
|
||||
|
||||
fn enable_tray(app: &mut tauri::App) {
|
||||
use tauri::{
|
||||
@@ -335,8 +357,9 @@ fn enable_tray(app: &mut tauri::App) {
|
||||
.unwrap();
|
||||
|
||||
let _tray = TrayIconBuilder::with_id("tray")
|
||||
.icon_as_template(true)
|
||||
// .icon(app.default_window_icon().unwrap().clone())
|
||||
.icon(Image::from_bytes(include_bytes!("../icons/light@2x.png")).expect("REASON"))
|
||||
.icon(Image::from_bytes(include_bytes!("../assets/tray-mac.ico")).expect("Failed to load icon"))
|
||||
.menu(&menu)
|
||||
.on_menu_event(|app, event| match event.id.as_ref() {
|
||||
"open" => {
|
||||
@@ -351,7 +374,7 @@ fn enable_tray(app: &mut tauri::App) {
|
||||
"settings" => {
|
||||
// windows failed to open second window, issue: https://github.com/tauri-apps/tauri/issues/11144 https://github.com/tauri-apps/tauri/issues/8196
|
||||
//#[cfg(windows)]
|
||||
let _ = app.emit("open_settings", "");
|
||||
let _ = app.emit("open_settings", "settings");
|
||||
|
||||
// #[cfg(not(windows))]
|
||||
// open_settings(&app);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2.0.0",
|
||||
"productName": "Coco AI",
|
||||
"version": "0.1.0",
|
||||
"version": "../package.json",
|
||||
"identifier": "rs.coco.app",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm dev",
|
||||
@@ -23,12 +23,13 @@
|
||||
"maximizable": false,
|
||||
"skipTaskbar": true,
|
||||
"resizable": false,
|
||||
"alwaysOnTop": false,
|
||||
"alwaysOnTop": true,
|
||||
"acceptFirstMouse": true,
|
||||
"shadow": true,
|
||||
"transparent": true,
|
||||
"fullscreen": false,
|
||||
"center": false,
|
||||
"visible": false,
|
||||
"windowEffects": {
|
||||
"effects": [],
|
||||
"radius": 12
|
||||
@@ -43,7 +44,8 @@
|
||||
"transparent": true,
|
||||
"maximizable": false,
|
||||
"skipTaskbar": false,
|
||||
"dragDropEnabled": true,
|
||||
"dragDropEnabled": false,
|
||||
"hiddenTitle": true,
|
||||
"visible": false,
|
||||
"windowEffects": {
|
||||
"effects": [
|
||||
@@ -54,17 +56,30 @@
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
"csp": null,
|
||||
"dangerousDisableAssetCspModification": true,
|
||||
"assetProtocol": {
|
||||
"enable": true,
|
||||
"scope": {
|
||||
"allow": [
|
||||
"**/*"
|
||||
],
|
||||
"requireLiteralLeadingDot": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"windows": {
|
||||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": "sha256",
|
||||
"timestampUrl": ""
|
||||
},
|
||||
"targets": [
|
||||
"nsis",
|
||||
"dmg",
|
||||
"app",
|
||||
"appimage",
|
||||
"deb",
|
||||
"rpm"
|
||||
],
|
||||
"shortDescription": "Coco AI",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
@@ -108,6 +123,12 @@
|
||||
]
|
||||
},
|
||||
"window": {},
|
||||
"updater": {
|
||||
"pubkey": "",
|
||||
"endpoints": [
|
||||
"https://api.coco.rs/update"
|
||||
]
|
||||
},
|
||||
"websocket": {},
|
||||
"shell": {},
|
||||
"globalShortcut": {},
|
||||
|
||||
15
src-tauri/tauri.linux.conf.json
Normal file
15
src-tauri/tauri.linux.conf.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"identifier": "rs.coco.app",
|
||||
"bundle": {
|
||||
"linux": {
|
||||
"deb": {
|
||||
"depends": [],
|
||||
"desktopTemplate": "./Coco.desktop"
|
||||
},
|
||||
"rpm": {
|
||||
"depends": [],
|
||||
"desktopTemplate": "./Coco.desktop"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src-tauri/tauri.macos.conf.json
Normal file
11
src-tauri/tauri.macos.conf.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"identifier": "rs.coco.app",
|
||||
"bundle": {
|
||||
"externalBin": [
|
||||
],
|
||||
"resources": [
|
||||
"assets/tray-mac.ico",
|
||||
"assets/drag-icon.png"
|
||||
]
|
||||
}
|
||||
}
|
||||
15
src-tauri/tauri.windows.conf.json
Normal file
15
src-tauri/tauri.windows.conf.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"identifier": "rs.coco.app",
|
||||
"bundle": {
|
||||
"externalBin": [
|
||||
],
|
||||
"windows": {
|
||||
"digestAlgorithm": "sha256",
|
||||
"nsis": {
|
||||
"languages": [
|
||||
],
|
||||
"installMode": "both"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,406 +1,419 @@
|
||||
import {
|
||||
Library,
|
||||
Mic,
|
||||
Send,
|
||||
Plus,
|
||||
AudioLines,
|
||||
Image,
|
||||
ArrowBigLeft,
|
||||
Search,
|
||||
} from "lucide-react";
|
||||
import { useRef, useState, useEffect, useCallback } from "react";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import { isTauri } from "@tauri-apps/api/core";
|
||||
import {ArrowBigLeft, AudioLines, Image, Library, Mic, Plus, Search, Send,} from "lucide-react";
|
||||
import {useCallback, useEffect, useRef, useState} from "react";
|
||||
import {listen} from "@tauri-apps/api/event";
|
||||
import {invoke, isTauri} from "@tauri-apps/api/core";
|
||||
|
||||
import ChatSwitch from "@/components/Common/ChatSwitch";
|
||||
import AutoResizeTextarea from "./AutoResizeTextarea";
|
||||
import { useChatStore } from "@/stores/chatStore";
|
||||
import {useChatStore} from "@/stores/chatStore";
|
||||
import StopIcon from "@/icons/Stop";
|
||||
import { useAppStore } from "@/stores/appStore";
|
||||
import { useSearchStore } from "@/stores/searchStore";
|
||||
import { metaOrCtrlKey } from "@/utils/keyboardUtils";
|
||||
import {useAppStore} from "@/stores/appStore";
|
||||
import {useSearchStore} from "@/stores/searchStore";
|
||||
import {metaOrCtrlKey} from "@/utils/keyboardUtils";
|
||||
|
||||
interface ChatInputProps {
|
||||
onSend: (message: string) => void;
|
||||
disabled: boolean;
|
||||
disabledChange: () => void;
|
||||
changeMode: (isChatMode: boolean) => void;
|
||||
isChatMode: boolean;
|
||||
inputValue: string;
|
||||
changeInput: (val: string) => void;
|
||||
reconnect: () => void;
|
||||
onSend: (message: string) => void;
|
||||
disabled: boolean;
|
||||
disabledChange: () => void;
|
||||
changeMode: (isChatMode: boolean) => void;
|
||||
isChatMode: boolean;
|
||||
inputValue: string;
|
||||
changeInput: (val: string) => void;
|
||||
reconnect: () => void;
|
||||
}
|
||||
|
||||
export default function ChatInput({
|
||||
onSend,
|
||||
disabled,
|
||||
changeMode,
|
||||
isChatMode,
|
||||
inputValue,
|
||||
changeInput,
|
||||
disabledChange,
|
||||
reconnect,
|
||||
}: ChatInputProps) {
|
||||
const showTooltip = useAppStore((state: { showTooltip: boolean }) => state.showTooltip);
|
||||
onSend,
|
||||
disabled,
|
||||
changeMode,
|
||||
isChatMode,
|
||||
inputValue,
|
||||
changeInput,
|
||||
disabledChange,
|
||||
reconnect,
|
||||
}: ChatInputProps) {
|
||||
const showTooltip = useAppStore((state: { showTooltip: boolean }) => state.showTooltip);
|
||||
|
||||
const sourceData = useSearchStore((state: { sourceData: any; }) => state.sourceData);
|
||||
const setSourceData = useSearchStore((state: { setSourceData: any; }) => state.setSourceData);
|
||||
const sourceData = useSearchStore((state: { sourceData: any; }) => state.sourceData);
|
||||
const setSourceData = useSearchStore((state: { setSourceData: any; }) => state.setSourceData);
|
||||
|
||||
useEffect(() => {
|
||||
setSourceData(undefined);
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
setSourceData(undefined);
|
||||
}, []);
|
||||
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const textareaRef = useRef<{ reset: () => void; focus: () => void }>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const textareaRef = useRef<{ reset: () => void; focus: () => void }>(null);
|
||||
|
||||
const { curChatEnd, connected } = useChatStore();
|
||||
const {curChatEnd, connected} = useChatStore();
|
||||
|
||||
const [isCommandPressed, setIsCommandPressed] = useState(false);
|
||||
const [isCommandPressed, setIsCommandPressed] = useState(false);
|
||||
|
||||
const handleToggleFocus = useCallback(() => {
|
||||
if (isChatMode) {
|
||||
textareaRef.current?.focus();
|
||||
} else {
|
||||
inputRef.current?.focus();
|
||||
}
|
||||
}, [isChatMode, textareaRef, inputRef]);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
const trimmedValue = inputValue.trim();
|
||||
if (trimmedValue && !disabled) {
|
||||
onSend(trimmedValue);
|
||||
}
|
||||
}, [inputValue, disabled, onSend]);
|
||||
|
||||
const pressedKeys = new Set<string>();
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: KeyboardEvent) => {
|
||||
console.log("handleKeyDown", e.code, e.key);
|
||||
pressedKeys.add(e.key);
|
||||
|
||||
if (e.key === metaOrCtrlKey()) {
|
||||
setIsCommandPressed(true);
|
||||
}
|
||||
|
||||
if (pressedKeys.has(metaOrCtrlKey())) {
|
||||
// e.preventDefault();
|
||||
switch (e.code) {
|
||||
case "Comma":
|
||||
setIsCommandPressed(false);
|
||||
break;
|
||||
case "KeyI":
|
||||
handleToggleFocus();
|
||||
break;
|
||||
case "ArrowLeft":
|
||||
setSourceData(undefined);
|
||||
break;
|
||||
case "KeyM":
|
||||
console.log("KeyM");
|
||||
break;
|
||||
case "Enter":
|
||||
isChatMode && (curChatEnd ? handleSubmit() : disabledChange?.());
|
||||
break;
|
||||
case "KeyO":
|
||||
console.log("KeyO");
|
||||
break;
|
||||
case "KeyU":
|
||||
console.log("KeyU");
|
||||
break;
|
||||
case "KeyN":
|
||||
console.log("KeyN");
|
||||
break;
|
||||
case "KeyG":
|
||||
console.log("KeyG");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
handleToggleFocus,
|
||||
isChatMode,
|
||||
handleSubmit,
|
||||
setSourceData,
|
||||
setIsCommandPressed,
|
||||
disabledChange,
|
||||
curChatEnd,
|
||||
]
|
||||
);
|
||||
|
||||
const handleKeyUp = useCallback((e: KeyboardEvent) => {
|
||||
pressedKeys.delete(e.key);
|
||||
if (e.key === metaOrCtrlKey()) {
|
||||
setIsCommandPressed(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
window.addEventListener("keyup", handleKeyUp);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyDown);
|
||||
window.removeEventListener("keyup", handleKeyUp);
|
||||
};
|
||||
}, [handleKeyDown, handleKeyUp]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTauri()) return;
|
||||
const setupListener = async () => {
|
||||
const unlisten = await listen("tauri://focus", () => {
|
||||
// console.log("Window focused!");
|
||||
const handleToggleFocus = useCallback(() => {
|
||||
if (isChatMode) {
|
||||
textareaRef.current?.focus();
|
||||
textareaRef.current?.focus();
|
||||
} else {
|
||||
inputRef.current?.focus();
|
||||
inputRef.current?.focus();
|
||||
}
|
||||
});
|
||||
}, [isChatMode, textareaRef, inputRef]);
|
||||
|
||||
return unlisten;
|
||||
};
|
||||
const handleSubmit = useCallback(() => {
|
||||
const trimmedValue = inputValue.trim();
|
||||
if (trimmedValue && !disabled) {
|
||||
onSend(trimmedValue);
|
||||
}
|
||||
}, [inputValue, disabled, onSend]);
|
||||
|
||||
let unlisten: (() => void) | undefined;
|
||||
const pressedKeys = new Set<string>();
|
||||
|
||||
setupListener().then((unlistener) => {
|
||||
unlisten = unlistener;
|
||||
});
|
||||
const handleKeyDown = useCallback(
|
||||
(e: KeyboardEvent) => {
|
||||
console.log("handleKeyDown", e.code, e.key);
|
||||
|
||||
return () => {
|
||||
unlisten?.();
|
||||
};
|
||||
}, [isChatMode]);
|
||||
|
||||
const openChatAI = async () => {
|
||||
console.log("Chat AI opened.");
|
||||
};
|
||||
|
||||
const [countdown, setCountdown] = useState(5);
|
||||
|
||||
useEffect(() => {
|
||||
if (connected) return;
|
||||
if (countdown <= 0) {
|
||||
ReconnectClick();
|
||||
return;
|
||||
}
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
setCountdown((prev) => prev - 1);
|
||||
}, 1000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [countdown, connected]);
|
||||
|
||||
const ReconnectClick = () => {
|
||||
setCountdown(5);
|
||||
reconnect();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full relative">
|
||||
<div className="p-2 flex items-center dark:text-[#D8D8D8] bg-[#ededed] dark:bg-[#202126] rounded transition-all relative">
|
||||
<div className="flex flex-wrap gap-2 flex-1 items-center relative">
|
||||
{!isChatMode && !sourceData ? (
|
||||
<Search
|
||||
className="w-4 h-4 text-[#ccc] dark:text-[#d8d8d8]"
|
||||
/>
|
||||
) : (
|
||||
!isChatMode && sourceData ? (
|
||||
<ArrowBigLeft
|
||||
className="w-4 h-4 text-[#ccc] dark:text-[#d8d8d8] cursor-pointer"
|
||||
onClick={() => setSourceData(undefined)}
|
||||
/>
|
||||
) : null
|
||||
)}
|
||||
|
||||
{isChatMode ? (
|
||||
<AutoResizeTextarea
|
||||
ref={textareaRef}
|
||||
input={inputValue}
|
||||
setInput={changeInput}
|
||||
connected={connected}
|
||||
handleKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
handleSubmit();
|
||||
if (e.key === "Escape") {
|
||||
console.log("Escape:" + inputValue);
|
||||
if (inputValue) {
|
||||
changeInput("");
|
||||
return;
|
||||
} else {
|
||||
console.log("empty value, but Escape key pressed.");
|
||||
invoke("hide_coco").then(() => {
|
||||
console.log("Hide Coco");
|
||||
}).finally(() => {
|
||||
console.log("Hide Coco");
|
||||
})
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
autoFocus
|
||||
autoComplete="off"
|
||||
autoCapitalize="none"
|
||||
spellCheck="false"
|
||||
className="text-base font-normal flex-1 outline-none min-w-[200px] text-[#333] dark:text-[#d8d8d8] placeholder-text-xs placeholder-[#999] dark:placeholder-gray-500 bg-transparent"
|
||||
placeholder="Search whatever you want ..."
|
||||
value={inputValue}
|
||||
onChange={(e) => {
|
||||
onSend(e.target.value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showTooltip && isCommandPressed && !isChatMode && sourceData ? (
|
||||
}
|
||||
|
||||
|
||||
pressedKeys.add(e.key);
|
||||
|
||||
if (e.key === metaOrCtrlKey()) {
|
||||
setIsCommandPressed(true);
|
||||
}
|
||||
|
||||
if (pressedKeys.has(metaOrCtrlKey())) {
|
||||
// e.preventDefault();
|
||||
switch (e.code) {
|
||||
case "Comma":
|
||||
setIsCommandPressed(false);
|
||||
break;
|
||||
case "KeyI":
|
||||
handleToggleFocus();
|
||||
break;
|
||||
case "ArrowLeft":
|
||||
setSourceData(undefined);
|
||||
break;
|
||||
case "KeyM":
|
||||
console.log("KeyM");
|
||||
break;
|
||||
case "Enter":
|
||||
isChatMode && (curChatEnd ? handleSubmit() : disabledChange?.());
|
||||
break;
|
||||
case "KeyO":
|
||||
console.log("KeyO");
|
||||
break;
|
||||
case "KeyU":
|
||||
console.log("KeyU");
|
||||
break;
|
||||
case "KeyN":
|
||||
console.log("KeyN");
|
||||
break;
|
||||
case "KeyG":
|
||||
console.log("KeyG");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
handleToggleFocus,
|
||||
isChatMode,
|
||||
handleSubmit,
|
||||
setSourceData,
|
||||
setIsCommandPressed,
|
||||
disabledChange,
|
||||
curChatEnd,
|
||||
]
|
||||
);
|
||||
|
||||
const handleKeyUp = useCallback((e: KeyboardEvent) => {
|
||||
pressedKeys.delete(e.key);
|
||||
if (e.key === metaOrCtrlKey()) {
|
||||
setIsCommandPressed(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
window.addEventListener("keyup", handleKeyUp);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyDown);
|
||||
window.removeEventListener("keyup", handleKeyUp);
|
||||
};
|
||||
}, [handleKeyDown, handleKeyUp]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTauri()) return;
|
||||
const setupListener = async () => {
|
||||
const unlisten = await listen("tauri://focus", () => {
|
||||
// console.log("Window focused!");
|
||||
if (isChatMode) {
|
||||
textareaRef.current?.focus();
|
||||
} else {
|
||||
inputRef.current?.focus();
|
||||
}
|
||||
});
|
||||
|
||||
return unlisten;
|
||||
};
|
||||
|
||||
let unlisten: (() => void) | undefined;
|
||||
|
||||
setupListener().then((unlistener) => {
|
||||
unlisten = unlistener;
|
||||
});
|
||||
|
||||
return () => {
|
||||
unlisten?.();
|
||||
};
|
||||
}, [isChatMode]);
|
||||
|
||||
const openChatAI = async () => {
|
||||
console.log("Chat AI opened.");
|
||||
};
|
||||
|
||||
const [countdown, setCountdown] = useState(5);
|
||||
|
||||
useEffect(() => {
|
||||
if (connected) return;
|
||||
if (countdown <= 0) {
|
||||
ReconnectClick();
|
||||
return;
|
||||
}
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
setCountdown((prev) => prev - 1);
|
||||
}, 1000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [countdown, connected]);
|
||||
|
||||
const ReconnectClick = () => {
|
||||
setCountdown(5);
|
||||
reconnect();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full relative">
|
||||
<div
|
||||
className={`absolute left-0 w-4 h-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#ededed] dark:shadow-[-6px_0px_6px_2px_#202126]`}
|
||||
>
|
||||
←
|
||||
className="p-2 flex items-center dark:text-[#D8D8D8] bg-[#ededed] dark:bg-[#202126] rounded transition-all relative">
|
||||
<div className="flex flex-wrap gap-2 flex-1 items-center relative">
|
||||
{!isChatMode && !sourceData ? (
|
||||
<Search
|
||||
className="w-4 h-4 text-[#ccc] dark:text-[#d8d8d8]"
|
||||
/>
|
||||
) : (
|
||||
!isChatMode && sourceData ? (
|
||||
<ArrowBigLeft
|
||||
className="w-4 h-4 text-[#ccc] dark:text-[#d8d8d8] cursor-pointer"
|
||||
onClick={() => setSourceData(undefined)}
|
||||
/>
|
||||
) : null
|
||||
)}
|
||||
|
||||
{isChatMode ? (
|
||||
<AutoResizeTextarea
|
||||
ref={textareaRef}
|
||||
input={inputValue}
|
||||
setInput={changeInput}
|
||||
connected={connected}
|
||||
handleKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
handleSubmit();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
autoFocus
|
||||
autoComplete="off"
|
||||
autoCapitalize="none"
|
||||
spellCheck="false"
|
||||
className="text-base font-normal flex-1 outline-none min-w-[200px] text-[#333] dark:text-[#d8d8d8] placeholder-text-xs placeholder-[#999] dark:placeholder-gray-500 bg-transparent"
|
||||
placeholder="Search whatever you want ..."
|
||||
value={inputValue}
|
||||
onChange={(e) => {
|
||||
onSend(e.target.value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showTooltip && isCommandPressed && !isChatMode && sourceData ? (
|
||||
<div
|
||||
className={`absolute left-0 w-4 h-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#ededed] dark:shadow-[-6px_0px_6px_2px_#202126]`}
|
||||
>
|
||||
←
|
||||
</div>
|
||||
) : null}
|
||||
{showTooltip && isCommandPressed ? (
|
||||
<div
|
||||
className={`absolute ${!isChatMode && sourceData ? "left-7" : ""
|
||||
} w-4 h-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#ededed] dark:shadow-[-6px_0px_6px_2px_#202126]`}
|
||||
>
|
||||
I
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{isChatMode ? (
|
||||
<button
|
||||
className="p-1 hover:bg-gray-50 dark:hover:bg-gray-700 rounded-full transition-colors"
|
||||
type="button"
|
||||
>
|
||||
<Mic className="w-4 h-4 text-[#999] dark:text-[#999]"/>
|
||||
</button>
|
||||
) : null}
|
||||
|
||||
{isChatMode && curChatEnd ? (
|
||||
<button
|
||||
className={`ml-1 p-1 ${inputValue
|
||||
? "bg-[#0072FF]"
|
||||
: "bg-[#E4E5F0] dark:bg-[rgb(84,84,84)]"
|
||||
} rounded-full transition-colors`}
|
||||
type="submit"
|
||||
onClick={() => onSend(inputValue.trim())}
|
||||
>
|
||||
<Send className="w-4 h-4 text-white"/>
|
||||
</button>
|
||||
) : null}
|
||||
{isChatMode && !curChatEnd ? (
|
||||
<button
|
||||
className={`ml-1 px-1 bg-[#0072FF] rounded-full transition-colors`}
|
||||
type="submit"
|
||||
onClick={() => disabledChange()}
|
||||
>
|
||||
<StopIcon
|
||||
size={16}
|
||||
className="w-4 h-4 text-white"
|
||||
aria-label="Stop message"
|
||||
/>
|
||||
</button>
|
||||
) : null}
|
||||
|
||||
{showTooltip && isChatMode && isCommandPressed ? (
|
||||
<div
|
||||
className={`absolute right-10 w-4 h-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#fff] dark:shadow-[-6px_0px_6px_2px_#000]`}
|
||||
>
|
||||
M
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{showTooltip && isChatMode && isCommandPressed ? (
|
||||
<div
|
||||
className={`absolute right-3 w-4 h-4 flex items-end justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#fff] dark:shadow-[-6px_0px_6px_2px_#000]`}
|
||||
>
|
||||
↩︎
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{!connected && isChatMode ? (
|
||||
<div
|
||||
className="absolute top-0 right-0 bottom-0 left-0 px-2 py-4 bg-red-500/10 rounded-md font-normal text-xs text-gray-400 flex items-center gap-4">
|
||||
Unable to connect to the server
|
||||
<div
|
||||
className="w-[96px] h-[24px] bg-[#0061FF] rounded-[12px] font-normal text-xs text-white flex items-center justify-center cursor-pointer"
|
||||
onClick={ReconnectClick}
|
||||
>
|
||||
Reconnect ({countdown})
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
{showTooltip && isCommandPressed ? (
|
||||
|
||||
<div
|
||||
className={`absolute ${!isChatMode && sourceData ? "left-7" : ""
|
||||
} w-4 h-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#ededed] dark:shadow-[-6px_0px_6px_2px_#202126]`}
|
||||
data-tauri-drag-region
|
||||
className="flex justify-between items-center p-2"
|
||||
>
|
||||
I
|
||||
{isChatMode ? (
|
||||
<div className="flex gap-2 text-xs text-[#333] dark:text-[#d8d8d8]">
|
||||
<button
|
||||
className="inline-flex items-center rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors relative"
|
||||
onClick={openChatAI}
|
||||
>
|
||||
<Library className="w-4 h-4 mr-1 text-[#000] dark:text-[#d8d8d8]"/>
|
||||
Coco
|
||||
</button>
|
||||
<button
|
||||
className="inline-flex items-center rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-color relative">
|
||||
<Plus className="w-4 h-4 mr-1 text-[#000] dark:text-[#d8d8d8]"/>
|
||||
Upload
|
||||
</button>
|
||||
{showTooltip && isCommandPressed ? (
|
||||
<div
|
||||
className={`absolute left-2 w-4 h-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#fff] dark:shadow-[-6px_0px_6px_2px_#000]`}
|
||||
>
|
||||
O
|
||||
</div>
|
||||
) : null}
|
||||
{showTooltip && isCommandPressed ? (
|
||||
<div
|
||||
className={`absolute left-16 w-4 h-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#fff] dark:shadow-[-6px_0px_6px_2px_#000]`}
|
||||
>
|
||||
U
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-28 flex gap-2 relative">
|
||||
<button
|
||||
className="inline-flex items-center rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors relative"
|
||||
onClick={openChatAI}
|
||||
>
|
||||
<AudioLines className="w-4 h-4 text-[#000] dark:text-[#d8d8d8]"/>
|
||||
</button>
|
||||
<button
|
||||
className="inline-flex items-center rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-color relative">
|
||||
<Image className="w-4 h-4 text-[#000] dark:text-[#d8d8d8]"/>
|
||||
</button>
|
||||
{showTooltip && isCommandPressed ? (
|
||||
<div
|
||||
className={`absolute left-0 w-4 h-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#fff] dark:shadow-[-6px_0px_6px_2px_#000]`}
|
||||
>
|
||||
N
|
||||
</div>
|
||||
) : null}
|
||||
{showTooltip && isCommandPressed ? (
|
||||
<div
|
||||
className={`absolute left-6 w-4 h-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#fff] dark:shadow-[-6px_0px_6px_2px_#000]`}
|
||||
>
|
||||
G
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="relative w-16 flex justify-end items-center">
|
||||
{showTooltip && isCommandPressed ? (
|
||||
<div
|
||||
className={`absolute left-1 z-10 w-4 h-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#fff] dark:shadow-[-6px_0px_6px_2px_#000]`}
|
||||
>
|
||||
T
|
||||
</div>
|
||||
) : null}
|
||||
<ChatSwitch
|
||||
isChatMode={isChatMode}
|
||||
onChange={(value: boolean) => {
|
||||
value && disabledChange();
|
||||
changeMode(value);
|
||||
setSourceData(undefined);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{isChatMode ? (
|
||||
<button
|
||||
className="p-1 hover:bg-gray-50 dark:hover:bg-gray-700 rounded-full transition-colors"
|
||||
type="button"
|
||||
>
|
||||
<Mic className="w-4 h-4 text-[#999] dark:text-[#999]" />
|
||||
</button>
|
||||
) : null}
|
||||
|
||||
{isChatMode && curChatEnd ? (
|
||||
<button
|
||||
className={`ml-1 p-1 ${inputValue
|
||||
? "bg-[#0072FF]"
|
||||
: "bg-[#E4E5F0] dark:bg-[rgb(84,84,84)]"
|
||||
} rounded-full transition-colors`}
|
||||
type="submit"
|
||||
onClick={() => onSend(inputValue.trim())}
|
||||
>
|
||||
<Send className="w-4 h-4 text-white" />
|
||||
</button>
|
||||
) : null}
|
||||
{isChatMode && !curChatEnd ? (
|
||||
<button
|
||||
className={`ml-1 px-1 bg-[#0072FF] rounded-full transition-colors`}
|
||||
type="submit"
|
||||
onClick={() => disabledChange()}
|
||||
>
|
||||
<StopIcon
|
||||
size={16}
|
||||
className="w-4 h-4 text-white"
|
||||
aria-label="Stop message"
|
||||
/>
|
||||
</button>
|
||||
) : null}
|
||||
|
||||
{showTooltip && isChatMode && isCommandPressed ? (
|
||||
<div
|
||||
className={`absolute right-10 w-4 h-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#fff] dark:shadow-[-6px_0px_6px_2px_#000]`}
|
||||
>
|
||||
M
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{showTooltip && isChatMode && isCommandPressed ? (
|
||||
<div
|
||||
className={`absolute right-3 w-4 h-4 flex items-end justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#fff] dark:shadow-[-6px_0px_6px_2px_#000]`}
|
||||
>
|
||||
↩︎
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{!connected && isChatMode ? (
|
||||
<div className="absolute top-0 right-0 bottom-0 left-0 px-2 py-4 bg-red-500/10 rounded-md font-normal text-xs text-gray-400 flex items-center gap-4">
|
||||
Unable to connect to the server
|
||||
<div
|
||||
className="w-[96px] h-[24px] bg-[#0061FF] rounded-[12px] font-normal text-xs text-white flex items-center justify-center cursor-pointer"
|
||||
onClick={ReconnectClick}
|
||||
>
|
||||
Reconnect ({countdown})
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className="flex justify-between items-center p-2"
|
||||
>
|
||||
{isChatMode ? (
|
||||
<div className="flex gap-2 text-xs text-[#333] dark:text-[#d8d8d8]">
|
||||
<button
|
||||
className="inline-flex items-center rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors relative"
|
||||
onClick={openChatAI}
|
||||
>
|
||||
<Library className="w-4 h-4 mr-1 text-[#000] dark:text-[#d8d8d8]" />
|
||||
Coco
|
||||
</button>
|
||||
<button className="inline-flex items-center rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-color relative">
|
||||
<Plus className="w-4 h-4 mr-1 text-[#000] dark:text-[#d8d8d8]" />
|
||||
Upload
|
||||
</button>
|
||||
{showTooltip && isCommandPressed ? (
|
||||
<div
|
||||
className={`absolute left-2 w-4 h-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#fff] dark:shadow-[-6px_0px_6px_2px_#000]`}
|
||||
>
|
||||
O
|
||||
</div>
|
||||
) : null}
|
||||
{showTooltip && isCommandPressed ? (
|
||||
<div
|
||||
className={`absolute left-16 w-4 h-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#fff] dark:shadow-[-6px_0px_6px_2px_#000]`}
|
||||
>
|
||||
U
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-28 flex gap-2 relative">
|
||||
<button
|
||||
className="inline-flex items-center rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors relative"
|
||||
onClick={openChatAI}
|
||||
>
|
||||
<AudioLines className="w-4 h-4 text-[#000] dark:text-[#d8d8d8]" />
|
||||
</button>
|
||||
<button className="inline-flex items-center rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-color relative">
|
||||
<Image className="w-4 h-4 text-[#000] dark:text-[#d8d8d8]" />
|
||||
</button>
|
||||
{showTooltip && isCommandPressed ? (
|
||||
<div
|
||||
className={`absolute left-0 w-4 h-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#fff] dark:shadow-[-6px_0px_6px_2px_#000]`}
|
||||
>
|
||||
N
|
||||
</div>
|
||||
) : null}
|
||||
{showTooltip && isCommandPressed ? (
|
||||
<div
|
||||
className={`absolute left-6 w-4 h-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#fff] dark:shadow-[-6px_0px_6px_2px_#000]`}
|
||||
>
|
||||
G
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="relative w-16 flex justify-end items-center">
|
||||
{showTooltip && isCommandPressed ? (
|
||||
<div
|
||||
className={`absolute left-1 z-10 w-4 h-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#fff] dark:shadow-[-6px_0px_6px_2px_#000]`}
|
||||
>
|
||||
T
|
||||
</div>
|
||||
) : null}
|
||||
<ChatSwitch
|
||||
isChatMode={isChatMode}
|
||||
onChange={(value: boolean) => {
|
||||
value && disabledChange();
|
||||
changeMode(value);
|
||||
setSourceData(undefined);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,32 +1,36 @@
|
||||
import { useEffect } from "react";
|
||||
import { isTauri, invoke } from "@tauri-apps/api/core";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import {useEffect} from "react";
|
||||
import {invoke, isTauri} from "@tauri-apps/api/core";
|
||||
import {listen} from "@tauri-apps/api/event";
|
||||
|
||||
const useEscape = () => {
|
||||
const handleEscape = async (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
// Hide the Tauri app window when 'Esc' is pressed
|
||||
await invoke("hide_coco");
|
||||
console.log("App window hidden successfully.");
|
||||
}
|
||||
};
|
||||
const handleEscape = async (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") {
|
||||
console.log("Escape key pressed.");
|
||||
|
||||
useEffect(() => {
|
||||
if(!isTauri()) return;
|
||||
|
||||
const unlisten = listen("tauri://focus", () => {
|
||||
// Add event listener for keydown
|
||||
window.addEventListener("keydown", handleEscape);
|
||||
});
|
||||
event.preventDefault();
|
||||
|
||||
// Cleanup event listener on component unmount
|
||||
return () => {
|
||||
unlisten.then((unlistenFn) => unlistenFn());
|
||||
// Hide the Tauri app window when 'Esc' is pressed
|
||||
await invoke("hide_coco");
|
||||
|
||||
window.removeEventListener("keydown", handleEscape);
|
||||
console.log("App window hidden successfully.");
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTauri()) return;
|
||||
|
||||
const unlisten = listen("tauri://focus", () => {
|
||||
// Add event listener for keydown
|
||||
window.addEventListener("keydown", handleEscape);
|
||||
});
|
||||
|
||||
// Cleanup event listener on component unmount
|
||||
return () => {
|
||||
unlisten.then((unlistenFn) => unlistenFn());
|
||||
|
||||
window.removeEventListener("keydown", handleEscape);
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
|
||||
export default useEscape;
|
||||
|
||||
@@ -1,124 +1,149 @@
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { isTauri } from "@tauri-apps/api/core";
|
||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
||||
import { LogicalSize } from "@tauri-apps/api/dpi";
|
||||
import {useEffect, useRef, useState} from "react";
|
||||
import {invoke, isTauri} from "@tauri-apps/api/core";
|
||||
import {getCurrentWebviewWindow} from "@tauri-apps/api/webviewWindow";
|
||||
import {LogicalSize} from "@tauri-apps/api/dpi";
|
||||
|
||||
import InputBox from "@/components/Search/InputBox";
|
||||
import Search from "@/components/Search/Search";
|
||||
import ChatAI, { ChatAIRef } from "@/components/Assistant/Chat";
|
||||
import { useAppStore } from "@/stores/appStore";
|
||||
import { useAuthStore } from "@/stores/authStore";
|
||||
import ChatAI, {ChatAIRef} from "@/components/Assistant/Chat";
|
||||
import {useAppStore} from "@/stores/appStore";
|
||||
import {useAuthStore} from "@/stores/authStore";
|
||||
import ApiDetails from "@/components/Common/ApiDetails";
|
||||
|
||||
export default function DesktopApp() {
|
||||
const initializeListeners = useAppStore((state) => state.initializeListeners);
|
||||
const initializeListeners_auth = useAuthStore(
|
||||
(state) => state.initializeListeners
|
||||
);
|
||||
const initializeListeners = useAppStore((state) => state.initializeListeners);
|
||||
const initializeListeners_auth = useAuthStore(
|
||||
(state) => state.initializeListeners
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
initializeListeners();
|
||||
initializeListeners_auth();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
initializeListeners();
|
||||
initializeListeners_auth();
|
||||
|
||||
const chatAIRef = useRef<ChatAIRef>(null);
|
||||
// Listen for window focus and blur events
|
||||
const handleBlur = () => {
|
||||
console.log("Window blurred");
|
||||
invoke('hide_coco').then(() => {
|
||||
console.log("Hide Coco");
|
||||
}).finally(() => {
|
||||
console.log("Hide Coco");
|
||||
});
|
||||
};
|
||||
|
||||
const [isChatMode, setIsChatMode] = useState(false);
|
||||
const [input, setInput] = useState("");
|
||||
const [isTransitioned, setIsTransitioned] = useState(false);
|
||||
const handleFocus = () => {
|
||||
// Optionally, show the window if needed when focus is regained
|
||||
console.log("Window focused");
|
||||
};
|
||||
|
||||
async function changeMode(value: boolean) {
|
||||
setIsChatMode(value);
|
||||
setIsTransitioned(value);
|
||||
}
|
||||
window.addEventListener('blur', handleBlur);
|
||||
window.addEventListener('focus', handleFocus);
|
||||
|
||||
async function changeInput(value: string) {
|
||||
setInput(value);
|
||||
}
|
||||
// Clean up event listeners on component unmount
|
||||
return () => {
|
||||
window.removeEventListener('blur', handleBlur);
|
||||
window.removeEventListener('focus', handleFocus);
|
||||
};
|
||||
|
||||
const handleSendMessage = async (value: string) => {
|
||||
setInput(value);
|
||||
if (isChatMode) {
|
||||
if (isTauri()) {
|
||||
await getCurrentWebviewWindow()?.setSize(new LogicalSize(680, 596));
|
||||
}
|
||||
chatAIRef.current?.init();
|
||||
}, []);
|
||||
|
||||
const chatAIRef = useRef<ChatAIRef>(null);
|
||||
|
||||
const [isChatMode, setIsChatMode] = useState(false);
|
||||
const [input, setInput] = useState("");
|
||||
const [isTransitioned, setIsTransitioned] = useState(false);
|
||||
|
||||
async function changeMode(value: boolean) {
|
||||
setIsChatMode(value);
|
||||
setIsTransitioned(value);
|
||||
}
|
||||
};
|
||||
const cancelChat = () => {
|
||||
chatAIRef.current?.cancelChat();
|
||||
};
|
||||
|
||||
const reconnect = () => {
|
||||
chatAIRef.current?.reconnect();
|
||||
};
|
||||
const isTyping = false;
|
||||
async function changeInput(value: string) {
|
||||
setInput(value);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className={`w-full h-full m-auto rounded-xl overflow-hidden relative border border-[#E6E6E6] dark:border-[#272626] ${
|
||||
isTransitioned
|
||||
? "bg-chat_bg_light dark:bg-chat_bg_dark"
|
||||
: "bg-search_bg_light dark:bg-search_bg_dark"
|
||||
} bg-no-repeat bg-cover bg-center`}
|
||||
>
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className={`p-2 pb-0 absolute w-full flex items-center justify-center transition-all duration-500 ${
|
||||
isTransitioned
|
||||
? "top-[500px] h-[90px] border-t"
|
||||
: "top-0 h-[90px] border-b"
|
||||
} border-[#E6E6E6] dark:border-[#272626]`}
|
||||
>
|
||||
<InputBox
|
||||
isChatMode={isChatMode}
|
||||
inputValue={input}
|
||||
onSend={handleSendMessage}
|
||||
disabled={isTyping}
|
||||
disabledChange={() => {
|
||||
cancelChat();
|
||||
}}
|
||||
changeMode={changeMode}
|
||||
changeInput={changeInput}
|
||||
reconnect={reconnect}
|
||||
/>
|
||||
</div>
|
||||
const handleSendMessage = async (value: string) => {
|
||||
setInput(value);
|
||||
if (isChatMode) {
|
||||
if (isTauri()) {
|
||||
await getCurrentWebviewWindow()?.setSize(new LogicalSize(680, 596));
|
||||
}
|
||||
chatAIRef.current?.init();
|
||||
}
|
||||
};
|
||||
const cancelChat = () => {
|
||||
chatAIRef.current?.cancelChat();
|
||||
};
|
||||
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className={`absolute w-full transition-opacity duration-500 ${
|
||||
isTransitioned ? "opacity-0 pointer-events-none" : "opacity-100"
|
||||
} bottom-0 h-[500px] `}
|
||||
>
|
||||
<Search
|
||||
key="Search"
|
||||
input={input}
|
||||
isChatMode={isChatMode}
|
||||
changeInput={changeInput}
|
||||
/>
|
||||
</div>
|
||||
const reconnect = () => {
|
||||
chatAIRef.current?.reconnect();
|
||||
};
|
||||
const isTyping = false;
|
||||
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className={`absolute w-full transition-all duration-500 ${
|
||||
isTransitioned
|
||||
? "top-0 opacity-100 pointer-events-auto"
|
||||
: "-top-[506px] opacity-0 pointer-events-none"
|
||||
} h-[500px]`}
|
||||
>
|
||||
{isTransitioned && isChatMode ? (
|
||||
<ChatAI
|
||||
ref={chatAIRef}
|
||||
key="ChatAI"
|
||||
inputValue={input}
|
||||
isTransitioned={isTransitioned}
|
||||
changeInput={changeInput}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
return (
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className={`w-full h-full m-auto rounded-xl overflow-hidden relative border border-[#E6E6E6] dark:border-[#272626] ${
|
||||
isTransitioned
|
||||
? "bg-chat_bg_light dark:bg-chat_bg_dark"
|
||||
: "bg-search_bg_light dark:bg-search_bg_dark"
|
||||
} bg-no-repeat bg-cover bg-center`}
|
||||
>
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className={`p-2 pb-0 absolute w-full flex items-center justify-center transition-all duration-500 ${
|
||||
isTransitioned
|
||||
? "top-[500px] h-[90px] border-t"
|
||||
: "top-0 h-[90px] border-b"
|
||||
} border-[#E6E6E6] dark:border-[#272626]`}
|
||||
>
|
||||
<InputBox
|
||||
isChatMode={isChatMode}
|
||||
inputValue={input}
|
||||
onSend={handleSendMessage}
|
||||
disabled={isTyping}
|
||||
disabledChange={() => {
|
||||
cancelChat();
|
||||
}}
|
||||
changeMode={changeMode}
|
||||
changeInput={changeInput}
|
||||
reconnect={reconnect}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ApiDetails />
|
||||
</div>
|
||||
);
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className={`absolute w-full transition-opacity duration-500 ${
|
||||
isTransitioned ? "opacity-0 pointer-events-none" : "opacity-100"
|
||||
} bottom-0 h-[500px] `}
|
||||
>
|
||||
<Search
|
||||
key="Search"
|
||||
input={input}
|
||||
isChatMode={isChatMode}
|
||||
changeInput={changeInput}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className={`absolute w-full transition-all duration-500 ${
|
||||
isTransitioned
|
||||
? "top-0 opacity-100 pointer-events-auto"
|
||||
: "-top-[506px] opacity-0 pointer-events-none"
|
||||
} h-[500px]`}
|
||||
>
|
||||
{isTransitioned && isChatMode ? (
|
||||
<ChatAI
|
||||
ref={chatAIRef}
|
||||
key="ChatAI"
|
||||
inputValue={input}
|
||||
isTransitioned={isTransitioned}
|
||||
changeInput={changeInput}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<ApiDetails/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user