Editful
DocsWorkflows

Build guided workflows.

Collect a few decisions with host-controlled UI before a plugin action finishes.

  • Async commands
  • Host prompts
  • Native controls

Guide a task with host prompts

Some actions need a few decisions before they can finish. You do not need to build an editor for that. A command can ask a question, present a choice, and request confirmation with action.ui.

Editful renders each prompt in host UI, so the dialog, focus behavior, keyboard controls, and cancellation all feel native. Your plugin provides the copy and options, then awaits the answer.

This example uses prompt(...) for the card title, pick(...) for its color, and confirm(...) before creating it. Each call pauses the command until the person answers. A cancelled prompt returns null or false, so the command can stop without changing the canvas.

Add commands to the plugin's capabilities. Choose Create card in the toolbar and follow the prompts.

toolbar-workflow.ts
import { definePlugin, hexColor } from '@editful/canvas-sdk';

const colors = {
  coral: { fill: hexColor('#ff6b4a'), stroke: hexColor('#a9321d') },
  blue: { fill: hexColor('#8ca2ff'), stroke: hexColor('#4057b5') },
  yellow: { fill: hexColor('#f1d45b'), stroke: hexColor('#9c8427') },
} as const;

export default definePlugin({
  register(context) {
    let cardCount = 0;

    context.command({
      id: 'example:create-card-workflow',
      label: 'Create a card with prompts',
      toolbar: { label: 'Create card', order: 40 },
      async run(action) {
        const titleValue = await action.ui.prompt({
          label: 'Name this card',
          description: 'Choose a short title to show on the canvas.',
          initial: 'New card',
        });
        if (titleValue === null) return;

        const color = await action.ui.pick({
          label: 'Choose a color',
          description: 'Editful renders this picker and returns the selected value.',
          items: [
            { label: 'Coral', value: 'coral' },
            { label: 'Blue', value: 'blue' },
            { label: 'Yellow', value: 'yellow' },
          ],
        });
        if (color !== 'coral' && color !== 'blue' && color !== 'yellow') return;

        const title = String(titleValue).trim() || 'New card';
        const confirmed = await action.ui.confirm({
          label: `Create “${title}”?`,
          description: 'The card will be added to the current canvas.',
        });
        if (!confirmed) return;

        const offset = cardCount * 18;
        const transaction = action.document.transaction('Create card');
        transaction.create({
          kind: 'example:card',
          x: 112 + offset,
          y: 62 + offset,
          width: 200,
          height: 96,
          fill: colors[color].fill,
          stroke: colors[color].stroke,
          strokeWidth: 2,
          fields: { title },
        });
        transaction.commit();
        cardCount++;

        action.ui.notify({ title: 'Card created', tone: 'success' });
      },
    });
  },
});
prompt(...)
Ask for a string or number. Returns null when cancelled.
pick(...)
Present a host-rendered list and receive the selected value.
confirm(...)
Ask for confirmation before committing a change.