diff --git a/package-lock.json b/package-lock.json index 4edb44c1..6c713457 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "plugins/importer-postman-environment", "plugins/importer-yaak", "plugins/template-function-cookie", + "plugins/template-function-ctx", "plugins/template-function-encode", "plugins/template-function-fs", "plugins/template-function-hash", @@ -4344,6 +4345,10 @@ "resolved": "plugins/template-function-cookie", "link": true }, + "node_modules/@yaak/template-function-ctx": { + "resolved": "plugins/template-function-ctx", + "link": true + }, "node_modules/@yaak/template-function-encode": { "resolved": "plugins/template-function-encode", "link": true @@ -18969,7 +18974,7 @@ }, "packages/plugin-runtime-types": { "name": "@yaakapp/api", - "version": "0.7.0", + "version": "0.7.1", "dependencies": { "@types/node": "^24.0.13" }, @@ -19097,6 +19102,10 @@ "name": "@yaak/template-function-cookie", "version": "0.1.0" }, + "plugins/template-function-ctx": { + "name": "@yaak/template-function-ctx", + "version": "0.1.0" + }, "plugins/template-function-datetime": { "version": "0.1.0", "extraneous": true, @@ -19128,7 +19137,10 @@ }, "plugins/template-function-prompt": { "name": "@yaak/template-function-prompt", - "version": "0.1.0" + "version": "0.1.0", + "dependencies": { + "slugify": "^1.6.6" + } }, "plugins/template-function-random": { "name": "@yaak/template-function-random", @@ -19177,6 +19189,11 @@ "uuid": "dist/esm/bin/uuid" } }, + "plugins/template-function-window": { + "name": "@yaak/template-function-window", + "version": "0.1.0", + "extraneous": true + }, "plugins/template-function-xml": { "name": "@yaak/template-function-xml", "version": "0.1.0", diff --git a/package.json b/package.json index e1b04a6f..1d2690aa 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "plugins/importer-postman-environment", "plugins/importer-yaak", "plugins/template-function-cookie", + "plugins/template-function-ctx", "plugins/template-function-encode", "plugins/template-function-fs", "plugins/template-function-hash", diff --git a/packages/plugin-runtime-types/package.json b/packages/plugin-runtime-types/package.json index 8ef62b29..ad8230dd 100644 --- a/packages/plugin-runtime-types/package.json +++ b/packages/plugin-runtime-types/package.json @@ -1,6 +1,6 @@ { "name": "@yaakapp/api", - "version": "0.7.0", + "version": "0.7.1", "keywords": [ "api-client", "insomnia-alternative", diff --git a/packages/plugin-runtime-types/src/bindings/gen_events.ts b/packages/plugin-runtime-types/src/bindings/gen_events.ts index cbb99501..1f91180d 100644 --- a/packages/plugin-runtime-types/src/bindings/gen_events.ts +++ b/packages/plugin-runtime-types/src/bindings/gen_events.ts @@ -224,7 +224,7 @@ defaultValue?: string, disabled?: boolean, */ description?: string, }; -export type FormInputHStack = { inputs?: Array, }; +export type FormInputHStack = { inputs?: Array, hidden?: boolean, }; export type FormInputHttpRequest = { /** @@ -391,7 +391,7 @@ export type ImportResponse = { resources: ImportResources, }; export type InternalEvent = { id: string, pluginRefId: string, pluginName: string, replyId: string | null, context: PluginContext, payload: InternalEventPayload, }; -export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } | { "type": "reload_response" } & ReloadResponse | { "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": "list_cookie_names_request" } & ListCookieNamesRequest | { "type": "list_cookie_names_response" } & ListCookieNamesResponse | { "type": "get_cookie_value_request" } & GetCookieValueRequest | { "type": "get_cookie_value_response" } & GetCookieValueResponse | { "type": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_grpc_request_actions_request" } & EmptyPayload | { "type": "get_grpc_request_actions_response" } & GetGrpcRequestActionsResponse | { "type": "call_grpc_request_action_request" } & CallGrpcRequestActionRequest | { "type": "get_template_function_summary_request" } & EmptyPayload | { "type": "get_template_function_summary_response" } & GetTemplateFunctionSummaryResponse | { "type": "get_template_function_config_request" } & GetTemplateFunctionConfigRequest | { "type": "get_template_function_config_response" } & GetTemplateFunctionConfigResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "get_http_authentication_summary_request" } & EmptyPayload | { "type": "get_http_authentication_summary_response" } & GetHttpAuthenticationSummaryResponse | { "type": "get_http_authentication_config_request" } & GetHttpAuthenticationConfigRequest | { "type": "get_http_authentication_config_response" } & GetHttpAuthenticationConfigResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "call_http_authentication_action_request" } & CallHttpAuthenticationActionRequest | { "type": "call_http_authentication_action_response" } & EmptyPayload | { "type": "copy_text_request" } & CopyTextRequest | { "type": "copy_text_response" } & EmptyPayload | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "render_grpc_request_request" } & RenderGrpcRequestRequest | { "type": "render_grpc_request_response" } & RenderGrpcRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "get_key_value_request" } & GetKeyValueRequest | { "type": "get_key_value_response" } & GetKeyValueResponse | { "type": "set_key_value_request" } & SetKeyValueRequest | { "type": "set_key_value_response" } & SetKeyValueResponse | { "type": "delete_key_value_request" } & DeleteKeyValueRequest | { "type": "delete_key_value_response" } & DeleteKeyValueResponse | { "type": "open_window_request" } & OpenWindowRequest | { "type": "window_navigate_event" } & WindowNavigateEvent | { "type": "window_close_event" } | { "type": "close_window_request" } & CloseWindowRequest | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_toast_response" } & EmptyPayload | { "type": "prompt_text_request" } & PromptTextRequest | { "type": "prompt_text_response" } & PromptTextResponse | { "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": "get_themes_request" } & GetThemesRequest | { "type": "get_themes_response" } & GetThemesResponse | { "type": "empty_response" } & EmptyPayload | { "type": "error_response" } & ErrorResponse; +export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } | { "type": "reload_response" } & ReloadResponse | { "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": "list_cookie_names_request" } & ListCookieNamesRequest | { "type": "list_cookie_names_response" } & ListCookieNamesResponse | { "type": "get_cookie_value_request" } & GetCookieValueRequest | { "type": "get_cookie_value_response" } & GetCookieValueResponse | { "type": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_grpc_request_actions_request" } & EmptyPayload | { "type": "get_grpc_request_actions_response" } & GetGrpcRequestActionsResponse | { "type": "call_grpc_request_action_request" } & CallGrpcRequestActionRequest | { "type": "get_template_function_summary_request" } & EmptyPayload | { "type": "get_template_function_summary_response" } & GetTemplateFunctionSummaryResponse | { "type": "get_template_function_config_request" } & GetTemplateFunctionConfigRequest | { "type": "get_template_function_config_response" } & GetTemplateFunctionConfigResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "get_http_authentication_summary_request" } & EmptyPayload | { "type": "get_http_authentication_summary_response" } & GetHttpAuthenticationSummaryResponse | { "type": "get_http_authentication_config_request" } & GetHttpAuthenticationConfigRequest | { "type": "get_http_authentication_config_response" } & GetHttpAuthenticationConfigResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "call_http_authentication_action_request" } & CallHttpAuthenticationActionRequest | { "type": "call_http_authentication_action_response" } & EmptyPayload | { "type": "copy_text_request" } & CopyTextRequest | { "type": "copy_text_response" } & EmptyPayload | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "render_grpc_request_request" } & RenderGrpcRequestRequest | { "type": "render_grpc_request_response" } & RenderGrpcRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "get_key_value_request" } & GetKeyValueRequest | { "type": "get_key_value_response" } & GetKeyValueResponse | { "type": "set_key_value_request" } & SetKeyValueRequest | { "type": "set_key_value_response" } & SetKeyValueResponse | { "type": "delete_key_value_request" } & DeleteKeyValueRequest | { "type": "delete_key_value_response" } & DeleteKeyValueResponse | { "type": "open_window_request" } & OpenWindowRequest | { "type": "window_navigate_event" } & WindowNavigateEvent | { "type": "window_close_event" } | { "type": "close_window_request" } & CloseWindowRequest | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_toast_response" } & EmptyPayload | { "type": "prompt_text_request" } & PromptTextRequest | { "type": "prompt_text_response" } & PromptTextResponse | { "type": "window_info_request" } & WindowInfoRequest | { "type": "window_info_response" } & WindowInfoResponse | { "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": "get_themes_request" } & GetThemesRequest | { "type": "get_themes_response" } & GetThemesResponse | { "type": "empty_response" } & EmptyPayload | { "type": "error_response" } & ErrorResponse; export type JsonPrimitive = string | number | boolean | null; @@ -411,7 +411,7 @@ export type PromptTextRequest = { id: string, title: string, label: string, desc /** * Text to add to the confirmation button */ -confirmText?: string, +confirmText?: string, password?: boolean, /** * Text to add to the cancel button */ @@ -445,7 +445,7 @@ export type SetKeyValueResponse = {}; export type ShowToastRequest = { message: string, color?: Color, icon?: Icon, timeout?: number, }; -export type TemplateFunction = { name: string, description?: string, +export type TemplateFunction = { name: string, previewType?: TemplateFunctionPreviewType, description?: string, /** * Also support alternative names. This is useful for not breaking existing * tags when changing the `name` property @@ -457,6 +457,8 @@ aliases?: Array, args: Array, }; */ export type TemplateFunctionArg = FormInput; +export type TemplateFunctionPreviewType = "live" | "click" | "none"; + export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, }; export type TemplateRenderResponse = { data: JsonValue, }; @@ -487,6 +489,10 @@ export type ThemeComponentColors = { surface?: string, surfaceHighlight?: string export type ThemeComponents = { dialog?: ThemeComponentColors, menu?: ThemeComponentColors, toast?: ThemeComponentColors, sidebar?: ThemeComponentColors, responsePane?: ThemeComponentColors, appHeader?: ThemeComponentColors, button?: ThemeComponentColors, banner?: ThemeComponentColors, templateTag?: ThemeComponentColors, urlBar?: ThemeComponentColors, editor?: ThemeComponentColors, input?: ThemeComponentColors, }; +export type WindowInfoRequest = { label: string, }; + +export type WindowInfoResponse = { requestId: string | null, environmentId: string | null, workspaceId: string | null, label: string, }; + export type WindowNavigateEvent = { url: string, }; export type WindowSize = { width: number, height: number, }; diff --git a/packages/plugin-runtime-types/src/plugins/Context.ts b/packages/plugin-runtime-types/src/plugins/Context.ts index ebe1d3f0..163a438c 100644 --- a/packages/plugin-runtime-types/src/plugins/Context.ts +++ b/packages/plugin-runtime-types/src/plugins/Context.ts @@ -18,7 +18,7 @@ import type { ShowToastRequest, TemplateRenderRequest, } from '../bindings/gen_events.ts'; -import { JsonValue } from '../bindings/serde_json/JsonValue'; +import type { JsonValue } from '../bindings/serde_json/JsonValue'; export interface Context { clipboard: { @@ -36,6 +36,9 @@ export interface Context { delete(key: string): Promise; }; window: { + requestId(): Promise; + workspaceId(): Promise; + environmentId(): Promise; openUrl( args: OpenWindowRequest & { onNavigate?: (args: { url: string }) => void; diff --git a/packages/plugin-runtime/src/PluginInstance.ts b/packages/plugin-runtime/src/PluginInstance.ts index 1e60b9af..bb969cb3 100644 --- a/packages/plugin-runtime/src/PluginInstance.ts +++ b/packages/plugin-runtime/src/PluginInstance.ts @@ -19,6 +19,7 @@ import { SendHttpRequestResponse, TemplateFunction, TemplateRenderResponse, + WindowInfoResponse, } from '@yaakapp-internal/plugins'; import { Context, PluginDefinition } from '@yaakapp/api'; import console from 'node:console'; @@ -317,7 +318,21 @@ export class PluginInstance { Array.isArray(this.#mod?.templateFunctions) ) { const fn = this.#mod.templateFunctions.find((a) => a.name === payload.name); - if (typeof fn?.onRender === 'function') { + if ( + payload.args.purpose === 'preview' && + (fn?.previewType === 'click' || fn?.previewType === 'none') + ) { + // Send empty render response + this.#sendPayload( + context, + { + type: 'call_template_function_response', + value: null, + error: 'Live preview disabled for this function', + }, + replyId, + ); + } else if (typeof fn?.onRender === 'function') { const resolvedArgs = await applyDynamicFormInput(ctx, fn.args, payload.args); payload.args.values = applyFormInputDefaults(resolvedArgs, payload.args.values); try { @@ -410,7 +425,7 @@ export class PluginInstance { return this.#sendPayload(context, { type: 'empty_response' }, replyId); } - #sendAndWaitForReply>( + #sendForReply>( context: PluginContext, payload: InternalEventPayload, ): Promise { @@ -456,10 +471,22 @@ export class PluginInstance { } #newCtx(context: PluginContext): Context { + const _windowInfo = async () => { + if (context.label == null) { + throw new Error("Can't get window context without an active window"); + } + const payload: InternalEventPayload = { + type: 'window_info_request', + label: context.label, + }; + + return this.#sendForReply(context, payload); + }; + return { clipboard: { copyText: async (text) => { - await this.#sendAndWaitForReply(context, { + await this.#sendForReply(context, { type: 'copy_text_request', text, }); @@ -467,7 +494,7 @@ export class PluginInstance { }, toast: { show: async (args) => { - await this.#sendAndWaitForReply(context, { + await this.#sendForReply(context, { type: 'show_toast_request', // Handle default here because null/undefined both convert to None in Rust translation timeout: args.timeout === undefined ? 5000 : args.timeout, @@ -476,6 +503,15 @@ export class PluginInstance { }, }, window: { + requestId: async () => { + return (await _windowInfo()).requestId; + }, + async workspaceId(): Promise { + return (await _windowInfo()).workspaceId; + }, + async environmentId(): Promise { + return (await _windowInfo()).environmentId; + }, openUrl: async ({ onNavigate, onClose, ...args }) => { args.label = args.label || `${Math.random()}`; const payload: InternalEventPayload = { type: 'open_window_request', ...args }; @@ -500,7 +536,7 @@ export class PluginInstance { }, prompt: { text: async (args) => { - const reply: PromptTextResponse = await this.#sendAndWaitForReply(context, { + const reply: PromptTextResponse = await this.#sendForReply(context, { type: 'prompt_text_request', ...args, }); @@ -513,7 +549,7 @@ export class PluginInstance { type: 'find_http_responses_request', ...args, } as const; - const { httpResponses } = await this.#sendAndWaitForReply( + const { httpResponses } = await this.#sendForReply( context, payload, ); @@ -526,7 +562,7 @@ export class PluginInstance { type: 'render_grpc_request_request', ...args, } as const; - const { grpcRequest } = await this.#sendAndWaitForReply( + const { grpcRequest } = await this.#sendForReply( context, payload, ); @@ -539,7 +575,7 @@ export class PluginInstance { type: 'get_http_request_by_id_request', ...args, } as const; - const { httpRequest } = await this.#sendAndWaitForReply( + const { httpRequest } = await this.#sendForReply( context, payload, ); @@ -550,7 +586,7 @@ export class PluginInstance { type: 'send_http_request_request', ...args, } as const; - const { httpResponse } = await this.#sendAndWaitForReply( + const { httpResponse } = await this.#sendForReply( context, payload, ); @@ -561,7 +597,7 @@ export class PluginInstance { type: 'render_http_request_request', ...args, } as const; - const { httpRequest } = await this.#sendAndWaitForReply( + const { httpRequest } = await this.#sendForReply( context, payload, ); @@ -574,18 +610,12 @@ export class PluginInstance { type: 'get_cookie_value_request', ...args, } as const; - const { value } = await this.#sendAndWaitForReply( - context, - payload, - ); + const { value } = await this.#sendForReply(context, payload); return value; }, listNames: async () => { const payload = { type: 'list_cookie_names_request' } as const; - const { names } = await this.#sendAndWaitForReply( - context, - payload, - ); + const { names } = await this.#sendForReply(context, payload); return names; }, }, @@ -596,14 +626,14 @@ export class PluginInstance { */ render: async (args) => { const payload = { type: 'template_render_request', ...args } as const; - const result = await this.#sendAndWaitForReply(context, payload); + const result = await this.#sendForReply(context, payload); return result.data as any; }, }, store: { get: async (key: string) => { const payload = { type: 'get_key_value_request', key } as const; - const result = await this.#sendAndWaitForReply(context, payload); + const result = await this.#sendForReply(context, payload); return result.value ? (JSON.parse(result.value) as T) : undefined; }, set: async (key: string, value: T) => { @@ -613,11 +643,11 @@ export class PluginInstance { key, value: valueStr, }; - await this.#sendAndWaitForReply(context, payload); + await this.#sendForReply(context, payload); }, delete: async (key: string) => { const payload = { type: 'delete_key_value_request', key } as const; - const result = await this.#sendAndWaitForReply(context, payload); + const result = await this.#sendForReply(context, payload); return result.deleted; }, }, diff --git a/plugins/template-function-ctx/package.json b/plugins/template-function-ctx/package.json new file mode 100644 index 00000000..f275ef97 --- /dev/null +++ b/plugins/template-function-ctx/package.json @@ -0,0 +1,12 @@ +{ + "name": "@yaak/template-function-ctx", + "displayName": "Window Template Functions", + "description": "Template functions for accessing attributes of the current window", + "private": true, + "version": "0.1.0", + "scripts": { + "build": "yaakcli build", + "dev": "yaakcli dev", + "lint": "tsc --noEmit && eslint . --ext .ts,.tsx" + } +} diff --git a/plugins/template-function-ctx/src/index.ts b/plugins/template-function-ctx/src/index.ts new file mode 100644 index 00000000..833cbd17 --- /dev/null +++ b/plugins/template-function-ctx/src/index.ts @@ -0,0 +1,30 @@ +import type { PluginDefinition } from '@yaakapp/api'; + +export const plugin: PluginDefinition = { + templateFunctions: [ + { + name: 'ctx.request', + description: 'Get the ID of the currently active request', + args: [], + async onRender(ctx) { + return ctx.window.requestId(); + }, + }, + { + name: 'ctx.environment', + description: 'Get the ID of the currently active environment', + args: [], + async onRender(ctx) { + return ctx.window.environmentId(); + }, + }, + { + name: 'ctx.workspace', + description: 'Get the ID of the currently active workspace', + args: [], + async onRender(ctx) { + return ctx.window.workspaceId(); + }, + }, + ], +}; diff --git a/plugins/template-function-ctx/tsconfig.json b/plugins/template-function-ctx/tsconfig.json new file mode 100644 index 00000000..4082f16a --- /dev/null +++ b/plugins/template-function-ctx/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.json" +} diff --git a/plugins/template-function-json/src/index.ts b/plugins/template-function-json/src/index.ts index 8c4e5949..617ed90d 100755 --- a/plugins/template-function-json/src/index.ts +++ b/plugins/template-function-json/src/index.ts @@ -58,7 +58,6 @@ export const plugin: PluginDefinition = { ], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { try { - console.log('formatted', args.values.formatted); return filterJSONPath( String(args.values.input), String(args.values.query), diff --git a/plugins/template-function-prompt/package.json b/plugins/template-function-prompt/package.json index ce04a42d..5329ac50 100644 --- a/plugins/template-function-prompt/package.json +++ b/plugins/template-function-prompt/package.json @@ -7,6 +7,9 @@ "scripts": { "build": "yaakcli build", "dev": "yaakcli dev", - "lint":"tsc --noEmit && eslint . --ext .ts,.tsx" + "lint": "tsc --noEmit && eslint . --ext .ts,.tsx" + }, + "dependencies": { + "slugify": "^1.6.6" } } diff --git a/plugins/template-function-prompt/src/index.ts b/plugins/template-function-prompt/src/index.ts index b780ecaf..2656aa54 100644 --- a/plugins/template-function-prompt/src/index.ts +++ b/plugins/template-function-prompt/src/index.ts @@ -1,25 +1,174 @@ import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api'; +import slugify from 'slugify'; + +const STORE_NONE = 'none'; +const STORE_FOREVER = 'forever'; +const STORE_EXPIRE = 'expire'; + +interface Saved { + value: string; + createdAt: number; +} export const plugin: PluginDefinition = { - templateFunctions: [{ - name: 'prompt.text', - description: 'Prompt the user for input when sending a request', - args: [ - { type: 'text', name: 'title', label: 'Title' }, - { type: 'text', name: 'label', label: 'Label', optional: true }, - { type: 'text', name: 'defaultValue', label: 'Default Value', optional: true }, - { type: 'text', name: 'placeholder', label: 'Placeholder', optional: true }, - ], - async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise { - if (args.purpose !== 'send') return null; + templateFunctions: [ + { + name: 'prompt.text', + description: 'Prompt the user for input when sending a request', + previewType: 'click', + args: [ + { type: 'text', name: 'label', label: 'Label' }, + { + type: 'select', + name: 'store', + label: 'Store Input', + defaultValue: STORE_NONE, + options: [ + { label: 'Never', value: STORE_NONE }, + { label: 'Expire', value: STORE_EXPIRE }, + { label: 'Forever', value: STORE_FOREVER }, + ], + }, + { + type: 'h_stack', + dynamic(_ctx, args) { + return { hidden: args.values.store === STORE_NONE }; + }, + inputs: [ + { + type: 'text', + name: 'namespace', + label: 'Namespace', + defaultValue: '${[ctx.workspace()]}', + optional: true, + }, + { + type: 'text', + name: 'key', + label: 'Key (defaults to Label)', + optional: true, + dynamic(_ctx, args) { + return { placeholder: String(args.values.label || '') }; + }, + }, + { + type: 'text', + name: 'ttl', + label: 'TTL (seconds)', + placeholder: '0', + defaultValue: '0', + optional: true, + dynamic(_ctx, args) { + return { hidden: args.values.store !== STORE_EXPIRE }; + }, + }, + ], + }, + { + type: 'banner', + color: 'info', + dynamic(_ctx, args) { + return { hidden: args.values.store === STORE_NONE }; + }, + inputs: [ + { + type: 'markdown', + content: '', + async dynamic(_ctx, args) { + const key = buildKey(args); + return { + content: ['Value will be saved under: `' + key + '`'].join('\n\n'), + }; + }, + }, + ], + }, + { + type: 'accordion', + label: 'Advanced', + inputs: [ + { + type: 'text', + name: 'title', + label: 'Prompt Title', + optional: true, + placeholder: 'Enter Value', + }, + { type: 'text', name: 'defaultValue', label: 'Default Value', optional: true }, + { type: 'text', name: 'placeholder', label: 'Input Placeholder', optional: true }, + { type: 'checkbox', name: 'password', label: 'Mask Value' }, + ], + }, + ], + async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise { + if (args.purpose !== 'send') return null; - return await ctx.prompt.text({ - id: `prompt-${args.values.label}`, - label: String(args.values.title ?? ''), - title: String(args.values.title ?? ''), - defaultValue: String(args.values.defaultValue), - placeholder: String(args.values.placeholder), - }); + if (args.values.store !== STORE_NONE && !args.values.namespace) { + throw new Error('Namespace is required when storing values') + } + + const existing = await maybeGetValue(ctx, args); + if (existing != null) { + return existing; + } + + const value = await ctx.prompt.text({ + id: `prompt-${args.values.label ?? 'none'}`, + label: String(args.values.label || 'Value'), + title: String(args.values.title ?? 'Enter Value'), + defaultValue: String(args.values.defaultValue ?? ''), + placeholder: String(args.values.placeholder ?? ''), + password: Boolean(args.values.password), + required: false, + }); + + if (value == null) { + throw new Error('Prompt cancelled'); + } + + if (args.values.store !== STORE_NONE) { + await maybeSetValue(ctx, args, value); + } + + return value; + }, }, - }], + ], }; + +function buildKey(args: CallTemplateFunctionArgs) { + return [args.values.namespace, args.values.key || args.values.label] + .filter((v) => !!v) + .map((v) => slugify(String(v), { lower: true, trim: true })) + .join('.'); +} + +async function maybeGetValue(ctx: Context, args: CallTemplateFunctionArgs) { + if (args.values.store === STORE_NONE) return null; + + const existing = await ctx.store.get(buildKey(args)); + if (existing == null) { + return null; + } + + if (args.values.store === STORE_FOREVER) { + return existing.value; + } + + const ttlSeconds = parseInt(String(args.values.ttl)) || 0; + const ageSeconds = (Date.now() - existing.createdAt) / 1000; + if (ageSeconds > ttlSeconds) { + ctx.store.delete(buildKey(args)).catch(console.error); + return null; + } + + return existing.value; +} + +async function maybeSetValue(ctx: Context, args: CallTemplateFunctionArgs, value: string) { + if (args.values.store === STORE_NONE) { + return; + } + + await ctx.store.set(buildKey(args), { value, createdAt: Date.now() }); +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index a4b91970..ae5fc0da 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -117,6 +117,7 @@ async fn cmd_render_template( template: &str, workspace_id: &str, environment_id: Option<&str>, + purpose: Option, ) -> YaakResult { let environment_chain = app_handle.db().resolve_environments(workspace_id, None, environment_id)?; @@ -126,7 +127,7 @@ async fn cmd_render_template( &PluginTemplateCallback::new( &app_handle, &PluginContext::new(&window), - RenderPurpose::Preview, + purpose.unwrap_or(RenderPurpose::Preview), ), &RenderOptions { error_behavior: RenderErrorBehavior::Throw, diff --git a/src-tauri/src/plugin_events.rs b/src-tauri/src/plugin_events.rs index 1c90c964..cb1103f8 100644 --- a/src-tauri/src/plugin_events.rs +++ b/src-tauri/src/plugin_events.rs @@ -16,12 +16,13 @@ use yaak_models::models::{HttpResponse, Plugin}; use yaak_models::queries::any_request::AnyRequest; use yaak_models::query_manager::QueryManagerExt; use yaak_models::util::UpdateSource; +use yaak_plugins::error::Error::PluginErr; use yaak_plugins::events::{ Color, DeleteKeyValueResponse, EmptyPayload, ErrorResponse, FindHttpResponsesResponse, GetCookieValueResponse, GetHttpRequestByIdResponse, GetKeyValueResponse, Icon, InternalEvent, InternalEventPayload, ListCookieNamesResponse, RenderGrpcRequestResponse, RenderHttpRequestResponse, SendHttpRequestResponse, SetKeyValueResponse, ShowToastRequest, - TemplateRenderResponse, WindowNavigateEvent, + TemplateRenderResponse, WindowInfoResponse, WindowNavigateEvent, }; use yaak_plugins::plugin_handle::PluginHandle; use yaak_plugins::template_callback::PluginTemplateCallback; @@ -334,6 +335,29 @@ pub(crate) async fn handle_plugin_event( }; Ok(Some(InternalEventPayload::GetCookieValueResponse(GetCookieValueResponse { value }))) } + InternalEventPayload::WindowInfoRequest(req) => { + let w = app_handle + .get_webview_window(&req.label) + .ok_or(PluginErr(format!("Failed to find window for {}", req.label)))?; + + // Actually look up the data so we never return an invalid ID + let environment_id = environment_from_window(&w).map(|m| m.id); + let workspace_id = workspace_from_window(&w).map(|m| m.id); + let request_id = + match app_handle.db().get_any_request(&w.request_id().unwrap_or_default()) { + Ok(AnyRequest::HttpRequest(r)) => Some(r.id), + Ok(AnyRequest::WebsocketRequest(r)) => Some(r.id), + Ok(AnyRequest::GrpcRequest(r)) => Some(r.id), + Err(_) => None, + }; + + Ok(Some(InternalEventPayload::WindowInfoResponse(WindowInfoResponse { + label: w.label().to_string(), + request_id, + workspace_id, + environment_id, + }))) + } _ => Ok(None), } } diff --git a/src-tauri/yaak-plugins/bindings/gen_events.ts b/src-tauri/yaak-plugins/bindings/gen_events.ts index cbb99501..1f91180d 100644 --- a/src-tauri/yaak-plugins/bindings/gen_events.ts +++ b/src-tauri/yaak-plugins/bindings/gen_events.ts @@ -224,7 +224,7 @@ defaultValue?: string, disabled?: boolean, */ description?: string, }; -export type FormInputHStack = { inputs?: Array, }; +export type FormInputHStack = { inputs?: Array, hidden?: boolean, }; export type FormInputHttpRequest = { /** @@ -391,7 +391,7 @@ export type ImportResponse = { resources: ImportResources, }; export type InternalEvent = { id: string, pluginRefId: string, pluginName: string, replyId: string | null, context: PluginContext, payload: InternalEventPayload, }; -export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } | { "type": "reload_response" } & ReloadResponse | { "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": "list_cookie_names_request" } & ListCookieNamesRequest | { "type": "list_cookie_names_response" } & ListCookieNamesResponse | { "type": "get_cookie_value_request" } & GetCookieValueRequest | { "type": "get_cookie_value_response" } & GetCookieValueResponse | { "type": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_grpc_request_actions_request" } & EmptyPayload | { "type": "get_grpc_request_actions_response" } & GetGrpcRequestActionsResponse | { "type": "call_grpc_request_action_request" } & CallGrpcRequestActionRequest | { "type": "get_template_function_summary_request" } & EmptyPayload | { "type": "get_template_function_summary_response" } & GetTemplateFunctionSummaryResponse | { "type": "get_template_function_config_request" } & GetTemplateFunctionConfigRequest | { "type": "get_template_function_config_response" } & GetTemplateFunctionConfigResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "get_http_authentication_summary_request" } & EmptyPayload | { "type": "get_http_authentication_summary_response" } & GetHttpAuthenticationSummaryResponse | { "type": "get_http_authentication_config_request" } & GetHttpAuthenticationConfigRequest | { "type": "get_http_authentication_config_response" } & GetHttpAuthenticationConfigResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "call_http_authentication_action_request" } & CallHttpAuthenticationActionRequest | { "type": "call_http_authentication_action_response" } & EmptyPayload | { "type": "copy_text_request" } & CopyTextRequest | { "type": "copy_text_response" } & EmptyPayload | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "render_grpc_request_request" } & RenderGrpcRequestRequest | { "type": "render_grpc_request_response" } & RenderGrpcRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "get_key_value_request" } & GetKeyValueRequest | { "type": "get_key_value_response" } & GetKeyValueResponse | { "type": "set_key_value_request" } & SetKeyValueRequest | { "type": "set_key_value_response" } & SetKeyValueResponse | { "type": "delete_key_value_request" } & DeleteKeyValueRequest | { "type": "delete_key_value_response" } & DeleteKeyValueResponse | { "type": "open_window_request" } & OpenWindowRequest | { "type": "window_navigate_event" } & WindowNavigateEvent | { "type": "window_close_event" } | { "type": "close_window_request" } & CloseWindowRequest | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_toast_response" } & EmptyPayload | { "type": "prompt_text_request" } & PromptTextRequest | { "type": "prompt_text_response" } & PromptTextResponse | { "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": "get_themes_request" } & GetThemesRequest | { "type": "get_themes_response" } & GetThemesResponse | { "type": "empty_response" } & EmptyPayload | { "type": "error_response" } & ErrorResponse; +export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } | { "type": "reload_response" } & ReloadResponse | { "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": "list_cookie_names_request" } & ListCookieNamesRequest | { "type": "list_cookie_names_response" } & ListCookieNamesResponse | { "type": "get_cookie_value_request" } & GetCookieValueRequest | { "type": "get_cookie_value_response" } & GetCookieValueResponse | { "type": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_grpc_request_actions_request" } & EmptyPayload | { "type": "get_grpc_request_actions_response" } & GetGrpcRequestActionsResponse | { "type": "call_grpc_request_action_request" } & CallGrpcRequestActionRequest | { "type": "get_template_function_summary_request" } & EmptyPayload | { "type": "get_template_function_summary_response" } & GetTemplateFunctionSummaryResponse | { "type": "get_template_function_config_request" } & GetTemplateFunctionConfigRequest | { "type": "get_template_function_config_response" } & GetTemplateFunctionConfigResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "get_http_authentication_summary_request" } & EmptyPayload | { "type": "get_http_authentication_summary_response" } & GetHttpAuthenticationSummaryResponse | { "type": "get_http_authentication_config_request" } & GetHttpAuthenticationConfigRequest | { "type": "get_http_authentication_config_response" } & GetHttpAuthenticationConfigResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "call_http_authentication_action_request" } & CallHttpAuthenticationActionRequest | { "type": "call_http_authentication_action_response" } & EmptyPayload | { "type": "copy_text_request" } & CopyTextRequest | { "type": "copy_text_response" } & EmptyPayload | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "render_grpc_request_request" } & RenderGrpcRequestRequest | { "type": "render_grpc_request_response" } & RenderGrpcRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "get_key_value_request" } & GetKeyValueRequest | { "type": "get_key_value_response" } & GetKeyValueResponse | { "type": "set_key_value_request" } & SetKeyValueRequest | { "type": "set_key_value_response" } & SetKeyValueResponse | { "type": "delete_key_value_request" } & DeleteKeyValueRequest | { "type": "delete_key_value_response" } & DeleteKeyValueResponse | { "type": "open_window_request" } & OpenWindowRequest | { "type": "window_navigate_event" } & WindowNavigateEvent | { "type": "window_close_event" } | { "type": "close_window_request" } & CloseWindowRequest | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_toast_response" } & EmptyPayload | { "type": "prompt_text_request" } & PromptTextRequest | { "type": "prompt_text_response" } & PromptTextResponse | { "type": "window_info_request" } & WindowInfoRequest | { "type": "window_info_response" } & WindowInfoResponse | { "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": "get_themes_request" } & GetThemesRequest | { "type": "get_themes_response" } & GetThemesResponse | { "type": "empty_response" } & EmptyPayload | { "type": "error_response" } & ErrorResponse; export type JsonPrimitive = string | number | boolean | null; @@ -411,7 +411,7 @@ export type PromptTextRequest = { id: string, title: string, label: string, desc /** * Text to add to the confirmation button */ -confirmText?: string, +confirmText?: string, password?: boolean, /** * Text to add to the cancel button */ @@ -445,7 +445,7 @@ export type SetKeyValueResponse = {}; export type ShowToastRequest = { message: string, color?: Color, icon?: Icon, timeout?: number, }; -export type TemplateFunction = { name: string, description?: string, +export type TemplateFunction = { name: string, previewType?: TemplateFunctionPreviewType, description?: string, /** * Also support alternative names. This is useful for not breaking existing * tags when changing the `name` property @@ -457,6 +457,8 @@ aliases?: Array, args: Array, }; */ export type TemplateFunctionArg = FormInput; +export type TemplateFunctionPreviewType = "live" | "click" | "none"; + export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, }; export type TemplateRenderResponse = { data: JsonValue, }; @@ -487,6 +489,10 @@ export type ThemeComponentColors = { surface?: string, surfaceHighlight?: string export type ThemeComponents = { dialog?: ThemeComponentColors, menu?: ThemeComponentColors, toast?: ThemeComponentColors, sidebar?: ThemeComponentColors, responsePane?: ThemeComponentColors, appHeader?: ThemeComponentColors, button?: ThemeComponentColors, banner?: ThemeComponentColors, templateTag?: ThemeComponentColors, urlBar?: ThemeComponentColors, editor?: ThemeComponentColors, input?: ThemeComponentColors, }; +export type WindowInfoRequest = { label: string, }; + +export type WindowInfoResponse = { requestId: string | null, environmentId: string | null, workspaceId: string | null, label: string, }; + export type WindowNavigateEvent = { url: string, }; export type WindowSize = { width: number, height: number, }; diff --git a/src-tauri/yaak-plugins/src/events.rs b/src-tauri/yaak-plugins/src/events.rs index d04affd8..6e529766 100644 --- a/src-tauri/yaak-plugins/src/events.rs +++ b/src-tauri/yaak-plugins/src/events.rs @@ -147,6 +147,9 @@ pub enum InternalEventPayload { PromptTextRequest(PromptTextRequest), PromptTextResponse(PromptTextResponse), + WindowInfoRequest(WindowInfoRequest), + WindowInfoResponse(WindowInfoResponse), + GetHttpRequestByIdRequest(GetHttpRequestByIdRequest), GetHttpRequestByIdResponse(GetHttpRequestByIdResponse), @@ -521,6 +524,8 @@ pub struct PromptTextRequest { /// Text to add to the confirmation button #[ts(optional)] pub confirm_text: Option, + #[ts(optional)] + pub password: Option, /// Text to add to the cancel button #[ts(optional)] pub cancel_text: Option, @@ -536,6 +541,23 @@ pub struct PromptTextResponse { pub value: Option, } +#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] +#[serde(default, rename_all = "camelCase")] +#[ts(export, export_to = "gen_events.ts")] +pub struct WindowInfoRequest { + pub label: String, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] +#[serde(default, rename_all = "camelCase")] +#[ts(export, export_to = "gen_events.ts")] +pub struct WindowInfoResponse { + pub request_id: Option, + pub environment_id: Option, + pub workspace_id: Option, + pub label: String, +} + #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "snake_case")] #[ts(export, export_to = "gen_events.ts")] @@ -710,12 +732,24 @@ pub struct GetTemplateFunctionConfigResponse { pub plugin_ref_id: String, } +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts(export, export_to = "gen_events.ts")] +pub enum TemplateFunctionPreviewType { + Live, + Click, + None, +} + #[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_events.ts")] pub struct TemplateFunction { pub name: String, + #[ts(optional)] + pub preview_type: Option, + #[ts(optional)] pub description: Option, @@ -972,6 +1006,9 @@ pub struct FormInputAccordion { pub struct FormInputHStack { #[ts(optional)] pub inputs: Option>, + + #[ts(optional)] + pub hidden: Option, } #[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] diff --git a/src-tauri/yaak-plugins/src/native_template_functions.rs b/src-tauri/yaak-plugins/src/native_template_functions.rs index b0548f2c..ff393ab4 100644 --- a/src-tauri/yaak-plugins/src/native_template_functions.rs +++ b/src-tauri/yaak-plugins/src/native_template_functions.rs @@ -1,6 +1,6 @@ use crate::events::{ FormInput, FormInputBase, FormInputText, PluginContext, RenderPurpose, TemplateFunction, - TemplateFunctionArg, + TemplateFunctionArg, TemplateFunctionPreviewType, }; use crate::template_callback::PluginTemplateCallback; use base64::Engine; @@ -17,6 +17,7 @@ use yaak_templates::{FnArg, Parser, Token, Tokens, Val, transform_args}; pub(crate) fn template_function_secure() -> TemplateFunction { TemplateFunction { name: "secure".to_string(), + preview_type: Some(TemplateFunctionPreviewType::None), description: Some("Securely store encrypted text".to_string()), aliases: None, args: vec![TemplateFunctionArg::FormInput(FormInput::Text( @@ -37,6 +38,7 @@ pub(crate) fn template_function_secure() -> TemplateFunction { pub(crate) fn template_function_keyring() -> TemplateFunction { TemplateFunction { name: "keychain".to_string(), + preview_type: Some(TemplateFunctionPreviewType::Live), description: Some("Get a password from the OS keychain or keyring".to_string()), aliases: Some(vec!["keyring".to_string()]), args: vec![ diff --git a/src-web/commands/commands.tsx b/src-web/commands/commands.tsx index 16c316c7..f39b48a3 100644 --- a/src-web/commands/commands.tsx +++ b/src-web/commands/commands.tsx @@ -30,7 +30,6 @@ export const createFolder = createFastMutation< label: 'Name', defaultValue: 'Folder', title: 'New Folder', - required: true, confirmText: 'Create', placeholder: 'Name', }); diff --git a/src-web/components/DynamicForm.tsx b/src-web/components/DynamicForm.tsx index 73c18a26..0bcc7e5d 100644 --- a/src-web/components/DynamicForm.tsx +++ b/src-web/components/DynamicForm.tsx @@ -26,7 +26,7 @@ import { IconButton } from './core/IconButton'; import { Input } from './core/Input'; import { Label } from './core/Label'; import { Select } from './core/Select'; -import { HStack, VStack } from './core/Stacks'; +import { VStack } from './core/Stacks'; import { Markdown } from './Markdown'; import { SelectFile } from './SelectFile'; @@ -216,7 +216,7 @@ function FormInputs>({ ); case 'h_stack': return ( - +
>({ autocompleteFunctions={autocompleteFunctions || false} autocompleteVariables={autocompleteVariables} /> - +
); case 'banner': return ( diff --git a/src-web/components/RequestMethodDropdown.tsx b/src-web/components/RequestMethodDropdown.tsx index a13b0ff1..506c8ca9 100644 --- a/src-web/components/RequestMethodDropdown.tsx +++ b/src-web/components/RequestMethodDropdown.tsx @@ -50,7 +50,6 @@ export const RequestMethodDropdown = memo(function RequestMethodDropdown({ const newMethod = await showPrompt({ id: 'custom-method', label: 'Http Method', - defaultValue: '', title: 'Custom Method', confirmText: 'Save', description: 'Enter a custom method name', diff --git a/src-web/components/TemplateFunctionDialog.tsx b/src-web/components/TemplateFunctionDialog.tsx index d9006fc4..33489732 100644 --- a/src-web/components/TemplateFunctionDialog.tsx +++ b/src-web/components/TemplateFunctionDialog.tsx @@ -54,7 +54,6 @@ export function TemplateFunctionDialog({ initialTokens, templateFunction, ...pro initial.value = await convertTemplateToInsecure(template); } - console.log('INITIAL', initial); setInitialArgValues(initial); })().catch(console.error); }, [ @@ -78,7 +77,7 @@ export function TemplateFunctionDialog({ initialTokens, templateFunction, ...pro } function InitializedTemplateFunctionDialog({ - templateFunction: { name }, + templateFunction: { name, previewType: ogPreviewType }, initialArgValues, hide, onChange, @@ -86,7 +85,7 @@ function InitializedTemplateFunctionDialog({ }: Omit & { initialArgValues: Record; }) { - const enablePreview = name !== 'secure'; + const previewType = ogPreviewType == null ? 'live' : ogPreviewType; const [showSecretsInPreview, toggleShowSecretsInPreview] = useToggle(false); const [argValues, setArgValues] = useState>(initialArgValues); @@ -126,7 +125,14 @@ function InitializedTemplateFunctionDialog({ }; const debouncedTagText = useDebouncedValue(tagText.data ?? '', 400); - const rendered = useRenderTemplate(debouncedTagText); + const [renderKey, setRenderKey] = useState(null); + const rendered = useRenderTemplate( + debouncedTagText, + previewType !== 'none', + previewType === 'click' ? 'send' : 'preview', + previewType === 'live' ? renderKey + debouncedTagText : renderKey, + ); + const tooLarge = rendered.data ? rendered.data.length > 10000 : false; const dataContainsSecrets = useMemo(() => { for (const [name, value] of Object.entries(argValues)) { @@ -174,12 +180,12 @@ function InitializedTemplateFunctionDialog({ )}
- {enablePreview ? ( + {previewType !== 'none' ? ( Rendered Preview - {rendered.isPending && } + {rendered.isLoading && }   )} +
+ { + setRenderKey(new Date().toISOString()); + }} + /> +
) : ( @@ -250,8 +269,16 @@ function collectArgumentValues(initialTokens: Tokens, templateFunction: Template if (!('name' in arg)) return; const initialArg = initialArgs.find((a) => a.name === arg.name); - const initialArgValue = initialArg?.value.type === 'str' ? initialArg?.value.text : undefined; - initial[arg.name] = initialArgValue ?? arg.defaultValue ?? DYNAMIC_FORM_NULL_ARG; + const initialArgValue = + initialArg?.value.type === 'str' + ? initialArg?.value.text + : initialArg?.value.type === 'bool' + ? initialArg.value.value + : undefined; + const value = initialArgValue ?? arg.defaultValue; + if (value != null) { + initial[arg.name] = value; + } }; templateFunction.args.forEach(processArg); diff --git a/src-web/components/TemplateVariableDialog.tsx b/src-web/components/TemplateVariableDialog.tsx deleted file mode 100644 index 2e678ca9..00000000 --- a/src-web/components/TemplateVariableDialog.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import type { Tokens } from '@yaakapp-internal/templates'; -import { useCallback, useMemo, useState } from 'react'; -import { useActiveEnvironmentVariables } from '../hooks/useActiveEnvironmentVariables'; -import { useRenderTemplate } from '../hooks/useRenderTemplate'; -import { useTemplateTokensToString } from '../hooks/useTemplateTokensToString'; -import { Button } from './core/Button'; -import { InlineCode } from './core/InlineCode'; -import { Select } from './core/Select'; -import { VStack } from './core/Stacks'; - -interface Props { - initialTokens: Tokens; - hide: () => void; - onChange: (rawTag: string) => void; -} - -export function TemplateVariableDialog({ hide, onChange, initialTokens }: Props) { - const variables = useActiveEnvironmentVariables(); - const [selectedVariableName, setSelectedVariableName] = useState(() => { - return initialTokens.tokens[0]?.type === 'tag' && initialTokens.tokens[0]?.val.type === 'var' - ? initialTokens.tokens[0]?.val.name - : ''; // Should never happen - }); - - const tokens: Tokens = useMemo(() => { - const selectedVariable = variables.find((v) => v.name === selectedVariableName); - return { - tokens: [ - { - type: 'tag', - val: { - type: 'var', - name: selectedVariable?.name ?? '', - }, - }, - ], - }; - }, [selectedVariableName, variables]); - - const tagText = useTemplateTokensToString(tokens); - - const handleDone = useCallback(async () => { - if (tagText.data != null) { - onChange(tagText.data); - } - hide(); - }, [hide, onChange, tagText.data]); - - const rendered = useRenderTemplate(tagText.data ?? ''); - - return ( - - -