diff --git a/.env b/.env index 6dca19db..07d095e2 100644 --- a/.env +++ b/.env @@ -1,5 +1,3 @@ COCO_SERVER_URL=http://localhost:9000 #https://coco.infini.cloud #http://localhost:9000 -COCO_WEBSOCKET_URL=ws://localhost:9000/ws #wss://coco.infini.cloud/ws #ws://localhost:9000/ws - #TAURI_DEV_HOST=0.0.0.0 \ No newline at end of file diff --git a/docs/content.en/docs/release-notes/_index.md b/docs/content.en/docs/release-notes/_index.md index aa3fb275..e1c4ddd9 100644 --- a/docs/content.en/docs/release-notes/_index.md +++ b/docs/content.en/docs/release-notes/_index.md @@ -24,6 +24,7 @@ Information about release notes of Coco App is provided here. - refactor: split query_coco_fusion() #836 - chore: web component loading font icon #838 +- chore: delete unused code files and dependencies #841 ## 0.7.1 (2025-07-27) diff --git a/package.json b/package.json index f779d1c2..7c0b574b 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,6 @@ "@tauri-apps/plugin-process": "^2.2.1", "@tauri-apps/plugin-shell": "^2.2.1", "@tauri-apps/plugin-updater": "github:infinilabs/tauri-plugin-updater#v2", - "@tauri-apps/plugin-websocket": "~2.3.0", "@tauri-apps/plugin-window": "2.0.0-alpha.1", "@wavesurfer/react": "^1.0.11", "ahooks": "^3.8.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6e1ddd5b..5d8407d7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,9 +47,6 @@ importers: '@tauri-apps/plugin-updater': specifier: github:infinilabs/tauri-plugin-updater#v2 version: https://codeload.github.com/infinilabs/tauri-plugin-updater/tar.gz/358e689c65e9943b53eff50bcb9dfd5b1cfc4072 - '@tauri-apps/plugin-websocket': - specifier: ~2.3.0 - version: 2.3.0 '@tauri-apps/plugin-window': specifier: 2.0.0-alpha.1 version: 2.0.0-alpha.1 @@ -1261,9 +1258,6 @@ packages: resolution: {tarball: https://codeload.github.com/infinilabs/tauri-plugin-updater/tar.gz/358e689c65e9943b53eff50bcb9dfd5b1cfc4072} version: 2.7.1 - '@tauri-apps/plugin-websocket@2.3.0': - resolution: {integrity: sha512-eAwRGe3tnqDeQYE0wq4g1PUKbam9tYvlC4uP/au12Y/z7MP4lrS4ylv+aoZ5Ly+hTlBdi7hDkhHomwF/UeBesA==} - '@tauri-apps/plugin-window@2.0.0-alpha.1': resolution: {integrity: sha512-dFOAgal/3Txz3SQ+LNQq0AK1EPC+acdaFlwPVB/6KXUZYmaFleIlzgxDVoJCQ+/xOhxvYrdQaFLefh0I/Kldbg==} @@ -4643,10 +4637,6 @@ snapshots: dependencies: '@tauri-apps/api': 2.5.0 - '@tauri-apps/plugin-websocket@2.3.0': - dependencies: - '@tauri-apps/api': 2.5.0 - '@tauri-apps/plugin-window@2.0.0-alpha.1': dependencies: '@tauri-apps/api': 2.0.0-alpha.6 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 3f61dc0e..f75645bf 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -900,13 +900,12 @@ dependencies = [ "tauri-plugin-single-instance", "tauri-plugin-store", "tauri-plugin-updater", - "tauri-plugin-websocket", "tauri-plugin-windows-version", "thiserror 1.0.69", "tokio", "tokio-native-tls", "tokio-stream", - "tokio-tungstenite 0.20.1", + "tokio-tungstenite", "tokio-util", "tungstenite 0.24.0", "url", @@ -6556,25 +6555,6 @@ dependencies = [ "zip 2.6.1", ] -[[package]] -name = "tauri-plugin-websocket" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3ac71aec5fb0ae5441e830cd075b1cbed49ac3d39cb975a4894ea8fa2e62b9" -dependencies = [ - "futures-util", - "http 1.3.1", - "log", - "rand 0.8.5", - "serde", - "serde_json", - "tauri", - "tauri-plugin", - "thiserror 2.0.12", - "tokio", - "tokio-tungstenite 0.26.2", -] - [[package]] name = "tauri-plugin-windows-version" version = "2.0.0" @@ -6909,22 +6889,6 @@ dependencies = [ "tungstenite 0.20.1", ] -[[package]] -name = "tokio-tungstenite" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" -dependencies = [ - "futures-util", - "log", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tungstenite 0.26.2", - "webpki-roots 0.26.11", -] - [[package]] name = "tokio-util" version = "0.7.15" @@ -7131,25 +7095,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "tungstenite" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" -dependencies = [ - "bytes", - "data-encoding", - "http 1.3.1", - "httparse", - "log", - "rand 0.9.1", - "rustls", - "rustls-pki-types", - "sha1", - "thiserror 2.0.12", - "utf-8", -] - [[package]] name = "typeid" version = "1.0.3" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 3c50cef0..c556b7d5 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -51,7 +51,6 @@ serde = { version = "1", features = ["derive"] } # see: https://docs.rs/serde_json/latest/serde_json/struct.Number.html#method.from_u128 serde_json = { version = "1", features = ["arbitrary_precision", "preserve_order"] } tauri-plugin-http = "2" -tauri-plugin-websocket = "2" tauri-plugin-deep-link = "2.0.0" tauri-plugin-store = "2.2.0" tauri-plugin-os = "2" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 1bcec493..b9e9bbb1 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -37,9 +37,6 @@ "http:allow-fetch-cancel", "http:allow-fetch-read-body", "http:allow-fetch-send", - "websocket:default", - "websocket:allow-connect", - "websocket:allow-send", "autostart:allow-enable", "autostart:allow-disable", "autostart:allow-is-enabled", diff --git a/src-tauri/src/assistant/mod.rs b/src-tauri/src/assistant/mod.rs index 95917485..d3caf69f 100644 --- a/src-tauri/src/assistant/mod.rs +++ b/src-tauri/src/assistant/mod.rs @@ -1,5 +1,5 @@ use crate::common::assistant::ChatRequestMessage; -use crate::common::http::{GetResponse, convert_query_params_to_strings}; +use crate::common::http::convert_query_params_to_strings; use crate::common::register::SearchSourceRegistry; use crate::server::http_client::HttpClient; use crate::{common, server::servers::COCO_SERVERS}; @@ -111,54 +111,6 @@ pub async fn cancel_session_chat( common::http::get_response_body_text(response).await } -#[tauri::command] -pub async fn new_chat( - _app_handle: AppHandle, - server_id: String, - websocket_id: String, - message: String, - query_params: Option>, -) -> Result { - let body = if !message.is_empty() { - let message = ChatRequestMessage { - message: Some(message), - }; - Some( - serde_json::to_string(&message) - .map_err(|e| format!("Failed to serialize message: {}", e))? - .into(), - ) - } else { - None - }; - - let mut headers = HashMap::new(); - headers.insert("WEBSOCKET-SESSION-ID".to_string(), websocket_id.into()); - - let response = HttpClient::advanced_post( - &server_id, - "/chat/_new", - Some(headers), - convert_query_params_to_strings(query_params), - body, - ) - .await - .map_err(|e| format!("Error sending message: {}", e))?; - - let body_text = common::http::get_response_body_text(response).await?; - - log::debug!("New chat response: {}", &body_text); - - let chat_response: GetResponse = serde_json::from_str(&body_text) - .map_err(|e| format!("Failed to parse response JSON: {}", e))?; - - if chat_response.result != "created" { - return Err(format!("Unexpected result: {}", chat_response.result)); - } - - Ok(chat_response) -} - #[tauri::command] pub async fn chat_create( app_handle: AppHandle, @@ -222,37 +174,6 @@ pub async fn chat_create( Ok(()) } -#[tauri::command] -pub async fn send_message( - _app_handle: AppHandle, - server_id: String, - websocket_id: String, - session_id: String, - message: String, - query_params: Option>, //search,deep_thinking -) -> Result { - let path = format!("/chat/{}/_send", session_id); - let msg = ChatRequestMessage { - message: Some(message), - }; - - let mut headers = HashMap::new(); - headers.insert("WEBSOCKET-SESSION-ID".to_string(), websocket_id.into()); - - let body = reqwest::Body::from(serde_json::to_string(&msg).unwrap()); - let response = HttpClient::advanced_post( - &server_id, - path.as_str(), - Some(headers), - convert_query_params_to_strings(query_params), - Some(body), - ) - .await - .map_err(|e| format!("Error cancel session: {}", e))?; - - common::http::get_response_body_text(response).await -} - #[tauri::command] pub async fn chat_chat( app_handle: AppHandle, diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 4c7aeaab..31c38ad8 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -130,9 +130,7 @@ pub fn run() { server::connector::get_connectors_by_server, search::query_coco_fusion, assistant::chat_history, - assistant::new_chat, assistant::chat_create, - assistant::send_message, assistant::chat_chat, assistant::session_chat_history, assistant::open_session_chat, @@ -145,8 +143,6 @@ pub fn run() { assistant::assistant_get_multi, // server::get_coco_server_datasources, // server::get_coco_server_connectors, - server::websocket::connect_to_server, - server::websocket::disconnect, get_app_search_source, server::attachment::upload_attachment, server::attachment::get_attachment, @@ -201,7 +197,6 @@ pub fn run() { let registry = SearchSourceRegistry::default(); app.manage(registry); // Store registry in Tauri's app state - app.manage(server::websocket::WebSocketManager::default()); // This has to be called before initializing extensions as doing that // requires access to the shortcut store, which will be set by this diff --git a/src-tauri/src/server/mod.rs b/src-tauri/src/server/mod.rs index e2eb6490..6048bff3 100644 --- a/src-tauri/src/server/mod.rs +++ b/src-tauri/src/server/mod.rs @@ -11,4 +11,3 @@ pub mod servers; pub mod synthesize; pub mod system_settings; pub mod transcription; -pub mod websocket; diff --git a/src-tauri/src/server/websocket.rs b/src-tauri/src/server/websocket.rs deleted file mode 100644 index 6820e179..00000000 --- a/src-tauri/src/server/websocket.rs +++ /dev/null @@ -1,172 +0,0 @@ -use crate::server::servers::{get_server_by_id, get_server_token}; -use futures::StreamExt; -use std::collections::HashMap; -use std::sync::Arc; -use tauri::{AppHandle, Emitter, Runtime}; -use tokio::net::TcpStream; -use tokio::sync::{Mutex, mpsc}; -use tokio_tungstenite::MaybeTlsStream; -use tokio_tungstenite::WebSocketStream; -use tokio_tungstenite::tungstenite::Message; -use tokio_tungstenite::tungstenite::handshake::client::generate_key; -use tokio_tungstenite::{Connector, connect_async_tls_with_config}; -#[derive(Default)] -pub struct WebSocketManager { - connections: Arc>>>, -} - -struct WebSocketInstance { - ws_connection: Mutex>>, // No need to lock the entire map - cancel_tx: mpsc::Sender<()>, -} - -fn convert_to_websocket(endpoint: &str) -> Result { - let url = url::Url::parse(endpoint).map_err(|e| format!("Invalid URL: {}", e))?; - let ws_protocol = if url.scheme() == "https" { - "wss://" - } else { - "ws://" - }; - let host = url.host_str().ok_or("No host found in URL")?; - let port = url - .port_or_known_default() - .unwrap_or(if url.scheme() == "https" { 443 } else { 80 }); - - let ws_endpoint = if port == 80 || port == 443 { - format!("{}{}{}", ws_protocol, host, "/ws") - } else { - format!("{}{}:{}/ws", ws_protocol, host, port) - }; - Ok(ws_endpoint) -} - -#[tauri::command] -pub async fn connect_to_server( - tauri_app_handle: AppHandle, - id: String, - client_id: String, - state: tauri::State<'_, WebSocketManager>, - app_handle: AppHandle, -) -> Result<(), String> { - let connections_clone = state.connections.clone(); - - // Disconnect old connection first - disconnect(client_id.clone(), state.clone()).await.ok(); - - let server = get_server_by_id(&id) - .await - .ok_or(format!("Server with ID {} not found", id))?; - let endpoint = convert_to_websocket(&server.endpoint)?; - let token = get_server_token(&id).await.map(|t| t.access_token.clone()); - - let mut request = - tokio_tungstenite::tungstenite::client::IntoClientRequest::into_client_request(&endpoint) - .map_err(|e| format!("Failed to create WebSocket request: {}", e))?; - - request - .headers_mut() - .insert("Connection", "Upgrade".parse().unwrap()); - request - .headers_mut() - .insert("Upgrade", "websocket".parse().unwrap()); - request - .headers_mut() - .insert("Sec-WebSocket-Version", "13".parse().unwrap()); - request - .headers_mut() - .insert("Sec-WebSocket-Key", generate_key().parse().unwrap()); - - if let Some(token) = token { - request - .headers_mut() - .insert("X-API-TOKEN", token.parse().unwrap()); - } - - let allow_self_signature = - crate::settings::get_allow_self_signature(tauri_app_handle.clone()).await; - let tls_connector = tokio_native_tls::native_tls::TlsConnector::builder() - .danger_accept_invalid_certs(allow_self_signature) - .build() - .map_err(|e| format!("TLS build error: {:?}", e))?; - - let connector = Connector::NativeTls(tls_connector.into()); - - let (ws_stream, _) = connect_async_tls_with_config( - request, - None, // WebSocketConfig - true, // disable_nagle - Some(connector), // Connector - ) - .await - .map_err(|e| format!("WebSocket TLS error: {:?}", e))?; - - let (cancel_tx, mut cancel_rx) = mpsc::channel(1); - - let instance = Arc::new(WebSocketInstance { - ws_connection: Mutex::new(ws_stream), - cancel_tx, - }); - - // Insert connection into the map (lock is held briefly) - { - let mut connections = connections_clone.lock().await; - connections.insert(client_id.clone(), instance.clone()); - } - - // Spawn WebSocket handler in a separate task - let app_handle_clone = app_handle.clone(); - let client_id_clone = client_id.clone(); - tokio::spawn(async move { - let ws = &mut *instance.ws_connection.lock().await; - - loop { - tokio::select! { - msg = ws.next() => { - match msg { - Some(Ok(Message::Text(text))) => { - let _ = app_handle_clone.emit(&format!("ws-message-{}", client_id_clone), text); - }, - Some(Err(_)) | None => { - log::debug!("WebSocket connection closed or error"); - let _ = app_handle_clone.emit(&format!("ws-error-{}", client_id_clone), id.clone()); - break; - } - _ => {} - } - } - _ = cancel_rx.recv() => { - log::debug!("WebSocket connection cancelled"); - let _ = app_handle_clone.emit(&format!("ws-cancel-{}", client_id_clone), id.clone()); - break; - } - } - } - - // Remove connection after it closes - let mut connections = connections_clone.lock().await; - connections.remove(&client_id_clone); - }); - - Ok(()) -} - -#[tauri::command] -pub async fn disconnect( - client_id: String, - state: tauri::State<'_, WebSocketManager>, -) -> Result<(), String> { - let instance = { - let mut connections = state.connections.lock().await; - connections.remove(&client_id) - }; - - if let Some(instance) = instance { - let _ = instance.cancel_tx.send(()).await; - - // Close WebSocket (lock only the connection, not the whole map) - let mut ws = instance.ws_connection.lock().await; - let _ = ws.close(None).await; - } - - Ok(()) -} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index f7a3385f..a583cf5f 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -126,7 +126,6 @@ "https://release.infinilabs.com/coco/app/.latest.json?target={{target}}&arch={{arch}}¤t_version={{current_version}}" ] }, - "websocket": {}, "shell": {}, "globalShortcut": {}, "deep-link": { diff --git a/src/assets/assets/fonts/icons/iconfont-app.js b/src/assets/assets/fonts/icons/iconfont-app.js deleted file mode 100644 index 6065362f..00000000 --- a/src/assets/assets/fonts/icons/iconfont-app.js +++ /dev/null @@ -1 +0,0 @@ -window._iconfont_svg_string_4878526='',(h=>{var l=(a=(a=document.getElementsByTagName("script"))[a.length-1]).getAttribute("data-injectcss"),a=a.getAttribute("data-disable-injectsvg");if(!a){var c,t,F,p,i,o=function(l,a){a.parentNode.insertBefore(l,a)};if(l&&!h.__iconfont__svg__cssinject__){h.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}c=function(){var l,a=document.createElement("div");a.innerHTML=h._iconfont_svg_string_4878526,(a=a.getElementsByTagName("svg")[0])&&(a.setAttribute("aria-hidden","true"),a.style.position="absolute",a.style.width=0,a.style.height=0,a.style.overflow="hidden",a=a,(l=document.body).firstChild?o(a,l.firstChild):l.appendChild(a))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(c,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),c()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(F=c,p=h.document,i=!1,d(),p.onreadystatechange=function(){"complete"==p.readyState&&(p.onreadystatechange=null,z())})}function z(){i||(i=!0,F())}function d(){try{p.documentElement.doScroll("left")}catch(l){return void setTimeout(d,50)}z()}})(window); \ No newline at end of file diff --git a/src/assets/assets/fonts/icons/iconfont.js b/src/assets/assets/fonts/icons/iconfont.js deleted file mode 100644 index 6065362f..00000000 --- a/src/assets/assets/fonts/icons/iconfont.js +++ /dev/null @@ -1 +0,0 @@ -window._iconfont_svg_string_4878526='',(h=>{var l=(a=(a=document.getElementsByTagName("script"))[a.length-1]).getAttribute("data-injectcss"),a=a.getAttribute("data-disable-injectsvg");if(!a){var c,t,F,p,i,o=function(l,a){a.parentNode.insertBefore(l,a)};if(l&&!h.__iconfont__svg__cssinject__){h.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}c=function(){var l,a=document.createElement("div");a.innerHTML=h._iconfont_svg_string_4878526,(a=a.getElementsByTagName("svg")[0])&&(a.setAttribute("aria-hidden","true"),a.style.position="absolute",a.style.width=0,a.style.height=0,a.style.overflow="hidden",a=a,(l=document.body).firstChild?o(a,l.firstChild):l.appendChild(a))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(c,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),c()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(F=c,p=h.document,i=!1,d(),p.onreadystatechange=function(){"complete"==p.readyState&&(p.onreadystatechange=null,z())})}function z(){i||(i=!0,F())}function d(){try{p.documentElement.doScroll("left")}catch(l){return void setTimeout(d,50)}z()}})(window); \ No newline at end of file diff --git a/src/commands/servers.ts b/src/commands/servers.ts index 7ed45c85..c82fd851 100644 --- a/src/commands/servers.ts +++ b/src/commands/servers.ts @@ -172,14 +172,6 @@ export function mcp_server_search({ return invokeWithErrorHandler(`mcp_server_search`, { id, queryParams }); } -export function connect_to_server(id: string, clientId: string): Promise { - return invokeWithErrorHandler(`connect_to_server`, { id, clientId }); -} - -export function disconnect(clientId: string): Promise { - return invokeWithErrorHandler(`disconnect`, { clientId }); -} - export function chat_history({ serverId, from = 0, @@ -260,25 +252,6 @@ export function cancel_session_chat({ }); } -export function new_chat({ - serverId, - websocketId, - message, - queryParams, -}: { - serverId: string; - websocketId: string; - message: string; - queryParams?: Record; -}): Promise { - return invokeWithErrorHandler(`new_chat`, { - serverId, - websocketId, - message, - queryParams, - }); -} - export function chat_create({ serverId, message, @@ -298,28 +271,6 @@ export function chat_create({ }); } -export function send_message({ - serverId, - websocketId, - sessionId, - message, - queryParams, -}: { - serverId: string; - websocketId: string; - sessionId: string; - message: string; - queryParams?: Record; -}): Promise { - return invokeWithErrorHandler(`send_message`, { - serverId, - websocketId, - sessionId, - message, - queryParams, - }); -} - export function chat_chat({ serverId, sessionId, diff --git a/src/components/Search/ConnectionError.tsx b/src/components/Search/ConnectionError.tsx deleted file mode 100644 index d3f34c29..00000000 --- a/src/components/Search/ConnectionError.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { useEffect, useState } from "react"; -import { useTranslation } from "react-i18next"; - -import VisibleKey from "@/components/Common/VisibleKey"; - -interface ConnectionErrorProps { - reconnect: () => void; - connected: boolean; -} - -export default function ConnectionError({ - reconnect, - connected, -}: ConnectionErrorProps) { - const { t } = useTranslation(); - - const [reconnectCountdown, setReconnectCountdown] = useState(0); - useEffect(() => { - if (!reconnectCountdown || connected) { - setReconnectCountdown(0); - return; - } - - if (reconnectCountdown > 0) { - const timer = setTimeout(() => { - setReconnectCountdown(reconnectCountdown - 1); - }, 1000); - return () => clearTimeout(timer); - } - }, [reconnectCountdown, connected]); - - return ( -
- {t("search.input.connectionError")} -
{ - reconnect(); - setReconnectCountdown(10); - }} - > - {reconnectCountdown > 0 ? ( - `${t("search.input.connecting")}(${reconnectCountdown}s)` - ) : ( - { - reconnect(); - setReconnectCountdown(10); - }} - > - {t("search.input.reconnect")} - - )} -
-
- ); -} diff --git a/src/components/Settings/AdvancedSettings.tsx b/src/components/Settings/AdvancedSettings.tsx deleted file mode 100644 index 2d333242..00000000 --- a/src/components/Settings/AdvancedSettings.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { useEffect } from "react"; -import { Globe } from "lucide-react"; -import { useTranslation } from "react-i18next"; - -import SettingsItem from "./SettingsItem"; -import { useAppStore } from "@/stores/appStore"; -import { AppEndpoint } from "@/types/index"; - -const ENDPOINTS = [ - { value: "https://coco.infini.cloud", label: "https://coco.infini.cloud" }, - { value: "http://localhost:9000", label: "http://localhost:9000" }, - { value: "http://infini.tpddns.cn:27200", label: "http://infini.tpddns.cn:27200" }, -]; - -export default function AdvancedSettings() { - const { t } = useTranslation(); - const endpoint = useAppStore(state => state.endpoint); - const setEndpoint = useAppStore(state => state.setEndpoint); - - useEffect(() => {}, [endpoint]); - - const onChangeEndpoint = async (newEndpoint: AppEndpoint) => { - await setEndpoint(newEndpoint); - }; - - return ( -
-
-

- {t('settings.advanced.title')} -

-
- -
- -
-
-
-
-
- ); -} diff --git a/src/components/Settings/SettingsSelect.tsx b/src/components/Settings/SettingsSelect.tsx deleted file mode 100644 index d128c5e4..00000000 --- a/src/components/Settings/SettingsSelect.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Select } from '@headlessui/react' - -interface SettingsSelectProps { - options: string[]; - value?: string; - onChange?: (value: string) => void; -} - -export default function SettingsSelect({ - options, - value, - onChange, -}: SettingsSelectProps) { - return ( - - ); -} diff --git a/src/hooks/useAutoResizeWindow.ts b/src/hooks/useAutoResizeWindow.ts deleted file mode 100644 index cad0f2c5..00000000 --- a/src/hooks/useAutoResizeWindow.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { useEffect } from "react"; -import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"; -import { LogicalSize } from "@tauri-apps/api/dpi"; - -// Custom hook to auto-resize window based on content height -const useAutoResizeWindow = () => { - // Function to resize the window to the content's size - const resizeWindowToContent = async () => { - const contentHeight = document.getElementById("main_window")?.scrollHeight || 0; - - try { - // Resize the window to fit content size - await getCurrentWebviewWindow()?.setSize( - new LogicalSize(680, contentHeight) - ); - - console.log("Window resized to content size"); - } catch (error) { - console.error("Error resizing window:", error); - } - - }; - - useEffect(() => { - // Initially resize the window - resizeWindowToContent(); - - // Set up a ResizeObserver to listen for changes in content size - const resizeObserver = new ResizeObserver(() => { - resizeWindowToContent(); - }); - - // Observe the document body for content size changes - resizeObserver.observe(document.body); - - // Clean up the observer when the component is unmounted - return () => { - resizeObserver.disconnect(); - }; - }, []); // Only run once when the component is mounted - - // Optionally, you can return values if you need to handle window size elsewhere -}; - -export default useAutoResizeWindow; diff --git a/src/hooks/useClickAway.ts b/src/hooks/useClickAway.ts deleted file mode 100644 index db36c78c..00000000 --- a/src/hooks/useClickAway.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { useEffect, RefObject } from 'react'; - -export function useClickAway( - ref: RefObject, - handler: (event: MouseEvent | TouchEvent) => void -) { - useEffect(() => { - const listener = (event: MouseEvent | TouchEvent) => { - if (!ref.current || ref.current.contains(event.target as Node)) { - return; - } - handler(event); - }; - - document.addEventListener('mousedown', listener); - document.addEventListener('touchstart', listener); - - return () => { - document.removeEventListener('mousedown', listener); - document.removeEventListener('touchstart', listener); - }; - }, [ref, handler]); -} \ No newline at end of file diff --git a/src/hooks/useFeatureControl.ts b/src/hooks/useFeatureControl.ts deleted file mode 100644 index 54b3df9f..00000000 --- a/src/hooks/useFeatureControl.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { useState, useEffect } from "react"; - -import { useConnectStore } from "@/stores/connectStore"; - -interface UseFeatureControlProps { - initialFeatures: string[]; - featureToToggle: string; - condition: (assistant: any) => boolean; -} - -export const useFeatureControl = ({ - initialFeatures, - featureToToggle, - condition, -}: UseFeatureControlProps) => { - const currentAssistant = useConnectStore((state) => state.currentAssistant); - const [features, setFeatures] = useState(initialFeatures); - - useEffect(() => { - if (condition(currentAssistant)) { - setFeatures((prev) => prev.filter((feature) => feature !== featureToToggle)); - } else { - setFeatures((prev) => { - if (!prev.includes(featureToToggle)) { - return [...prev, featureToToggle]; - } - return prev; - }); - } - }, [JSON.stringify(currentAssistant), featureToToggle]); - - return features; -}; \ No newline at end of file diff --git a/src/hooks/useInfiniteScroll.ts b/src/hooks/useInfiniteScroll.ts deleted file mode 100644 index 5582e50b..00000000 --- a/src/hooks/useInfiniteScroll.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { useEffect, useRef } from "react"; - -const useInfiniteScroll = (callback: () => void) => { - const loaderRef = useRef(null); - - useEffect(() => { - const observer = new IntersectionObserver( - (entries) => { - if (entries[0].isIntersecting) { - callback(); - } - }, - { threshold: 1.0 } - ); - - if (loaderRef.current) { - observer.observe(loaderRef.current); - } - - return () => { - if (loaderRef.current) { - observer.unobserve(loaderRef.current); - } - }; - }, [callback]); - - return loaderRef; -}; - -export default useInfiniteScroll; diff --git a/src/hooks/useWebSocket.ts b/src/hooks/useWebSocket.ts deleted file mode 100644 index f33c24a7..00000000 --- a/src/hooks/useWebSocket.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { useEffect, useCallback, useRef } from "react"; -import { useWebSocket as useWebSocketAHook } from "ahooks"; - -import { useAppStore } from "@/stores/appStore"; -import platformAdapter from "@/utils/platformAdapter"; -import { Server } from "@/types/server"; -import { useConnectStore } from "@/stores/connectStore"; - -enum ReadyState { - Connecting = 0, - Open = 1, - Closing = 2, - Closed = 3, -} - -interface WebSocketProps { - clientId: string; - connected: boolean; - setConnected: (connected: boolean) => void; - dealMsgRef: React.MutableRefObject<((msg: string) => void) | null>; - onWebsocketSessionId?: (sessionId: string) => void; -} - -export default function useWebSocket({ - clientId, - connected, - setConnected, - dealMsgRef, - onWebsocketSessionId, -}: WebSocketProps) { - const isTauri = useAppStore((state) => state.isTauri); - const endpoint_websocket = useAppStore((state) => state.endpoint_websocket); - const addError = useAppStore((state) => state.addError); - - const currentService = useConnectStore((state) => state.currentService); - - const websocketIdRef = useRef(""); - const messageQueue = useRef([]); - const processingRef = useRef(false); - - // web - const { readyState, connect, disconnect } = useWebSocketAHook( - //"wss://coco.infini.cloud/ws", - //"ws://localhost:9000/ws", - isTauri ? "" : endpoint_websocket, - { - manual: true, - reconnectLimit: 3, - reconnectInterval: 3000, - onMessage: (event) => { - const msg = event.data as string; - messageQueue.current.push(msg); - processQueue(); - }, - } - ); - useEffect(() => { - if (!isTauri) { - connect(); // web - } - }, [isTauri, connect]); - - const processMessage = useCallback( - (msg: string) => { - try { - if (msg.includes("websocket-session-id")) { - const sessionId = msg.split(":")[1].trim(); - websocketIdRef.current = sessionId; - setConnected(true); // web connected - console.log("setConnected:", sessionId); - onWebsocketSessionId?.(sessionId); - } else { - dealMsgRef.current?.(msg); - } - } catch (error) { - console.error("Error processing message:", error, msg); - } - }, - [onWebsocketSessionId] - ); - const processQueue = useCallback(() => { - if (processingRef.current || messageQueue.current.length === 0) return; - - processingRef.current = true; - while (messageQueue.current.length > 0) { - const msg = messageQueue.current.shift(); - if (msg) { - // console.log("Processing message:", msg.substring(0, 100)); - processMessage(msg); - } - } - processingRef.current = false; - }, [processMessage]); - useEffect(() => { - // web - if (readyState !== ReadyState.Open) { - setConnected(false); // state - } - }, [readyState]); - - // Tauri - // 1. WebSocket connects when loading or switching services - // src/components/Assistant/ChatHeader.tsx - // 2. If not connected or disconnected, input box has a connect button, clicking it will connect to WebSocket - // src/components/Search/InputBox.tsx - const reconnect = useCallback( - async (server?: Server) => { - setConnected(false); // Disconnect before attempting to reconnect - if (isTauri) { - const targetServer = server || currentService; - if (!targetServer?.id) return; - try { - // console.log("reconnect", targetServer.id); - await platformAdapter.commands("connect_to_server", targetServer.id, clientId); - } catch (error) { - setConnected(false); // error - console.error("Failed to connect:", error); - } - } else { - connect(); - } - }, - [currentService, clientId] - ); - const disconnectWS = useCallback(async () => { - if (!connected) return; - if (isTauri) { - try { - console.log("disconnect"); - await platformAdapter.commands("disconnect", clientId); - setConnected(false); // disconnected - } catch (error) { - console.error("Failed to disconnect:", error); - } - } else { - disconnect(); - } - }, [connected]); - - const updateDealMsg = useCallback( - (newDealMsg: (msg: string) => void) => { - dealMsgRef.current = newDealMsg; - }, - [dealMsgRef] - ); - - const unlistenErrorRef = useRef void> | null>(null); - const unlistenCancelRef = useRef void> | null>(null); - useEffect(() => { - if (!isTauri || !currentService?.id) return; - - const unlisten_message = platformAdapter.listenEvent(`ws-message-${clientId}`, (event) => { - const msg = event.payload as string; - // console.log(`ws-message-${clientId}`, msg); - if (msg.includes("websocket-session-id")) { - const sessionId = msg.split(":")[1].trim(); - websocketIdRef.current = sessionId; - console.log("setConnected sessionId:", sessionId); - setConnected(true); // Tauri connected - if (onWebsocketSessionId) { - onWebsocketSessionId(sessionId); - } - // Listen for errors - unlistenErrorRef.current = platformAdapter.listenEvent(`ws-error-${clientId}`, (event) => { - if (connected) { - const id = event.payload as string; - console.error(`ws-error-${clientId}`, id === currentService?.id, connected); - if (id === currentService?.id) { - addError("WebSocket connection failed."); - } - } - setConnected(false); // error - }); - // Listen for cancel - unlistenCancelRef.current = platformAdapter.listenEvent(`ws-cancel-${clientId}`, () => { - setConnected(false); - }); - - return; - } - dealMsgRef.current?.(msg); - }); - - return () => { - unlisten_message.then((fn) => fn()); - if (unlistenErrorRef.current) { - unlistenErrorRef.current.then((fn) => fn()); - } - if (unlistenCancelRef.current) { - unlistenCancelRef.current.then((fn) => fn()); - } - }; - }, [currentService?.id, dealMsgRef, connected, clientId]); - - return { reconnect, disconnectWS, updateDealMsg }; -} \ No newline at end of file diff --git a/src/icons/ArrowLeft.tsx b/src/icons/ArrowLeft.tsx deleted file mode 100644 index 31f6a2df..00000000 --- a/src/icons/ArrowLeft.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import SVGWrap from "./SVGWrap"; - -export default function ArrowLeft(props: I.SVG) { - return ( - - - - ); -} diff --git a/src/icons/Ask.tsx b/src/icons/Ask.tsx deleted file mode 100644 index 81989c39..00000000 --- a/src/icons/Ask.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import SVGWrap from "./SVGWrap"; - -export default function Ask(props: I.SVG) { - return ( - - - - ); -} diff --git a/src/icons/Link.tsx b/src/icons/Link.tsx deleted file mode 100644 index 77a10318..00000000 --- a/src/icons/Link.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import SVGWrap from "./SVGWrap"; - -export default function Link(props: I.SVG) { - return ( - - - - ); -} diff --git a/src/icons/Reload.tsx b/src/icons/Reload.tsx deleted file mode 100644 index ba353842..00000000 --- a/src/icons/Reload.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import SVGWrap from "./SVGWrap"; - -export default function Reload(props: I.SVG) { - return ( - - - - ); -} diff --git a/src/icons/Send.tsx b/src/icons/Send.tsx deleted file mode 100644 index 47f0a6cd..00000000 --- a/src/icons/Send.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import SVGWrap from "./SVGWrap"; - -export default function Send(props: I.SVG) { - return ( - - - - - - - ); -} diff --git a/src/icons/Setting.tsx b/src/icons/Setting.tsx deleted file mode 100644 index 91ca113a..00000000 --- a/src/icons/Setting.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import SVGWrap from "./SVGWrap"; - -export default function Setting(props: I.SVG) { - return ( - - - - - - - ); -} diff --git a/src/icons/ThemeAuto.tsx b/src/icons/ThemeAuto.tsx deleted file mode 100644 index 8ecb7603..00000000 --- a/src/icons/ThemeAuto.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import SVGWrap from "./SVGWrap"; - -export default function ThemeAuto(props: I.SVG) { - return ( - - - - ); -} diff --git a/src/icons/ThemeDark.tsx b/src/icons/ThemeDark.tsx deleted file mode 100644 index e63ca8cc..00000000 --- a/src/icons/ThemeDark.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import SVGWrap from "./SVGWrap"; - -export default function ThemeDark(props: I.SVG) { - return ( - - - - ); -} diff --git a/src/icons/ThemeLight.tsx b/src/icons/ThemeLight.tsx deleted file mode 100644 index 15b30b2f..00000000 --- a/src/icons/ThemeLight.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import SVGWrap from "./SVGWrap"; - -export default function ThemeLight(props: I.SVG) { - return ( - - - - ); -} diff --git a/src/icons/UnPin.tsx b/src/icons/UnPin.tsx deleted file mode 100644 index 6db96a5d..00000000 --- a/src/icons/UnPin.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import SVGWrap from "./SVGWrap"; - -export default function UnPin(props: I.SVG) { - return ( - - - - ); -} diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 6f710f4f..f5344dfe 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -42,11 +42,6 @@ "copyright": "©{{year}} INFINI Labs, All Rights Reserved." }, "advanced": { - "title": "Advanced Settings", - "endpoint": { - "title": "API Endpoint", - "description": "Domain name for interface and websocket" - }, "startup": { "title": "Startup Settings", "defaultStartupWindow": { diff --git a/src/locales/zh/translation.json b/src/locales/zh/translation.json index 333e8758..3c61b1cc 100644 --- a/src/locales/zh/translation.json +++ b/src/locales/zh/translation.json @@ -42,11 +42,6 @@ "copyright": "©{{year}} INFINI Labs,保留所有权利。" }, "advanced": { - "title": "高级设置", - "endpoint": { - "title": "API 接口", - "description": "接口和 WebSocket 的域名" - }, "startup": { "title": "启动设置", "defaultStartupWindow": { diff --git a/src/mock/index.ts b/src/mock/index.ts deleted file mode 100644 index 8942d3a8..00000000 --- a/src/mock/index.ts +++ /dev/null @@ -1,263 +0,0 @@ -// mock -export const res_search = { - took: 2590, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0 - }, - hits: { - total: { - value: 253, - relation: "eq" - }, - max_score: 32.709457, - hits: [ - { - _index: "coco_document", - _type: "_doc", - _id: "3ac857ef30d101b1e5880b53b1438b1a", - _score: 32.709457, - _source: { - icon: "web", - id: "3ac857ef30d101b1e5880b53b1438b1a", - source: { - name: "hugo_site", - type: "connector" - }, - type: "web_page", - category: "Aggregation", - subcategory: "Metric", - title: "Avg aggregation", - content: "", - author: "liaosy", - url: "https://pizza.rs/docs/references/aggregation/avg/", - tags: [ - "avg", - "aggregation" - ] - } - }, - { - _index: "coco_document", - _type: "_doc", - _id: "2485a744c5dae1278a01c04d39bf60a6", - _score: 32.37022, - _source: { - icon: "web", - id: "2485a744c5dae1278a01c04d39bf60a6", - source: { - name: "hugo_site", - type: "connector" - }, - type: "web_page", - title: "auto_generate_doc_id", - content: "", - author: "liaosy", - url: "https://infinilabs.cn/docs/latest/gateway/references/filters/auto_generate_doc_id/" - } - }, - { - _index: "coco_document", - _type: "_doc", - _id: "15aa340fa9ddfcfbf793b8707a4fa16b", - _score: 21.983166, - _source: { - icon: "web", - id: "15aa340fa9ddfcfbf793b8707a4fa16b", - source: { - name: "hugo_site", - type: "connector" - }, - type: "web_page", - category: "Overview", - subcategory: "Architecture", - title: "Architecture", - content: "", - author: "yangfan", - url: "https://pizza.rs/docs/overview/architecture/", - tags: [ - "architecture" - ] - } - }, - { - _index: "coco_document", - _type: "_doc", - _id: "f96b1af318d62a43a44f54731409ff52", - _score: 21.887964, - _source: { - icon: "web", - id: "f96b1af318d62a43a44f54731409ff52", - source: { - name: "hugo_site", - type: "connector" - }, - type: "web_page", - title: "Introducing Coco AI in Two Minutes - A Quick Start Video 🥥", - content: "", - author: "yangfan", - url: "https://blog.infinilabs.com/posts/2024/a-quick-start-viideo-to-introduce-coco-ai-in-two-minutes/", - tags: [ - "Coco AI", - "Search", - "Gen-AI", - "Enterprise" - ] - } - }, - { - _index: "coco_document", - _type: "_doc", - _id: "3a806937f9e7fe55905a7f71d111e523", - _score: 21.286049, - _source: { - icon: "web", - id: "3a806937f9e7fe55905a7f71d111e523", - source: { - name: "hugo_site", - type: "connector" - }, - type: "web_page", - category: "Catalog", - subcategory: "Namespace", - title: "Create a namespace", - content: "", - author: "yangfan", - url: "https://pizza.rs/docs/references/namespace/create/", - tags: [ - "create", - "namespace" - ] - } - }, - { - _index: "coco_document", - _type: "_doc", - _id: "04f48643c2c52b872c149e077765f8cb", - _score: 21.286049, - _source: { - icon: "web", - id: "04f48643c2c52b872c149e077765f8cb", - source: { - name: "hugo_site", - type: "connector" - }, - type: "web_page", - category: "Document", - subcategory: "Index", - title: "Create a document", - content: "", - author: "zouwenan", - url: "https://pizza.rs/docs/references/document/create/", - tags: [ - "create", - "index" - ] - } - }, - { - _index: "coco_document", - _type: "_doc", - _id: "d101818b1e6d2eb23ca2f813ef3a9648", - _score: 21.213268, - _source: { - icon: "web", - id: "d101818b1e6d2eb23ca2f813ef3a9648", - source: { - name: "hugo_site", - type: "connector" - }, - type: "web_page", - category: "Catalog", - subcategory: "Collection", - title: "Create a collection", - content: " ", - author: "zouwenan", - url: "https://pizza.rs/docs/references/collection/create/", - tags: [ - "create", - "collection" - ] - } - }, - { - _index: "coco_document", - _type: "_doc", - _id: "ee2228755039808c199e6812d09c745e", - _score: 20.967154, - _source: { - icon: "web", - id: "ee2228755039808c199e6812d09c745e", - source: { - name: "hugo_site", - type: "connector" - }, - type: "web_page", - category: "Document", - subcategory: "Index", - title: "Delete a document", - content: "", - author: "zouwenan", - url: "https://pizza.rs/docs/references/document/delete/", - tags: [ - "delete", - "index" - ] - } - }, - { - _index: "coco_document", - _type: "_doc", - _id: "60584d12aba0ff569e7b79a7be168810", - _score: 20.967154, - _source: { - icon: "web", - id: "60584d12aba0ff569e7b79a7be168810", - source: { - name: "hugo_site", - type: "connector" - }, - type: "web_page", - category: "Catalog", - subcategory: "Namespace", - title: "Delete a namespace", - content: "", - author: "zouwenan", - url: "https://pizza.rs/docs/references/namespace/delete/", - tags: [ - "delete", - "namespace" - ] - } - }, - { - _index: "coco_document", - _type: "_doc", - _id: "73e31d0feeb8d3d97e4b06a98de54672", - _score: 20.934437, - _source: { - icon: "web", - id: "73e31d0feeb8d3d97e4b06a98de54672", - source: { - name: "hugo_site", - type: "connector" - }, - type: "web_page", - category: "Document", - subcategory: "Index", - title: "Replace a document", - content: " ", - author: "medcl", - url: "https://pizza.rs/docs/references/document/replace/", - tags: [ - "replace", - "index" - ] - } - } - ] - } -} \ No newline at end of file diff --git a/src/stores/appStore.ts b/src/stores/appStore.ts index 48275cc7..866c3223 100644 --- a/src/stores/appStore.ts +++ b/src/stores/appStore.ts @@ -24,7 +24,6 @@ export type IAppStore = { endpoint: AppEndpoint; endpoint_http: string; - endpoint_websocket: string; setEndpoint: (endpoint: AppEndpoint) => void; language: string; setLanguage: (language: string) => void; @@ -78,20 +77,12 @@ export const useAppStore = create()( endpoint: "https://coco.infini.cloud/", endpoint_http: "https://coco.infini.cloud", - endpoint_websocket: "wss://coco.infini.cloud/ws", setEndpoint: async (endpoint: AppEndpoint) => { const endpoint_http = endpoint; - const withoutProtocol = endpoint.split("//")[1]; - - const endpoint_websocket = endpoint?.includes("https") - ? `wss://${withoutProtocol}/ws` - : `ws://${withoutProtocol}/ws`; - return set({ endpoint, endpoint_http, - endpoint_websocket, }); }, language: "en", @@ -128,7 +119,6 @@ export const useAppStore = create()( ssoRequestID: state.ssoRequestID, endpoint: state.endpoint, endpoint_http: state.endpoint_http, - endpoint_websocket: state.endpoint_websocket, language: state.language, }), } diff --git a/src/types/platform.ts b/src/types/platform.ts index 97f3fc07..12150590 100644 --- a/src/types/platform.ts +++ b/src/types/platform.ts @@ -13,7 +13,6 @@ export interface EventPayloads { "endpoint-changed": { endpoint: string; endpoint_http: string; - endpoint_websocket: string; }; "showTooltip-changed": { showTooltip: boolean; diff --git a/src/utils/env.ts b/src/utils/env.ts deleted file mode 100644 index f710a46b..00000000 --- a/src/utils/env.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const clientEnv = { - COCO_SERVER_URL: "https://coco.infini.cloud", // http://localhost:9000 - COCO_WEBSOCKET_URL: "wss://coco.infini.cloud/ws", // ws://localhost:9000/ws -}; diff --git a/src/utils/tauri.ts b/src/utils/tauri.ts deleted file mode 100644 index 2aa7f966..00000000 --- a/src/utils/tauri.ts +++ /dev/null @@ -1,531 +0,0 @@ -// This file was generated by [tauri-specta](https://github.com/oscartbeaumont/tauri-specta). Do not edit this file manually. - -/** user-defined commands **/ -export const commands = { - async getRecordingOptions(): Promise { - return await TAURI_INVOKE("get_recording_options"); - }, - async setRecordingOptions(options: RecordingOptions): Promise { - return await TAURI_INVOKE("set_recording_options", { options }); - }, - async startRecording(): Promise { - return await TAURI_INVOKE("start_recording"); - }, - async stopRecording(): Promise { - return await TAURI_INVOKE("stop_recording"); - }, - async pauseRecording(): Promise { - return await TAURI_INVOKE("pause_recording"); - }, - async resumeRecording(): Promise { - return await TAURI_INVOKE("resume_recording"); - }, - async listCameras(): Promise { - return await TAURI_INVOKE("list_cameras"); - }, - async listCaptureWindows(): Promise { - return await TAURI_INVOKE("list_capture_windows"); - }, - async listCaptureScreens(): Promise { - return await TAURI_INVOKE("list_capture_screens"); - }, - async takeScreenshot(): Promise { - return await TAURI_INVOKE("take_screenshot"); - }, - async listAudioDevices(): Promise { - return await TAURI_INVOKE("list_audio_devices"); - }, - async closePreviousRecordingsWindow(): Promise { - await TAURI_INVOKE("close_previous_recordings_window"); - }, - async setFakeWindowBounds(name: string, bounds: Bounds): Promise { - return await TAURI_INVOKE("set_fake_window_bounds", { name, bounds }); - }, - async removeFakeWindow(name: string): Promise { - return await TAURI_INVOKE("remove_fake_window", { name }); - }, - async focusCapturesPanel(): Promise { - await TAURI_INVOKE("focus_captures_panel"); - }, - async getCurrentRecording(): Promise> { - return await TAURI_INVOKE("get_current_recording"); - }, - async exportVideo( - videoId: string, - project: ProjectConfiguration, - progress: TAURI_CHANNEL, - force: boolean - ): Promise { - return await TAURI_INVOKE("export_video", { - videoId, - project, - progress, - force, - }); - }, - async copyFileToPath(src: string, dst: string): Promise { - return await TAURI_INVOKE("copy_file_to_path", { src, dst }); - }, - async copyVideoToClipboard(path: string): Promise { - return await TAURI_INVOKE("copy_video_to_clipboard", { path }); - }, - async copyScreenshotToClipboard(path: string): Promise { - return await TAURI_INVOKE("copy_screenshot_to_clipboard", { path }); - }, - async openFilePath(path: string): Promise { - return await TAURI_INVOKE("open_file_path", { path }); - }, - async getVideoMetadata( - videoId: string, - videoType: VideoType | null - ): Promise { - return await TAURI_INVOKE("get_video_metadata", { videoId, videoType }); - }, - async createEditorInstance( - videoId: string - ): Promise { - return await TAURI_INVOKE("create_editor_instance", { videoId }); - }, - async startPlayback(videoId: string): Promise { - await TAURI_INVOKE("start_playback", { videoId }); - }, - async stopPlayback(videoId: string): Promise { - await TAURI_INVOKE("stop_playback", { videoId }); - }, - async setPlayheadPosition( - videoId: string, - frameNumber: number - ): Promise { - await TAURI_INVOKE("set_playhead_position", { videoId, frameNumber }); - }, - async setProjectConfig( - videoId: string, - config: ProjectConfiguration - ): Promise { - await TAURI_INVOKE("set_project_config", { videoId, config }); - }, - async openEditor(id: string): Promise { - await TAURI_INVOKE("open_editor", { id }); - }, - async openMainWindow(): Promise { - await TAURI_INVOKE("open_main_window"); - }, - async openPermissionSettings(permission: OSPermission): Promise { - await TAURI_INVOKE("open_permission_settings", { permission }); - }, - async doPermissionsCheck(initialCheck: boolean): Promise { - return await TAURI_INVOKE("do_permissions_check", { initialCheck }); - }, - async requestPermission(permission: OSPermission): Promise { - await TAURI_INVOKE("request_permission", { permission }); - }, - async uploadExportedVideo( - videoId: string, - mode: UploadMode - ): Promise { - return await TAURI_INVOKE("upload_exported_video", { videoId, mode }); - }, - async uploadScreenshot(screenshotPath: string): Promise { - return await TAURI_INVOKE("upload_screenshot", { screenshotPath }); - }, - async getRecordingMeta(id: string, fileType: string): Promise { - return await TAURI_INVOKE("get_recording_meta", { id, fileType }); - }, - async saveFileDialog( - fileName: string, - fileType: string - ): Promise { - return await TAURI_INVOKE("save_file_dialog", { fileName, fileType }); - }, - async listRecordings(): Promise<[string, string, RecordingMeta][]> { - return await TAURI_INVOKE("list_recordings"); - }, - async listScreenshots(): Promise<[string, string, RecordingMeta][]> { - return await TAURI_INVOKE("list_screenshots"); - }, - async checkUpgradedAndUpdate(): Promise { - return await TAURI_INVOKE("check_upgraded_and_update"); - }, - async openExternalLink(url: string): Promise { - return await TAURI_INVOKE("open_external_link", { url }); - }, - async setHotkey(action: HotkeyAction, hotkey: Hotkey | null): Promise { - return await TAURI_INVOKE("set_hotkey", { action, hotkey }); - }, - async deleteAuthOpenSignin(): Promise { - return await TAURI_INVOKE("delete_auth_open_signin"); - }, - async resetCameraPermissions(): Promise { - return await TAURI_INVOKE("reset_camera_permissions"); - }, - async resetMicrophonePermissions(): Promise { - return await TAURI_INVOKE("reset_microphone_permissions"); - }, - async isCameraWindowOpen(): Promise { - return await TAURI_INVOKE("is_camera_window_open"); - }, - async seekTo(videoId: string, frameNumber: number): Promise { - await TAURI_INVOKE("seek_to", { videoId, frameNumber }); - }, - async sendFeedbackRequest(feedback: string): Promise { - return await TAURI_INVOKE("send_feedback_request", { feedback }); - }, - async positionTrafficLights( - controlsInset: [number, number] | null - ): Promise { - await TAURI_INVOKE("position_traffic_lights", { controlsInset }); - }, - async setTheme(theme: AppTheme): Promise { - await TAURI_INVOKE("set_theme", { theme }); - }, - async globalMessageDialog(message: string): Promise { - await TAURI_INVOKE("global_message_dialog", { message }); - }, - async showWindow(window: ShowCapWindow): Promise { - await TAURI_INVOKE("show_window", { window }); - }, - async writeClipboardString(text: string): Promise { - return await TAURI_INVOKE("write_clipboard_string", { text }); - }, -}; - -/** user-defined events **/ - -export const events = __makeEvents__<{ - audioInputLevelChange: AudioInputLevelChange; - authenticationInvalid: AuthenticationInvalid; - currentRecordingChanged: CurrentRecordingChanged; - editorStateChanged: EditorStateChanged; - newNotification: NewNotification; - newRecordingAdded: NewRecordingAdded; - newScreenshotAdded: NewScreenshotAdded; - recordingMetaChanged: RecordingMetaChanged; - recordingOptionsChanged: RecordingOptionsChanged; - recordingStarted: RecordingStarted; - recordingStopped: RecordingStopped; - renderFrameEvent: RenderFrameEvent; - requestNewScreenshot: RequestNewScreenshot; - requestOpenSettings: RequestOpenSettings; - requestRestartRecording: RequestRestartRecording; - requestStartRecording: RequestStartRecording; - requestStopRecording: RequestStopRecording; - uploadProgress: UploadProgress; -}>({ - audioInputLevelChange: "audio-input-level-change", - authenticationInvalid: "authentication-invalid", - currentRecordingChanged: "current-recording-changed", - editorStateChanged: "editor-state-changed", - newNotification: "new-notification", - newRecordingAdded: "new-recording-added", - newScreenshotAdded: "new-screenshot-added", - recordingMetaChanged: "recording-meta-changed", - recordingOptionsChanged: "recording-options-changed", - recordingStarted: "recording-started", - recordingStopped: "recording-stopped", - renderFrameEvent: "render-frame-event", - requestNewScreenshot: "request-new-screenshot", - requestOpenSettings: "request-open-settings", - requestRestartRecording: "request-restart-recording", - requestStartRecording: "request-start-recording", - requestStopRecording: "request-stop-recording", - uploadProgress: "upload-progress", -}); - -/** user-defined constants **/ - -/** user-defined types **/ -export type AppEndpoint = string; -export type AppTheme = "auto" | "light" | "dark"; -export type WindowTheme = "light" | "dark"; -export type AspectRatio = "wide" | "vertical" | "square" | "classic" | "tall"; -export type Audio = { duration: number; sample_rate: number; channels: number }; -export type AudioConfiguration = { mute: boolean; improve: boolean }; -export type AudioInputLevelChange = number; -export type AudioMeta = { path: string }; -export type AuthStore = { - token: string; - user_id: string | null; - expires: number; - plan: Plan | null; -}; -export type AuthenticationInvalid = null; -export type BackgroundConfiguration = { - source: BackgroundSource; - blur: number; - padding: number; - rounding: number; - inset: number; - crop: Crop | null; -}; -export type BackgroundSource = - | { type: "wallpaper"; id: number } - | { type: "image"; path: string | null } - | { type: "color"; value: [number, number, number] } - | { - type: "gradient"; - from: [number, number, number]; - to: [number, number, number]; - angle?: number; - }; -export type Bounds = { x: number; y: number; width: number; height: number }; -export type Camera = { - hide: boolean; - mirror: boolean; - position: CameraPosition; - size: number; - zoom_size: number | null; - rounding: number; - shadow: number; -}; -export type CameraMeta = { path: string }; -export type CameraPosition = { x: CameraXPosition; y: CameraYPosition }; -export type CameraXPosition = "left" | "center" | "right"; -export type CameraYPosition = "top" | "bottom"; -export type CaptureScreen = { id: number; name: string }; -export type CaptureWindow = { - id: number; - owner_name: string; - name: string; - bounds: Bounds; -}; -export type Crop = { position: XY; size: XY }; -export type CurrentRecordingChanged = null; -export type CursorAnimationStyle = "regular" | "slow" | "fast"; -export type CursorConfiguration = { - hideWhenIdle: boolean; - size: number; - type: CursorType; - animationStyle: CursorAnimationStyle; -}; -export type CursorType = "pointer" | "circle"; -export type Display = { path: string }; -export type EditorStateChanged = { playhead_position: number }; -export type Flags = { - recordMouse: boolean; - split: boolean; - pauseResume: boolean; - zoom: boolean; - customS3: boolean; -}; -export type GeneralSettingsStore = { - uploadIndividualFiles?: boolean; - openEditorAfterRecording?: boolean; - hideDockIcon?: boolean; - autoCreateShareableLink?: boolean; - enableNotifications?: boolean; - disableAutoOpenLinks?: boolean; - hasCompletedStartup?: boolean; - theme?: AppTheme; -}; -export type Hotkey = { - code: string; - meta: boolean; - ctrl: boolean; - alt: boolean; - shift: boolean; -}; -export type HotkeyAction = - | "startRecording" - | "stopRecording" - | "restartRecording" - | "takeScreenshot"; -export type HotkeysConfiguration = { show: boolean }; -export type HotkeysStore = { hotkeys: { [key in HotkeyAction]: Hotkey } }; -export type JsonValue = [T]; -export type MultipleSegment = { - display: Display; - camera?: CameraMeta | null; - audio?: AudioMeta | null; - cursor?: string | null; -}; -export type MultipleSegments = { - segments: MultipleSegment[]; - cursors: { [key in string]: string }; -}; -export type NewNotification = { - title: string; - body: string; - is_error: boolean; -}; -export type NewRecordingAdded = { path: string }; -export type NewScreenshotAdded = { path: string }; -export type OSPermission = - | "screenRecording" - | "camera" - | "microphone" - | "accessibility"; -export type OSPermissionStatus = "notNeeded" | "empty" | "granted" | "denied"; -export type OSPermissionsCheck = { - screenRecording: OSPermissionStatus; - microphone: OSPermissionStatus; - camera: OSPermissionStatus; - accessibility: OSPermissionStatus; -}; -export type Plan = { upgraded: boolean; last_checked: number }; -export type PreCreatedVideo = { - id: string; - link: string; - config: S3UploadMeta; -}; -export type ProjectConfiguration = { - aspectRatio: AspectRatio | null; - background: BackgroundConfiguration; - camera: Camera; - audio: AudioConfiguration; - cursor: CursorConfiguration; - hotkeys: HotkeysConfiguration; - timeline?: TimelineConfiguration | null; - motionBlur: number | null; -}; -export type ProjectRecordings = { segments: SegmentRecordings[] }; -export type RecordingInfo = { captureTarget: ScreenCaptureTarget }; -export type RecordingMeta = ( - | { segment: SingleSegment } - | { inner: MultipleSegments } -) & { pretty_name: string; sharing?: SharingMeta | null }; -export type RecordingMetaChanged = { id: string }; -export type RecordingOptions = { - captureTarget: ScreenCaptureTarget; - cameraLabel: string | null; - audioInputName: string | null; -}; -export type RecordingOptionsChanged = null; -export type RecordingStarted = null; -export type RecordingStopped = { path: string }; -export type RenderFrameEvent = { frame_number: number }; -export type RenderProgress = - | { type: "Starting"; total_frames: number } - | { type: "EstimatedTotalFrames"; total_frames: number } - | { type: "FrameRendered"; current_frame: number }; -export type RequestNewScreenshot = null; -export type RequestOpenSettings = { page: string }; -export type RequestRestartRecording = null; -export type RequestStartRecording = null; -export type RequestStopRecording = null; -export type S3UploadMeta = { - id: string; - user_id: string; - aws_region?: string; - aws_bucket?: string; -}; -export type ScreenCaptureTarget = - | ({ variant: "window" } & CaptureWindow) - | ({ variant: "screen" } & CaptureScreen); -export type SegmentRecordings = { - display: Video; - camera: Video | null; - audio: Audio | null; -}; -export type SerializedEditorInstance = { - framesSocketUrl: string; - recordingDuration: number; - savedProjectConfig: ProjectConfiguration; - recordings: ProjectRecordings; - path: string; - prettyName: string; -}; -export type SharingMeta = { id: string; link: string }; -export type ShowCapWindow = - | "Setup" - | "Main" - | { Settings: { page: string | null } } - | { Editor: { project_id: string } } - | "PrevRecordings" - | "WindowCaptureOccluder" - | { Camera: { ws_port: number } } - | { InProgressRecording: { position: [number, number] | null } } - | "Upgrade"; -export type SingleSegment = { - display: Display; - camera?: CameraMeta | null; - audio?: AudioMeta | null; - cursor?: string | null; -}; -export type TimelineConfiguration = { - segments: TimelineSegment[]; - zoomSegments?: ZoomSegment[]; -}; -export type TimelineSegment = { - recordingSegment: number | null; - timescale: number; - start: number; - end: number; -}; -export type UploadMode = - | { Initial: { pre_created_video: PreCreatedVideo | null } } - | "Reupload"; -export type UploadProgress = { - stage: string; - progress: number; - message: string; -}; -export type UploadResult = - | { Success: string } - | "NotAuthenticated" - | "PlanCheckFailed" - | "UpgradeRequired"; -export type Video = { duration: number; width: number; height: number }; -export type VideoRecordingMetadata = { duration: number; size: number }; -export type VideoType = "screen" | "output"; -export type XY = { x: T; y: T }; -export type ZoomSegment = { start: number; end: number; amount: number }; - -/** tauri-specta globals **/ - -import { - invoke as TAURI_INVOKE, - Channel as TAURI_CHANNEL, -} from "@tauri-apps/api/core"; -import * as TAURI_API_EVENT from "@tauri-apps/api/event"; -import { type WebviewWindow as __WebviewWindow__ } from "@tauri-apps/api/webviewWindow"; - -type __EventObj__ = { - listen: ( - cb: TAURI_API_EVENT.EventCallback - ) => ReturnType>; - once: ( - cb: TAURI_API_EVENT.EventCallback - ) => ReturnType>; - emit: null extends T - ? (payload?: T) => ReturnType - : (payload: T) => ReturnType; -}; - -export type Result = - | { status: "ok"; data: T } - | { status: "error"; error: E }; - -function __makeEvents__>( - mappings: Record -) { - return new Proxy( - {} as unknown as { - [K in keyof T]: __EventObj__ & { - (handle: __WebviewWindow__): __EventObj__; - }; - }, - { - get: (_, event) => { - const name = mappings[event as keyof T]; - - return new Proxy((() => {}) as any, { - apply: (_, __, [window]: [__WebviewWindow__]) => ({ - listen: (arg: any) => window.listen(name, arg), - once: (arg: any) => window.once(name, arg), - emit: (arg: any) => window.emit(name, arg), - }), - get: (_, command: keyof __EventObj__) => { - switch (command) { - case "listen": - return (arg: any) => TAURI_API_EVENT.listen(name, arg); - case "once": - return (arg: any) => TAURI_API_EVENT.once(name, arg); - case "emit": - return (arg: any) => TAURI_API_EVENT.emit(name, arg); - } - }, - }); - }, - } - ); -} diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index c075c48c..7537e061 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -2,7 +2,6 @@ interface ImportMetaEnv { readonly COCO_SERVER_URL: string; - readonly COCO_WEBSOCKET_URL: string; // more env variables... }