дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 03:05:40 +02:00
feat(plugins): notifications with command buttons (#1974)
Adds API 1.3 `ui.notify(level, title, body, actions) -> result<_, error>`: up to three buttons, each running one of the plugin's declared commands with an optional argument in the balloon's window and dismissing the balloon. An invalid action or an undeclared command fails the call and shows nothing. The plugin `Notification` event carries the actions; SDK `host::notify_with_actions` and `NotificationAction`. Closes #1955. Co-authored-by: arnaud-ritti <[email protected]>
Цей коміт міститься в:
1 джерело
f3837e0fe7
коміт
112ed773fb
43 files changed
+419
-39
No files matched your search
@@ -488,12 +488,41 @@ describe("bridge events", () => {
|
||||
expect(useTasks.getState().tasks.find((t) => t.id === "plugin:7")?.done).toBe(true);
|
||||
});
|
||||
|
||||
it("runs notification buttons as commands of the publishing plugin with their arguments", async () => {
|
||||
const runCommand = vi.fn().mockResolvedValue(null);
|
||||
setPluginsBackend({ ...backend, runCommand });
|
||||
handlePluginEvent({
|
||||
type: "notification",
|
||||
pluginId: "hello",
|
||||
level: "info",
|
||||
title: "Done",
|
||||
body: "Pick an action",
|
||||
actions: [
|
||||
{ label: "Open", command: "insert-date", argument: "first\nsecond" },
|
||||
{ label: "Refresh", command: "word-count", argument: null },
|
||||
{ label: "Undo", command: "insert-date", argument: "undo" },
|
||||
],
|
||||
});
|
||||
expect(notes[0]?.actions?.map((a) => a.label)).toEqual(["Open", "Refresh", "Undo"]);
|
||||
notes[0]!.actions![0]!.run();
|
||||
await waitFor(() => expect(runCommand).toHaveBeenCalledWith("hello", "insert-date", "first\nsecond", null, []));
|
||||
});
|
||||
|
||||
it("shows plugin notifications, copies to clipboard, opens URLs and tracks theme changes", () => {
|
||||
const writeText = vi.fn().mockResolvedValue(undefined);
|
||||
Object.defineProperty(navigator, "clipboard", { value: { writeText }, configurable: true });
|
||||
const open = vi.spyOn(window, "open").mockImplementation(() => null);
|
||||
handlePluginEvent({ type: "notification", pluginId: "p", level: "info", title: "Done", body: "3 words" });
|
||||
expect(notes).toEqual([{ group: "plugins", level: "info", title: "Done", body: "3 words", verbatim: true }]);
|
||||
handlePluginEvent({
|
||||
type: "notification",
|
||||
pluginId: "p",
|
||||
level: "info",
|
||||
title: "Done",
|
||||
body: "3 words",
|
||||
actions: [],
|
||||
});
|
||||
expect(notes).toEqual([
|
||||
{ group: "plugins", level: "info", title: "Done", body: "3 words", verbatim: true, actions: [] },
|
||||
]);
|
||||
handlePluginEvent({ type: "copyToClipboard", pluginId: "p", text: "https://github.com/o/r" });
|
||||
expect(writeText).toHaveBeenCalledWith("https://github.com/o/r");
|
||||
handlePluginEvent({ type: "openUrl", pluginId: "p", url: "https://github.com/o/r/blob/main/a.rs" });
|
||||
|
||||
@@ -540,7 +540,14 @@ export function createMemoryPluginsBackend(
|
||||
if (commandId === "word-count") {
|
||||
if (!activeFile) throw new IpcError({ kind: "internal", message: "no file is open in the editor" });
|
||||
const name = activeFile.split("/").pop() ?? activeFile;
|
||||
emit({ type: "notification", pluginId, level: "info", title: `Word count: ${name}`, body: "(simulated)" });
|
||||
emit({
|
||||
type: "notification",
|
||||
pluginId,
|
||||
level: "info",
|
||||
title: `Word count: ${name}`,
|
||||
body: "(simulated)",
|
||||
actions: [],
|
||||
});
|
||||
return null;
|
||||
}
|
||||
throw new IpcError({ kind: "internal", message: `unknown command: ${commandId}` });
|
||||
|
||||
@@ -36,7 +36,14 @@ import { defineBuiltinUiPoints } from "./points";
|
||||
import { localizeContributions, loadPluginCatalogs, usePluginCatalogs } from "./pluginText";
|
||||
import { clearUiContributions, syncUiContributions } from "./projection";
|
||||
import { handleRuntimeEvent, refreshEventKinds, startRuntimeUi } from "./runtimeUi";
|
||||
import { grantPlugin, refreshPlugins, selectPluginTheme, updateAllPlugins, usePlugins } from "./store";
|
||||
import {
|
||||
grantPlugin,
|
||||
runPluginCommand,
|
||||
refreshPlugins,
|
||||
selectPluginTheme,
|
||||
updateAllPlugins,
|
||||
usePlugins,
|
||||
} from "./store";
|
||||
|
||||
export { pluginActionId } from "./points";
|
||||
|
||||
@@ -284,6 +291,10 @@ export function handlePluginEvent(e: PluginEvent) {
|
||||
title: e.title || name,
|
||||
body: e.body || undefined,
|
||||
verbatim: true,
|
||||
actions: e.actions.map((a) => ({
|
||||
label: a.label,
|
||||
run: () => void runPluginCommand(e.pluginId, a.command, a.argument),
|
||||
})),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in new issue
Заблокувати користувача