Skip to content

Commit 23ae478

Browse files
authored
feat: networked search data sources support search and keyboard-only operation (#367)
* feat: networked search data sources support search and keyboard-only operation * docs: update changelog
1 parent 6ecb232 commit 23ae478

13 files changed

Lines changed: 459 additions & 230 deletions

File tree

docs/content.en/docs/release-notes/_index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Information about release notes of Coco Server is provided here.
2020
- feat: add a border to the main window in Windows 10 #343
2121
- feat: mobile terminal adaptation about style #348
2222
- feat: service list popup box supports keyboard-only operation #359
23+
- feat: networked search data sources support search and keyboard-only operation #367
2324

2425
### Bug fix
2526

@@ -37,6 +38,7 @@ Information about release notes of Coco Server is provided here.
3738
### Breaking changes
3839

3940
### Features
41+
4042
- feat: add web pages components #277
4143
- feat: support for customizing some of the preset shortcuts #316
4244
- feat: support multi websocket connections #314

src-tauri/src/server/datasource.rs

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ use std::collections::HashMap;
88
use std::sync::{Arc, RwLock};
99
use tauri::{AppHandle, Runtime};
1010

11+
#[derive(serde::Deserialize, Debug)]
12+
pub struct GetDatasourcesByServerOptions {
13+
pub from: Option<u32>,
14+
pub size: Option<u32>,
15+
pub query: Option<String>,
16+
}
17+
1118
lazy_static! {
1219
static ref DATASOURCE_CACHE: Arc<RwLock<HashMap<String, HashMap<String, DataSource>>>> =
1320
Arc::new(RwLock::new(HashMap::new()));
@@ -25,7 +32,7 @@ pub fn save_datasource_to_cache(server_id: &str, datasources: Vec<DataSource>) {
2532
#[allow(dead_code)]
2633
pub fn get_datasources_from_cache(server_id: &str) -> Option<HashMap<String, DataSource>> {
2734
let cache = DATASOURCE_CACHE.read().unwrap(); // Acquire read lock
28-
// dbg!("cache: {:?}", &cache);
35+
// dbg!("cache: {:?}", &cache);
2936
let server_cache = cache.get(server_id)?; // Get the server's cache
3037
Some(server_cache.clone())
3138
}
@@ -41,7 +48,7 @@ pub async fn refresh_all_datasources<R: Runtime>(_app_handle: &AppHandle<R>) ->
4148
// dbg!("fetch datasources for server: {}", &server.id);
4249

4350
// Attempt to get datasources by server, and continue even if it fails
44-
let connectors = match get_datasources_by_server(server.id.as_str()).await {
51+
let connectors = match get_datasources_by_server(server.id.as_str(), None).await {
4552
Ok(connectors) => {
4653
// Process connectors only after fetching them
4754
let connectors_map: HashMap<String, DataSource> = connectors
@@ -83,13 +90,48 @@ pub async fn refresh_all_datasources<R: Runtime>(_app_handle: &AppHandle<R>) ->
8390
}
8491

8592
#[tauri::command]
86-
pub async fn get_datasources_by_server(id: &str) -> Result<Vec<DataSource>, String> {
93+
pub async fn get_datasources_by_server(
94+
id: &str,
95+
options: Option<GetDatasourcesByServerOptions>,
96+
) -> Result<Vec<DataSource>, String> {
97+
let from = options.as_ref().and_then(|opt| opt.from).unwrap_or(0);
98+
let size = options.as_ref().and_then(|opt| opt.size).unwrap_or(10000);
99+
let query = options
100+
.and_then(|opt| opt.query)
101+
.unwrap_or(String::default());
102+
103+
let mut body = serde_json::json!({
104+
"from": from,
105+
"size": size,
106+
});
107+
108+
if !query.is_empty() {
109+
body["query"] = serde_json::json!({
110+
"bool": {
111+
"must": [{
112+
"query_string": {
113+
"fields": ["combined_fulltext"],
114+
"query": query,
115+
"fuzziness": "AUTO",
116+
"fuzzy_prefix_length": 2,
117+
"fuzzy_max_expansions": 10,
118+
"fuzzy_transpositions": true,
119+
"allow_leading_wildcard": false
120+
}
121+
}]
122+
}
123+
});
124+
}
125+
87126
// Perform the async HTTP request outside the cache lock
88-
let resp = HttpClient::get(id, "/datasource/_search", None)
89-
.await
90-
.map_err(|e| {
91-
format!("Error fetching datasource: {}", e)
92-
})?;
127+
let resp = HttpClient::post(
128+
id,
129+
"/datasource/_search",
130+
None,
131+
Some(reqwest::Body::from(body.to_string())),
132+
)
133+
.await
134+
.map_err(|e| format!("Error fetching datasource: {}", e))?;
93135

94136
// Parse the search results from the response
95137
let datasources: Vec<DataSource> = parse_search_results(resp).await.map_err(|e| {

src-tauri/src/server/servers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ pub async fn refresh_coco_server_info<R: Runtime>(
333333
//refresh connectors and datasources
334334
let _ = fetch_connectors_by_server(&id).await;
335335

336-
let _ = get_datasources_by_server(&id).await;
336+
let _ = get_datasources_by_server(&id, None).await;
337337

338338
Ok(server)
339339
}

src/components/Common/VisibleKey.tsx

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import { useShortcutsStore } from "@/stores/shortcutsStore";
22
import { useKeyPress } from "ahooks";
3+
import clsx from "clsx";
34
import { FC, ReactNode } from "react";
45

56
interface VisibleKeyProps {
67
shortcut: string;
7-
children: ReactNode;
8+
children?: ReactNode;
9+
className?: string;
810
onKeypress?: () => void;
911
}
1012

1113
const VisibleKey: FC<VisibleKeyProps> = (props) => {
12-
const { shortcut, children, onKeypress } = props;
14+
const { shortcut, children, className, onKeypress } = props;
1315

1416
const modifierKey = useShortcutsStore((state) => {
1517
return state.modifierKey;
@@ -22,9 +24,26 @@ const VisibleKey: FC<VisibleKeyProps> = (props) => {
2224
onKeypress?.();
2325
});
2426

27+
const renderShortcut = () => {
28+
if (shortcut === "leftarrow") {
29+
return "←";
30+
}
31+
32+
if (shortcut === "rightarrow") {
33+
return "→";
34+
}
35+
36+
return shortcut;
37+
};
38+
2539
return modifierKeyPressed ? (
26-
<div className="size-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#fff] dark:shadow-[-6px_0px_6px_2px_#000]">
27-
{shortcut}
40+
<div
41+
className={clsx(
42+
"size-4 flex items-center justify-center font-normal text-xs text-[#333] leading-[14px] bg-[#ccc] dark:bg-[#6B6B6B] rounded-md shadow-[-6px_0px_6px_2px_#fff] dark:shadow-[-6px_0px_6px_2px_#000]",
43+
className
44+
)}
45+
>
46+
{renderShortcut()}
2847
</div>
2948
) : (
3049
children

src/components/Search/InputBox.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,14 @@ interface ChatInputProps {
3434
isDeepThinkActive: boolean;
3535
setIsDeepThinkActive: () => void;
3636
isChatPage?: boolean;
37-
getDataSourcesByServer: (serverId: string) => Promise<DataSource[]>;
37+
getDataSourcesByServer: (
38+
serverId: string,
39+
options?: {
40+
from?: number;
41+
size?: number;
42+
query?: string;
43+
}
44+
) => Promise<DataSource[]>;
3845
setupWindowFocusListener: (callback: () => void) => Promise<() => void>;
3946
checkScreenPermission: () => Promise<boolean>;
4047
requestScreenPermission: () => void;
@@ -98,6 +105,7 @@ export default function ChatInput({
98105
const modifierKeyPressed = useShortcutsStore((state) => {
99106
return state.modifierKeyPressed;
100107
});
108+
console.log("modifierKeyPressed", modifierKeyPressed);
101109
const modeSwitch = useShortcutsStore((state) => state.modeSwitch);
102110
const returnToInput = useShortcutsStore((state) => state.returnToInput);
103111
const deepThinking = useShortcutsStore((state) => state.deepThinking);

0 commit comments

Comments
 (0)