Skip to content

Commit 5436456

Browse files
authored
feat: right-click menu support for search (#423)
* feat: right-click menu support for search * docs: update changelog
1 parent ee4a06b commit 5436456

File tree

5 files changed

+62
-18
lines changed

5 files changed

+62
-18
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Information about release notes of Coco Server is provided here.
2828
- feat: add support for calculator function #399
2929
- feat: auto selects the first item after searching #411
3030
- feat: web components assistant #422
31+
- feat: right-click menu support for search #423
3132

3233
### Bug fix
3334

src/components/Search/ContextMenu.tsx

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { useClickAway, useCreation, useReactive } from "ahooks";
22
import clsx from "clsx";
3-
import { isNil, noop } from "lodash-es";
3+
import { isNil, lowerCase, noop } from "lodash-es";
44
import { Copy, Link, SquareArrowOutUpRight } from "lucide-react";
5-
import { cloneElement, useEffect, useRef } from "react";
5+
import { cloneElement, useEffect, useRef, useState } from "react";
66
import { useTranslation } from "react-i18next";
77

88
import { useOSKeyPress } from "@/hooks/useOSKeyPress";
@@ -11,6 +11,7 @@ import { copyToClipboard, OpenURLWithBrowser } from "@/utils";
1111
import { isMac } from "@/utils/platform";
1212
import { CONTEXT_MENU_PANEL_ID } from "@/constants";
1313
import { useShortcutsStore } from "@/stores/shortcutsStore";
14+
import { Input } from "@headlessui/react";
1415

1516
interface State {
1617
activeMenuIndex: number;
@@ -36,6 +37,15 @@ const ContextMenu = ({ hideCoco }: ContextMenuProps) => {
3637
const selectedSearchContent = useSearchStore((state) => {
3738
return state.selectedSearchContent;
3839
});
40+
const [searchMenus, setSearchMenus] = useState<typeof menus>([]);
41+
42+
const title = useCreation(() => {
43+
if (selectedSearchContent?.id === "Calculator") {
44+
return t("search.contextMenu.title.calculator");
45+
}
46+
47+
return selectedSearchContent?.title;
48+
}, [selectedSearchContent]);
3949

4050
const menus = useCreation(() => {
4151
if (isNil(selectedSearchContent)) return [];
@@ -45,7 +55,7 @@ const ContextMenu = ({ hideCoco }: ContextMenuProps) => {
4555

4656
const menus = [
4757
{
48-
name: "search.contextMenu.open",
58+
name: t("search.contextMenu.open"),
4959
icon: <SquareArrowOutUpRight />,
5060
keys: isMac ? ["↩︎"] : ["Enter"],
5161
shortcut: "enter",
@@ -57,7 +67,7 @@ const ContextMenu = ({ hideCoco }: ContextMenuProps) => {
5767
},
5868
},
5969
{
60-
name: "search.contextMenu.copyLink",
70+
name: t("search.contextMenu.copyLink"),
6171
icon: <Link />,
6272
keys: isMac ? ["⌘", "L"] : ["Ctrl", "L"],
6373
shortcut: isMac ? "meta.l" : "ctrl.l",
@@ -67,7 +77,7 @@ const ContextMenu = ({ hideCoco }: ContextMenuProps) => {
6777
},
6878
},
6979
{
70-
name: "search.contextMenu.copyAnswer",
80+
name: t("search.contextMenu.copyAnswer"),
7181
icon: <Copy />,
7282
keys: isMac ? ["↩︎"] : ["Enter"],
7383
shortcut: "enter",
@@ -77,7 +87,7 @@ const ContextMenu = ({ hideCoco }: ContextMenuProps) => {
7787
},
7888
},
7989
{
80-
name: "search.contextMenu.copyUppercaseAnswer",
90+
name: t("search.contextMenu.copyUppercaseAnswer"),
8191
icon: <Copy />,
8292
keys: isMac ? ["⌘", "↩︎"] : ["Ctrl", "Enter"],
8393
shortcut: "meta.enter",
@@ -87,7 +97,7 @@ const ContextMenu = ({ hideCoco }: ContextMenuProps) => {
8797
},
8898
},
8999
{
90-
name: "search.contextMenu.copyQuestionAndAnswer",
100+
name: t("search.contextMenu.copyQuestionAndAnswer"),
91101
icon: <Copy />,
92102
keys: isMac ? ["⌘", "L"] : ["Ctrl", "L"],
93103
shortcut: "meta.l",
@@ -98,7 +108,11 @@ const ContextMenu = ({ hideCoco }: ContextMenuProps) => {
98108
},
99109
];
100110

101-
return menus.filter((item) => !item.hide);
111+
const filterMenus = menus.filter((item) => !item.hide);
112+
113+
setSearchMenus(filterMenus);
114+
115+
return filterMenus;
102116
}, [selectedSearchContent]);
103117

104118
const shortcuts = useCreation(() => {
@@ -182,23 +196,25 @@ const ContextMenu = ({ hideCoco }: ContextMenuProps) => {
182196
ref={containerRef}
183197
id={visibleContextMenu ? CONTEXT_MENU_PANEL_ID : ""}
184198
className={clsx(
185-
"absolute bottom-[40px] right-[8px] min-w-[280px] scale-0 transition origin-bottom-right text-sm p-1 bg-white dark:bg-[#202126] rounded-lg shadow-xs border border-gray-200 dark:border-gray-700",
199+
"absolute bottom-[50px] right-[18px] w-[300px] flex flex-col gap-2 scale-0 transition origin-bottom-right text-sm p-3 pb-0 bg-white dark:bg-black rounded-lg shadow-xs border border-[#EDEDED] dark:border-[#272828] shadow-lg dark:shadow-white/15",
186200
{
187201
"!scale-100": visibleContextMenu,
188202
}
189203
)}
190204
>
191-
<ul className="flex flex-col">
192-
{menus.map((item, index) => {
205+
<div className="text-[#999] dark:text-[#666] truncate">{title}</div>
206+
207+
<ul className="flex flex-col -mx-2">
208+
{searchMenus.map((item, index) => {
193209
const { name, icon, keys, clickEvent } = item;
194210

195211
return (
196212
<li
197213
key={name}
198214
className={clsx(
199-
"flex justify-between items-center gap-2 px-3 py-2 rounded-lg cursor-pointer",
215+
"flex justify-between items-center gap-2 px-2 py-2 rounded-lg cursor-pointer",
200216
{
201-
"bg-black/5 dark:bg-white/5":
217+
"bg-[#EDEDED] dark:bg-[#202126]":
202218
index === state.activeMenuIndex,
203219
}
204220
)}
@@ -210,15 +226,15 @@ const ContextMenu = ({ hideCoco }: ContextMenuProps) => {
210226
<div className="flex items-center gap-2 text-black/80 dark:text-white/80">
211227
{cloneElement(icon, { className: "size-4" })}
212228

213-
<span>{t(name)}</span>
229+
<span>{name}</span>
214230
</div>
215231

216232
<div className="flex gap-[4px] text-black/60 dark:text-white/60">
217233
{keys.map((key) => (
218234
<kbd
219235
key={key}
220236
className={clsx(
221-
"flex justify-center items-center font-sans h-[20px] min-w-[20px] text-[10px] rounded-md border border-black/10 dark:border-white/10",
237+
"flex justify-center items-center font-sans h-[20px] min-w-[20px] text-[10px] rounded-md border border-[#EDEDED] dark:border-white/10 bg-white dark:bg-[#202126]",
222238
{
223239
"px-1": key.length > 1,
224240
}
@@ -232,6 +248,25 @@ const ContextMenu = ({ hideCoco }: ContextMenuProps) => {
232248
);
233249
})}
234250
</ul>
251+
252+
<div className="-mx-3 p-2 border-t border-[#E6E6E6] dark:border-[#262626]">
253+
{visibleContextMenu && (
254+
<Input
255+
autoFocus
256+
placeholder={t("search.contextMenu.search")}
257+
className="w-full bg-transparent"
258+
onChange={(event) => {
259+
const value = event.target.value;
260+
261+
const searchMenus = menus.filter((item) => {
262+
return lowerCase(item.name).includes(lowerCase(value));
263+
});
264+
265+
setSearchMenus(searchMenus);
266+
}}
267+
/>
268+
)}
269+
</div>
235270
</div>
236271
</>
237272
);

src/components/Search/SearchListItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const SearchListItem: React.FC<SearchListItemProps> = React.memo(
4242
className={clsx(
4343
"w-full px-2 py-2.5 text-sm flex mb-0 flex-row items-center mobile:mb-2 mobile:flex-col mobile:items-start justify-between rounded-lg transition-colors cursor-pointer text-[#333] dark:text-[#d8d8d8] mobile:bg-gray-200/80 mobile:dark:bg-gray-700/50",
4444
{
45-
"bg-[#EDEDED] dark:bg-[#202126]": isSelected,
45+
"bg-black/10 dark:bg-white/15": isSelected,
4646
"gap-7 mobile:gap-1": showListRight,
4747
}
4848
)}

src/locales/en/translation.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,11 @@
245245
"copyLink": "Copy Link",
246246
"copyAnswer": "Copy Answer",
247247
"copyUppercaseAnswer": "Copy Answer (in Word)",
248-
"copyQuestionAndAnswer": "Copy Question and Answer"
248+
"copyQuestionAndAnswer": "Copy Question and Answer",
249+
"title": {
250+
"calculator": "Calculator"
251+
},
252+
"search": "Search Operation"
249253
}
250254
},
251255
"assistant": {

src/locales/zh/translation.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,11 @@
247247
"copyLink": "复制链接",
248248
"copyAnswer": "复制答案",
249249
"copyUppercaseAnswer": "复制答案(大写)",
250-
"copyQuestionAndAnswer": "复制问题和答案"
250+
"copyQuestionAndAnswer": "复制问题和答案",
251+
"title": {
252+
"calculator": "计算器"
253+
},
254+
"search": "搜索操作"
251255
}
252256
},
253257
"assistant": {

0 commit comments

Comments
 (0)