diff --git a/.env b/.env index 8f03c3af..c15d962f 100644 --- a/.env +++ b/.env @@ -1,3 +1,3 @@ -COCO_SERVER_URL=https://coco.infini.cloud # http://localhost:2900 +COCO_SERVER_URL=https://infini.tpddns.cn:27200 #https://coco.infini.cloud # http://localhost:2900 -COCO_WEBSOCKET_URL=wss://coco.infini.cloud/ws # ws://localhost:2900/ws \ No newline at end of file +COCO_WEBSOCKET_URL=wss://infini.tpddns.cn:27200/ws #wss://coco.infini.cloud/ws # ws://localhost:2900/ws \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index ce5aa524..26da56c6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "cSpell.words": [ + "ahooks", "autolaunch", "Avenir", "callout", @@ -30,6 +31,7 @@ "tailwindcss", "tauri", "titlebar", + "tpddns", "traptitech", "unlisten", "unlistener", diff --git a/package.json b/package.json index 9d8d099c..a95a743c 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@tauri-apps/plugin-shell": ">=2.0.0", "@tauri-apps/plugin-websocket": "~2", "@tauri-apps/plugin-window": "2.0.0-alpha.1", + "ahooks": "^3.8.4", "axios": "^1.7.7", "clsx": "^2.1.1", "dotenv": "^16.4.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c2160b54..12b9bf81 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: '@tauri-apps/plugin-window': specifier: 2.0.0-alpha.1 version: 2.0.0-alpha.1 + ahooks: + specifier: ^3.8.4 + version: 3.8.4(react@18.3.1) axios: specifier: ^1.7.7 version: 1.7.7 @@ -551,46 +554,55 @@ packages: resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.24.0': resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.24.0': resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.24.0': resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.24.0': resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.24.0': resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.24.0': resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.24.0': resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.24.0': resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==} @@ -649,24 +661,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@tauri-apps/cli-linux-arm64-musl@2.0.3': resolution: {integrity: sha512-I4MVD7nf6lLLRmNQPpe5beEIFM6q7Zkmh77ROA5BNu/+vHNL5kiTMD+bmd10ZL2r753A6pO7AvqkIxcBuIl0tg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@tauri-apps/cli-linux-x64-gnu@2.0.3': resolution: {integrity: sha512-C6Jkx2zZGKkoi+sg5FK9GoH/0EvAaOgrZfF5azV5EALGba46g7VpWcZgp9zFUd7K2IzTi+0OOY8TQ2OVfKZgew==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@tauri-apps/cli-linux-x64-musl@2.0.3': resolution: {integrity: sha512-qi4ghmTfSAl+EEUDwmwI9AJUiOLNSmU1RgiGgcPRE+7A/W+Am9UnxYySAiRbB/gJgTl9sj/pqH5Y9duP1/sqHg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@tauri-apps/cli-win32-arm64-msvc@2.0.3': resolution: {integrity: sha512-UXxHkYmFesC97qVmZre4vY7oDxRDtC2OeKNv0bH+iSnuUp/ROxzJYGyaelnv9Ybvgl4YVqDCnxgB28qMM938TA==} @@ -901,6 +917,12 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + ahooks@3.8.4: + resolution: {integrity: sha512-39wDEw2ZHvypaT14EpMMk4AzosHWt0z9bulY0BeDsvc9PqJEV+Kjh/4TZfftSsotBMq52iYIOFPd3PR56e0ZJg==} + engines: {node: '>=8.0.0'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1428,6 +1450,9 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} + intersection-observer@0.12.2: + resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} + is-alphabetical@2.0.1: resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} @@ -1478,6 +1503,10 @@ packages: resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} hasBin: true + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1867,6 +1896,9 @@ packages: peerDependencies: react: ^18.3.1 + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + react-hotkeys-hook@4.5.1: resolution: {integrity: sha512-scAEJOh3Irm0g95NIn6+tQVf/OICCjsQsC9NBHfQws/Vxw4sfq1tDQut5fhTEvPraXhu/sHxRd9lOtxzyYuNAg==} peerDependencies: @@ -1947,6 +1979,9 @@ packages: remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -1978,6 +2013,10 @@ packages: scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + screenfull@5.2.0: + resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} + engines: {node: '>=0.10.0'} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -2985,6 +3024,19 @@ snapshots: acorn@8.14.0: {} + ahooks@3.8.4(react@18.3.1): + dependencies: + '@babel/runtime': 7.25.9 + dayjs: 1.11.13 + intersection-observer: 0.12.2 + js-cookie: 3.0.5 + lodash: 4.17.21 + react: 18.3.1 + react-fast-compare: 3.2.2 + resize-observer-polyfill: 1.5.1 + screenfull: 5.2.0 + tslib: 2.8.0 + ansi-regex@5.0.1: {} ansi-regex@6.1.0: {} @@ -3570,6 +3622,8 @@ snapshots: internmap@2.0.3: {} + intersection-observer@0.12.2: {} + is-alphabetical@2.0.1: {} is-alphanumerical@2.0.1: @@ -3611,6 +3665,8 @@ snapshots: jiti@1.21.6: {} + js-cookie@3.0.5: {} + js-tokens@4.0.0: {} jsesc@3.0.2: {} @@ -4223,6 +4279,8 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 + react-fast-compare@3.2.2: {} + react-hotkeys-hook@4.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 @@ -4349,6 +4407,8 @@ snapshots: mdast-util-to-markdown: 2.1.2 unified: 11.0.5 + resize-observer-polyfill@1.5.1: {} + resolve@1.22.8: dependencies: is-core-module: 2.15.1 @@ -4400,6 +4460,8 @@ snapshots: dependencies: loose-envify: 1.4.0 + screenfull@5.2.0: {} + semver@6.3.1: {} shebang-command@2.0.0: diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 6bed515a..2751e793 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -83,7 +83,6 @@ pub fn run() { // show_panel, // hide_panel, // close_panel - shortcut::check_shortcut_available, ]) .setup(|app| { init(app.app_handle()); @@ -168,7 +167,7 @@ fn hide_coco(app: tauri::AppHandle) { } fn handle_open_coco(app: &AppHandle) { - println!("Open Coco menu clicked!"); + // println!("Open Coco menu clicked!"); if let Some(window) = app.get_window("main") { window.show().unwrap(); @@ -179,7 +178,7 @@ fn handle_open_coco(app: &AppHandle) { } fn handle_hide_coco(app: &AppHandle) { - println!("Hide Coco menu clicked!"); + // println!("Hide Coco menu clicked!"); if let Some(window) = app.get_window("main") { if let Err(err) = window.hide() { @@ -196,7 +195,7 @@ fn handle_hide_coco(app: &AppHandle) { fn switch_tray_icon(app: tauri::AppHandle, is_dark_mode: bool) { let app_handle = app.app_handle(); - println!("is_dark_mode: {}", is_dark_mode); + // println!("is_dark_mode: {}", is_dark_mode); const DARK_ICON_PATH: &[u8] = include_bytes!("../icons/dark@2x.png"); const LIGHT_ICON_PATH: &[u8] = include_bytes!("../icons/light@2x.png"); diff --git a/src-tauri/src/shortcut.rs b/src-tauri/src/shortcut.rs index 5fd96a49..bb77de2b 100644 --- a/src-tauri/src/shortcut.rs +++ b/src-tauri/src/shortcut.rs @@ -160,31 +160,3 @@ pub fn _get_shortcut(app: &AppHandle) -> String { ), } } - -#[tauri::command] -pub async fn check_shortcut_available(key: String) -> bool { - // 这里可以实现系统级的快捷键检查 - // 可以检查是否与其他应用的全局快捷键冲突 - // 返回 true 表示可用,false 表示已被占用 - - // 简单实现示例: - !is_system_shortcut(&key) -} - -fn is_system_shortcut(key: &str) -> bool { - let system_shortcuts = vec![ - "Command+C", - "Command+V", - "Command+X", - "Command+A", - "Command+Z", - "Control+C", - "Control+V", - "Control+X", - "Control+A", - "Control+Z", - // 添加更多系统快捷键 - ]; - - system_shortcuts.contains(&key) -} diff --git a/src/assets/app-icon.png b/src/assets/app-icon.png new file mode 100644 index 00000000..8d5776de Binary files /dev/null and b/src/assets/app-icon.png differ diff --git a/src/assets/hugo_site/blog.png b/src/assets/hugo_site/blog.png new file mode 100644 index 00000000..0b70c63c Binary files /dev/null and b/src/assets/hugo_site/blog.png differ diff --git a/src/assets/hugo_site/icon.png b/src/assets/hugo_site/icon.png new file mode 100644 index 00000000..a8833e4c Binary files /dev/null and b/src/assets/hugo_site/icon.png differ diff --git a/src/assets/hugo_site/news.png b/src/assets/hugo_site/news.png new file mode 100644 index 00000000..794ea4c7 Binary files /dev/null and b/src/assets/hugo_site/news.png differ diff --git a/src/assets/hugo_site/web.png b/src/assets/hugo_site/web.png new file mode 100644 index 00000000..88fe2d7f Binary files /dev/null and b/src/assets/hugo_site/web.png differ diff --git a/src/assets/hugo_site/web_page.png b/src/assets/hugo_site/web_page.png new file mode 100644 index 00000000..2581d662 Binary files /dev/null and b/src/assets/hugo_site/web_page.png differ diff --git a/src/assets/images/file_efault.png b/src/assets/images/file_efault.png new file mode 100644 index 00000000..dd6197e0 Binary files /dev/null and b/src/assets/images/file_efault.png differ diff --git a/src/assets/images/source_default.png b/src/assets/images/source_default.png new file mode 100644 index 00000000..0816f0f7 Binary files /dev/null and b/src/assets/images/source_default.png differ diff --git a/src/assets/images/source_default_dark.png b/src/assets/images/source_default_dark.png new file mode 100644 index 00000000..9d9593c1 Binary files /dev/null and b/src/assets/images/source_default_dark.png differ diff --git a/src/components/AppAI/DropdownList.tsx b/src/components/AppAI/DropdownList.tsx index 7cdc6304..d2023e5f 100644 --- a/src/components/AppAI/DropdownList.tsx +++ b/src/components/AppAI/DropdownList.tsx @@ -1,28 +1,67 @@ import { useEffect, useRef, useState } from "react"; -import { CircleAlert, Bolt, X } from "lucide-react"; +import { + CircleAlert, + Bolt, + X, + SquareArrowRight, + // UserRoundPen, +} from "lucide-react"; + import { isTauri } from "@tauri-apps/api/core"; import { open } from "@tauri-apps/plugin-shell"; import { useAppStore } from "@/stores/appStore"; +import { useSearchStore } from "@/stores/searchStore"; +import source_default_img from "@/assets/images/source_default.png"; +import source_default_dark_img from "@/assets/images/source_default_dark.png"; +import file_efault_img from "@/assets/images/file_efault.png"; +import { useTheme } from "@/contexts/ThemeContext"; + +type ISearchData = Record; interface DropdownListProps { selected: (item: any) => void; suggests: any[]; + SearchData: ISearchData; IsError: boolean; isSearchComplete: boolean; + isChatMode: boolean; } -function DropdownList({ selected, suggests, IsError }: DropdownListProps) { +function DropdownList({ + selected, + suggests, + SearchData, + IsError, + isChatMode, +}: DropdownListProps) { + let globalIndex = 0; + const globalItemIndexMap: any[] = []; + // const letterFirstIndex: any = { + // a: 0, + // s: 0, + // d: 0, + // f: 0, + // }; + + const { theme } = useTheme(); + const connector_data = useAppStore((state) => state.connector_data); const datasourceData = useAppStore((state) => state.datasourceData); const endpoint_http = useAppStore((state) => state.endpoint_http); + const setSourceData = useSearchStore((state) => state.setSourceData); const [showError, setShowError] = useState(IsError); const [selectedItem, setSelectedItem] = useState(null); + const [selectedName, setSelectedName] = useState(""); const [showIndex, setShowIndex] = useState(false); const containerRef = useRef(null); const itemRefs = useRef<(HTMLDivElement | null)[]>([]); + useEffect(() => { + isChatMode && setSelectedItem(null); + }, [isChatMode]); + const handleOpenURL = async (url: string) => { if (!url) return; try { @@ -46,9 +85,12 @@ function DropdownList({ selected, suggests, IsError }: DropdownListProps) { if (e.key === "ArrowUp") { e.preventDefault(); - setSelectedItem((prev) => - prev === null || prev === 0 ? suggests.length - 1 : prev - 1 - ); + setSelectedItem((prev) => { + const res = + prev === null || prev === 0 ? suggests.length - 1 : prev - 1; + + return res; + }); } else if (e.key === "ArrowDown") { e.preventDefault(); setSelectedItem((prev) => @@ -56,12 +98,22 @@ function DropdownList({ selected, suggests, IsError }: DropdownListProps) { ); } else if (e.key === "Meta") { e.preventDefault(); + if (selectedItem !== null) { + const item = globalItemIndexMap[selectedItem]; + setSelectedName(item?._source?.source?.name); + } setShowIndex(true); } + if (e.key === "ArrowRight" && selectedItem !== null) { + e.preventDefault(); + const item = globalItemIndexMap[selectedItem]; + goToTwoPage(item); + } + if (e.key === "Enter" && selectedItem !== null) { // console.log("Enter key pressed", selectedItem); - const item = suggests[selectedItem]; + const item = globalItemIndexMap[selectedItem]; if (item?._source?.url) { handleOpenURL(item?._source?.url); } else { @@ -71,7 +123,7 @@ function DropdownList({ selected, suggests, IsError }: DropdownListProps) { if (e.key >= "0" && e.key <= "9" && showIndex) { // console.log(`number ${e.key}`); - const item = suggests[parseInt(e.key, 10)]; + const item = globalItemIndexMap[parseInt(e.key, 10)]; if (item?._source?.url) { handleOpenURL(item?._source?.url); } else { @@ -108,26 +160,87 @@ function DropdownList({ selected, suggests, IsError }: DropdownListProps) { } }, [selectedItem]); - function getIcon(_source: any) { - const id = _source?.source?.id || ""; + function findConnectorIcon(item: any) { + const id = item?._source?.source?.id || ""; - const result = datasourceData.find((item: any) => item._source.id === id); - - const connector_id = result?._source?.connector?.id; - - const result1 = connector_data.find( - (item: any) => item._source.id === connector_id + const result_source = datasourceData.find( + (data: any) => data._source.id === id ); - const icons = result1?._source?.assets?.icons || {}; + const connector_id = result_source?._source?.connector?.id; - if (icons[_source.icon]?.includes("http")) { - return icons[_source.icon]; + const result_connector = connector_data.find( + (data: any) => data._source.id === connector_id + ); + + return result_connector?._source; + } + + function getTypeIcon(item: any) { + const connectorSource = findConnectorIcon(item); + const icons = connectorSource?.icon; + + if (!icons) { + return theme === "dark" ? source_default_dark_img : source_default_img; + } + + if (icons?.includes("http")) { + return icons; } else { - return endpoint_http + icons[_source.icon]; + return endpoint_http + icons; } } + function getIcon(item: any) { + const connectorSource = findConnectorIcon(item); + const icons = connectorSource?.assets?.icons || {}; + + const selectedIcon = icons[item?._source?.icon]; + + if (!selectedIcon) { + return file_efault_img; + } + + if (selectedIcon?.includes("http")) { + return selectedIcon; + } else { + return endpoint_http + selectedIcon; + } + } + + function getRichIcon(item: any) { + const connectorSource = findConnectorIcon(item); + const icons = connectorSource?.assets?.icons || {}; + + const selectedIcon = icons[item?._source?.rich_categories?.[0]?.icon]; + + if (!selectedIcon) { + return theme === "dark" ? source_default_dark_img : source_default_img; + } + + if (selectedIcon?.includes("http")) { + return selectedIcon; + } else { + return endpoint_http + selectedIcon; + } + } + + function goToTwoPage(item: any) { + setSourceData(item); + selected && selected(item); + } + + // function numberToLetter(num: number): string { + // const mapping = ["A", "S", "D", "F"]; + // if (num >= 0 && num < mapping.length) { + // const letter = mapping[num]; + // letterFirstIndex[letter.toLocaleLowerCase()] = globalIndex + // return letter + // } else { + // return ""; + // } + // } + return (
) : null} -
Results
- {suggests?.map((item, index) => { - const isSelected = selectedItem === index; - return ( -
(itemRefs.current[index] = el)} - onMouseEnter={() => setSelectedItem(index)} - onClick={() => { - if (item?._source?.url) { - handleOpenURL(item?._source?.url); - } else { - selected(item); - } - }} - className={`w-full px-2 py-2.5 text-sm flex items-center justify-between rounded-lg transition-colors ${ - isSelected - ? "bg-[rgba(0,0,0,0.1)] dark:bg-[rgba(255,255,255,0.1)] hover:bg-[rgba(0,0,0,0.1)] dark:hover:bg-[rgba(255,255,255,0.1)]" - : "" - }`} - > -
- icon ( +
+ {items.length > 2 ? ( +
+ icon + {sourceName} +
+ goToTwoPage(items[0])} /> - - {item?._source?.title} - -
-
- - {item?._source?.source?.name} - - {showIndex && index < 10 ? ( + {showIndex && sourceName === selectedName ? (
- {index} + →
) : null}
-
- ); - })} + ) : null} + {items.map((item: any) => { + const isSelected = selectedItem === globalIndex; + const currentIndex = globalIndex; + globalItemIndexMap.push(item); + globalIndex++; + return ( +
(itemRefs.current[currentIndex] = el)} + onMouseEnter={() => setSelectedItem(currentIndex)} + onClick={() => { + if (item?._source?.url) { + handleOpenURL(item?._source?.url); + } else { + selected(item); + } + }} + className={`w-full px-2 py-2.5 text-sm flex items-center justify-between rounded-lg transition-colors ${ + isSelected + ? "text-white bg-[#950599] hover:bg-[#950599]" + : "text-[#333] dark:text-[#d8d8d8]" + }`} + > +
+ icon + + {item?._source?.title} + +
+
+ + {(item?._source?.category || "") + + (item?._source?.subcategory + ? `/${item?._source?.subcategory}` + : "")} + + {item?._source?.rich_categories ? ( +
+ icon { + e.stopPropagation(); + goToTwoPage(item); + }} + /> + {item?._source?.rich_categories?.map((rich_item: any) => ( + + {rich_item?.label} + + ))} +
+ ) : null} + + {isSelected ? ( +
+ ↩︎ +
+ ) : null} + + {showIndex && currentIndex < 10 ? ( +
+ {currentIndex} +
+ ) : null} +
+
+ ); + })} +
+ ))}
); } diff --git a/src/components/AppAI/Footer.tsx b/src/components/AppAI/Footer.tsx index 85624b74..249ac693 100644 --- a/src/components/AppAI/Footer.tsx +++ b/src/components/AppAI/Footer.tsx @@ -5,7 +5,12 @@ import { CornerDownLeft, } from "lucide-react"; -import logoImg from "@/assets/32x32.png"; +import logoImg from "@/assets/app-icon.png"; +import source_default_img from "@/assets/images/source_default.png"; +import source_default_dark_img from "@/assets/images/source_default_dark.png"; +import { useSearchStore } from "@/stores/searchStore"; +import { useAppStore } from "@/stores/appStore"; +import { useTheme } from "@/contexts/ThemeContext"; interface FooterProps { isChat: boolean; @@ -13,16 +18,58 @@ interface FooterProps { } export default function Footer({ name }: FooterProps) { + const sourceData = useSearchStore((state) => state.sourceData); + const connector_data = useAppStore((state) => state.connector_data); + const datasourceData = useAppStore((state) => state.datasourceData); + const endpoint_http = useAppStore((state) => state.endpoint_http); + + const { theme } = useTheme(); + + function findConnectorIcon(item: any) { + const id = item?._source?.source?.id || ""; + + const result_source = datasourceData.find( + (data: any) => data._source.id === id + ); + + const connector_id = result_source?._source?.connector?.id; + + const result_connector = connector_data.find( + (data: any) => data._source.id === connector_id + ); + + return result_connector?._source; + } + + function getTypeIcon(item: any) { + const connectorSource = findConnectorIcon(item); + const icons = connectorSource?.icon; + + if (!icons) { + return theme === "dark" ? source_default_dark_img : source_default_img; + } + + if (icons?.includes("http")) { + return icons; + } else { + return endpoint_http + icons; + } + } + return (
-
- +
+ {sourceData?._source?.source?.name ? ( + icon + ) : ( + + )} - Version 1.0.0 + {sourceData?._source?.source?.name || "Version 1.0.0"}
@@ -36,12 +83,18 @@ export default function Footer({ name }: FooterProps) {
Quick open - - + + + + + +
Open - + + +
diff --git a/src/components/AppAI/InputBox.tsx b/src/components/AppAI/InputBox.tsx index d22234f3..0872afd8 100644 --- a/src/components/AppAI/InputBox.tsx +++ b/src/components/AppAI/InputBox.tsx @@ -1,4 +1,12 @@ -import { Library, Mic, Send, Plus, AudioLines, Image } from "lucide-react"; +import { + Library, + Mic, + Send, + Plus, + AudioLines, + Image, + SquareArrowLeft, +} from "lucide-react"; import { useRef, useState, useEffect, useCallback } from "react"; import { listen } from "@tauri-apps/api/event"; import { isTauri } from "@tauri-apps/api/core"; @@ -8,6 +16,7 @@ import AutoResizeTextarea from "./AutoResizeTextarea"; import { useChatStore } from "@/stores/chatStore"; import StopIcon from "@/icons/Stop"; import { useAppStore } from "@/stores/appStore"; +import { useSearchStore } from "@/stores/searchStore"; interface ChatInputProps { onSend: (message: string) => void; @@ -32,6 +41,13 @@ export default function ChatInput({ }: ChatInputProps) { const showTooltip = useAppStore((state) => state.showTooltip); + const sourceData = useSearchStore((state) => state.sourceData); + const setSourceData = useSearchStore((state) => state.setSourceData); + + useEffect(() => { + setSourceData(undefined); + }, []); + const inputRef = useRef(null); const textareaRef = useRef<{ reset: () => void; focus: () => void }>(null); @@ -65,11 +81,14 @@ export default function ChatInput({ case "KeyI": handleToggleFocus(); break; + case "ArrowLeft": + setSourceData(undefined); + break; case "KeyM": console.log("KeyM"); break; case "Enter": - isChatMode && handleSubmit(); + isChatMode && (curChatEnd ? handleSubmit() : disabledChange()); break; case "KeyO": console.log("KeyO"); @@ -139,7 +158,7 @@ export default function ChatInput({ const [countdown, setCountdown] = useState(5); useEffect(() => { - if (connected) return + if (connected) return; if (countdown <= 0) { ReconnectClick(); return; @@ -153,14 +172,21 @@ export default function ChatInput({ }, [countdown, connected]); const ReconnectClick = () => { - setCountdown(5) - reconnect() - } + setCountdown(5); + reconnect(); + }; return (
-
+
+ {!isChatMode && sourceData ? ( + setSourceData(undefined)} + /> + ) : null} + {isChatMode ? ( )} + {showTooltip && isCommandPressed && !isChatMode && sourceData ? ( +
+ ← +
+ ) : null} {showTooltip && isCommandPressed ? (
- ⌘ + I + I
) : null}
@@ -237,28 +272,31 @@ export default function ChatInput({ {showTooltip && isChatMode && isCommandPressed ? (
- ⌘ + M + M
) : null} {showTooltip && isChatMode && isCommandPressed ? (
- ⌘ + ↩︎ + ↩︎
) : null} {!connected && isChatMode ? (
Unable to connect to the server -
+
Reconnect ({countdown})
- ): null} + ) : null}
{showTooltip && isCommandPressed ? (
- ⌘ + O + O
) : null} {showTooltip && isCommandPressed ? (
- ⌘ + U + U
) : null}
@@ -306,27 +344,27 @@ export default function ChatInput({ {showTooltip && isCommandPressed ? (
- ⌘ + N + N
) : null} {showTooltip && isCommandPressed ? (
- ⌘ + G + G
) : null}
)} -
+
{showTooltip && isCommandPressed ? (
- ⌘ + T + T
) : null} { value && disabledChange(); changeMode(value); + setSourceData(undefined); }} />
diff --git a/src/components/AppAI/Search.tsx b/src/components/AppAI/Search.tsx index ee879239..317ef770 100644 --- a/src/components/AppAI/Search.tsx +++ b/src/components/AppAI/Search.tsx @@ -8,6 +8,9 @@ import Footer from "./Footer"; import { tauriFetch } from "@/api/tauriFetchClient"; import noDataImg from "@/assets/coconut-tree.png"; import { useAppStore } from "@/stores/appStore"; +// import { res_search } from "@/mock/index"; +import { SearchResults } from "../SearchChat/SearchResults"; +import { useSearchStore } from "@/stores/searchStore"; interface SearchProps { changeInput: (val: string) => void; @@ -18,8 +21,11 @@ interface SearchProps { function Search({ isChatMode, input }: SearchProps) { const appStore = useAppStore(); + const sourceData = useSearchStore((state) => state.sourceData); + const [IsError, setIsError] = useState(false); const [suggests, setSuggests] = useState([]); + const [SearchData, setSearchData] = useState({}); const [isSearchComplete, setIsSearchComplete] = useState(false); const [selectedItem, setSelectedItem] = useState(); @@ -55,13 +61,18 @@ function Search({ isChatMode, input }: SearchProps) { const getSuggest = async () => { if (!input) return; // - // const list = []; - // for (let i = 0; i < input.length; i++) { - // list.push({ - // _source: { url: `https://www.google.com/search?q=${i}`, _id: i }, - // }); - // } + // mock + // let list = res_search?.hits?.hits; // setSuggests(list); + // const search_data = list.reduce((acc: any, item) => { + // const name = item._source.source.name; + // if (!acc[name]) { + // acc[name] = []; + // } + // acc[name].push(item); + // return acc; + // }, {}); + // setSearchData(search_data); // return; // try { @@ -72,10 +83,20 @@ function Search({ isChatMode, input }: SearchProps) { }); console.log("_suggest", input, response); - const data = response.data?.hits?.hits || []; + let data = response.data?.hits?.hits || []; setSuggests(data); - setIsError(false); + const search_data = data.reduce((acc: any, item: any) => { + const name = item?._source?.source?.name; + if (!acc[name]) { + acc[name] = []; + } + acc[name].push(item); + return acc; + }, {}); + setSearchData(search_data); + + setIsError(false); setIsSearchComplete(true); } catch (error) { setSuggests([]); @@ -95,7 +116,7 @@ function Search({ isChatMode, input }: SearchProps) { const debouncedSearch = useCallback(debounce(getSuggest, 300), [input]); useEffect(() => { - !isChatMode && debouncedSearch(); + !isChatMode && !sourceData && debouncedSearch(); if (!input) setSuggests([]); }, [input]); @@ -103,12 +124,18 @@ function Search({ isChatMode, input }: SearchProps) {
{/* Search Results Panel */} {suggests.length > 0 ? ( - setSelectedItem(item)} - /> + sourceData ? ( + + ) : ( + setSelectedItem(item)} + /> + ) ) : (
= ({ isChatMode, onChange }) => { role="switch" aria-checked={isChatMode} className={`relative flex items-center justify-between w-10 h-[18px] rounded-full cursor-pointer transition-colors duration-300 ${ - isChatMode ? "bg-[#0072ff]" : "bg-[#6000FF]" + isChatMode ? "bg-[#0072ff]" : "bg-[#950599]" }`} onClick={handleToggle} > diff --git a/src/components/SearchChat/DocumentDetail.tsx b/src/components/SearchChat/DocumentDetail.tsx index 8b178e99..5576568b 100644 --- a/src/components/SearchChat/DocumentDetail.tsx +++ b/src/components/SearchChat/DocumentDetail.tsx @@ -1,78 +1,138 @@ import React from "react"; -import { Calendar, User, Clock } from "lucide-react"; + +import { useAppStore } from "@/stores/appStore"; +import {formatter} from "@/utils/index" +import source_default_img from "@/assets/images/source_default.png"; +import source_default_dark_img from "@/assets/images/source_default_dark.png"; +import { useTheme } from "@/contexts/ThemeContext"; interface DocumentDetailProps { - documentId?: string; + document: any; } -export const DocumentDetail: React.FC = ({ - documentId, -}) => { - if (!documentId) { - return ( -
- 请选择一个文档查看详情 -
+export const DocumentDetail: React.FC = ({ document }) => { + const connector_data = useAppStore((state) => state.connector_data); + const datasourceData = useAppStore((state) => state.datasourceData); + const endpoint_http = useAppStore((state) => state.endpoint_http); + + const { theme } = useTheme(); + + function findConnectorIcon(item: any) { + const id = item?._source?.source?.id || ""; + + const result_source = datasourceData.find( + (data: any) => data._source.id === id ); + + const connector_id = result_source?._source?.connector?.id; + + const result_connector = connector_data.find( + (data: any) => data._source.id === connector_id + ); + + return result_connector?._source; + } + + function getTypeIcon(item: any) { + const connectorSource = findConnectorIcon(item); + const icons = connectorSource?.icon; + + if (!icons) { + return theme === "dark" ? source_default_dark_img : source_default_img; + } + + if (icons?.includes("http")) { + return icons; + } else { + return endpoint_http + icons; + } } return ( -
-
-

- 产品需求规划文档 -

- -
-
-
- - 2024-02-20 -
-
- - 张小明 -
-
-
-
- - 最近更新于 2小时前 -
-
-
+
+
+ Details
- +