mirror of
https://github.com/infinilabs/coco-app.git
synced 2025-12-16 11:37:47 +01:00
chore: delete unused code files and dependencies (#841)
Mainly delete unused webSocket content, and delete other unused code files and dependencies
This commit is contained in:
2
.env
2
.env
@@ -1,5 +1,3 @@
|
|||||||
COCO_SERVER_URL=http://localhost:9000 #https://coco.infini.cloud #http://localhost:9000
|
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
|
#TAURI_DEV_HOST=0.0.0.0
|
||||||
@@ -24,6 +24,7 @@ Information about release notes of Coco App is provided here.
|
|||||||
|
|
||||||
- refactor: split query_coco_fusion() #836
|
- refactor: split query_coco_fusion() #836
|
||||||
- chore: web component loading font icon #838
|
- chore: web component loading font icon #838
|
||||||
|
- chore: delete unused code files and dependencies #841
|
||||||
|
|
||||||
## 0.7.1 (2025-07-27)
|
## 0.7.1 (2025-07-27)
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
"@tauri-apps/plugin-process": "^2.2.1",
|
"@tauri-apps/plugin-process": "^2.2.1",
|
||||||
"@tauri-apps/plugin-shell": "^2.2.1",
|
"@tauri-apps/plugin-shell": "^2.2.1",
|
||||||
"@tauri-apps/plugin-updater": "github:infinilabs/tauri-plugin-updater#v2",
|
"@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",
|
"@tauri-apps/plugin-window": "2.0.0-alpha.1",
|
||||||
"@wavesurfer/react": "^1.0.11",
|
"@wavesurfer/react": "^1.0.11",
|
||||||
"ahooks": "^3.8.4",
|
"ahooks": "^3.8.4",
|
||||||
|
|||||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@@ -47,9 +47,6 @@ importers:
|
|||||||
'@tauri-apps/plugin-updater':
|
'@tauri-apps/plugin-updater':
|
||||||
specifier: github:infinilabs/tauri-plugin-updater#v2
|
specifier: github:infinilabs/tauri-plugin-updater#v2
|
||||||
version: https://codeload.github.com/infinilabs/tauri-plugin-updater/tar.gz/358e689c65e9943b53eff50bcb9dfd5b1cfc4072
|
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':
|
'@tauri-apps/plugin-window':
|
||||||
specifier: 2.0.0-alpha.1
|
specifier: 2.0.0-alpha.1
|
||||||
version: 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}
|
resolution: {tarball: https://codeload.github.com/infinilabs/tauri-plugin-updater/tar.gz/358e689c65e9943b53eff50bcb9dfd5b1cfc4072}
|
||||||
version: 2.7.1
|
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':
|
'@tauri-apps/plugin-window@2.0.0-alpha.1':
|
||||||
resolution: {integrity: sha512-dFOAgal/3Txz3SQ+LNQq0AK1EPC+acdaFlwPVB/6KXUZYmaFleIlzgxDVoJCQ+/xOhxvYrdQaFLefh0I/Kldbg==}
|
resolution: {integrity: sha512-dFOAgal/3Txz3SQ+LNQq0AK1EPC+acdaFlwPVB/6KXUZYmaFleIlzgxDVoJCQ+/xOhxvYrdQaFLefh0I/Kldbg==}
|
||||||
|
|
||||||
@@ -4643,10 +4637,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@tauri-apps/api': 2.5.0
|
'@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':
|
'@tauri-apps/plugin-window@2.0.0-alpha.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tauri-apps/api': 2.0.0-alpha.6
|
'@tauri-apps/api': 2.0.0-alpha.6
|
||||||
|
|||||||
57
src-tauri/Cargo.lock
generated
57
src-tauri/Cargo.lock
generated
@@ -900,13 +900,12 @@ dependencies = [
|
|||||||
"tauri-plugin-single-instance",
|
"tauri-plugin-single-instance",
|
||||||
"tauri-plugin-store",
|
"tauri-plugin-store",
|
||||||
"tauri-plugin-updater",
|
"tauri-plugin-updater",
|
||||||
"tauri-plugin-websocket",
|
|
||||||
"tauri-plugin-windows-version",
|
"tauri-plugin-windows-version",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-native-tls",
|
"tokio-native-tls",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tokio-tungstenite 0.20.1",
|
"tokio-tungstenite",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tungstenite 0.24.0",
|
"tungstenite 0.24.0",
|
||||||
"url",
|
"url",
|
||||||
@@ -6556,25 +6555,6 @@ dependencies = [
|
|||||||
"zip 2.6.1",
|
"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]]
|
[[package]]
|
||||||
name = "tauri-plugin-windows-version"
|
name = "tauri-plugin-windows-version"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@@ -6909,22 +6889,6 @@ dependencies = [
|
|||||||
"tungstenite 0.20.1",
|
"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]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.15"
|
version = "0.7.15"
|
||||||
@@ -7131,25 +7095,6 @@ dependencies = [
|
|||||||
"utf-8",
|
"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]]
|
[[package]]
|
||||||
name = "typeid"
|
name = "typeid"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ serde = { version = "1", features = ["derive"] }
|
|||||||
# see: https://docs.rs/serde_json/latest/serde_json/struct.Number.html#method.from_u128
|
# see: https://docs.rs/serde_json/latest/serde_json/struct.Number.html#method.from_u128
|
||||||
serde_json = { version = "1", features = ["arbitrary_precision", "preserve_order"] }
|
serde_json = { version = "1", features = ["arbitrary_precision", "preserve_order"] }
|
||||||
tauri-plugin-http = "2"
|
tauri-plugin-http = "2"
|
||||||
tauri-plugin-websocket = "2"
|
|
||||||
tauri-plugin-deep-link = "2.0.0"
|
tauri-plugin-deep-link = "2.0.0"
|
||||||
tauri-plugin-store = "2.2.0"
|
tauri-plugin-store = "2.2.0"
|
||||||
tauri-plugin-os = "2"
|
tauri-plugin-os = "2"
|
||||||
|
|||||||
@@ -37,9 +37,6 @@
|
|||||||
"http:allow-fetch-cancel",
|
"http:allow-fetch-cancel",
|
||||||
"http:allow-fetch-read-body",
|
"http:allow-fetch-read-body",
|
||||||
"http:allow-fetch-send",
|
"http:allow-fetch-send",
|
||||||
"websocket:default",
|
|
||||||
"websocket:allow-connect",
|
|
||||||
"websocket:allow-send",
|
|
||||||
"autostart:allow-enable",
|
"autostart:allow-enable",
|
||||||
"autostart:allow-disable",
|
"autostart:allow-disable",
|
||||||
"autostart:allow-is-enabled",
|
"autostart:allow-is-enabled",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::common::assistant::ChatRequestMessage;
|
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::common::register::SearchSourceRegistry;
|
||||||
use crate::server::http_client::HttpClient;
|
use crate::server::http_client::HttpClient;
|
||||||
use crate::{common, server::servers::COCO_SERVERS};
|
use crate::{common, server::servers::COCO_SERVERS};
|
||||||
@@ -111,54 +111,6 @@ pub async fn cancel_session_chat<R: Runtime>(
|
|||||||
common::http::get_response_body_text(response).await
|
common::http::get_response_body_text(response).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn new_chat<R: Runtime>(
|
|
||||||
_app_handle: AppHandle<R>,
|
|
||||||
server_id: String,
|
|
||||||
websocket_id: String,
|
|
||||||
message: String,
|
|
||||||
query_params: Option<HashMap<String, Value>>,
|
|
||||||
) -> Result<GetResponse, String> {
|
|
||||||
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]
|
#[tauri::command]
|
||||||
pub async fn chat_create<R: Runtime>(
|
pub async fn chat_create<R: Runtime>(
|
||||||
app_handle: AppHandle<R>,
|
app_handle: AppHandle<R>,
|
||||||
@@ -222,37 +174,6 @@ pub async fn chat_create<R: Runtime>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn send_message<R: Runtime>(
|
|
||||||
_app_handle: AppHandle<R>,
|
|
||||||
server_id: String,
|
|
||||||
websocket_id: String,
|
|
||||||
session_id: String,
|
|
||||||
message: String,
|
|
||||||
query_params: Option<HashMap<String, Value>>, //search,deep_thinking
|
|
||||||
) -> Result<String, String> {
|
|
||||||
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]
|
#[tauri::command]
|
||||||
pub async fn chat_chat<R: Runtime>(
|
pub async fn chat_chat<R: Runtime>(
|
||||||
app_handle: AppHandle<R>,
|
app_handle: AppHandle<R>,
|
||||||
|
|||||||
@@ -130,9 +130,7 @@ pub fn run() {
|
|||||||
server::connector::get_connectors_by_server,
|
server::connector::get_connectors_by_server,
|
||||||
search::query_coco_fusion,
|
search::query_coco_fusion,
|
||||||
assistant::chat_history,
|
assistant::chat_history,
|
||||||
assistant::new_chat,
|
|
||||||
assistant::chat_create,
|
assistant::chat_create,
|
||||||
assistant::send_message,
|
|
||||||
assistant::chat_chat,
|
assistant::chat_chat,
|
||||||
assistant::session_chat_history,
|
assistant::session_chat_history,
|
||||||
assistant::open_session_chat,
|
assistant::open_session_chat,
|
||||||
@@ -145,8 +143,6 @@ pub fn run() {
|
|||||||
assistant::assistant_get_multi,
|
assistant::assistant_get_multi,
|
||||||
// server::get_coco_server_datasources,
|
// server::get_coco_server_datasources,
|
||||||
// server::get_coco_server_connectors,
|
// server::get_coco_server_connectors,
|
||||||
server::websocket::connect_to_server,
|
|
||||||
server::websocket::disconnect,
|
|
||||||
get_app_search_source,
|
get_app_search_source,
|
||||||
server::attachment::upload_attachment,
|
server::attachment::upload_attachment,
|
||||||
server::attachment::get_attachment,
|
server::attachment::get_attachment,
|
||||||
@@ -201,7 +197,6 @@ pub fn run() {
|
|||||||
let registry = SearchSourceRegistry::default();
|
let registry = SearchSourceRegistry::default();
|
||||||
|
|
||||||
app.manage(registry); // Store registry in Tauri's app state
|
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
|
// This has to be called before initializing extensions as doing that
|
||||||
// requires access to the shortcut store, which will be set by this
|
// requires access to the shortcut store, which will be set by this
|
||||||
|
|||||||
@@ -11,4 +11,3 @@ pub mod servers;
|
|||||||
pub mod synthesize;
|
pub mod synthesize;
|
||||||
pub mod system_settings;
|
pub mod system_settings;
|
||||||
pub mod transcription;
|
pub mod transcription;
|
||||||
pub mod websocket;
|
|
||||||
|
|||||||
@@ -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<Mutex<HashMap<String, Arc<WebSocketInstance>>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WebSocketInstance {
|
|
||||||
ws_connection: Mutex<WebSocketStream<MaybeTlsStream<TcpStream>>>, // No need to lock the entire map
|
|
||||||
cancel_tx: mpsc::Sender<()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_to_websocket(endpoint: &str) -> Result<String, String> {
|
|
||||||
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<R: Runtime>(
|
|
||||||
tauri_app_handle: AppHandle<R>,
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
@@ -126,7 +126,6 @@
|
|||||||
"https://release.infinilabs.com/coco/app/.latest.json?target={{target}}&arch={{arch}}¤t_version={{current_version}}"
|
"https://release.infinilabs.com/coco/app/.latest.json?target={{target}}&arch={{arch}}¤t_version={{current_version}}"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"websocket": {},
|
|
||||||
"shell": {},
|
"shell": {},
|
||||||
"globalShortcut": {},
|
"globalShortcut": {},
|
||||||
"deep-link": {
|
"deep-link": {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -172,14 +172,6 @@ export function mcp_server_search({
|
|||||||
return invokeWithErrorHandler(`mcp_server_search`, { id, queryParams });
|
return invokeWithErrorHandler(`mcp_server_search`, { id, queryParams });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function connect_to_server(id: string, clientId: string): Promise<void> {
|
|
||||||
return invokeWithErrorHandler(`connect_to_server`, { id, clientId });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function disconnect(clientId: string): Promise<void> {
|
|
||||||
return invokeWithErrorHandler(`disconnect`, { clientId });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function chat_history({
|
export function chat_history({
|
||||||
serverId,
|
serverId,
|
||||||
from = 0,
|
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<string, any>;
|
|
||||||
}): Promise<GetResponse> {
|
|
||||||
return invokeWithErrorHandler(`new_chat`, {
|
|
||||||
serverId,
|
|
||||||
websocketId,
|
|
||||||
message,
|
|
||||||
queryParams,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function chat_create({
|
export function chat_create({
|
||||||
serverId,
|
serverId,
|
||||||
message,
|
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<string, any>;
|
|
||||||
}): Promise<string> {
|
|
||||||
return invokeWithErrorHandler(`send_message`, {
|
|
||||||
serverId,
|
|
||||||
websocketId,
|
|
||||||
sessionId,
|
|
||||||
message,
|
|
||||||
queryParams,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function chat_chat({
|
export function chat_chat({
|
||||||
serverId,
|
serverId,
|
||||||
sessionId,
|
sessionId,
|
||||||
|
|||||||
@@ -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<number>(0);
|
|
||||||
useEffect(() => {
|
|
||||||
if (!reconnectCountdown || connected) {
|
|
||||||
setReconnectCountdown(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reconnectCountdown > 0) {
|
|
||||||
const timer = setTimeout(() => {
|
|
||||||
setReconnectCountdown(reconnectCountdown - 1);
|
|
||||||
}, 1000);
|
|
||||||
return () => clearTimeout(timer);
|
|
||||||
}
|
|
||||||
}, [reconnectCountdown, connected]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="absolute top-0 right-0 bottom-0 left-0 px-2 py-4 bg-[rgba(238,238,238,0.98)] dark:bg-[rgba(32,33,38,0.9)] backdrop-blur-[2px] rounded-md font-normal text-xs text-gray-400 flex items-center gap-4 z-10">
|
|
||||||
{t("search.input.connectionError")}
|
|
||||||
<div
|
|
||||||
className="px-1 h-[24px] text-[#0061FF] font-normal text-xs flex items-center justify-center cursor-pointer underline"
|
|
||||||
onClick={() => {
|
|
||||||
reconnect();
|
|
||||||
setReconnectCountdown(10);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{reconnectCountdown > 0 ? (
|
|
||||||
`${t("search.input.connecting")}(${reconnectCountdown}s)`
|
|
||||||
) : (
|
|
||||||
<VisibleKey
|
|
||||||
shortcut="R"
|
|
||||||
onKeyPress={() => {
|
|
||||||
reconnect();
|
|
||||||
setReconnectCountdown(10);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t("search.input.reconnect")}
|
|
||||||
</VisibleKey>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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 (
|
|
||||||
<div className="space-y-8">
|
|
||||||
<div>
|
|
||||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
|
||||||
{t('settings.advanced.title')}
|
|
||||||
</h2>
|
|
||||||
<div className="space-y-6">
|
|
||||||
<SettingsItem
|
|
||||||
icon={Globe}
|
|
||||||
title={t('settings.advanced.endpoint.title')}
|
|
||||||
description={t('settings.advanced.endpoint.description')}
|
|
||||||
>
|
|
||||||
<div className={`p-4 rounded-lg`}>
|
|
||||||
<select
|
|
||||||
value={endpoint}
|
|
||||||
onChange={(e) => onChangeEndpoint(e.target.value as AppEndpoint)}
|
|
||||||
className={`w-full px-3 py-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white border-gray-300 text-gray-900 dark:bg-gray-800 dark:border-gray-600 dark:text-white`}
|
|
||||||
>
|
|
||||||
{ENDPOINTS.map(({ value, label }) => (
|
|
||||||
<option key={value} value={value}>
|
|
||||||
{label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</SettingsItem>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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 (
|
|
||||||
<Select
|
|
||||||
value={value}
|
|
||||||
onChange={(e) => onChange?.(e.target.value)}
|
|
||||||
className="rounded-md border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:border-blue-500 focus:ring-blue-500"
|
|
||||||
>
|
|
||||||
{options.map((option) => (
|
|
||||||
<option key={option} value={option.toLowerCase()}>
|
|
||||||
{option}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { useEffect, RefObject } from 'react';
|
|
||||||
|
|
||||||
export function useClickAway(
|
|
||||||
ref: RefObject<HTMLElement>,
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
@@ -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<string[]>(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;
|
|
||||||
};
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { useEffect, useRef } from "react";
|
|
||||||
|
|
||||||
const useInfiniteScroll = (callback: () => void) => {
|
|
||||||
const loaderRef = useRef<HTMLDivElement | null>(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;
|
|
||||||
@@ -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<string>("");
|
|
||||||
const messageQueue = useRef<string[]>([]);
|
|
||||||
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<Promise<() => void> | null>(null);
|
|
||||||
const unlistenCancelRef = useRef<Promise<() => 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 };
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import SVGWrap from "./SVGWrap";
|
|
||||||
|
|
||||||
export default function ArrowLeft(props: I.SVG) {
|
|
||||||
return (
|
|
||||||
<SVGWrap {...props} viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="m7.85 13l2.85 2.85q.3.3.288.7t-.288.7q-.3.3-.712.313t-.713-.288L4.7 12.7q-.3-.3-.3-.7t.3-.7l4.575-4.575q.3-.3.713-.287t.712.312q.275.3.288.7t-.288.7L7.85 11H19q.425 0 .713.288T20 12t-.288.713T19 13z"
|
|
||||||
/>
|
|
||||||
</SVGWrap>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import SVGWrap from "./SVGWrap";
|
|
||||||
|
|
||||||
export default function Ask(props: I.SVG) {
|
|
||||||
return (
|
|
||||||
<SVGWrap {...props} viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth="2"
|
|
||||||
d="M13.038 19.927A9.93 9.93 0 0 1 7.7 19L3 20l1.3-3.9C1.976 12.663 2.874 8.228 6.4 5.726c3.526-2.501 8.59-2.296 11.845.48c1.993 1.7 2.93 4.043 2.746 6.346M19 16l-2 3h4l-2 3"
|
|
||||||
/>
|
|
||||||
</SVGWrap>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import SVGWrap from "./SVGWrap";
|
|
||||||
|
|
||||||
export default function Link(props: I.SVG) {
|
|
||||||
return (
|
|
||||||
<SVGWrap {...props} viewBox="0 0 256 256">
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M117.18 188.74a12 12 0 0 1 0 17l-5.12 5.12A58.26 58.26 0 0 1 70.6 228a58.62 58.62 0 0 1-41.46-100.08l34.75-34.75a58.64 58.64 0 0 1 98.56 28.11a12 12 0 1 1-23.37 5.44a34.65 34.65 0 0 0-58.22-16.58l-34.75 34.75A34.62 34.62 0 0 0 70.57 204a34.41 34.41 0 0 0 24.49-10.14l5.11-5.12a12 12 0 0 1 17.01 0M226.83 45.17a58.65 58.65 0 0 0-82.93 0l-5.11 5.11a12 12 0 0 0 17 17l5.12-5.12a34.63 34.63 0 1 1 49 49l-34.81 34.7A34.39 34.39 0 0 1 150.61 156a34.63 34.63 0 0 1-33.69-26.72a12 12 0 0 0-23.38 5.44A58.64 58.64 0 0 0 150.56 180h.05a58.28 58.28 0 0 0 41.47-17.17l34.75-34.75a58.62 58.62 0 0 0 0-82.91"
|
|
||||||
/>
|
|
||||||
</SVGWrap>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import SVGWrap from "./SVGWrap";
|
|
||||||
|
|
||||||
export default function Reload(props: I.SVG) {
|
|
||||||
return (
|
|
||||||
<SVGWrap {...props} viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M12 20q-3.35 0-5.675-2.325T4 12t2.325-5.675T12 4q1.725 0 3.3.712T18 6.75V5q0-.425.288-.712T19 4t.713.288T20 5v5q0 .425-.288.713T19 11h-5q-.425 0-.712-.288T13 10t.288-.712T14 9h3.2q-.8-1.4-2.187-2.2T12 6Q9.5 6 7.75 7.75T6 12t1.75 4.25T12 18q1.7 0 3.113-.862t2.187-2.313q.2-.35.563-.487t.737-.013q.4.125.575.525t-.025.75q-1.025 2-2.925 3.2T12 20"
|
|
||||||
/>
|
|
||||||
</SVGWrap>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import SVGWrap from "./SVGWrap";
|
|
||||||
|
|
||||||
export default function Send(props: I.SVG) {
|
|
||||||
return (
|
|
||||||
<SVGWrap {...props} viewBox="0 0 24 24">
|
|
||||||
<g fill="none">
|
|
||||||
<path d="M24 0v24H0V0zM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035c-.01-.004-.019-.001-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427c-.002-.01-.009-.017-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093c.012.004.023 0 .029-.008l.004-.014l-.034-.614c-.003-.012-.01-.02-.02-.022m-.715.002a.023.023 0 0 0-.027.006l-.006.014l-.034.614c0 .012.007.02.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="m21.433 4.861l-6 15.5a1 1 0 0 1-1.624.362l-3.382-3.235l-2.074 2.073a.5.5 0 0 1-.853-.354v-4.519L2.309 9.723a1 1 0 0 1 .442-1.691l17.5-4.5a1 1 0 0 1 1.181 1.329ZM19 6.001L8.032 13.152l1.735 1.66L19 6Z"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
</SVGWrap>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import SVGWrap from "./SVGWrap";
|
|
||||||
|
|
||||||
export default function Setting(props: I.SVG) {
|
|
||||||
return (
|
|
||||||
<SVGWrap {...props} viewBox="0 0 24 24">
|
|
||||||
<g fill="none" stroke="currentColor" strokeWidth="2">
|
|
||||||
<path d="M3.082 13.945c-.529-.95-.793-1.426-.793-1.945c0-.519.264-.994.793-1.944L4.43 7.63l1.426-2.381c.559-.933.838-1.4 1.287-1.66c.45-.259.993-.267 2.08-.285L12 3.26l2.775.044c1.088.018 1.631.026 2.08.286c.45.26.73.726 1.288 1.659L19.57 7.63l1.35 2.426c.528.95.792 1.425.792 1.944c0 .519-.264.994-.793 1.944L19.57 16.37l-1.426 2.381c-.559.933-.838 1.4-1.287 1.66c-.45.259-.993.267-2.08.285L12 20.74l-2.775-.044c-1.088-.018-1.631-.026-2.08-.286c-.45-.26-.73-.726-1.288-1.659L4.43 16.37z" />
|
|
||||||
<circle cx="12" cy="12" r="3" />
|
|
||||||
</g>
|
|
||||||
</SVGWrap>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import SVGWrap from "./SVGWrap";
|
|
||||||
|
|
||||||
export default function ThemeAuto(props: I.SVG) {
|
|
||||||
return (
|
|
||||||
<SVGWrap {...props} viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2S2 6.477 2 12s4.477 10 10 10m0-2V4a8 8 0 1 1 0 16"
|
|
||||||
/>
|
|
||||||
</SVGWrap>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import SVGWrap from "./SVGWrap";
|
|
||||||
|
|
||||||
export default function ThemeDark(props: I.SVG) {
|
|
||||||
return (
|
|
||||||
<SVGWrap {...props} viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M12 21q-3.775 0-6.387-2.613T3 12q0-3.45 2.25-5.988T11 3.05q.325-.05.575.088t.4.362t.163.525t-.188.575q-.425.65-.638 1.375T11.1 7.5q0 2.25 1.575 3.825T16.5 12.9q.775 0 1.538-.225t1.362-.625q.275-.175.563-.162t.512.137q.25.125.388.375t.087.6q-.35 3.45-2.937 5.725T12 21m0-2q2.2 0 3.95-1.213t2.55-3.162q-.5.125-1 .2t-1 .075q-3.075 0-5.238-2.163T9.1 7.5q0-.5.075-1t.2-1q-1.95.8-3.163 2.55T5 12q0 2.9 2.05 4.95T12 19m-.25-6.75"
|
|
||||||
/>
|
|
||||||
</SVGWrap>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import SVGWrap from "./SVGWrap";
|
|
||||||
|
|
||||||
export default function ThemeLight(props: I.SVG) {
|
|
||||||
return (
|
|
||||||
<SVGWrap {...props} viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M12 15q1.25 0 2.125-.875T15 12t-.875-2.125T12 9t-2.125.875T9 12t.875 2.125T12 15m0 2q-2.075 0-3.537-1.463T7 12t1.463-3.537T12 7t3.538 1.463T17 12t-1.463 3.538T12 17M2 13q-.425 0-.712-.288T1 12t.288-.712T2 11h2q.425 0 .713.288T5 12t-.288.713T4 13zm18 0q-.425 0-.712-.288T19 12t.288-.712T20 11h2q.425 0 .713.288T23 12t-.288.713T22 13zm-8-8q-.425 0-.712-.288T11 4V2q0-.425.288-.712T12 1t.713.288T13 2v2q0 .425-.288.713T12 5m0 18q-.425 0-.712-.288T11 22v-2q0-.425.288-.712T12 19t.713.288T13 20v2q0 .425-.288.713T12 23M5.65 7.05L4.575 6q-.3-.275-.288-.7t.288-.725q.3-.3.725-.3t.7.3L7.05 5.65q.275.3.275.7t-.275.7t-.687.288t-.713-.288M18 19.425l-1.05-1.075q-.275-.3-.275-.712t.275-.688q.275-.3.688-.287t.712.287L19.425 18q.3.275.288.7t-.288.725q-.3.3-.725.3t-.7-.3M16.95 7.05q-.3-.275-.288-.687t.288-.713L18 4.575q.275-.3.7-.288t.725.288q.3.3.3.725t-.3.7L18.35 7.05q-.3.275-.7.275t-.7-.275M4.575 19.425q-.3-.3-.3-.725t.3-.7l1.075-1.05q.3-.275.712-.275t.688.275q.3.275.288.688t-.288.712L6 19.425q-.275.3-.7.288t-.725-.288M12 12"
|
|
||||||
/>
|
|
||||||
</SVGWrap>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import SVGWrap from "./SVGWrap";
|
|
||||||
|
|
||||||
export default function UnPin(props: I.SVG) {
|
|
||||||
return (
|
|
||||||
<SVGWrap {...props} viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M14 4v5c0 1.12.37 2.16 1 3H9c.65-.86 1-1.9 1-3V4zm3-2H7c-.55 0-1 .45-1 1s.45 1 1 1h1v5c0 1.66-1.34 3-3 3v2h5.97v7l1 1l1-1v-7H19v-2c-1.66 0-3-1.34-3-3V4h1c.55 0 1-.45 1-1s-.45-1-1-1"
|
|
||||||
/>
|
|
||||||
</SVGWrap>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -42,11 +42,6 @@
|
|||||||
"copyright": "©{{year}} INFINI Labs, All Rights Reserved."
|
"copyright": "©{{year}} INFINI Labs, All Rights Reserved."
|
||||||
},
|
},
|
||||||
"advanced": {
|
"advanced": {
|
||||||
"title": "Advanced Settings",
|
|
||||||
"endpoint": {
|
|
||||||
"title": "API Endpoint",
|
|
||||||
"description": "Domain name for interface and websocket"
|
|
||||||
},
|
|
||||||
"startup": {
|
"startup": {
|
||||||
"title": "Startup Settings",
|
"title": "Startup Settings",
|
||||||
"defaultStartupWindow": {
|
"defaultStartupWindow": {
|
||||||
|
|||||||
@@ -42,11 +42,6 @@
|
|||||||
"copyright": "©{{year}} INFINI Labs,保留所有权利。"
|
"copyright": "©{{year}} INFINI Labs,保留所有权利。"
|
||||||
},
|
},
|
||||||
"advanced": {
|
"advanced": {
|
||||||
"title": "高级设置",
|
|
||||||
"endpoint": {
|
|
||||||
"title": "API 接口",
|
|
||||||
"description": "接口和 WebSocket 的域名"
|
|
||||||
},
|
|
||||||
"startup": {
|
"startup": {
|
||||||
"title": "启动设置",
|
"title": "启动设置",
|
||||||
"defaultStartupWindow": {
|
"defaultStartupWindow": {
|
||||||
|
|||||||
@@ -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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,7 +24,6 @@ export type IAppStore = {
|
|||||||
|
|
||||||
endpoint: AppEndpoint;
|
endpoint: AppEndpoint;
|
||||||
endpoint_http: string;
|
endpoint_http: string;
|
||||||
endpoint_websocket: string;
|
|
||||||
setEndpoint: (endpoint: AppEndpoint) => void;
|
setEndpoint: (endpoint: AppEndpoint) => void;
|
||||||
language: string;
|
language: string;
|
||||||
setLanguage: (language: string) => void;
|
setLanguage: (language: string) => void;
|
||||||
@@ -78,20 +77,12 @@ export const useAppStore = create<IAppStore>()(
|
|||||||
|
|
||||||
endpoint: "https://coco.infini.cloud/",
|
endpoint: "https://coco.infini.cloud/",
|
||||||
endpoint_http: "https://coco.infini.cloud",
|
endpoint_http: "https://coco.infini.cloud",
|
||||||
endpoint_websocket: "wss://coco.infini.cloud/ws",
|
|
||||||
setEndpoint: async (endpoint: AppEndpoint) => {
|
setEndpoint: async (endpoint: AppEndpoint) => {
|
||||||
const endpoint_http = endpoint;
|
const endpoint_http = endpoint;
|
||||||
|
|
||||||
const withoutProtocol = endpoint.split("//")[1];
|
|
||||||
|
|
||||||
const endpoint_websocket = endpoint?.includes("https")
|
|
||||||
? `wss://${withoutProtocol}/ws`
|
|
||||||
: `ws://${withoutProtocol}/ws`;
|
|
||||||
|
|
||||||
return set({
|
return set({
|
||||||
endpoint,
|
endpoint,
|
||||||
endpoint_http,
|
endpoint_http,
|
||||||
endpoint_websocket,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
language: "en",
|
language: "en",
|
||||||
@@ -128,7 +119,6 @@ export const useAppStore = create<IAppStore>()(
|
|||||||
ssoRequestID: state.ssoRequestID,
|
ssoRequestID: state.ssoRequestID,
|
||||||
endpoint: state.endpoint,
|
endpoint: state.endpoint,
|
||||||
endpoint_http: state.endpoint_http,
|
endpoint_http: state.endpoint_http,
|
||||||
endpoint_websocket: state.endpoint_websocket,
|
|
||||||
language: state.language,
|
language: state.language,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ export interface EventPayloads {
|
|||||||
"endpoint-changed": {
|
"endpoint-changed": {
|
||||||
endpoint: string;
|
endpoint: string;
|
||||||
endpoint_http: string;
|
endpoint_http: string;
|
||||||
endpoint_websocket: string;
|
|
||||||
};
|
};
|
||||||
"showTooltip-changed": {
|
"showTooltip-changed": {
|
||||||
showTooltip: boolean;
|
showTooltip: boolean;
|
||||||
|
|||||||
@@ -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
|
|
||||||
};
|
|
||||||
@@ -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<RecordingOptions> {
|
|
||||||
return await TAURI_INVOKE("get_recording_options");
|
|
||||||
},
|
|
||||||
async setRecordingOptions(options: RecordingOptions): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("set_recording_options", { options });
|
|
||||||
},
|
|
||||||
async startRecording(): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("start_recording");
|
|
||||||
},
|
|
||||||
async stopRecording(): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("stop_recording");
|
|
||||||
},
|
|
||||||
async pauseRecording(): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("pause_recording");
|
|
||||||
},
|
|
||||||
async resumeRecording(): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("resume_recording");
|
|
||||||
},
|
|
||||||
async listCameras(): Promise<string[]> {
|
|
||||||
return await TAURI_INVOKE("list_cameras");
|
|
||||||
},
|
|
||||||
async listCaptureWindows(): Promise<CaptureWindow[]> {
|
|
||||||
return await TAURI_INVOKE("list_capture_windows");
|
|
||||||
},
|
|
||||||
async listCaptureScreens(): Promise<CaptureScreen[]> {
|
|
||||||
return await TAURI_INVOKE("list_capture_screens");
|
|
||||||
},
|
|
||||||
async takeScreenshot(): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("take_screenshot");
|
|
||||||
},
|
|
||||||
async listAudioDevices(): Promise<string[]> {
|
|
||||||
return await TAURI_INVOKE("list_audio_devices");
|
|
||||||
},
|
|
||||||
async closePreviousRecordingsWindow(): Promise<void> {
|
|
||||||
await TAURI_INVOKE("close_previous_recordings_window");
|
|
||||||
},
|
|
||||||
async setFakeWindowBounds(name: string, bounds: Bounds): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("set_fake_window_bounds", { name, bounds });
|
|
||||||
},
|
|
||||||
async removeFakeWindow(name: string): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("remove_fake_window", { name });
|
|
||||||
},
|
|
||||||
async focusCapturesPanel(): Promise<void> {
|
|
||||||
await TAURI_INVOKE("focus_captures_panel");
|
|
||||||
},
|
|
||||||
async getCurrentRecording(): Promise<JsonValue<RecordingInfo | null>> {
|
|
||||||
return await TAURI_INVOKE("get_current_recording");
|
|
||||||
},
|
|
||||||
async exportVideo(
|
|
||||||
videoId: string,
|
|
||||||
project: ProjectConfiguration,
|
|
||||||
progress: TAURI_CHANNEL<RenderProgress>,
|
|
||||||
force: boolean
|
|
||||||
): Promise<string> {
|
|
||||||
return await TAURI_INVOKE("export_video", {
|
|
||||||
videoId,
|
|
||||||
project,
|
|
||||||
progress,
|
|
||||||
force,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async copyFileToPath(src: string, dst: string): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("copy_file_to_path", { src, dst });
|
|
||||||
},
|
|
||||||
async copyVideoToClipboard(path: string): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("copy_video_to_clipboard", { path });
|
|
||||||
},
|
|
||||||
async copyScreenshotToClipboard(path: string): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("copy_screenshot_to_clipboard", { path });
|
|
||||||
},
|
|
||||||
async openFilePath(path: string): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("open_file_path", { path });
|
|
||||||
},
|
|
||||||
async getVideoMetadata(
|
|
||||||
videoId: string,
|
|
||||||
videoType: VideoType | null
|
|
||||||
): Promise<VideoRecordingMetadata> {
|
|
||||||
return await TAURI_INVOKE("get_video_metadata", { videoId, videoType });
|
|
||||||
},
|
|
||||||
async createEditorInstance(
|
|
||||||
videoId: string
|
|
||||||
): Promise<SerializedEditorInstance> {
|
|
||||||
return await TAURI_INVOKE("create_editor_instance", { videoId });
|
|
||||||
},
|
|
||||||
async startPlayback(videoId: string): Promise<void> {
|
|
||||||
await TAURI_INVOKE("start_playback", { videoId });
|
|
||||||
},
|
|
||||||
async stopPlayback(videoId: string): Promise<void> {
|
|
||||||
await TAURI_INVOKE("stop_playback", { videoId });
|
|
||||||
},
|
|
||||||
async setPlayheadPosition(
|
|
||||||
videoId: string,
|
|
||||||
frameNumber: number
|
|
||||||
): Promise<void> {
|
|
||||||
await TAURI_INVOKE("set_playhead_position", { videoId, frameNumber });
|
|
||||||
},
|
|
||||||
async setProjectConfig(
|
|
||||||
videoId: string,
|
|
||||||
config: ProjectConfiguration
|
|
||||||
): Promise<void> {
|
|
||||||
await TAURI_INVOKE("set_project_config", { videoId, config });
|
|
||||||
},
|
|
||||||
async openEditor(id: string): Promise<void> {
|
|
||||||
await TAURI_INVOKE("open_editor", { id });
|
|
||||||
},
|
|
||||||
async openMainWindow(): Promise<void> {
|
|
||||||
await TAURI_INVOKE("open_main_window");
|
|
||||||
},
|
|
||||||
async openPermissionSettings(permission: OSPermission): Promise<void> {
|
|
||||||
await TAURI_INVOKE("open_permission_settings", { permission });
|
|
||||||
},
|
|
||||||
async doPermissionsCheck(initialCheck: boolean): Promise<OSPermissionsCheck> {
|
|
||||||
return await TAURI_INVOKE("do_permissions_check", { initialCheck });
|
|
||||||
},
|
|
||||||
async requestPermission(permission: OSPermission): Promise<void> {
|
|
||||||
await TAURI_INVOKE("request_permission", { permission });
|
|
||||||
},
|
|
||||||
async uploadExportedVideo(
|
|
||||||
videoId: string,
|
|
||||||
mode: UploadMode
|
|
||||||
): Promise<UploadResult> {
|
|
||||||
return await TAURI_INVOKE("upload_exported_video", { videoId, mode });
|
|
||||||
},
|
|
||||||
async uploadScreenshot(screenshotPath: string): Promise<UploadResult> {
|
|
||||||
return await TAURI_INVOKE("upload_screenshot", { screenshotPath });
|
|
||||||
},
|
|
||||||
async getRecordingMeta(id: string, fileType: string): Promise<RecordingMeta> {
|
|
||||||
return await TAURI_INVOKE("get_recording_meta", { id, fileType });
|
|
||||||
},
|
|
||||||
async saveFileDialog(
|
|
||||||
fileName: string,
|
|
||||||
fileType: string
|
|
||||||
): Promise<string | null> {
|
|
||||||
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<boolean> {
|
|
||||||
return await TAURI_INVOKE("check_upgraded_and_update");
|
|
||||||
},
|
|
||||||
async openExternalLink(url: string): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("open_external_link", { url });
|
|
||||||
},
|
|
||||||
async setHotkey(action: HotkeyAction, hotkey: Hotkey | null): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("set_hotkey", { action, hotkey });
|
|
||||||
},
|
|
||||||
async deleteAuthOpenSignin(): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("delete_auth_open_signin");
|
|
||||||
},
|
|
||||||
async resetCameraPermissions(): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("reset_camera_permissions");
|
|
||||||
},
|
|
||||||
async resetMicrophonePermissions(): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("reset_microphone_permissions");
|
|
||||||
},
|
|
||||||
async isCameraWindowOpen(): Promise<boolean> {
|
|
||||||
return await TAURI_INVOKE("is_camera_window_open");
|
|
||||||
},
|
|
||||||
async seekTo(videoId: string, frameNumber: number): Promise<void> {
|
|
||||||
await TAURI_INVOKE("seek_to", { videoId, frameNumber });
|
|
||||||
},
|
|
||||||
async sendFeedbackRequest(feedback: string): Promise<null> {
|
|
||||||
return await TAURI_INVOKE("send_feedback_request", { feedback });
|
|
||||||
},
|
|
||||||
async positionTrafficLights(
|
|
||||||
controlsInset: [number, number] | null
|
|
||||||
): Promise<void> {
|
|
||||||
await TAURI_INVOKE("position_traffic_lights", { controlsInset });
|
|
||||||
},
|
|
||||||
async setTheme(theme: AppTheme): Promise<void> {
|
|
||||||
await TAURI_INVOKE("set_theme", { theme });
|
|
||||||
},
|
|
||||||
async globalMessageDialog(message: string): Promise<void> {
|
|
||||||
await TAURI_INVOKE("global_message_dialog", { message });
|
|
||||||
},
|
|
||||||
async showWindow(window: ShowCapWindow): Promise<void> {
|
|
||||||
await TAURI_INVOKE("show_window", { window });
|
|
||||||
},
|
|
||||||
async writeClipboardString(text: string): Promise<null> {
|
|
||||||
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<number>; size: XY<number> };
|
|
||||||
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> = [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<T> = { 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__<T> = {
|
|
||||||
listen: (
|
|
||||||
cb: TAURI_API_EVENT.EventCallback<T>
|
|
||||||
) => ReturnType<typeof TAURI_API_EVENT.listen<T>>;
|
|
||||||
once: (
|
|
||||||
cb: TAURI_API_EVENT.EventCallback<T>
|
|
||||||
) => ReturnType<typeof TAURI_API_EVENT.once<T>>;
|
|
||||||
emit: null extends T
|
|
||||||
? (payload?: T) => ReturnType<typeof TAURI_API_EVENT.emit>
|
|
||||||
: (payload: T) => ReturnType<typeof TAURI_API_EVENT.emit>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Result<T, E> =
|
|
||||||
| { status: "ok"; data: T }
|
|
||||||
| { status: "error"; error: E };
|
|
||||||
|
|
||||||
function __makeEvents__<T extends Record<string, any>>(
|
|
||||||
mappings: Record<keyof T, string>
|
|
||||||
) {
|
|
||||||
return new Proxy(
|
|
||||||
{} as unknown as {
|
|
||||||
[K in keyof T]: __EventObj__<T[K]> & {
|
|
||||||
(handle: __WebviewWindow__): __EventObj__<T[K]>;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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__<any>) => {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
1
src/vite-env.d.ts
vendored
1
src/vite-env.d.ts
vendored
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
readonly COCO_SERVER_URL: string;
|
readonly COCO_SERVER_URL: string;
|
||||||
readonly COCO_WEBSOCKET_URL: string;
|
|
||||||
// more env variables...
|
// more env variables...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user