Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/content.en/docs/release-notes/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Information about release notes of Coco App is provided here.

- feat: support app search even if Spotlight is disabled #1028
- feat: read localized names from root InfoPlist.strings #1029
- feat: view extension supports detachable #1042

### 🐛 Bug fix

Expand All @@ -23,6 +24,7 @@ Information about release notes of Coco App is provided here.
- fix: fix arrow keys not working after closing the context menu #1035
- fix: fix some filter fields not working #1037
- fix: apply local results weight to scores generated by rerank() #1036
- fix: prevent window collapse when chat history is open #1039

### ✈️ Improvements

Expand Down
2 changes: 1 addition & 1 deletion src-tauri/capabilities/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -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", "view_extension_*"],
"permissions": [
"core:default",
"core:event:allow-emit",
Expand Down
11 changes: 8 additions & 3 deletions src-tauri/src/common/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,9 +291,14 @@ pub(crate) async fn open(
to_value(permission).unwrap(),
to_value(ui).unwrap(),
];
tauri_app_handle
.emit("open_view_extension", view_extension_opened)
.unwrap();
use crate::common::MAIN_WINDOW_LABEL;
if let Err(e) = tauri_app_handle.emit_to(
MAIN_WINDOW_LABEL,
"open_view_extension",
view_extension_opened,
) {
log::error!("Failed to emit open_view_extension event: {}", e);
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
58 changes: 56 additions & 2 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand All @@ -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;

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -394,6 +398,56 @@ async fn show_settings(app_handle: AppHandle) {
window.set_focus().unwrap();
}

#[tauri::command]
async fn show_view_extension(
app_handle: AppHandle,
label: Option<String>,
query: String,
width: Option<f64>,
height: Option<f64>,
title: Option<String>,
) -> Result<(), String> {
log::debug!("view extension menu item was clicked");
if !(query.contains("manual=1") && query.contains("ext=")) {
log::error!("Invalid query for view extension: manual=1 and ext= required");
return Err("invalid argument: manual=1 and ext= should be provided".into());
}
let window_label = label.unwrap_or_else(|| VIEW_EXTENSION_WINDOW_LABEL.to_string());

if let Some(window) = app_handle.get_webview_window(&window_label) {
window.show().unwrap();
window.unminimize().unwrap();
window.set_focus().unwrap();
return Ok(());
}

// If window doesn't exist (e.g. was closed), create it
let url_suffix = query;
let url = WebviewUrl::App(format!("/ui/view-extension{}", url_suffix).into());
let w = width.unwrap_or(1000.0);
let h = height.unwrap_or(800.0);

let build_result = WebviewWindowBuilder::new(&app_handle, &window_label, url)
.title(title.unwrap_or_else(|| "View Extension".to_string()))
.inner_size(w, h)
.min_inner_size(800.0, 600.0)
.resizable(true)
.center()
.visible(false)
.build();

match build_result {
Ok(win) => {
let _ = win.set_focus();
Ok(())
}
Err(e) => {
log::error!("Failed to create view extension window: {}", e);
Err(e.to_string())
}
}
}

#[tauri::command]
async fn show_check(app_handle: AppHandle) {
log::debug!("check menu item was clicked");
Expand Down
18 changes: 18 additions & 0 deletions src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
9 changes: 5 additions & 4 deletions src/components/Search/InputBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ import {
canNavigateBack,
getUploadedAttachmentsId,
isDefaultServer,
visibleFilterBar,
visibleSearchBar,
} from "@/utils";
import { useVisibleSearchBar, useVisibleFilterBar } from "@/hooks/useViewExtensionUI";
import { useTauriFocus } from "@/hooks/useTauriFocus";
import { SendMessageParams } from "../Assistant/Chat";
import { isEmpty } from "lodash-es";
Expand Down Expand Up @@ -92,6 +91,8 @@ export default function ChatInput({
getFileIcon,
}: ChatInputProps) {
const { t } = useTranslation();
const isVisibleSearchBar = useVisibleSearchBar();
const isVisibleFilterBar = useVisibleFilterBar();

const { currentAssistant } = useConnectStore();

Expand Down Expand Up @@ -311,7 +312,7 @@ export default function ChatInput({
className={`flex items-center dark:text-[#D8D8D8] rounded-md transition-all relative overflow-hidden`}
>
{lineCount === 1 && renderSearchIcon()}
{visibleSearchBar() && (
{isVisibleSearchBar && (
<div
className={clsx(
"min-h-10 w-full p-[7px] bg-[#ededed] dark:bg-[#202126]",
Expand All @@ -335,7 +336,7 @@ export default function ChatInput({
)}
</div>

{visibleFilterBar() && (
{isVisibleFilterBar && (
<InputControls
isChatMode={isChatMode}
isChatPage={isChatPage}
Expand Down
14 changes: 9 additions & 5 deletions src/components/Search/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -12,7 +13,7 @@ import { useSearch } from "@/hooks/useSearch";
import ExtensionStore from "./ExtensionStore";
import platformAdapter from "@/utils/platformAdapter";
import ViewExtension from "./ViewExtension";
import { visibleFooterBar } from "@/utils";
import { useVisibleFooterBar } from "@/hooks/useViewExtensionUI";

const SearchResultsPanel = memo<{
input: string;
Expand Down Expand Up @@ -52,9 +53,11 @@ const SearchResultsPanel = memo<{
const {
setSelectedAssistant,
selectedSearchContent,
visibleExtensionStore,
viewExtensionOpened,
visibleExtensionStore
} = useSearchStore();
const viewExtensionOpened = useExtensionStore((state) =>
state.viewExtensions.length > 0 ? state.viewExtensions[state.viewExtensions.length - 1] : undefined
);

useEffect(() => {
if (selectedSearchContent?.type === "AI Assistant") {
Expand Down Expand Up @@ -173,12 +176,13 @@ function Search({
formatUrl,
}: SearchProps) {
const mainWindowRef = useRef<HTMLDivElement>(null);
const isVisibleFooterBar = useVisibleFooterBar();

return (
<div
ref={mainWindowRef}
className={clsx("h-full w-full relative", {
"pb-8": visibleFooterBar(),
"pb-8": isVisibleFooterBar,
})}
>
<SearchResultsPanel
Expand All @@ -189,7 +193,7 @@ function Search({
formatUrl={formatUrl}
/>

{visibleFooterBar() && <Footer setIsPinnedWeb={setIsPinned} />}
{isVisibleFooterBar && <Footer setIsPinnedWeb={setIsPinned} />}

<ContextMenu formatUrl={formatUrl} />
</div>
Expand Down
18 changes: 12 additions & 6 deletions src/components/Search/SearchIcons.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useSearchStore } from "@/stores/searchStore";
import { ChevronLeft, Search } from "lucide-react";
import { FC } from "react";
import clsx from "clsx";
Expand All @@ -7,8 +6,11 @@ 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 { navigateBack } from "@/utils";
import { useVisibleSearchBar } from "@/hooks/useViewExtensionUI";
import VisibleKey from "../Common/VisibleKey";
import { cn } from "@/lib/utils";

Expand All @@ -20,6 +22,7 @@ interface MultilevelWrapperProps {
const MultilevelWrapper: FC<MultilevelWrapperProps> = (props) => {
const { icon, title = "" } = props;
const { isDark } = useThemeStore();
const isVisibleSearchBar = useVisibleSearchBar();

const renderIcon = () => {
if (!icon) {
Expand All @@ -39,8 +42,8 @@ const MultilevelWrapper: FC<MultilevelWrapperProps> = (props) => {
className={clsx(
"flex items-center h-10 gap-1 px-2 border border-(--border) rounded-l-lg",
{
"justify-center": visibleSearchBar(),
"w-[calc(100vw-16px)] rounded-r-lg": !visibleSearchBar(),
"justify-center": isVisibleSearchBar,
"w-[calc(100vw-16px)] rounded-r-lg": !isVisibleSearchBar,
}
)}
>
Expand Down Expand Up @@ -74,9 +77,12 @@ export default function SearchIcons({
goAskAi,
visibleExtensionStore,
visibleExtensionDetail,
selectedExtension,
viewExtensionOpened,
selectedExtension
} = useSearchStore();

const viewExtensionOpened = useExtensionStore((state) =>
state.viewExtensions.length > 0 ? state.viewExtensions[state.viewExtensions.length - 1] : undefined
);

if (isChatMode) {
return null;
Expand Down
Loading