Skip to content

Commit a092354

Browse files
authored
feat: supports setting of out-of-focus transparency on top (#470)
* feat: supports setting of out-of-focus transparency on top * docs: update changelog * refactor: optimize translation content * docs: update changelog
1 parent 2ffbb79 commit a092354

File tree

11 files changed

+156
-13
lines changed

11 files changed

+156
-13
lines changed

docs/content.en/docs/release-notes/_index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Information about release notes of Coco Server is provided here.
1414
### 🚀 Features
1515

1616
- feat: check or enter to close the list of assistants #469
17+
- feat: add dimness settings for pinned window #470
1718

1819
### 🐛 Bug fix
1920

src/components/SearchChat/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { DataSource } from "@/types/commands";
2525
import { useThemeStore } from "@/stores/themeStore";
2626
import { Get } from "@/api/axiosRequest";
2727
import { useConnectStore } from "@/stores/connectStore";
28+
import { useAppearanceStore } from "@/stores/appearance";
2829

2930
interface SearchChatProps {
3031
isTauri?: boolean;
@@ -300,6 +301,7 @@ function SearchChat({
300301
const defaultStartupWindow = useStartupStore((state) => {
301302
return state.defaultStartupWindow;
302303
});
304+
const opacity = useAppearanceStore((state) => state.opacity);
303305

304306
useEffect(() => {
305307
if (platformAdapter.isTauri()) {
@@ -329,9 +331,9 @@ function SearchChat({
329331
"rounded-xl": !isMobile && !isWin,
330332
"border border-[#E6E6E6] dark:border-[#272626]": isTauri && isLinux,
331333
"border-t border-t-[#999] dark:border-t-[#333]": isTauri && isWin10,
332-
"opacity-30": blurred,
333334
}
334335
)}
336+
style={{ opacity: blurred ? opacity / 100 : 100 }}
335337
>
336338
<div
337339
data-tauri-drag-region={isTauri}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import SettingsInput from "@/components/Settings/SettingsInput";
2+
import SettingsItem from "@/components/Settings/SettingsItem";
3+
import { useAppearanceStore } from "@/stores/appearance";
4+
import platformAdapter from "@/utils/platformAdapter";
5+
import { Unplug } from "lucide-react";
6+
import { useEffect } from "react";
7+
import { useTranslation } from "react-i18next";
8+
9+
const Appearance = () => {
10+
const { t } = useTranslation();
11+
const opacity = useAppearanceStore((state) => state.opacity);
12+
const setOpacity = useAppearanceStore((state) => state.setOpacity);
13+
14+
useEffect(() => {
15+
const unlisten = useAppearanceStore.subscribe((state) => {
16+
platformAdapter.emitEvent("change-appearance-store", state);
17+
});
18+
19+
return unlisten;
20+
}, []);
21+
22+
return (
23+
<>
24+
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
25+
{t("settings.advanced.appearance.title")}
26+
</h2>
27+
28+
<SettingsItem
29+
icon={Unplug}
30+
title={t("settings.advanced.appearance.opacity.title")}
31+
description={t("settings.advanced.appearance.opacity.description")}
32+
>
33+
<SettingsInput
34+
type="number"
35+
min={10}
36+
max={100}
37+
value={opacity}
38+
onChange={(event) => {
39+
const nextOpacity = Number(event.target.value);
40+
41+
if (nextOpacity < 10 || nextOpacity > 100) {
42+
return;
43+
}
44+
45+
return setOpacity(nextOpacity || 30);
46+
}}
47+
/>
48+
</SettingsItem>
49+
</>
50+
);
51+
};
52+
53+
export default Appearance;

src/components/Settings/Advanced/components/Shortcuts/index.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
import { ModifierKey } from "@/types/index";
2727
import platformAdapter from "@/utils/platformAdapter";
2828
import { useAppStore } from "@/stores/appStore";
29+
import SettingsInput from "@/components/Settings/SettingsInput";
2930

3031
export const modifierKeys: ModifierKey[] = isMac
3132
? ["meta", "ctrl"]
@@ -284,7 +285,11 @@ const Shortcuts = () => {
284285
}}
285286
>
286287
{modifierKeys.map((item) => {
287-
return <option value={item}>{formatKey(item)}</option>;
288+
return (
289+
<option key={item} value={item}>
290+
{formatKey(item)}
291+
</option>
292+
);
288293
})}
289294
</select>
290295
</SettingsItem>
@@ -302,10 +307,9 @@ const Shortcuts = () => {
302307
<div className="flex items-center gap-2">
303308
<span>{formatKey(modifierKey)}</span>
304309
<span>+</span>
305-
<input
306-
className="w-20 h-8 px-2 rounded-md border bg-transparent border-black/5 dark:border-white/10 hover:border-[#0072FF] focus:border-[#0072FF] transition"
310+
<SettingsInput
307311
value={value}
308-
maxLength={1}
312+
max={1}
309313
onChange={(event) => {
310314
handleChange(event, setValue);
311315
}}

src/components/Settings/Advanced/index.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import SettingsItem from "../SettingsItem";
44
import { AppWindowMac, MessageSquareMore, Search, Unplug } from "lucide-react";
55
import { useStartupStore } from "@/stores/startupStore";
66
import { useEffect } from "react";
7-
import { emit } from "@tauri-apps/api/event";
87
import { useConnectStore } from "@/stores/connectStore";
8+
import Appearance from "./components/Appearance";
9+
import SettingsInput from "../SettingsInput";
10+
import platformAdapter from "@/utils/platformAdapter";
911

1012
const Advanced = () => {
1113
const { t } = useTranslation();
@@ -42,11 +44,11 @@ const Advanced = () => {
4244

4345
useEffect(() => {
4446
const unsubscribeStartup = useStartupStore.subscribe((state) => {
45-
emit("change-startup-store", state);
47+
platformAdapter.emitEvent("change-startup-store", state);
4648
});
4749

4850
const unsubscribeConnect = useConnectStore.subscribe((state) => {
49-
emit("change-connect-store", state);
51+
platformAdapter.emitEvent("change-connect-store", state);
5052
});
5153

5254
return () => {
@@ -165,12 +167,17 @@ const Advanced = () => {
165167
"settings.advanced.connect.connectionTimeout.description"
166168
)}
167169
>
168-
<input
170+
<SettingsInput
169171
type="number"
170172
min={10}
171173
value={connectionTimeout}
172-
className="w-20 h-8 px-2 rounded-md border bg-transparent border-black/5 dark:border-white/10"
173174
onChange={(event) => {
175+
const nextValue = Number(event.target.value);
176+
177+
if (nextValue < 10) {
178+
return;
179+
}
180+
174181
setConnectionTimeout(Number(event.target.value) || 120);
175182
}}
176183
/>
@@ -181,17 +188,24 @@ const Advanced = () => {
181188
title={t("settings.advanced.connect.queryTimeout.title")}
182189
description={t("settings.advanced.connect.queryTimeout.description")}
183190
>
184-
<input
191+
<SettingsInput
185192
type="number"
186193
min={1}
187194
value={queryTimeout}
188-
className="w-20 h-8 px-2 rounded-md border bg-transparent border-black/5 dark:border-white/10"
189195
onChange={(event) => {
190-
setQueryTimeout(Number(event.target.value) || 500);
196+
const nextValue = Number(event.target.value);
197+
198+
if (nextValue < 1) {
199+
return;
200+
}
201+
202+
setQueryTimeout(nextValue || 500);
191203
}}
192204
/>
193205
</SettingsItem>
194206
</div>
207+
208+
<Appearance />
195209
</div>
196210
);
197211
};
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Input, InputProps } from "@headlessui/react";
2+
import clsx from "clsx";
3+
import { FC } from "react";
4+
5+
const SettingsInput: FC<InputProps> = (props) => {
6+
const { className, ...rest } = props;
7+
8+
return (
9+
<Input
10+
{...rest}
11+
className={clsx(
12+
"w-20 h-8 px-2 rounded-md border bg-transparent border-black/5 dark:border-white/10 hover:border-[#0072FF] focus:border-[#0072FF] transition",
13+
className
14+
)}
15+
/>
16+
);
17+
};
18+
19+
export default SettingsInput;

src/hooks/useSyncStore.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useAppearanceStore } from "@/stores/appearance";
12
import { useConnectStore } from "@/stores/connectStore";
23
import { useShortcutsStore } from "@/stores/shortcutsStore";
34
import { useStartupStore } from "@/stores/startupStore";
@@ -77,6 +78,7 @@ export const useSyncStore = () => {
7778
const setQueryTimeout = useConnectStore((state) => {
7879
return state.setQuerySourceTimeout;
7980
});
81+
const setOpacity = useAppearanceStore((state) => state.setOpacity);
8082

8183
useEffect(() => {
8284
if (!resetFixedWindow) {
@@ -141,6 +143,12 @@ export const useSyncStore = () => {
141143
setConnectionTimeout(connectionTimeout);
142144
setQueryTimeout(querySourceTimeout);
143145
}),
146+
147+
platformAdapter.listenEvent("change-appearance-store", ({ payload }) => {
148+
const { opacity } = payload;
149+
150+
setOpacity(opacity);
151+
}),
144152
]);
145153

146154
return () => {

src/locales/en/translation.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,13 @@
162162
"title": "Query Timeout",
163163
"description": "Terminates the query if no search results are returned within this time. Default: 500ms."
164164
}
165+
},
166+
"appearance": {
167+
"title": "Appearance Settings",
168+
"opacity": {
169+
"title": "Pinned Window Dimness Setting",
170+
"description": "Adjusts the opacity level of the Coco AI window when it’s pinned and not in focus. Set a value between 10% and 100%, where 100% means fully opaque (no dimming), and lower values increase transparency, allowing underlying content to show through."
171+
}
165172
}
166173
},
167174
"tabs": {

src/locales/zh/translation.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,13 @@
162162
"title": "查询超时",
163163
"description": "在此时间内未返回搜索结果,则终止查询。默认值:500 毫秒。"
164164
}
165+
},
166+
"appearance": {
167+
"title": "外观设置",
168+
"opacity": {
169+
"title": "置顶时失焦透明度",
170+
"description": "设置 Coco AI 窗口在置顶且失去焦点时的不透明度(10%–100%,100% 表示完全不透明)。"
171+
}
165172
}
166173
},
167174
"tabs": {

src/stores/appearance.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { create } from "zustand";
2+
import { persist, subscribeWithSelector } from "zustand/middleware";
3+
4+
export type IAppearanceStore = {
5+
opacity: number;
6+
setOpacity: (opacity: number) => void;
7+
};
8+
9+
export const useAppearanceStore = create<IAppearanceStore>()(
10+
subscribeWithSelector(
11+
persist(
12+
(set) => ({
13+
opacity: 30,
14+
setOpacity: (opacity) => {
15+
return set({ opacity });
16+
},
17+
}),
18+
{
19+
name: "startup-store",
20+
partialize: (state) => ({
21+
opacity: state.opacity,
22+
}),
23+
}
24+
)
25+
)
26+
);

0 commit comments

Comments
 (0)