diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 7a61d628..78f0e48f 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -2,7 +2,7 @@ "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", "description": "Capability for the main window", - "windows": ["main", "chat", "settings", "check", "selection"], + "windows": ["main", "chat", "settings", "check", "selection", "view_extension"], "permissions": [ "core:default", "core:event:allow-emit", diff --git a/src-tauri/src/common/mod.rs b/src-tauri/src/common/mod.rs index 034dc553..26d675fa 100644 --- a/src-tauri/src/common/mod.rs +++ b/src-tauri/src/common/mod.rs @@ -14,3 +14,4 @@ pub mod traits; pub static MAIN_WINDOW_LABEL: &str = "main"; pub static SETTINGS_WINDOW_LABEL: &str = "settings"; pub static CHECK_WINDOW_LABEL: &str = "check"; +pub static VIEW_EXTENSION_WINDOW_LABEL: &str = "view_extension"; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index e16ed86d..9c1b2fd1 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -12,7 +12,9 @@ mod shortcut; pub mod util; use crate::common::register::SearchSourceRegistry; -use crate::common::{CHECK_WINDOW_LABEL, MAIN_WINDOW_LABEL, SETTINGS_WINDOW_LABEL}; +use crate::common::{ + CHECK_WINDOW_LABEL, MAIN_WINDOW_LABEL, SETTINGS_WINDOW_LABEL, VIEW_EXTENSION_WINDOW_LABEL, +}; use crate::server::servers::{ load_or_insert_default_server, load_servers_token, start_bg_heartbeat_worker, }; @@ -23,7 +25,8 @@ use lazy_static::lazy_static; use std::sync::Mutex; use std::sync::OnceLock; use tauri::{ - AppHandle, Emitter, LogicalPosition, Manager, PhysicalPosition, WebviewWindow, WindowEvent, + AppHandle, Emitter, LogicalPosition, Manager, PhysicalPosition, WebviewUrl, WebviewWindow, + WebviewWindowBuilder, WindowEvent, }; use tauri_plugin_autostart::MacosLauncher; @@ -98,6 +101,7 @@ pub fn run() { show_coco, hide_coco, show_settings, + show_view_extension, show_check, hide_check, server::servers::add_coco_server, @@ -394,6 +398,36 @@ async fn show_settings(app_handle: AppHandle) { window.set_focus().unwrap(); } +#[tauri::command] +async fn show_view_extension(app_handle: AppHandle) { + log::debug!("view extension menu item was clicked"); + let label = VIEW_EXTENSION_WINDOW_LABEL; + + if let Some(window) = app_handle.get_webview_window(label) { + window.show().unwrap(); + window.unminimize().unwrap(); + window.set_focus().unwrap(); + return; + } + + // If window doesn't exist (e.g. was closed), create it + let url = WebviewUrl::App("/ui/view-extension".into()); + let build_result = WebviewWindowBuilder::new(&app_handle, label, url) + .title("View Extension") + .inner_size(1000.0, 800.0) + .min_inner_size(800.0, 600.0) + .resizable(true) + .visible(true) + .build(); + + match build_result { + Ok(win) => { + let _ = win.set_focus(); + } + Err(e) => log::error!("Failed to create view extension window: {}", e), + } +} + #[tauri::command] async fn show_check(app_handle: AppHandle) { log::debug!("check menu item was clicked"); diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 5ad8899d..923e23a6 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -79,6 +79,24 @@ "radius": 7 } }, + { + "label": "view_extension", + "title": "View Extension", + "url": "/ui/view-extension", + "width": 1000, + "minWidth": 800, + "height": 800, + "minHeight": 600, + "center": true, + "decorations": true, + "transparent": false, + "maximizable": true, + "skipTaskbar": false, + "dragDropEnabled": false, + "hiddenTitle": true, + "visible": false, + "resizable": true + }, { "label": "selection", "title": "Selection", diff --git a/src/commands/system.ts b/src/commands/system.ts index f1893359..0e392741 100644 --- a/src/commands/system.ts +++ b/src/commands/system.ts @@ -28,6 +28,10 @@ export function show_settings(): Promise { return invoke('show_settings'); } +export function show_view_extension(): Promise { + return invoke('show_view_extension'); +} + export function show_check(): Promise { return invoke('show_check'); } diff --git a/src/components/Search/Search.tsx b/src/components/Search/Search.tsx index 2efc4f7c..5112cb18 100644 --- a/src/components/Search/Search.tsx +++ b/src/components/Search/Search.tsx @@ -4,6 +4,7 @@ import clsx from "clsx"; import DropdownList from "./DropdownList"; import { SearchResults } from "@/components/Search/SearchResults"; import { useSearchStore } from "@/stores/searchStore"; +import { useExtensionStore } from "@/stores/extensionStore"; import ContextMenu from "./ContextMenu"; import { NoResults } from "@/components/Common/UI/NoResults"; import Footer from "@/components/Common/UI/Footer"; @@ -52,9 +53,9 @@ const SearchResultsPanel = memo<{ const { setSelectedAssistant, selectedSearchContent, - visibleExtensionStore, - viewExtensionOpened, + visibleExtensionStore } = useSearchStore(); + const viewExtensionOpened = useExtensionStore((state) => state.viewExtensionOpened); useEffect(() => { if (selectedSearchContent?.type === "AI Assistant") { diff --git a/src/components/Search/SearchIcons.tsx b/src/components/Search/SearchIcons.tsx index 28e0773b..cefee51c 100644 --- a/src/components/Search/SearchIcons.tsx +++ b/src/components/Search/SearchIcons.tsx @@ -1,4 +1,3 @@ -import { useSearchStore } from "@/stores/searchStore"; import { ChevronLeft, Search } from "lucide-react"; import { FC } from "react"; import clsx from "clsx"; @@ -7,6 +6,8 @@ import FontIcon from "@/components/Common/Icons/FontIcon"; import lightDefaultIcon from "@/assets/images/source_default.png"; import darkDefaultIcon from "@/assets/images/source_default_dark.png"; import { useThemeStore } from "@/stores/themeStore"; +import { useSearchStore } from "@/stores/searchStore"; +import { useExtensionStore } from "@/stores/extensionStore"; import platformAdapter from "@/utils/platformAdapter"; import { navigateBack, visibleSearchBar } from "@/utils"; import VisibleKey from "../Common/VisibleKey"; @@ -74,9 +75,10 @@ export default function SearchIcons({ goAskAi, visibleExtensionStore, visibleExtensionDetail, - selectedExtension, - viewExtensionOpened, + selectedExtension } = useSearchStore(); + + const viewExtensionOpened = useExtensionStore((state) => state.viewExtensionOpened); if (isChatMode) { return null; diff --git a/src/components/Search/ViewExtension.tsx b/src/components/Search/ViewExtension.tsx index 1976893a..84a3a1a0 100644 --- a/src/components/Search/ViewExtension.tsx +++ b/src/components/Search/ViewExtension.tsx @@ -1,30 +1,35 @@ import React from "react"; import { useState, useEffect, useMemo } from "react"; import { useTranslation } from "react-i18next"; -import { Maximize2, Minimize2, Focus } from "lucide-react"; +import { Focus, ExternalLink } from "lucide-react"; -import { useSearchStore } from "@/stores/searchStore"; +import { useExtensionStore } from "@/stores/extensionStore"; import platformAdapter from "@/utils/platformAdapter"; import { useShortcutsStore } from "@/stores/shortcutsStore"; import { useViewExtensionWindow } from "@/hooks/useViewExtensionWindow"; import ViewExtensionIframe from "./ViewExtensionIframe"; import { apiPermissionCheck, fsPermissionCheck } from "./viewExtensionPermissions"; -const ViewExtension: React.FC = () => { - const { viewExtensionOpened } = useSearchStore(); +type ControlsProps = { + showFullscreen?: boolean; + showDetach?: boolean; + showFocus?: boolean; + forceResizable?: boolean; +}; + +const ViewExtensionContent: React.FC = ({ + showFullscreen = true, + showDetach = true, + showFocus = true, + forceResizable = false, +}) => { + const viewExtensionOpened = useExtensionStore((state) => state.viewExtensionOpened); // Complete list of the backend APIs, grouped by their category. const [apis, setApis] = useState | null>(null); const { setModifierKeyPressed } = useShortcutsStore(); const { t } = useTranslation(); - if (viewExtensionOpened == null) { - // When this view gets loaded, this state should not be NULL. - throw new Error( - "ViewExtension Error: viewExtensionOpened is null. This should not happen." - ); - } - // invoke `apis()` and set the state useEffect(() => { setModifierKeyPressed(false); @@ -44,7 +49,7 @@ const ViewExtension: React.FC = () => { }, []); // White list of the permission entries - const permission = viewExtensionOpened[3]; + const permission = viewExtensionOpened![3]; // apis is in format {"category": ["api1", "api2"]}, to make the permission check // easier, reverse the map key values: {"api1": "category", "api2": "category"} @@ -165,47 +170,39 @@ const ViewExtension: React.FC = () => { }; }, [reversedApis, permission]); // Add apiPermissions as dependency - const fileUrl = viewExtensionOpened[2]; + const fileUrl = viewExtensionOpened![2]; const { resizable, + detachable, hideScrollbar, scale, iframeRef, - isFullscreen, - toggleFullscreen, focusIframe, - } = useViewExtensionWindow(); + } = useViewExtensionWindow({ forceResizable }); return (
- {resizable && ( - - )} - {/* Focus helper button */} - {resizable && ( - - )} +
+ {resizable && showFocus && ( + + )} + {((detachable && showDetach) || (resizable && showFullscreen)) && ( + + )} +
{ ); }; +const ViewExtension: React.FC = (props) => { + const viewExtensionOpened = useExtensionStore((state) => state.viewExtensionOpened); + + if (viewExtensionOpened == null) { + return ( +
+ Loading... +
+ ); + } + + return ; +}; + export default ViewExtension; diff --git a/src/components/Search/ViewExtensionIframe.tsx b/src/components/Search/ViewExtensionIframe.tsx index 9a9f8ccb..124ae183 100644 --- a/src/components/Search/ViewExtensionIframe.tsx +++ b/src/components/Search/ViewExtensionIframe.tsx @@ -49,8 +49,24 @@ type ViewExtensionIframeProps = { export default function ViewExtensionIframe(props: ViewExtensionIframeProps) { const { fileUrl, scale, iframeRef, hideScrollbar, focusIframe } = props; + const isSameOrigin = () => { + try { + const target = new URL(fileUrl); + const current = new URL(window.location.href); + return ( + target.protocol === current.protocol && + target.hostname === current.hostname && + target.port === current.port + ); + } catch { + return false; + } + }; + useEffect(() => { - applyHideScrollbarToIframe(iframeRef.current, hideScrollbar); + if (isSameOrigin()) { + applyHideScrollbarToIframe(iframeRef.current, hideScrollbar); + } }, [hideScrollbar, iframeRef]); return ( @@ -77,10 +93,12 @@ export default function ViewExtensionIframe(props: ViewExtensionIframeProps) { tabIndex={-1} onLoad={(event) => { event.currentTarget.focus(); - try { - iframeRef.current?.contentWindow?.focus(); - } catch {} - applyHideScrollbarToIframe(event.currentTarget, hideScrollbar); + if (isSameOrigin()) { + try { + iframeRef.current?.contentWindow?.focus(); + } catch {} + applyHideScrollbarToIframe(event.currentTarget, hideScrollbar); + } }} />
diff --git a/src/components/SearchChat/index.tsx b/src/components/SearchChat/index.tsx index 53731261..82ded109 100644 --- a/src/components/SearchChat/index.tsx +++ b/src/components/SearchChat/index.tsx @@ -41,6 +41,7 @@ import { } from "@/constants"; import { useChatStore } from "@/stores/chatStore"; import { useSearchStore } from "@/stores/searchStore"; +import { useExtensionStore } from "@/stores/extensionStore"; interface SearchChatProps { isTauri?: boolean; @@ -117,9 +118,10 @@ function SearchChat({ windowPositionRef.current = await window.outerPosition(); }, }); - + const setWindowSize = useCallback(() => { - const { viewExtensionOpened } = useSearchStore.getState(); + const { viewExtensionOpened } = useExtensionStore.getState(); + if (collapseWindowTimer.current) { clearTimeout(collapseWindowTimer.current); } diff --git a/src/constants/index.ts b/src/constants/index.ts index c5d1af69..8609bc44 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -13,6 +13,8 @@ export const MAIN_WINDOW_LABEL = "main"; export const SETTINGS_WINDOW_LABEL = "settings"; +export const VIEW_EXTENSION_WINDOW_LABEL = "view_extension"; + export const CHECK_WINDOW_LABEL = "check"; export const CHAT_WINDOW_LABEL = "chat"; diff --git a/src/hooks/useCanNavigateBack.ts b/src/hooks/useCanNavigateBack.ts index 2b1513e3..1da65fd8 100644 --- a/src/hooks/useCanNavigateBack.ts +++ b/src/hooks/useCanNavigateBack.ts @@ -1,14 +1,17 @@ -import { useSearchStore } from "@/stores/searchStore"; import { useMemo } from "react"; +import { useSearchStore } from "@/stores/searchStore"; +import { useExtensionStore } from "@/stores/extensionStore"; + export const useCanNavigateBack = () => { const { goAskAi, visibleExtensionStore, visibleExtensionDetail, - viewExtensionOpened, sourceData, } = useSearchStore(); + + const { viewExtensionOpened } = useExtensionStore.getState(); const canNavigateBack = useMemo(() => { return ( diff --git a/src/hooks/useEscape.ts b/src/hooks/useEscape.ts index a9ecb158..3716edd1 100644 --- a/src/hooks/useEscape.ts +++ b/src/hooks/useEscape.ts @@ -1,17 +1,20 @@ +import { useKeyPress } from "ahooks"; + import platformAdapter from "@/utils/platformAdapter"; import { useSearchStore } from "@/stores/searchStore"; -import { useKeyPress } from "ahooks"; +import { useExtensionStore } from "@/stores/extensionStore"; import { HISTORY_PANEL_ID } from "@/constants"; import { closeHistoryPanel } from "@/utils"; const useEscape = () => { const { setVisibleContextMenu } = useSearchStore(); + const viewExtensionOpened = useExtensionStore((state) => state.viewExtensionOpened); useKeyPress("esc", (event) => { event.preventDefault(); event.stopPropagation(); - const { visibleContextMenu, viewExtensionOpened } = + const { visibleContextMenu } = useSearchStore.getState(); if ( diff --git a/src/hooks/useViewExtensionWindow.ts b/src/hooks/useViewExtensionWindow.ts index 73bb0835..f4cb987f 100644 --- a/src/hooks/useViewExtensionWindow.ts +++ b/src/hooks/useViewExtensionWindow.ts @@ -4,7 +4,7 @@ import platformAdapter from "@/utils/platformAdapter"; import { isMac } from "@/utils/platform"; import type { ViewExtensionUISettingsOrNull } from "@/components/Settings/Extensions"; import { useAppStore } from "@/stores/appStore"; -import { useSearchStore } from "@/stores/searchStore"; +import { useExtensionStore } from "@/stores/extensionStore"; type WindowSnapshot = { width: number; @@ -14,9 +14,9 @@ type WindowSnapshot = { y: number; }; -export function useViewExtensionWindow() { +export function useViewExtensionWindow(opts?: { forceResizable?: boolean }) { const isTauri = useAppStore((state) => state.isTauri); - const viewExtensionOpened = useSearchStore((state) => state.viewExtensionOpened); + const viewExtensionOpened = useExtensionStore((state) => state.viewExtensionOpened); if (viewExtensionOpened == null) { throw new Error( @@ -27,8 +27,9 @@ export function useViewExtensionWindow() { const ui: ViewExtensionUISettingsOrNull = useMemo(() => { return viewExtensionOpened[4] as ViewExtensionUISettingsOrNull; }, [viewExtensionOpened]); - const resizable = ui?.resizable; + const resizable = opts?.forceResizable ? true : ui?.resizable; const hideScrollbar = ui?.hide_scrollbar ?? true; + const detachable = ui?.detachable ?? false; const uiWidth = ui && typeof ui.width === "number" ? ui.width : null; const uiHeight = ui && typeof ui.height === "number" ? ui.height : null; @@ -181,7 +182,11 @@ export function useViewExtensionWindow() { if (hasExplicitWindowSize) { const nextResizable = - ui && typeof ui.resizable === "boolean" ? ui.resizable : true; + opts?.forceResizable + ? true + : ui && typeof ui.resizable === "boolean" + ? ui.resizable + : true; await platformAdapter.setWindowSize(uiWidth, uiHeight); await platformAdapter.setWindowResizable(nextResizable); await platformAdapter.centerOnCurrentMonitor(); @@ -234,6 +239,7 @@ export function useViewExtensionWindow() { return { ui, resizable, + detachable, hideScrollbar, scale, iframeRef, diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 7c1c55a1..d4bc6b90 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -647,7 +647,8 @@ "enter": "Enter Full Screen", "exit": "Exit Full Screen" }, - "focus": "Focus" + "focus": "Focus", + "detach": "Detach" }, "deleteDialog": { "button": { diff --git a/src/locales/zh/translation.json b/src/locales/zh/translation.json index 88f74a72..7563db55 100644 --- a/src/locales/zh/translation.json +++ b/src/locales/zh/translation.json @@ -646,7 +646,8 @@ "enter": "进入全屏", "exit": "退出全屏" }, - "focus": "聚焦" + "focus": "聚焦", + "detach": "分离" }, "deleteDialog": { "button": { diff --git a/src/pages/main/index.tsx b/src/pages/main/index.tsx index f76654bc..df028327 100644 --- a/src/pages/main/index.tsx +++ b/src/pages/main/index.tsx @@ -6,12 +6,13 @@ import { useSyncStore } from "@/hooks/useSyncStore"; import UpdateApp from "@/components/UpdateApp"; import Synthesize from "@/components/Assistant/Synthesize"; import { useChatStore } from "@/stores/chatStore"; -import { useSearchStore } from "@/stores/searchStore"; +import { useExtensionStore } from "@/stores/extensionStore"; import platformAdapter from "@/utils/platformAdapter"; function MainApp() { const { setIsTauri } = useAppStore(); - const { setViewExtensionOpened } = useSearchStore(); + + const setViewExtensionOpened = useExtensionStore((state) => state.setViewExtensionOpened); useEffect(() => { setIsTauri(true); diff --git a/src/pages/view-extension/index.tsx b/src/pages/view-extension/index.tsx new file mode 100644 index 00000000..117e830a --- /dev/null +++ b/src/pages/view-extension/index.tsx @@ -0,0 +1,13 @@ +import React from "react"; + +import ViewExtension from "@/components/Search/ViewExtension"; + +const ViewExtensionPage: React.FC = () => { + return ( +
+ +
+ ); +}; + +export default ViewExtensionPage; diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 5deda374..6ad854a2 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -9,6 +9,7 @@ const SettingsPage = lazy(() => import("@/pages/settings/index")); const StandaloneChat = lazy(() => import("@/pages/chat/index")); const CheckPage = lazy(() => import("@/pages/check/index")); const SelectionWindow = lazy(() => import("@/pages/selection/index")); +const ViewExtensionPage = lazy(() => import("@/pages/view-extension/index")); const routerOptions = { basename: "/", @@ -30,6 +31,7 @@ export const router = createBrowserRouter( { path: "/ui/chat", element: (}>) }, { path: "/ui/check", element: (}>) }, { path: "/ui/selection", element: (}>) }, + { path: "/ui/view-extension", element: (}>) }, ], }, ], diff --git a/src/routes/outlet.tsx b/src/routes/outlet.tsx index dad9eac1..c306535c 100644 --- a/src/routes/outlet.tsx +++ b/src/routes/outlet.tsx @@ -16,6 +16,7 @@ import { useIconfontScript } from "@/hooks/useScript"; import { Extension } from "@/components/Settings/Extensions"; import { useExtensionsStore } from "@/stores/extensionsStore"; import { useSelectionStore, startSelectionStorePersistence } from "@/stores/selectionStore"; +import { startExtensionStorePersistence } from "@/stores/extensionStore"; import { useServers } from "@/hooks/useServers"; import { useDeepLinkManager } from "@/hooks/useDeepLinkManager"; // import { useSelectionWindow } from "@/hooks/useSelectionWindow"; @@ -34,6 +35,9 @@ export default function LayoutOutlet() { useMount(() => { startSelectionStorePersistence(); }); + useMount(() => { + startExtensionStorePersistence(); + }); // init servers isTauri useServers(); diff --git a/src/stores/extensionStore.ts b/src/stores/extensionStore.ts new file mode 100644 index 00000000..c4d1705e --- /dev/null +++ b/src/stores/extensionStore.ts @@ -0,0 +1,47 @@ +import { create } from "zustand"; + +import platformAdapter from "@/utils/platformAdapter"; +import { + ExtensionPermission, + ViewExtensionUISettings, +} from "@/components/Settings/Extensions"; + +export type ViewExtensionOpened = [ + // Extension name + string, + // An absolute path to the extension icon or a font code. + string, + // HTML file URL + string, + ExtensionPermission | null, + ViewExtensionUISettings | null +]; + +type ExtensionStore = { + // When we open a View extension, we set this to a non-null value. + viewExtensionOpened?: ViewExtensionOpened; + setViewExtensionOpened: (showViewExtension?: ViewExtensionOpened) => void; +} + +// A Zustand store, like any other. +export const useExtensionStore = create((set) => ({ + setViewExtensionOpened: (viewExtensionOpened) => { + return set({ viewExtensionOpened }); + }, +})); + +/** + * Initialize Extension store persistence on Tauri only. + * In Web mode, this is a no-op to avoid loading Tauri-specific plugins. + * + * Returns a promise that resolves when persistence has been started on Tauri. + */ +export async function startExtensionStorePersistence(): Promise { + if (!platformAdapter.isTauri()) return; + + const { createTauriStore } = await import("@tauri-store/zustand"); + createTauriStore("extension-store", useExtensionStore, { + saveOnChange: true, + autoStart: true, + }); +} diff --git a/src/stores/searchStore.ts b/src/stores/searchStore.ts index f16d46fb..4b2657d0 100644 --- a/src/stores/searchStore.ts +++ b/src/stores/searchStore.ts @@ -1,23 +1,10 @@ import { SearchExtensionItem } from "@/components/Search/ExtensionStore"; -import { - ExtensionPermission, - ViewExtensionUISettings, -} from "@/components/Settings/Extensions"; import { AggregationBucket, Aggregations } from "@/types/search"; import { DateRange } from "react-day-picker"; import { create } from "zustand"; import { persist } from "zustand/middleware"; -export type ViewExtensionOpened = [ - // Extension name - string, - // An absolute path to the extension icon or a font code. - string, - // HTML file URL - string, - ExtensionPermission | null, - ViewExtensionUISettings | null -]; + export interface AggregateFilter { [key: string]: AggregationBucket[]; @@ -67,10 +54,6 @@ export type ISearchStore = { visibleExtensionDetail: boolean; setVisibleExtensionDetail: (visibleExtensionDetail: boolean) => void; - // When we open a View extension, we set this to a non-null value. - viewExtensionOpened?: ViewExtensionOpened; - setViewExtensionOpened: (showViewExtension?: ViewExtensionOpened) => void; - enabledFuzzyMatch: boolean; setEnabledFuzzyMatch: (enabledFuzzyMatch: boolean) => void; @@ -161,9 +144,6 @@ export const useSearchStore = create()( setVisibleExtensionDetail: (visibleExtensionDetail) => { return set({ visibleExtensionDetail }); }, - setViewExtensionOpened: (viewExtensionOpened) => { - return set({ viewExtensionOpened }); - }, enabledFuzzyMatch: false, setEnabledFuzzyMatch: (enabledFuzzyMatch) => { return set({ enabledFuzzyMatch }); diff --git a/src/types/platform.ts b/src/types/platform.ts index 9bc45586..1b732ff9 100644 --- a/src/types/platform.ts +++ b/src/types/platform.ts @@ -7,7 +7,7 @@ import { AppTheme } from "@/types/index"; import { SearchDocument } from "./search"; import { IAppStore } from "@/stores/appStore"; import type { Server } from "@/types/server"; -import { ViewExtensionOpened } from "@/stores/searchStore"; +import { ViewExtensionOpened } from "@/stores/extensionStore"; export interface EventPayloads { "theme-changed": string; diff --git a/src/utils/index.ts b/src/utils/index.ts index a096fa58..444da67e 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -10,6 +10,7 @@ import { } from "lodash-es"; import { filesize as filesizeLib } from "filesize"; import i18next from "i18next"; +import dayjs from "dayjs"; import platformAdapter from "./platformAdapter"; import { useAppStore } from "@/stores/appStore"; @@ -17,8 +18,8 @@ import { DEFAULT_COCO_SERVER_ID, HISTORY_PANEL_ID } from "@/constants"; import { useChatStore } from "@/stores/chatStore"; import { getCurrentWindowService } from "@/commands/windowService"; import { useSearchStore } from "@/stores/searchStore"; +import { useExtensionStore } from "@/stores/extensionStore"; import { MultiSourceQueryResponse } from "@/types/search"; -import dayjs from "dayjs"; export async function copyToClipboard(text: string, noTip = false) { const addError = useAppStore.getState().addError; @@ -229,9 +230,9 @@ export const canNavigateBack = () => { goAskAi, visibleExtensionStore, visibleExtensionDetail, - viewExtensionOpened, sourceData, } = useSearchStore.getState(); + const { viewExtensionOpened } = useExtensionStore.getState(); return ( goAskAi || @@ -247,14 +248,14 @@ export const navigateBack = () => { goAskAi, visibleExtensionStore, visibleExtensionDetail, - viewExtensionOpened, setGoAskAi, setVisibleExtensionDetail, setVisibleExtensionStore, setSourceData, - setViewExtensionOpened, } = useSearchStore.getState(); + const { viewExtensionOpened, setViewExtensionOpened } = useExtensionStore.getState(); + if (goAskAi) { return setGoAskAi(false); } @@ -304,8 +305,9 @@ export const dispatchEvent = ( }; export const visibleSearchBar = () => { - const { viewExtensionOpened, visibleExtensionDetail } = - useSearchStore.getState(); + const { visibleExtensionDetail } = useSearchStore.getState(); + + const { viewExtensionOpened } = useExtensionStore.getState(); if (visibleExtensionDetail) return false; @@ -318,12 +320,13 @@ export const visibleSearchBar = () => { export const visibleFilterBar = () => { const { - viewExtensionOpened, visibleExtensionStore, visibleExtensionDetail, goAskAi, } = useSearchStore.getState(); + const { viewExtensionOpened } = useExtensionStore.getState(); + if (visibleExtensionStore || visibleExtensionDetail || goAskAi) return false; if (isNil(viewExtensionOpened)) return true; @@ -334,7 +337,7 @@ export const visibleFilterBar = () => { }; export const visibleFooterBar = () => { - const { viewExtensionOpened } = useSearchStore.getState(); + const { viewExtensionOpened } = useExtensionStore.getState(); if (isNil(viewExtensionOpened)) return true;