mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-30 22:31:52 +02:00
Add prompt() plugin API (#121)
This commit is contained in:
@@ -17,6 +17,7 @@ use fern::colors::ColoredLevelConfig;
|
||||
use log::{debug, error, info, warn};
|
||||
use rand::random;
|
||||
use regex::Regex;
|
||||
use serde::Serialize;
|
||||
use serde_json::{json, Value};
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::TitleBarStyle;
|
||||
@@ -62,7 +63,8 @@ use yaak_plugin_runtime::events::{
|
||||
BootResponse, CallHttpRequestActionRequest, FilterResponse, FindHttpResponsesResponse,
|
||||
GetHttpRequestActionsResponse, GetHttpRequestByIdResponse, GetTemplateFunctionsResponse, Icon,
|
||||
InternalEvent, InternalEventPayload, RenderHttpRequestResponse, RenderPurpose,
|
||||
SendHttpRequestResponse, ShowToastRequest, TemplateRenderResponse, WindowContext,
|
||||
SendHttpRequestResponse, ShowPromptResponse, ShowToastRequest, TemplateRenderResponse,
|
||||
WindowContext,
|
||||
};
|
||||
use yaak_plugin_runtime::plugin_handle::PluginHandle;
|
||||
use yaak_templates::{Parser, Tokens};
|
||||
@@ -2158,6 +2160,40 @@ fn monitor_plugin_events<R: Runtime>(app_handle: &AppHandle<R>) {
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct FrontendCall<T: Serialize + Clone> {
|
||||
args: T,
|
||||
reply_id: String,
|
||||
}
|
||||
|
||||
async fn call_frontend<T: Serialize + Clone, R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
event_name: &str,
|
||||
args: T,
|
||||
) -> ShowPromptResponse {
|
||||
let reply_id = format!("{event_name}_reply");
|
||||
let payload = FrontendCall {
|
||||
args,
|
||||
reply_id: reply_id.clone(),
|
||||
};
|
||||
window.emit_to(window.label(), event_name, payload).unwrap();
|
||||
let (tx, mut rx) = tokio::sync::watch::channel(ShowPromptResponse::default());
|
||||
|
||||
let event_id = window.clone().listen(reply_id, move |ev| {
|
||||
println!("GOT REPLY {ev:?}");
|
||||
let resp: ShowPromptResponse = serde_json::from_str(ev.payload()).unwrap();
|
||||
_ = tx.send(resp);
|
||||
});
|
||||
|
||||
// When reply shows up, unlisten to events and return
|
||||
_ = rx.changed().await;
|
||||
window.unlisten(event_id);
|
||||
|
||||
let foo = rx.borrow();
|
||||
foo.clone()
|
||||
}
|
||||
|
||||
async fn handle_plugin_event<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
event: &InternalEvent,
|
||||
@@ -2184,6 +2220,12 @@ async fn handle_plugin_event<R: Runtime>(
|
||||
};
|
||||
None
|
||||
}
|
||||
InternalEventPayload::ShowPromptRequest(req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for render");
|
||||
let resp = call_frontend(window, "show_prompt", req).await;
|
||||
Some(InternalEventPayload::ShowPromptResponse(resp))
|
||||
}
|
||||
InternalEventPayload::FindHttpResponsesRequest(req) => {
|
||||
let http_responses = list_http_responses(
|
||||
app_handle,
|
||||
|
||||
@@ -55,6 +55,7 @@ impl TemplateCallback for PluginTemplateCallback {
|
||||
TemplateFunctionArg::Text(a) => a.base,
|
||||
TemplateFunctionArg::Select(a) => a.base,
|
||||
TemplateFunctionArg::Checkbox(a) => a.base,
|
||||
TemplateFunctionArg::File(a) => a.base,
|
||||
TemplateFunctionArg::HttpRequest(a) => a.base,
|
||||
};
|
||||
if let None = args_with_defaults.get(base.name.as_str()) {
|
||||
|
||||
@@ -33,7 +33,7 @@ export type FilterRequest = { content: string, filter: string, };
|
||||
|
||||
export type FilterResponse = { content: string, };
|
||||
|
||||
export type FindHttpResponsesRequest = { requestId: string, limit: number | null, };
|
||||
export type FindHttpResponsesRequest = { requestId: string, limit?: number, };
|
||||
|
||||
export type FindHttpResponsesResponse = { httpResponses: Array<HttpResponse>, };
|
||||
|
||||
@@ -47,9 +47,9 @@ export type GetHttpRequestByIdResponse = { httpRequest: HttpRequest | null, };
|
||||
|
||||
export type GetTemplateFunctionsResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
|
||||
|
||||
export type HttpRequestAction = { key: string, label: string, icon: string | null, };
|
||||
export type HttpRequestAction = { key: string, label: string, icon?: Icon, };
|
||||
|
||||
export type Icon = "copy" | "info" | "check_circle" | "alert_triangle";
|
||||
export type Icon = "copy" | "info" | "check_circle" | "alert_triangle" | "_unknown";
|
||||
|
||||
export type ImportRequest = { content: string, };
|
||||
|
||||
@@ -59,7 +59,9 @@ export type ImportResponse = { resources: ImportResources, };
|
||||
|
||||
export type InternalEvent = { id: string, pluginRefId: string, replyId: string | null, payload: InternalEventPayload, windowContext: WindowContext, };
|
||||
|
||||
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "reload_request" } | { "type": "reload_response" } | { "type": "terminate_request" } | { "type": "terminate_response" } | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "get_http_request_actions_request" } & GetHttpRequestActionsRequest | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "empty_response" };
|
||||
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "reload_request" } | { "type": "reload_response" } | { "type": "terminate_request" } | { "type": "terminate_response" } | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "get_http_request_actions_request" } & GetHttpRequestActionsRequest | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_prompt_request" } & ShowPromptRequest | { "type": "show_prompt_response" } & ShowPromptResponse | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "empty_response" };
|
||||
|
||||
export type OpenFileFilter = { name: string, extensions: Array<string>, };
|
||||
|
||||
export type RenderHttpRequestRequest = { httpRequest: HttpRequest, purpose: RenderPurpose, };
|
||||
|
||||
@@ -71,23 +73,41 @@ export type SendHttpRequestRequest = { httpRequest: HttpRequest, };
|
||||
|
||||
export type SendHttpRequestResponse = { httpResponse: HttpResponse, };
|
||||
|
||||
export type ShowToastRequest = { message: string, color?: Color | null, icon?: Icon | null, };
|
||||
export type ShowPromptRequest = { id: string, title: string, label: string, description?: string, defaultValue?: string, placeholder?: string,
|
||||
/**
|
||||
* Text to add to the confirmation button
|
||||
*/
|
||||
confirmText?: string,
|
||||
/**
|
||||
* Text to add to the cancel button
|
||||
*/
|
||||
cancelText?: string,
|
||||
/**
|
||||
* Require the user to enter a non-empty value
|
||||
*/
|
||||
require?: boolean, };
|
||||
|
||||
export type TemplateFunction = { name: string, aliases: Array<string>, args: Array<TemplateFunctionArg>, };
|
||||
export type ShowPromptResponse = { value: string, };
|
||||
|
||||
export type TemplateFunctionArg = { "type": "text" } & TemplateFunctionTextArg | { "type": "select" } & TemplateFunctionSelectArg | { "type": "checkbox" } & TemplateFunctionCheckboxArg | { "type": "http_request" } & TemplateFunctionHttpRequestArg;
|
||||
export type ShowToastRequest = { message: string, color?: Color, icon?: Icon, };
|
||||
|
||||
export type TemplateFunctionBaseArg = { name: string, optional?: boolean | null, label?: string | null, defaultValue?: string | null, };
|
||||
export type TemplateFunction = { name: string, aliases?: Array<string>, args: Array<TemplateFunctionArg>, };
|
||||
|
||||
export type TemplateFunctionCheckboxArg = { name: string, optional?: boolean | null, label?: string | null, defaultValue?: string | null, };
|
||||
export type TemplateFunctionArg = { "type": "text" } & TemplateFunctionTextArg | { "type": "select" } & TemplateFunctionSelectArg | { "type": "checkbox" } & TemplateFunctionCheckboxArg | { "type": "http_request" } & TemplateFunctionHttpRequestArg | { "type": "file" } & TemplateFunctionFileArg;
|
||||
|
||||
export type TemplateFunctionHttpRequestArg = { name: string, optional?: boolean | null, label?: string | null, defaultValue?: string | null, };
|
||||
export type TemplateFunctionBaseArg = { name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
|
||||
export type TemplateFunctionSelectArg = { options: Array<TemplateFunctionSelectOption>, name: string, optional?: boolean | null, label?: string | null, defaultValue?: string | null, };
|
||||
export type TemplateFunctionCheckboxArg = { name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
|
||||
export type TemplateFunctionFileArg = { title: string, multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array<OpenFileFilter>, name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
|
||||
export type TemplateFunctionHttpRequestArg = { name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
|
||||
export type TemplateFunctionSelectArg = { options: Array<TemplateFunctionSelectOption>, name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
|
||||
export type TemplateFunctionSelectOption = { name: string, value: string, };
|
||||
|
||||
export type TemplateFunctionTextArg = { placeholder?: string | null, name: string, optional?: boolean | null, label?: string | null, defaultValue?: string | null, };
|
||||
export type TemplateFunctionTextArg = { placeholder?: string, name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
|
||||
export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, };
|
||||
|
||||
|
||||
@@ -76,6 +76,9 @@ pub enum InternalEventPayload {
|
||||
|
||||
ShowToastRequest(ShowToastRequest),
|
||||
|
||||
ShowPromptRequest(ShowPromptRequest),
|
||||
ShowPromptResponse(ShowPromptResponse),
|
||||
|
||||
GetHttpRequestByIdRequest(GetHttpRequestByIdRequest),
|
||||
GetHttpRequestByIdResponse(GetHttpRequestByIdResponse),
|
||||
|
||||
@@ -203,12 +206,46 @@ pub struct TemplateRenderResponse {
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
pub struct ShowToastRequest {
|
||||
pub message: String,
|
||||
#[ts(optional = nullable)]
|
||||
#[ts(optional)]
|
||||
pub color: Option<Color>,
|
||||
#[ts(optional = nullable)]
|
||||
#[ts(optional)]
|
||||
pub icon: Option<Icon>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
pub struct ShowPromptRequest {
|
||||
// A unique ID to identify the prompt (eg. "enter-password")
|
||||
pub id: String,
|
||||
// Title to show on the prompt dialog
|
||||
pub title: String,
|
||||
// Text to show on the label above the input
|
||||
pub label: String,
|
||||
#[ts(optional)]
|
||||
pub description: Option<String>,
|
||||
#[ts(optional)]
|
||||
pub default_value: Option<String>,
|
||||
#[ts(optional)]
|
||||
pub placeholder: Option<String>,
|
||||
/// Text to add to the confirmation button
|
||||
#[ts(optional)]
|
||||
pub confirm_text: Option<String>,
|
||||
/// Text to add to the cancel button
|
||||
#[ts(optional)]
|
||||
pub cancel_text: Option<String>,
|
||||
/// Require the user to enter a non-empty value
|
||||
#[ts(optional)]
|
||||
pub require: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
pub struct ShowPromptResponse {
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
@@ -238,6 +275,10 @@ pub enum Icon {
|
||||
Info,
|
||||
CheckCircle,
|
||||
AlertTriangle,
|
||||
|
||||
#[serde(untagged)]
|
||||
#[ts(type = "\"_unknown\"")]
|
||||
_Unknown(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
@@ -253,7 +294,8 @@ pub struct GetTemplateFunctionsResponse {
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
pub struct TemplateFunction {
|
||||
pub name: String,
|
||||
pub aliases: Vec<String>,
|
||||
#[ts(optional)]
|
||||
pub aliases: Option<Vec<String>>,
|
||||
pub args: Vec<TemplateFunctionArg>,
|
||||
}
|
||||
|
||||
@@ -265,6 +307,7 @@ pub enum TemplateFunctionArg {
|
||||
Select(TemplateFunctionSelectArg),
|
||||
Checkbox(TemplateFunctionCheckboxArg),
|
||||
HttpRequest(TemplateFunctionHttpRequestArg),
|
||||
File(TemplateFunctionFileArg),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
@@ -272,11 +315,11 @@ pub enum TemplateFunctionArg {
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
pub struct TemplateFunctionBaseArg {
|
||||
pub name: String,
|
||||
#[ts(optional = nullable)]
|
||||
#[ts(optional)]
|
||||
pub optional: Option<bool>,
|
||||
#[ts(optional = nullable)]
|
||||
#[ts(optional)]
|
||||
pub label: Option<String>,
|
||||
#[ts(optional = nullable)]
|
||||
#[ts(optional)]
|
||||
pub default_value: Option<String>,
|
||||
}
|
||||
|
||||
@@ -286,7 +329,7 @@ pub struct TemplateFunctionBaseArg {
|
||||
pub struct TemplateFunctionTextArg {
|
||||
#[serde(flatten)]
|
||||
pub base: TemplateFunctionBaseArg,
|
||||
#[ts(optional = nullable)]
|
||||
#[ts(optional)]
|
||||
pub placeholder: Option<String>,
|
||||
}
|
||||
|
||||
@@ -298,6 +341,31 @@ pub struct TemplateFunctionHttpRequestArg {
|
||||
pub base: TemplateFunctionBaseArg,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
pub struct TemplateFunctionFileArg {
|
||||
#[serde(flatten)]
|
||||
pub base: TemplateFunctionBaseArg,
|
||||
pub title: String,
|
||||
#[ts(optional)]
|
||||
pub multiple: Option<bool>,
|
||||
#[ts(optional)]
|
||||
pub directory: Option<bool>,
|
||||
#[ts(optional)]
|
||||
pub default_path: Option<String>,
|
||||
#[ts(optional)]
|
||||
pub filters: Option<Vec<OpenFileFilter>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
pub struct OpenFileFilter {
|
||||
pub name: String,
|
||||
pub extensions: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
@@ -379,7 +447,8 @@ pub struct GetHttpRequestActionsResponse {
|
||||
pub struct HttpRequestAction {
|
||||
pub key: String,
|
||||
pub label: String,
|
||||
pub icon: Option<String>,
|
||||
#[ts(optional)]
|
||||
pub icon: Option<Icon>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
@@ -417,6 +486,7 @@ pub struct GetHttpRequestByIdResponse {
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
pub struct FindHttpResponsesRequest {
|
||||
pub request_id: String,
|
||||
#[ts(optional)]
|
||||
pub limit: Option<i32>,
|
||||
}
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@ impl PluginManager {
|
||||
};
|
||||
let plugin_handle = PluginHandle::new(dir, tx.clone());
|
||||
|
||||
// Add the new plugin
|
||||
// Add the new plugin
|
||||
self.plugins.lock().await.push(plugin_handle.clone());
|
||||
|
||||
// Boot the plugin
|
||||
|
||||
Reference in New Issue
Block a user