Skip to content

Commit e81c5bb

Browse files
authored
feat: advanced settings content improvement (#281)
* feat: advanced settings content improvement * feat: support for switching to the default mode * refactor: shortcut keys support only one letter * refactor: fix key reporting errors * feat: listen for changes to `ShortcutsStore` * feat: add configuration items for modifier keys * feat: new connection settings configuration item * refactor: replacing the connection timeout icon * refactor: optimized the style of the input box * refactor: update Icons * refactor: defaults to last chat
1 parent bfc7b48 commit e81c5bb

12 files changed

Lines changed: 609 additions & 10 deletions

File tree

src-tauri/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ fn hide_coco(app: tauri::AppHandle) {
266266

267267
fn handle_open_coco(app: &AppHandle) {
268268
if let Some(window) = app.get_window(MAIN_WINDOW_LABEL) {
269+
let _ = app.emit("show-coco", ());
270+
269271
move_window_to_active_monitor(&window);
270272

271273
window.show().unwrap();

src-tauri/src/shortcut.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::{move_window_to_active_monitor, COCO_TAURI_STORE};
22
use tauri::App;
33
use tauri::AppHandle;
4+
use tauri::Emitter;
45
use tauri::Manager;
56
use tauri::Runtime;
67
use tauri_plugin_global_shortcut::GlobalShortcutExt;
@@ -109,6 +110,8 @@ fn _register_shortcut<R: Runtime>(app: &AppHandle<R>, shortcut: Shortcut) {
109110
dbg!("hiding window");
110111
main_window.hide().unwrap();
111112
} else {
113+
let _ = app.emit("show-coco", ());
114+
112115
dbg!("showing window");
113116
move_window_to_active_monitor(&main_window);
114117
main_window.set_visible_on_all_workspaces(true).unwrap();
@@ -138,6 +141,8 @@ fn _register_shortcut_upon_start(app: &App, shortcut: Shortcut) {
138141
if window.is_visible().unwrap() {
139142
window.hide().unwrap();
140143
} else {
144+
let _ = app.emit("show-coco", ());
145+
141146
// dbg!("showing window");
142147
move_window_to_active_monitor(&window);
143148
window.set_visible_on_all_workspaces(true).unwrap();
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { ModifierKey, useShortcutsStore } from "@/stores/shortcutsStore";
2+
import { useTranslation } from "react-i18next";
3+
import { formatKey } from "@/utils/keyboardUtils";
4+
import SettingsItem from "@/components/Settings/SettingsItem";
5+
import { Command } from "lucide-react";
6+
import { ChangeEvent, useEffect } from "react";
7+
import { emit } from "@tauri-apps/api/event";
8+
9+
const Shortcuts = () => {
10+
const { t } = useTranslation();
11+
const modifierKey = useShortcutsStore((state) => state.modifierKey);
12+
const setModifierKey = useShortcutsStore((state) => state.setModifierKey);
13+
const modeSwitch = useShortcutsStore((state) => state.modeSwitch);
14+
const setModeSwitch = useShortcutsStore((state) => state.setModeSwitch);
15+
const returnToInput = useShortcutsStore((state) => state.returnToInput);
16+
const setReturnToInput = useShortcutsStore((state) => state.setReturnToInput);
17+
const voiceInput = useShortcutsStore((state) => state.voiceInput);
18+
const setVoiceInput = useShortcutsStore((state) => state.setVoiceInput);
19+
// const addImage = useShortcutsStore((state) => state.addImage);
20+
// const setAddImage = useShortcutsStore((state) => state.setAddImage);
21+
// const selectLlmModel = useShortcutsStore((state) => state.selectLlmModel);
22+
// const setSelectLlmModel = useShortcutsStore((state) => {
23+
// return state.setSelectLlmModel;
24+
// });
25+
const addFile = useShortcutsStore((state) => state.addFile);
26+
const setAddFile = useShortcutsStore((state) => state.setAddFile);
27+
28+
useEffect(() => {
29+
const unlisten = useShortcutsStore.subscribe((state) => {
30+
emit("change-shortcuts-store", state);
31+
});
32+
33+
return unlisten;
34+
}, []);
35+
36+
const list = [
37+
{
38+
title: "settings.advanced.shortcuts.modeSwitch.title",
39+
description: "settings.advanced.shortcuts.modeSwitch.description",
40+
value: modeSwitch,
41+
setValue: setModeSwitch,
42+
},
43+
{
44+
title: "settings.advanced.shortcuts.returnToInput.title",
45+
description: "settings.advanced.shortcuts.returnToInput.description",
46+
value: returnToInput,
47+
setValue: setReturnToInput,
48+
},
49+
{
50+
title: "settings.advanced.shortcuts.voiceInput.title",
51+
description: "settings.advanced.shortcuts.voiceInput.description",
52+
value: voiceInput,
53+
setValue: setVoiceInput,
54+
},
55+
// {
56+
// title: "settings.advanced.shortcuts.addImage.title",
57+
// description: "settings.advanced.shortcuts.addImage.description",
58+
// value: addImage,
59+
// setValue: setAddImage,
60+
// },
61+
// {
62+
// title: "settings.advanced.shortcuts.selectLlmModel.title",
63+
// description: "settings.advanced.shortcuts.selectLlmModel.description",
64+
// value: selectLlmModel,
65+
// setValue: setSelectLlmModel,
66+
// },
67+
{
68+
title: "settings.advanced.shortcuts.addFile.title",
69+
description: "settings.advanced.shortcuts.addFile.description",
70+
value: addFile,
71+
setValue: setAddFile,
72+
},
73+
];
74+
75+
const handleChange = (
76+
event: ChangeEvent<HTMLInputElement>,
77+
setValue: (value: string) => void
78+
) => {
79+
const value = event.target.value.toUpperCase();
80+
81+
if (value.length > 1) return;
82+
83+
const state = useShortcutsStore.getState();
84+
85+
if (Object.values(state).includes(value)) return;
86+
87+
setValue(value);
88+
};
89+
90+
return (
91+
<div className="space-y-8">
92+
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
93+
{t("settings.advanced.shortcuts.title")}
94+
</h2>
95+
96+
<div className="space-y-6">
97+
<SettingsItem
98+
icon={Command}
99+
title={t("settings.advanced.shortcuts.modifierKey.title")}
100+
description={t("settings.advanced.shortcuts.modifierKey.description")}
101+
>
102+
<select
103+
value={modifierKey}
104+
className="px-3 py-1.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
105+
onChange={(event) => {
106+
setModifierKey(event.target.value as ModifierKey);
107+
}}
108+
>
109+
{["Command", "Control", "Option"].map((item) => {
110+
return <option value={item}>{formatKey(item)}</option>;
111+
})}
112+
</select>
113+
</SettingsItem>
114+
115+
{list.map((item) => {
116+
const { title, description, value, setValue } = item;
117+
118+
return (
119+
<SettingsItem
120+
key={title}
121+
icon={Command}
122+
title={t(title)}
123+
description={t(description)}
124+
>
125+
<input
126+
className="w-20 h-8 px-2 rounded-md border bg-transparent border-black/5 dark:border-white/10"
127+
value={value}
128+
maxLength={1}
129+
onChange={(event) => {
130+
handleChange(event, setValue);
131+
}}
132+
/>
133+
</SettingsItem>
134+
);
135+
})}
136+
</div>
137+
</div>
138+
);
139+
};
140+
141+
export default Shortcuts;
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import { useTranslation } from "react-i18next";
2+
import Shortcuts from "./components/Shortcuts";
3+
import SettingsItem from "../SettingsItem";
4+
import { AppWindowMac, MessageSquareMore, Search, Unplug } from "lucide-react";
5+
import { useStartupStore } from "@/stores/startupStore";
6+
import { useEffect } from "react";
7+
import { emit } from "@tauri-apps/api/event";
8+
import { useConnectStore } from "@/stores/connectStore";
9+
10+
const Advanced = () => {
11+
const { t } = useTranslation();
12+
const defaultStartupWindow = useStartupStore((state) => {
13+
return state.defaultStartupWindow;
14+
});
15+
const setDefaultStartupWindow = useStartupStore((state) => {
16+
return state.setDefaultStartupWindow;
17+
});
18+
const defaultContentForSearchWindow = useStartupStore((state) => {
19+
return state.defaultContentForSearchWindow;
20+
});
21+
const setDefaultContentForSearchWindow = useStartupStore((state) => {
22+
return state.setDefaultContentForSearchWindow;
23+
});
24+
const defaultContentForChatWindow = useStartupStore((state) => {
25+
return state.defaultContentForChatWindow;
26+
});
27+
const setDefaultContentForChatWindow = useStartupStore((state) => {
28+
return state.setDefaultContentForChatWindow;
29+
});
30+
const connectionTimeout = useConnectStore((state) => {
31+
return state.connectionTimeout;
32+
});
33+
const setConnectionTimeout = useConnectStore((state) => {
34+
return state.setConnectionTimeout;
35+
});
36+
37+
useEffect(() => {
38+
const unlisten = useStartupStore.subscribe((state) => {
39+
emit("change-startup-store", state);
40+
});
41+
42+
return unlisten;
43+
}, []);
44+
45+
const startupList = [
46+
{
47+
icon: AppWindowMac,
48+
title: "settings.advanced.startup.defaultStartupWindow.title",
49+
description: "settings.advanced.startup.defaultStartupWindow.description",
50+
value: defaultStartupWindow,
51+
items: [
52+
{
53+
label:
54+
"settings.advanced.startup.defaultStartupWindow.select.searchMode",
55+
value: "searchMode",
56+
},
57+
{
58+
label:
59+
"settings.advanced.startup.defaultStartupWindow.select.chatMode",
60+
value: "chatMode",
61+
},
62+
],
63+
onChange: setDefaultStartupWindow,
64+
},
65+
{
66+
icon: Search,
67+
title: "settings.advanced.startup.defaultContentForSearchWindow.title",
68+
description:
69+
"settings.advanced.startup.defaultContentForSearchWindow.description",
70+
value: defaultContentForSearchWindow,
71+
items: [
72+
{
73+
label:
74+
"settings.advanced.startup.defaultContentForSearchWindow.select.systemDefault",
75+
value: "systemDefault",
76+
},
77+
],
78+
onChange: setDefaultContentForSearchWindow,
79+
},
80+
{
81+
icon: MessageSquareMore,
82+
title: "settings.advanced.startup.defaultContentForChatWindow.title",
83+
description:
84+
"settings.advanced.startup.defaultContentForChatWindow.description",
85+
value: defaultContentForChatWindow,
86+
items: [
87+
{
88+
label:
89+
"settings.advanced.startup.defaultContentForChatWindow.select.newChat",
90+
value: "newChat",
91+
},
92+
{
93+
label:
94+
"settings.advanced.startup.defaultContentForChatWindow.select.oldChat",
95+
value: "oldChat",
96+
},
97+
],
98+
onChange: setDefaultContentForChatWindow,
99+
},
100+
];
101+
102+
return (
103+
<div className="space-y-8">
104+
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
105+
{t("settings.advanced.startup.title")}
106+
</h2>
107+
108+
<div className="space-y-6">
109+
{startupList.map((item) => {
110+
const { icon, title, description, value, items, onChange } = item;
111+
112+
return (
113+
<SettingsItem
114+
key={title}
115+
icon={icon}
116+
title={t(title)}
117+
description={t(description)}
118+
>
119+
<select
120+
value={value}
121+
onChange={(event) => {
122+
onChange(event.target.value as never);
123+
}}
124+
className="px-3 py-1.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
125+
>
126+
{items.map((item) => {
127+
const { label, value } = item;
128+
129+
return (
130+
<option key={value} value={value}>
131+
{t(label)}
132+
</option>
133+
);
134+
})}
135+
</select>
136+
</SettingsItem>
137+
);
138+
})}
139+
</div>
140+
141+
<Shortcuts />
142+
143+
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
144+
{t("settings.advanced.connect.title")}
145+
</h2>
146+
147+
<div className="space-y-6">
148+
<SettingsItem
149+
icon={Unplug}
150+
title={t("settings.advanced.connect.connectionTimeout.title")}
151+
description={t(
152+
"settings.advanced.connect.connectionTimeout.description"
153+
)}
154+
>
155+
<input
156+
type="number"
157+
min={10}
158+
value={connectionTimeout}
159+
className="w-20 h-8 px-2 rounded-md border bg-transparent border-black/5 dark:border-white/10"
160+
onChange={(event) => {
161+
setConnectionTimeout(Number(event.target.value) || 120);
162+
}}
163+
/>
164+
</SettingsItem>
165+
</div>
166+
</div>
167+
);
168+
};
169+
170+
export default Advanced;

0 commit comments

Comments
 (0)