Improved prompt function add add ctx.* functions (#301)

This commit is contained in:
Gregory Schier
2025-11-15 08:19:58 -08:00
committed by GitHub
parent 7ced183b11
commit 84219571e8
29 changed files with 454 additions and 150 deletions

View File

@@ -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"
}
}

View File

@@ -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();
},
},
],
};

View File

@@ -0,0 +1,3 @@
{
"extends": "../../tsconfig.json"
}

View File

@@ -58,7 +58,6 @@ export const plugin: PluginDefinition = {
],
async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
try {
console.log('formatted', args.values.formatted);
return filterJSONPath(
String(args.values.input),
String(args.values.query),

View File

@@ -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"
}
}

View File

@@ -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<string | null> {
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<string | null> {
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<Saved>(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<Saved>(buildKey(args), { value, createdAt: Date.now() });
}