Skip to content

Commit 568db6a

Browse files
authored
feat: add extension uninstall option in settings (#855)
* feat: add extension uninstall option in settings * docs: update changelog * update
1 parent 2eb1093 commit 568db6a

5 files changed

Lines changed: 79 additions & 9 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Information about release notes of Coco App is provided here.
1717
- feat: support installing local extensions #749
1818
- feat: support sending files in chat messages #764
1919
- feat: sub extension can set 'platforms' now #847
20+
- feat: add extension uninstall option in settings #855
2021

2122
### 🐛 Bug fix
2223

src/components/Settings/Extensions/components/Details/index.tsx

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ import SharedAi from "./SharedAi";
88
import AiOverview from "./AiOverview";
99
import Calculator from "./Calculator";
1010
import FileSearch from "./FileSearch";
11+
import { Ellipsis } from "lucide-react";
12+
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
13+
import platformAdapter from "@/utils/platformAdapter";
14+
import { useAppStore } from "@/stores/appStore";
15+
import { useTranslation } from "react-i18next";
1116

1217
const Details = () => {
1318
const { rootState } = useContext(ExtensionsContext);
@@ -23,6 +28,10 @@ const Details = () => {
2328
const setQuickAiAccessAssistant = useExtensionsStore((state) => {
2429
return state.setQuickAiAccessAssistant;
2530
});
31+
const addError = useAppStore((state) => {
32+
return state.addError;
33+
});
34+
const { t } = useTranslation();
2635

2736
const renderContent = () => {
2837
if (!rootState.activeExtension) return;
@@ -66,12 +75,62 @@ const Details = () => {
6675
};
6776

6877
return (
69-
<div className="flex-1 h-full overflow-auto">
70-
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
71-
{rootState.activeExtension?.name}
72-
</h2>
78+
<div className="flex-1 h-full pr-4 pb-4 overflow-auto">
79+
<div className="flex items-start justify-between gap-4 mb-4">
80+
<h2 className="text-lg font-semibold text-gray-900 dark:text-white">
81+
{rootState.activeExtension?.name}
82+
</h2>
83+
84+
{rootState.activeExtension?.developer && (
85+
<Menu>
86+
<MenuButton className="h-7">
87+
<Ellipsis className="size-5" />
88+
</MenuButton>
89+
90+
<MenuItems
91+
anchor="bottom end"
92+
className="p-1 text-sm bg-white dark:bg-[#202126] rounded-lg shadow-xs border border-gray-200 dark:border-gray-700"
93+
>
94+
<MenuItem>
95+
<div
96+
className="px-3 py-2 text-nowrap text-red-500 hover:bg-black/5 hover:dark:bg-white/5 rounded-lg cursor-pointer"
97+
onClick={async () => {
98+
try {
99+
const { id, developer } = rootState.activeExtension!;
100+
101+
await platformAdapter.invokeBackend(
102+
"uninstall_extension",
103+
{
104+
extensionId: id,
105+
developer: developer,
106+
}
107+
);
108+
109+
Object.assign(rootState, {
110+
activeExtension: void 0,
111+
extensions: rootState.extensions.filter((item) => {
112+
return item.id !== id;
113+
}),
114+
});
115+
116+
addError(
117+
t("settings.extensions.hints.uninstallSuccess"),
118+
"info"
119+
);
120+
} catch (error) {
121+
addError(String(error));
122+
}
123+
}}
124+
>
125+
{t("settings.extensions.hints.uninstall")}
126+
</div>
127+
</MenuItem>
128+
</MenuItems>
129+
</Menu>
130+
)}
131+
</div>
73132

74-
<div className="pr-4 pb-4 text-sm">{renderContent()}</div>
133+
<div className="text-sm">{renderContent()}</div>
75134
</div>
76135
);
77136
};

src/components/Settings/Extensions/index.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,15 @@ export const Extensions = () => {
205205
const errorMessage = String(error);
206206

207207
if (errorMessage === "already imported") {
208-
addError(t("settings.extensions.hints.extensionAlreadyImported"));
208+
addError(
209+
t(
210+
"settings.extensions.hints.extensionAlreadyImported"
211+
)
212+
);
209213
} else if (errorMessage === "incompatible") {
210-
addError(t("settings.extensions.hints.incompatibleExtension"));
214+
addError(
215+
t("settings.extensions.hints.incompatibleExtension")
216+
);
211217
} else {
212218
addError(t("settings.extensions.hints.importFailed"));
213219
}

src/locales/en/translation.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,9 @@
209209
"importSuccess": "Extension imported successfully.",
210210
"importFailed": "No valid extension found in the selected folder. Please check the folder structure.",
211211
"extensionAlreadyImported": "Extension already imported. Please remove it first.",
212-
"incompatibleExtension": "This extension is incompatible with your OS."
212+
"incompatibleExtension": "This extension is incompatible with your OS.",
213+
"uninstall": "Uninstall",
214+
"uninstallSuccess": "Uninstall Success"
213215
},
214216
"application": {
215217
"title": "Applications",

src/locales/zh/translation.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,9 @@
209209
"importSuccess": "插件导入成功。",
210210
"importFailed": "未在该目录中找到有效的插件,请检查目录结构是否正确。",
211211
"extensionAlreadyImported": "插件已存在,无法重复导入。请先将其删除后再尝试。",
212-
"incompatibleExtension": "此插件与当前操作系统不兼容。"
212+
"incompatibleExtension": "此插件与当前操作系统不兼容。",
213+
"uninstall": "卸载",
214+
"uninstallSuccess": "卸载成功"
213215
},
214216
"application": {
215217
"title": "应用程序",

0 commit comments

Comments
 (0)