mirror of
https://github.com/infinilabs/coco-app.git
synced 2025-12-18 20:39:25 +01:00
Compare commits
8 Commits
v0.7.0
...
add-macos-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7f8f7ef6e | ||
|
|
4709f8c660 | ||
|
|
4696aa1759 | ||
|
|
924fc09516 | ||
|
|
5a700662dd | ||
|
|
8f992bfa92 | ||
|
|
e7dd27c744 | ||
|
|
7914836c3e |
@@ -5,22 +5,36 @@ title: "Release Notes"
|
||||
|
||||
# Release Notes
|
||||
|
||||
Information about release notes of Coco Server is provided here.
|
||||
Information about release notes of Coco App is provided here.
|
||||
|
||||
## Latest (In development)
|
||||
|
||||
### ❌ Breaking changes
|
||||
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- feat: enhance ui for skipped version #834
|
||||
|
||||
### 🐛 Bug fix
|
||||
|
||||
- fix: fix issue with update check failure #833
|
||||
|
||||
### ✈️ Improvements
|
||||
|
||||
## 0.7.1 (2025-07-27)
|
||||
|
||||
### ❌ Breaking changes
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
### 🐛 Bug fix
|
||||
|
||||
- fix: correct enter key behavior #828
|
||||
|
||||
### ✈️ Improvements
|
||||
|
||||
- chore: web component add notification component #825
|
||||
- refactor: collection behavior defaults to `MoveToActiveSpace`, and only use `CanJoinAllSpaces` when window is pinned #829
|
||||
|
||||
## 0.7.0 (2025-07-25)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "coco",
|
||||
"private": true,
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
83
src-tauri/Cargo.lock
generated
83
src-tauri/Cargo.lock
generated
@@ -840,7 +840,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "coco"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"applications",
|
||||
@@ -852,6 +852,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"chinese-number",
|
||||
"chrono",
|
||||
"cocoa 0.24.1",
|
||||
"derive_more 2.0.1",
|
||||
"dirs 5.0.1",
|
||||
"enigo",
|
||||
@@ -914,6 +915,22 @@ dependencies = [
|
||||
"zip 4.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cocoa"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"block",
|
||||
"cocoa-foundation 0.1.2",
|
||||
"core-foundation 0.9.4",
|
||||
"core-graphics 0.22.3",
|
||||
"foreign-types 0.3.2",
|
||||
"libc",
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cocoa"
|
||||
version = "0.26.0"
|
||||
@@ -922,14 +939,28 @@ checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"block",
|
||||
"cocoa-foundation",
|
||||
"cocoa-foundation 0.2.0",
|
||||
"core-foundation 0.10.0",
|
||||
"core-graphics",
|
||||
"core-graphics 0.24.0",
|
||||
"foreign-types 0.5.0",
|
||||
"libc",
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cocoa-foundation"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"block",
|
||||
"core-foundation 0.9.4",
|
||||
"core-graphics-types 0.1.3",
|
||||
"libc",
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cocoa-foundation"
|
||||
version = "0.2.0"
|
||||
@@ -939,7 +970,7 @@ dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"block",
|
||||
"core-foundation 0.10.0",
|
||||
"core-graphics-types",
|
||||
"core-graphics-types 0.2.0",
|
||||
"libc",
|
||||
"objc",
|
||||
]
|
||||
@@ -1056,6 +1087,19 @@ version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "core-graphics"
|
||||
version = "0.22.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation 0.9.4",
|
||||
"core-graphics-types 0.1.3",
|
||||
"foreign-types 0.3.2",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-graphics"
|
||||
version = "0.24.0"
|
||||
@@ -1064,11 +1108,22 @@ checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"core-foundation 0.10.0",
|
||||
"core-graphics-types",
|
||||
"core-graphics-types 0.2.0",
|
||||
"foreign-types 0.5.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-graphics-types"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation 0.9.4",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-graphics-types"
|
||||
version = "0.2.0"
|
||||
@@ -1472,8 +1527,8 @@ version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67fd9ae1736d6ebb2e472740fbee86fb2178b8d56feb98a6751411d4c95b7e72"
|
||||
dependencies = [
|
||||
"cocoa",
|
||||
"core-graphics",
|
||||
"cocoa 0.26.0",
|
||||
"core-graphics 0.24.0",
|
||||
"dunce",
|
||||
"gdk",
|
||||
"gdkx11",
|
||||
@@ -1562,7 +1617,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cf6f550bbbdd5fe66f39d429cb2604bcdacbf00dca0f5bbe2e9306a0009b7c6"
|
||||
dependencies = [
|
||||
"core-foundation 0.10.0",
|
||||
"core-graphics",
|
||||
"core-graphics 0.24.0",
|
||||
"foreign-types-shared 0.3.1",
|
||||
"libc",
|
||||
"log",
|
||||
@@ -2714,7 +2769,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core 0.59.0",
|
||||
"windows-core 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5721,7 +5776,7 @@ checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"cfg_aliases",
|
||||
"core-graphics",
|
||||
"core-graphics 0.24.0",
|
||||
"foreign-types 0.5.0",
|
||||
"js-sys",
|
||||
"log",
|
||||
@@ -5889,7 +5944,7 @@ dependencies = [
|
||||
"ntapi",
|
||||
"objc2-core-foundation",
|
||||
"objc2-io-kit",
|
||||
"windows 0.59.0",
|
||||
"windows 0.61.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5947,7 +6002,7 @@ checksum = "1e59c1f38e657351a2e822eadf40d6a2ad4627b9c25557bc1180ec1b3295ef82"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"core-foundation 0.10.0",
|
||||
"core-graphics",
|
||||
"core-graphics 0.24.0",
|
||||
"crossbeam-channel",
|
||||
"dispatch",
|
||||
"dlopen2",
|
||||
@@ -6145,9 +6200,9 @@ source = "git+https://github.com/ahkohd/tauri-nspanel?branch=v2#d4b9df797959f8fa
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"block",
|
||||
"cocoa",
|
||||
"cocoa 0.26.0",
|
||||
"core-foundation 0.10.0",
|
||||
"core-graphics",
|
||||
"core-graphics 0.24.0",
|
||||
"objc",
|
||||
"objc-foundation",
|
||||
"objc_id",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "coco"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
description = "Search, connect, collaborate – all in one place."
|
||||
authors = ["INFINI Labs"]
|
||||
edition = "2024"
|
||||
@@ -109,6 +109,7 @@ sysinfo = "0.35.2"
|
||||
|
||||
[target."cfg(target_os = \"macos\")".dependencies]
|
||||
tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" }
|
||||
cocoa = "0.24"
|
||||
|
||||
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\"))".dependencies]
|
||||
tauri-plugin-single-instance = { version = "2.0.0", features = ["deep-link"] }
|
||||
|
||||
@@ -180,6 +180,8 @@ pub fn run() {
|
||||
server::synthesize::synthesize,
|
||||
util::file::get_file_icon,
|
||||
util::app_lang::update_app_lang,
|
||||
#[cfg(target_os = "macos")]
|
||||
setup::toggle_move_to_active_space_attribute,
|
||||
])
|
||||
.setup(|app| {
|
||||
let app_handle = app.handle().clone();
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
//credits to: https://github.com/ayangweb/ayangweb-EcoPaste/blob/169323dbe6365ffe4abb64d867439ed2ea84c6d1/src-tauri/src/core/setup/mac.rs
|
||||
use tauri::{App, Emitter, EventTarget, WebviewWindow};
|
||||
//! credits to: https://github.com/ayangweb/ayangweb-EcoPaste/blob/169323dbe6365ffe4abb64d867439ed2ea84c6d1/src-tauri/src/core/setup/mac.rs
|
||||
|
||||
use cocoa::appkit::NSWindow;
|
||||
use tauri::Manager;
|
||||
use tauri::{App, AppHandle, Emitter, EventTarget, WebviewWindow};
|
||||
use tauri_nspanel::{WebviewWindowExt, cocoa::appkit::NSWindowCollectionBehavior, panel_delegate};
|
||||
|
||||
use crate::common::MAIN_WINDOW_LABEL;
|
||||
@@ -29,7 +32,7 @@ pub fn platform(
|
||||
|
||||
// Share the window across all desktop spaces and full screen
|
||||
panel.set_collection_behaviour(
|
||||
NSWindowCollectionBehavior::NSWindowCollectionBehaviorCanJoinAllSpaces
|
||||
NSWindowCollectionBehavior::NSWindowCollectionBehaviorMoveToActiveSpace
|
||||
| NSWindowCollectionBehavior::NSWindowCollectionBehaviorStationary
|
||||
| NSWindowCollectionBehavior::NSWindowCollectionBehaviorFullScreenAuxiliary,
|
||||
);
|
||||
@@ -78,3 +81,50 @@ pub fn platform(
|
||||
// Set the delegate object for the window to handle window events
|
||||
panel.set_delegate(delegate);
|
||||
}
|
||||
|
||||
/// Change NS window attribute between `NSWindowCollectionBehaviorCanJoinAllSpaces`
|
||||
/// and `NSWindowCollectionBehaviorMoveToActiveSpace` accordingly.
|
||||
///
|
||||
/// NOTE: this tauri command is not async because we should run it in the main
|
||||
/// thread, or `ns_window.setCollectionBehavior_(collection_behavior)` would lead
|
||||
/// to UB.
|
||||
#[tauri::command]
|
||||
pub(crate) fn toggle_move_to_active_space_attribute(tauri_app_hanlde: AppHandle) {
|
||||
use cocoa::appkit::NSWindowCollectionBehavior;
|
||||
use cocoa::base::id;
|
||||
|
||||
let main_window = tauri_app_hanlde
|
||||
.get_webview_window(MAIN_WINDOW_LABEL)
|
||||
.unwrap();
|
||||
let ns_window = main_window.ns_window().unwrap() as id;
|
||||
let mut collection_behavior = unsafe { ns_window.collectionBehavior() };
|
||||
let join_all_spaces = collection_behavior
|
||||
.contains(NSWindowCollectionBehavior::NSWindowCollectionBehaviorCanJoinAllSpaces);
|
||||
let move_to_active_space = collection_behavior
|
||||
.contains(NSWindowCollectionBehavior::NSWindowCollectionBehaviorMoveToActiveSpace);
|
||||
|
||||
match (join_all_spaces, move_to_active_space) {
|
||||
(true, false) => {
|
||||
collection_behavior
|
||||
.remove(NSWindowCollectionBehavior::NSWindowCollectionBehaviorCanJoinAllSpaces);
|
||||
collection_behavior
|
||||
.insert(NSWindowCollectionBehavior::NSWindowCollectionBehaviorMoveToActiveSpace);
|
||||
}
|
||||
(false, true) => {
|
||||
collection_behavior
|
||||
.remove(NSWindowCollectionBehavior::NSWindowCollectionBehaviorMoveToActiveSpace);
|
||||
collection_behavior
|
||||
.insert(NSWindowCollectionBehavior::NSWindowCollectionBehaviorCanJoinAllSpaces);
|
||||
}
|
||||
_ => {
|
||||
panic!(
|
||||
"invalid NS window attribute, NSWindowCollectionBehaviorCanJoinAllSpaces is set [{}], NSWindowCollectionBehaviorMoveToActiveSpace is set [{}]",
|
||||
join_all_spaces, move_to_active_space
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
ns_window.setCollectionBehavior_(collection_behavior);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use tauri_plugin_updater::RemoteRelease;
|
||||
///
|
||||
/// If the version string is in the `x.y.z` format and does not include a build
|
||||
/// number, we assume a build number of 0.
|
||||
fn extract_version_number(version: &Version) -> u32 {
|
||||
fn extract_build_number(version: &Version) -> u32 {
|
||||
let pre = &version.pre;
|
||||
|
||||
if pre.is_empty() {
|
||||
@@ -52,8 +52,8 @@ fn extract_version_number(version: &Version) -> u32 {
|
||||
pub(crate) fn custom_version_comparator(local: Version, remote_release: RemoteRelease) -> bool {
|
||||
let remote = remote_release.version;
|
||||
|
||||
let local_build_number = extract_version_number(&local);
|
||||
let remote_build_number = extract_version_number(&remote);
|
||||
let local_build_number = extract_build_number(&local);
|
||||
let remote_build_number = extract_build_number(&remote);
|
||||
|
||||
let should_update = remote_build_number > local_build_number;
|
||||
log::debug!(
|
||||
@@ -65,3 +65,23 @@ pub(crate) fn custom_version_comparator(local: Version, remote_release: RemoteRe
|
||||
|
||||
should_update
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_extract_build_number() {
|
||||
// 0.6.0 => 0
|
||||
let version = Version::parse("0.6.0").unwrap();
|
||||
assert_eq!(extract_build_number(&version), 0);
|
||||
|
||||
// 0.6.0-2371 => 2371
|
||||
let version = Version::parse("0.6.0-2371").unwrap();
|
||||
assert_eq!(extract_build_number(&version), 2371);
|
||||
|
||||
// 0.6.0-SNAPSHOT-2371 => 2371
|
||||
let version = Version::parse("0.6.0-SNAPSHOT-2371").unwrap();
|
||||
assert_eq!(extract_build_number(&version), 2371);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"identifier": "rs.coco.app",
|
||||
"bundle": {
|
||||
"macOS": {
|
||||
"entitlements": "./Entitlements.plist",
|
||||
@@ -7,4 +8,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
8
src-tauri/tauri.macos.conf.json
Normal file
8
src-tauri/tauri.macos.conf.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"identifier": "rs.coco.app",
|
||||
"bundle": {
|
||||
"macOS": {
|
||||
"entitlements": "./Entitlements.plist"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,4 +34,8 @@ export function show_check(): Promise<void> {
|
||||
|
||||
export function hide_check(): Promise<void> {
|
||||
return invoke('hide_check');
|
||||
}
|
||||
|
||||
export function toggle_move_to_active_space_attribute(): Promise<void> {
|
||||
return invoke('toggle_move_to_active_space_attribute');
|
||||
}
|
||||
@@ -7,12 +7,12 @@ import PinIcon from "@/icons/Pin";
|
||||
import WindowsFullIcon from "@/icons/WindowsFull";
|
||||
import { useAppStore } from "@/stores/appStore";
|
||||
import type { Chat } from "@/types/chat";
|
||||
import platformAdapter from "@/utils/platformAdapter";
|
||||
import VisibleKey from "../Common/VisibleKey";
|
||||
import { useShortcutsStore } from "@/stores/shortcutsStore";
|
||||
import { HISTORY_PANEL_ID } from "@/constants";
|
||||
import { AssistantList } from "./AssistantList";
|
||||
import { ServerList } from "./ServerList";
|
||||
import { useTogglePin } from "@/hooks/useTogglePin";
|
||||
|
||||
interface ChatHeaderProps {
|
||||
clearChat: () => void;
|
||||
@@ -35,22 +35,12 @@ export function ChatHeader({
|
||||
showChatHistory = true,
|
||||
assistantIDs,
|
||||
}: ChatHeaderProps) {
|
||||
const { isPinned, setIsPinned, isTauri } = useAppStore();
|
||||
const { isTauri } = useAppStore();
|
||||
const { isPinned, togglePin } = useTogglePin();
|
||||
|
||||
const { historicalRecords, newSession, fixedWindow, external } =
|
||||
useShortcutsStore();
|
||||
|
||||
const togglePin = async () => {
|
||||
try {
|
||||
const newPinned = !isPinned;
|
||||
await platformAdapter.setAlwaysOnTop(newPinned);
|
||||
setIsPinned(newPinned);
|
||||
} catch (err) {
|
||||
console.error("Failed to toggle window pin state:", err);
|
||||
setIsPinned(isPinned);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<header
|
||||
className="flex items-center justify-between py-2 px-3 select-none"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback } from "react";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { ArrowDown01, CornerDownLeft } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import clsx from "clsx";
|
||||
@@ -19,6 +19,7 @@ import source_default_dark_img from "@/assets/images/source_default_dark.png";
|
||||
import { useThemeStore } from "@/stores/themeStore";
|
||||
import platformAdapter from "@/utils/platformAdapter";
|
||||
import FontIcon from "../Icons/FontIcon";
|
||||
import { useTogglePin } from "@/hooks/useTogglePin";
|
||||
|
||||
interface FooterProps {
|
||||
setIsPinnedWeb?: (value: boolean) => void;
|
||||
@@ -37,32 +38,24 @@ export default function Footer({ setIsPinnedWeb }: FooterProps) {
|
||||
|
||||
const isDark = useThemeStore((state) => state.isDark);
|
||||
|
||||
const { isTauri, isPinned, setIsPinned } = useAppStore();
|
||||
const { isTauri } = useAppStore();
|
||||
|
||||
const { setVisible, updateInfo } = useUpdateStore();
|
||||
const { isPinned, togglePin } = useTogglePin({
|
||||
onPinChange: setIsPinnedWeb,
|
||||
});
|
||||
|
||||
const { setVisible, updateInfo, skipVersions } = useUpdateStore();
|
||||
|
||||
const { fixedWindow, modifierKey } = useShortcutsStore();
|
||||
|
||||
const setWindowAlwaysOnTop = useCallback(async (isPinned: boolean) => {
|
||||
setIsPinnedWeb?.(isPinned);
|
||||
return platformAdapter.setAlwaysOnTop(isPinned);
|
||||
}, []);
|
||||
|
||||
const togglePin = async () => {
|
||||
try {
|
||||
const newPinned = !isPinned;
|
||||
await setWindowAlwaysOnTop(newPinned);
|
||||
setIsPinned(newPinned);
|
||||
} catch (err) {
|
||||
console.error("Failed to toggle window pin state:", err);
|
||||
setIsPinned(isPinned);
|
||||
}
|
||||
};
|
||||
|
||||
const openSetting = useCallback(() => {
|
||||
return platformAdapter.emitEvent("open_settings", "");
|
||||
}, []);
|
||||
|
||||
const hasUpdate = useMemo(() => {
|
||||
return updateInfo && !skipVersions.includes(updateInfo.version);
|
||||
}, [updateInfo, skipVersions]);
|
||||
|
||||
const renderLeft = () => {
|
||||
if (sourceData?.source?.name) {
|
||||
return (
|
||||
@@ -108,7 +101,7 @@ export default function Footer({ setIsPinnedWeb }: FooterProps) {
|
||||
/>
|
||||
|
||||
<div className="relative text-xs text-gray-500 dark:text-gray-400">
|
||||
{updateInfo?.available ? (
|
||||
{hasUpdate ? (
|
||||
<div className="cursor-pointer" onClick={() => setVisible(true)}>
|
||||
<span>{t("search.footer.updateAvailable")}</span>
|
||||
<span className="absolute top-0 -right-2 size-1.5 bg-[#FF3434] rounded-full"></span>
|
||||
@@ -138,7 +131,7 @@ export default function Footer({ setIsPinnedWeb }: FooterProps) {
|
||||
onClick={togglePin}
|
||||
className={clsx({
|
||||
"text-blue-500": isPinned,
|
||||
"pl-2": updateInfo?.available,
|
||||
"pl-2": hasUpdate,
|
||||
})}
|
||||
>
|
||||
<VisibleKey shortcut={fixedWindow} onKeyPress={togglePin}>
|
||||
|
||||
@@ -135,12 +135,13 @@ export function useAssistantManager({
|
||||
}
|
||||
|
||||
if (key === "Enter" && !shiftKey) {
|
||||
if (!isEmpty(value)) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
e.preventDefault();
|
||||
|
||||
if (isTauri && !isChatMode && goAskAi) {
|
||||
if (!isEmpty(value)) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
return handleAskAi();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ const UpdateApp = ({ isCheckPage }: UpdateAppProps) => {
|
||||
const {
|
||||
visible,
|
||||
setVisible,
|
||||
skipVersion,
|
||||
setSkipVersion,
|
||||
skipVersions,
|
||||
setSkipVersions,
|
||||
isOptional,
|
||||
updateInfo,
|
||||
setUpdateInfo,
|
||||
@@ -50,11 +50,7 @@ const UpdateApp = ({ isCheckPage }: UpdateAppProps) => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!snapshotUpdate) return;
|
||||
|
||||
checkUpdate().catch((error) => {
|
||||
addError("Update failed:" + error, "error");
|
||||
});
|
||||
checkUpdateStatus();
|
||||
}, [snapshotUpdate]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -79,13 +75,13 @@ const UpdateApp = ({ isCheckPage }: UpdateAppProps) => {
|
||||
const update = await checkUpdate();
|
||||
|
||||
if (update) {
|
||||
const { skipVersions } = useUpdateStore.getState();
|
||||
|
||||
setVisible(!skipVersions.includes(update.version));
|
||||
|
||||
setUpdateInfo(update);
|
||||
|
||||
if (skipVersion === update.version) return;
|
||||
|
||||
setVisible(true);
|
||||
}
|
||||
}, [skipVersion]);
|
||||
}, [skipVersions]);
|
||||
|
||||
const cursorClassName = useMemo(() => {
|
||||
return state.loading ? "cursor-not-allowed" : "cursor-pointer";
|
||||
@@ -133,7 +129,9 @@ const UpdateApp = ({ isCheckPage }: UpdateAppProps) => {
|
||||
const handleSkip = () => {
|
||||
if (state.loading) return;
|
||||
|
||||
setSkipVersion(updateInfo?.version);
|
||||
const { skipVersions, updateInfo } = useUpdateStore.getState();
|
||||
|
||||
setSkipVersions([...skipVersions, updateInfo.version]);
|
||||
|
||||
isCheckPage ? hide_check() : setVisible(false);
|
||||
};
|
||||
@@ -182,7 +180,7 @@ const UpdateApp = ({ isCheckPage }: UpdateAppProps) => {
|
||||
<img src={isDark ? darkIcon : lightIcon} className="h-6" />
|
||||
|
||||
<div className="text-[#333] text-sm leading-5 py-2 dark:text-[#D8D8D8] text-center">
|
||||
{updateInfo?.available ? (
|
||||
{updateInfo ? (
|
||||
isOptional ? (
|
||||
t("update.optional_description")
|
||||
) : (
|
||||
@@ -196,7 +194,7 @@ const UpdateApp = ({ isCheckPage }: UpdateAppProps) => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{updateInfo?.available ? (
|
||||
{updateInfo ? (
|
||||
<div
|
||||
className="text-xs text-[#0072FF] cursor-pointer"
|
||||
onClick={() =>
|
||||
@@ -223,21 +221,21 @@ const UpdateApp = ({ isCheckPage }: UpdateAppProps) => {
|
||||
cursorClassName,
|
||||
state.loading && "opacity-50"
|
||||
)}
|
||||
onClick={updateInfo?.available ? handleDownload : handleSkip}
|
||||
onClick={updateInfo ? handleDownload : handleSkip}
|
||||
>
|
||||
{state.loading ? (
|
||||
<div className="flex justify-center items-center gap-2">
|
||||
<LoaderCircle className="animate-spin size-5" />
|
||||
{percent}%
|
||||
</div>
|
||||
) : updateInfo?.available ? (
|
||||
) : updateInfo ? (
|
||||
t("update.button.install")
|
||||
) : (
|
||||
t("update.button.ok")
|
||||
)}
|
||||
</Button>
|
||||
|
||||
{updateInfo?.available && isOptional && (
|
||||
{!isCheckPage && updateInfo && isOptional && (
|
||||
<div
|
||||
className={clsx("text-xs text-[#999]", cursorClassName)}
|
||||
onClick={handleSkip}
|
||||
|
||||
35
src/hooks/useTogglePin.ts
Normal file
35
src/hooks/useTogglePin.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { useCallback } from "react";
|
||||
|
||||
import { useAppStore } from "@/stores/appStore";
|
||||
import platformAdapter from "@/utils/platformAdapter";
|
||||
import { toggle_move_to_active_space_attribute } from "@/commands/system";
|
||||
import { isMac } from "@/utils/platform";
|
||||
|
||||
interface UseTogglePinOptions {
|
||||
onPinChange?: (isPinned: boolean) => void;
|
||||
}
|
||||
|
||||
export const useTogglePin = (options?: UseTogglePinOptions) => {
|
||||
const { isPinned, setIsPinned } = useAppStore();
|
||||
|
||||
const togglePin = useCallback(async () => {
|
||||
try {
|
||||
const newPinned = !isPinned;
|
||||
|
||||
if (options?.onPinChange) {
|
||||
options.onPinChange(newPinned);
|
||||
}
|
||||
|
||||
await platformAdapter.setAlwaysOnTop(newPinned);
|
||||
setIsPinned(newPinned);
|
||||
isMac && toggle_move_to_active_space_attribute();
|
||||
} catch (err) {
|
||||
console.error("Failed to toggle window pin state:", err);
|
||||
}
|
||||
}, [isPinned, setIsPinned, options?.onPinChange]);
|
||||
|
||||
return {
|
||||
isPinned,
|
||||
togglePin,
|
||||
};
|
||||
};
|
||||
@@ -4,8 +4,8 @@ import { persist } from "zustand/middleware";
|
||||
export type IUpdateStore = {
|
||||
visible: boolean;
|
||||
setVisible: (visible: boolean) => void;
|
||||
skipVersion?: string;
|
||||
setSkipVersion: (skipVersion?: string) => void;
|
||||
skipVersions: string[];
|
||||
setSkipVersions: (skipVersions: string[]) => void;
|
||||
isOptional: boolean;
|
||||
setIsOptional: (isOptional: boolean) => void;
|
||||
updateInfo?: any;
|
||||
@@ -19,8 +19,9 @@ export const useUpdateStore = create<IUpdateStore>()(
|
||||
setVisible: (visible: boolean) => {
|
||||
return set({ visible });
|
||||
},
|
||||
setSkipVersion: (skipVersion?: string) => {
|
||||
return set({ skipVersion });
|
||||
skipVersions: [],
|
||||
setSkipVersions: (skipVersions: string[]) => {
|
||||
return set({ skipVersions });
|
||||
},
|
||||
isOptional: true,
|
||||
setIsOptional: (isOptional: boolean) => {
|
||||
@@ -33,7 +34,7 @@ export const useUpdateStore = create<IUpdateStore>()(
|
||||
{
|
||||
name: "update-store",
|
||||
partialize: (state) => ({
|
||||
skipVersion: state.skipVersion,
|
||||
skipVersions: state.skipVersions,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user