feat: persist store across windows, right-align controls, and fullscreen new window

This commit is contained in:
rain9
2025-12-31 15:56:36 +08:00
parent 81a8631411
commit 8062e3fced
24 changed files with 258 additions and 101 deletions

View File

@@ -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",

View File

@@ -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";

View File

@@ -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");

View File

@@ -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",

View File

@@ -28,6 +28,10 @@ export function show_settings(): Promise<void> {
return invoke('show_settings');
}
export function show_view_extension(): Promise<void> {
return invoke('show_view_extension');
}
export function show_check(): Promise<void> {
return invoke('show_check');
}

View File

@@ -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") {

View File

@@ -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;

View File

@@ -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<ControlsProps> = ({
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<Map<string, string[]> | 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 (
<div className="relative w-full h-full">
{resizable && (
<button
aria-label={
isFullscreen
? t("viewExtension.fullscreen.exit")
: t("viewExtension.fullscreen.enter")
}
className="absolute top-2 right-2 z-10 rounded-md bg-black/40 text-white p-2 hover:bg-black/60 focus:outline-none"
onClick={toggleFullscreen}
>
{isFullscreen ? (
<Minimize2 className="size-4" />
) : (
<Maximize2 className="size-4" />
)}
</button>
)}
{/* Focus helper button */}
{resizable && (
<button
aria-label={t("viewExtension.focus")}
className="absolute top-2 right-12 z-10 rounded-md bg-black/40 text-white p-2 hover:bg-black/60 focus:outline-none"
onClick={focusIframe}
>
<Focus className="size-4" />
</button>
)}
<div className="absolute top-2 right-2 z-10 flex items-center gap-2">
{resizable && showFocus && (
<button
aria-label={t("viewExtension.focus")}
className="rounded-md bg-black/40 text-white p-2 hover:bg-black/60 focus:outline-none"
onClick={focusIframe}
>
<Focus className="size-4" />
</button>
)}
{((detachable && showDetach) || (resizable && showFullscreen)) && (
<button
aria-label={t("viewExtension.detach")}
className="rounded-md bg-black/40 text-white p-2 hover:bg-black/60 focus:outline-none"
onClick={() => platformAdapter.invokeBackend("show_view_extension")}
>
<ExternalLink className="size-4" />
</button>
)}
</div>
<ViewExtensionIframe
fileUrl={fileUrl}
scale={scale}
@@ -217,4 +214,18 @@ const ViewExtension: React.FC = () => {
);
};
const ViewExtension: React.FC<ControlsProps> = (props) => {
const viewExtensionOpened = useExtensionStore((state) => state.viewExtensionOpened);
if (viewExtensionOpened == null) {
return (
<div className="flex h-full w-full items-center justify-center text-muted-foreground">
Loading...
</div>
);
}
return <ViewExtensionContent {...props} />;
};
export default ViewExtension;

View File

@@ -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);
}
}}
/>
</div>

View File

@@ -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);
}

View File

@@ -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";

View File

@@ -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 (

View File

@@ -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 (

View File

@@ -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,

View File

@@ -647,7 +647,8 @@
"enter": "Enter Full Screen",
"exit": "Exit Full Screen"
},
"focus": "Focus"
"focus": "Focus",
"detach": "Detach"
},
"deleteDialog": {
"button": {

View File

@@ -646,7 +646,8 @@
"enter": "进入全屏",
"exit": "退出全屏"
},
"focus": "聚焦"
"focus": "聚焦",
"detach": "分离"
},
"deleteDialog": {
"button": {

View File

@@ -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);

View File

@@ -0,0 +1,13 @@
import React from "react";
import ViewExtension from "@/components/Search/ViewExtension";
const ViewExtensionPage: React.FC = () => {
return (
<div className="w-screen h-screen bg-background text-foreground overflow-hidden">
<ViewExtension showFullscreen={false} showDetach={false} showFocus forceResizable />
</div>
);
};
export default ViewExtensionPage;

View File

@@ -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: (<Suspense fallback={<></>}><StandaloneChat /></Suspense>) },
{ path: "/ui/check", element: (<Suspense fallback={<></>}><CheckPage /></Suspense>) },
{ path: "/ui/selection", element: (<Suspense fallback={<></>}><SelectionWindow /></Suspense>) },
{ path: "/ui/view-extension", element: (<Suspense fallback={<></>}><ViewExtensionPage /></Suspense>) },
],
},
],

View File

@@ -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();

View File

@@ -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<ExtensionStore>((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<void> {
if (!platformAdapter.isTauri()) return;
const { createTauriStore } = await import("@tauri-store/zustand");
createTauriStore("extension-store", useExtensionStore, {
saveOnChange: true,
autoStart: true,
});
}

View File

@@ -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<ISearchStore>()(
setVisibleExtensionDetail: (visibleExtensionDetail) => {
return set({ visibleExtensionDetail });
},
setViewExtensionOpened: (viewExtensionOpened) => {
return set({ viewExtensionOpened });
},
enabledFuzzyMatch: false,
setEnabledFuzzyMatch: (enabledFuzzyMatch) => {
return set({ enabledFuzzyMatch });

View File

@@ -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;

View File

@@ -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;