дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-08 19:02:17 +02:00
fix(plugins): install plugin packages from a .zip (#896)
Root cause: Install Plugin from Disk only opened a folder picker and the host could only copy folders, so a zipped plugin could not be installed. The picker now accepts a .zip or a package's plugin.toml, and the host's install_from_path unpacks a zip to a temporary folder and runs it through the existing install_from_dir validation. The package may sit at the archive root or in its only folder (archiver metadata such as __MACOSX is ignored). Archives with unsafe paths or symbolic links, more than 10,000 entries or over 1 GB unpacked are refused, and a zip without plugin.toml (e.g. an IntelliJ plugin) gets a clear error. Fixes #892 Co-authored-by: florentvdry <[email protected]>
Цей коміт міститься в:
1 джерело
eb633f6ca6
коміт
db5251efe6
12 files changed
+208
-21
No files matched your search
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
import { Channel } from "@tauri-apps/api/core";
|
||||
import type { ActiveTheme, CatalogPlugin, PluginEvent, PluginInfo, PluginsSnapshot } from "@/bindings";
|
||||
import { tx } from "@/platform/i18n";
|
||||
import { call, commands, IpcError, isTauri } from "@/platform/ipc";
|
||||
|
||||
export interface PluginsBackend {
|
||||
@@ -21,7 +22,8 @@ export interface PluginsBackend {
|
||||
/** The selected theme's resolved frame colour (`#rrggbb[aa]`), which new windows open in. */
|
||||
setThemeFrame(color: string | null): Promise<void>;
|
||||
/** Folder picker; `null` when cancelled. */
|
||||
pickFolder(): Promise<string | null>;
|
||||
/** A package to install: a `.zip`, or the `plugin.toml` of a folder. */
|
||||
pickPackage(): Promise<string | null>;
|
||||
/** Subscribes to backend plugin events (this window only). */
|
||||
onEvent(handler: (e: PluginEvent) => void): () => void;
|
||||
}
|
||||
@@ -45,9 +47,13 @@ export const tauriPluginsBackend: PluginsBackend = {
|
||||
setThemeFrame: async (color) => {
|
||||
await call(commands.pluginSetThemeFrame(color));
|
||||
},
|
||||
pickFolder: async () => {
|
||||
pickPackage: async () => {
|
||||
const { open } = await import("@tauri-apps/plugin-dialog");
|
||||
const picked = await open({ directory: true, multiple: false, title: "Install Plugin from Disk" });
|
||||
const picked = await open({
|
||||
multiple: false,
|
||||
title: tx("Install Plugin from Disk"),
|
||||
filters: [{ name: tx("Plugin package"), extensions: ["zip", "toml"] }],
|
||||
});
|
||||
return (Array.isArray(picked) ? picked[0] : picked) ?? null;
|
||||
},
|
||||
onEvent: (handler) => {
|
||||
@@ -247,7 +253,7 @@ export function createMemoryPluginsBackend(
|
||||
return t;
|
||||
},
|
||||
setThemeFrame: async () => {},
|
||||
pickFolder: async () => null,
|
||||
pickPackage: async () => null,
|
||||
onEvent: (handler) => {
|
||||
listeners.add(handler);
|
||||
return () => void listeners.delete(handler);
|
||||
|
||||
@@ -124,25 +124,25 @@ export function compareVersions(a: string, b: string): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Asks for a plugin folder (containing plugin.toml) and installs it. Returns the new plugin id. */
|
||||
/** Asks for a plugin package (a `.zip` or a `plugin.toml`) and installs it. Returns the new plugin id. */
|
||||
export async function installFromDisk(path?: string): Promise<string | null> {
|
||||
const backend = pluginsBackend();
|
||||
let dir: string | null = path ?? null;
|
||||
let source: string | null = path ?? null;
|
||||
try {
|
||||
dir ??= await backend.pickFolder();
|
||||
source ??= await backend.pickPackage();
|
||||
} catch (e) {
|
||||
notifications().notify({
|
||||
group: "plugins",
|
||||
level: "error",
|
||||
title: "Could not open the folder picker",
|
||||
title: "Could not open the file picker",
|
||||
body: errorText(e),
|
||||
});
|
||||
return null;
|
||||
}
|
||||
if (!dir) return null;
|
||||
if (!source) return null;
|
||||
usePlugins.setState({ installing: true });
|
||||
try {
|
||||
const info = await backend.installDir(dir);
|
||||
const info = await backend.installDir(source);
|
||||
notifications().notify({
|
||||
group: "plugins",
|
||||
level: info.status === "failed" ? "warning" : "info",
|
||||
|
||||
Reference in new issue
Заблокувати користувача