Skip to main content

Forms and Inputs

ProActions provides powerful form-building capabilities to collect user input and display information. This guide explains how to create interactive forms using the FORM step.

FORM Step Overview

The FORM step displays a modal dialog with configurable form fields, allowing users to:

  • Enter text, numbers, dates, and other data
  • Select from options (dropdowns, radio buttons, checkboxes)
  • View formatted content (HTML, Markdown, diffs)
  • Choose between multiple actions via custom buttons

Basic Form Structure

- step: FORM
form:
# Form field definitions
fieldName:
type: text
label: "Field Label"
required: true

# More fields...

# Button configuration
buttons:
- type: cancel
label: "Cancel"
- type: submit
label: "Submit"

Form Field Types

Text Input

userName:
type: text
label: "User Name"
placeholder: "Enter your name"
required: true
requiredMessage: "Name is required"

Password Input

apiKey:
type: password
label: "API Key"
placeholder: "Enter API key"
required: true

Textarea

comments:
type: textarea
label: "Comments"
placeholder: "Enter your comments"
rows: 5
resize: vertical

Number Input

wordCount:
type: number
label: "Word Count"
min: 100
max: 1000
step: 50
default: 500

The min and max properties enforce validation constraints. You can also provide custom error messages:

age:
type: number
label: "Age"
min: 18
max: 120
minMessage: "You must be at least 18 years old"
maxMessage: "Please enter a valid age (maximum 120)"
required: true

Select Dropdown

language:
type: select
label: "Target Language"
options:
- English
- French
- German
- Spanish
default: English

Select with Key-Value Pairs

language:
type: select
label: "Target Language"
options:
- key: EN
value: English
- key: FR
value: French
- key: DE
value: German
default: EN

Radio Buttons

tone:
type: radio
label: "Article Tone"
options:
- Formal
- Casual
- Technical
default: Formal

Checkbox

acceptTerms:
type: checkbox
label: "I accept the terms and conditions"
default: false
required: true

Date Input

publishDate:
type: date
label: "Publish Date"
default: "2024-01-01"

You can restrict the date range using min and max properties:

appointmentDate:
type: date
label: "Appointment Date"
min: "2025-01-01"
max: "2025-12-31"
minMessage: "Appointments start from January 1st, 2025"
maxMessage: "Appointments must be within 2025"
required: true

DateTime Input

publishDateTime:
type: datetime
label: "Publish Date and Time"

Similar to date inputs, you can constrain datetime values with min and max:

eventTime:
type: datetime
label: "Event Start Time"
min: "2025-10-24T09:00"
max: "2025-10-24T18:00"
minMessage: "Event must start after 9:00 AM"
maxMessage: "Event must start before 6:00 PM"
required: true

Color Picker

highlightColor:
type: color
label: "Highlight Color"
default: "#FF0000"

Range Slider

creativity:
type: range
label: "Creativity Level"
min: 0
max: 100
step: 10
default: 70

File Upload

attachment:
type: file
label: "Upload File"
accept: "image/*,.pdf"
maxSize: 5242880 # 5MB in bytes
maxSizeMessage: "File must be less than 5MB"

Display Components

Headline

sectionTitle:
type: headline
text: "Section Title"

Horizontal Rule

separator:
type: hr

HTML Content

instructions:
type: html
html: |
<p><strong>Instructions:</strong></p>
<ul>
<li>Fill in all required fields</li>
<li>Review your input before submitting</li>
</ul>

Markdown Content

description:
type: markdown
text: |
## Important Information

Please read carefully:
- **Bold text**
- *Italic text*
- [Links](https://example.com)

Diff Display

Show differences between two texts:

comparison:
type: diff
label: "Changes"
prevText: "{{ flowContext.originalText }}"
text: "{{ flowContext.newText }}"
mode: words # Options: chars, words, lines, sentences
showDeletions: true
interactive: false

Diff Toolbar Controls

Customize toolbar behavior for interactive diff review:

comparison:
type: diff
prevText: "{{ flowContext.originalText }}"
text: "{{ flowContext.newText }}"
interactive: true
diffControls:
position: top # top | bottom
zoom: true
acceptRejectAll: true
acceptRejectAllEnabled: true
showStats: true # shows Length: source → target
symbols:
zoomIn: "A+"
zoomOut: "A−"
acceptAll: "✔"
rejectAll: "✖"

When showStats is enabled, the indicator displays Length: <source> → <target>. In interactive mode, the target value updates live as additions/deletions/replacements are selected.

Advanced Form Features

Field Validation

email:
type: text
label: "Email Address"
required: true
validation:
pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
errorMessage: "Please enter a valid email address"

Conditional Visibility

Show fields based on other field values:

translationService:
type: select
label: "Translation Service"
options:
- DeepL
- Google Translate

deeplApiKey:
type: password
label: "DeepL API Key"
visibleIf:
translationService: DeepL

Tooltips

temperature:
type: range
label: "Temperature"
min: 0
max: 1
step: 0.1
default: 0.7
tooltip: "Higher values make output more creative, lower values more deterministic"

Form Buttons

Submit Button

buttons:
- type: submit
label: "Submit"

Cancel Button

buttons:
- type: cancel
label: "Cancel"

Multiple Submit Buttons

Create different actions based on which button is clicked:

buttons:
- type: cancel
label: "Discard"

- type: submit
flowAttribute: "action"
name: "copy"
class: "btn btn-info"
label: "Copy to Clipboard"

- type: submit
flowAttribute: "action"
name: "replace"
label: "Replace Text"

Then handle the action:

- step: SWITCH
condition: "{{ flowContext.action }}"
cases:
'copy':
- step: WRITE_CLIPBOARD
'replace':
- step: REPLACE_TEXT
at: CURSOR

Configure the modal dialog appearance using the formConfig option:

- step: FORM
title: "My Form"
formConfig:
dialogSize: lg # Options: sm, md, lg, xl
width: "800px"
maxWidth: "90vw"
height: "600px"
maxHeight: "80vh"
fullScreen: false
form:
# fields...

Predefined Sizes:

formConfig:
dialogSize: sm # Small modal (~300px)
dialogSize: md # Medium (default, ~600px)
dialogSize: lg # Large (~900px)
dialogSize: xl # Extra large (~1200px or 90vw)

Custom Dimensions:

formConfig:
# Width options
width: "720px" # Fixed width in pixels
width: "80vw" # 80% of viewport width
maxWidth: "1200px" # Maximum width constraint

# Height options
height: "500px" # Fixed height in pixels
height: "70vh" # 70% of viewport height
maxHeight: "90vh" # Maximum height constraint

# Fullscreen mode
fullScreen: true # Cover entire viewport (100vw x 100vh)

Important Notes:

  • When height or maxHeight is set, the modal body becomes scrollable
  • The header and footer remain fixed while the body scrolls
  • Fullscreen mode overrides width and height settings
  • The modal auto-adjusts if requested height exceeds viewport

Customize text appearance throughout the modal:

formConfig:
# Modal body styling
bodyFontSize: "16px"
bodyLineHeight: "1.6"

# Form labels
labelFontSize: "14px"

# Input fields (text, textarea, select, etc.)
inputFontSize: "15px"
inputLineHeight: "1.5"

# Diff component specific
diffFontSize: "13px"
diffLineHeight: "1.4"

Supported Units:

  • Pixels: "14px", "16px"
  • Relative: "1rem", "1.2em"
  • Line height: "1.5", "1.6"

Custom CSS Class

Add custom CSS classes for additional styling:

formConfig:
modalClass: "my-custom-modal"

Then define styles in your custom CSS:

.my-custom-modal .modal-dialog {
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
}

Complete formConfig Reference

OptionTypeDescriptionExample
dialogSizestringPredefined size: 'sm', 'md', 'lg', 'xl''lg'
widthstringCustom width (px or vw)'80vw', '1000px'
maxWidthstringMaximum width'1200px'
heightstringCustom height (px or vh)'70vh', '600px'
maxHeightstringMaximum height'800px'
fullScreenbooleanCover entire viewporttrue
bodyFontSizestringModal body font size'16px', '1rem'
bodyLineHeightstringModal body line height'1.6'
labelFontSizestringForm label font size'14px'
inputFontSizestringInput field font size'15px'
inputLineHeightstringInput field line height'1.5'
diffFontSizestringDiff component font size'13px'
diffLineHeightstringDiff component line height'1.4'
modalClassstringCustom CSS class name'my-modal-class'

Example 1: Large Form with Scrolling

- step: FORM
title: "Content Editor"
formConfig:
dialogSize: 'xl'
height: '85vh'
maxHeight: '900px'
form:
content:
type: textarea
rows: 20
resize: vertical
metadata:
type: text
label: "Metadata"

Example 2: Fullscreen Review Interface

- step: FORM
title: "Review Changes"
formConfig:
fullScreen: true
diffFontSize: '14px'
diffLineHeight: '1.5'
form:
comparison:
type: diff
mode: words
prevText: '{{ flowContext.original }}'
text: '{{ flowContext.improved }}'
interactive: true

Example 3: Compact Modal with Custom Typography

- step: FORM
title: "Quick Settings"
formConfig:
dialogSize: 'sm'
bodyFontSize: '13px'
labelFontSize: '12px'
inputFontSize: '13px'
form:
option1:
type: checkbox
label: "Enable feature A"
option2:
type: checkbox
label: "Enable feature B"

Example 4: Responsive Modal

- step: FORM
title: "Translation Settings"
formConfig:
width: '90vw'
maxWidth: '1000px'
height: '80vh'
maxHeight: '700px'
inputFontSize: '14px'
form:
sourceText:
type: textarea
label: "Source Text"
rows: 8
targetLang:
type: select
label: "Target Language"
options:
- English
- French
- German

Complete Form Examples

Example 1: Simple Input Form

- title: 'Generate Custom Content'
flow:
- step: FORM
form:
topic:
type: text
label: "Topic"
placeholder: "Enter article topic"
required: true

wordCount:
type: number
label: "Word Count"
min: 100
max: 2000
default: 500

tone:
type: select
label: "Tone"
options:
- Professional
- Casual
- Academic

buttons:
- type: cancel
- type: submit
label: "Generate"

- step: HUB_COMPLETION
instruction: |
Write a {{ flowContext.wordCount }}-word article about {{ flowContext.topic }}
in a {{ flowContext.tone }} tone.

- step: INSERT_TEXT
at: CURSOR

Example 2: Translation Form

- title: 'Configure Translation'
flow:
- step: FORM
form:
headline1:
type: headline
text: "Translation Settings"

targetLang:
type: select
label: "Target Language"
options:
- key: FR
value: French
- key: DE
value: German
- key: ES
value: Spanish
required: true

formality:
type: radio
label: "Formality"
options:
- default
- more
- less
default: default

preserveFormatting:
type: checkbox
label: "Preserve formatting"
default: true

buttons:
- type: cancel
- type: submit
label: "Translate"

- step: DEEPL_TRANSLATE
instruction: "{selectedText}"
target_lang: "{{ flowContext.targetLang }}"
formality: "{{ flowContext.formality }}"

- step: REPLACE_TEXT
at: CURSOR

Example 3: Diff Display Form

- title: 'Review Text Improvements'
flow:
- step: SET
currentText: "{selectedText}"

- step: DEEPL_WRITE
instruction: "{{ flowContext.currentText | safe }}"

- step: FORM
modal:
width: "900px"
height: "700px"
form:
headline1:
type: headline
text: "Current Text"

original:
type: html
html: "<p><i>{{ flowContext.currentText }}</i></p>"

separator:
type: hr

headline2:
type: headline
text: "Improved Text"

comparison:
type: diff
mode: words
prevText: "{{ flowContext.currentText }}"
text: "{{ flowContext.text }}"
showDeletions: false

buttons:
- type: cancel
label: "Discard"
- type: submit
flowAttribute: "mode"
name: "copy"
class: "btn btn-info"
label: "Copy"
- type: submit
flowAttribute: "mode"
name: "replace"
label: "Replace"

- step: SWITCH
condition: "{{ flowContext.mode }}"
cases:
'replace':
- step: REPLACE_TEXT
at: CURSOR
'copy':
- step: WRITE_CLIPBOARD
- step: SHOW_NOTIFICATION
notificationType: success
message: "Copied to clipboard"

Example 4: File Upload Form

- title: 'Upload and Process Image'
flow:
- step: FORM
form:
instructions:
type: html
html: |
<p>Upload an image to process</p>

imageFile:
type: file
label: "Select Image"
accept: "image/*"
maxSize: 10485760 # 10MB
required: true

operation:
type: select
label: "Operation"
options:
- Upscale
- Anonymize
- Enhance

buttons:
- type: cancel
- type: submit
label: "Process"

# Process based on selected operation
- step: SWITCH
condition: "{{ flowContext.operation }}"
cases:
'Upscale':
- step: STABILITY_AI_UPSCALE
'Anonymize':
- step: BRIGHTER_AI

User Input Steps

In addition to FORM, ProActions provides simpler input steps:

PROMPT Step

Simple text input:

- step: PROMPT
promptText: "Enter your search query:"

- step: SHOW_NOTIFICATION
message: "You entered: {{ flowContext.text }}"

USER_SELECT Step

Choose from a list:

- step: HUB_COMPLETION
instruction: "Generate 5 headline options"
response_format: "list"

- step: USER_SELECT
promptText: "Choose a headline:"

- step: REPLACE_TEXT
at: XPATH
xpath: "/doc/story/headline/p"

FILE_UPLOAD Step

Upload files:

- step: FILE_UPLOAD
accept: "audio/*"
multiple: false

- step: HUB_TRANSCRIPTION

Form Best Practices

1. Provide Clear Labels

# Good
targetLanguage:
type: select
label: "Target Language for Translation"
tooltip: "Select the language you want to translate to"

# Avoid
lang:
type: select
label: "Lang"

2. Use Appropriate Input Types

# Good - number type with constraints
wordCount:
type: number
min: 100
max: 5000

# Avoid - text type for numbers
wordCount:
type: text
placeholder: "Enter 100-5000"

3. Set Sensible Defaults

temperature:
type: range
label: "Creativity"
min: 0
max: 1
step: 0.1
default: 0.7 # Sensible default

4. Validate User Input

url:
type: text
label: "Website URL"
required: true
validation:
pattern: "^https?://.+"
errorMessage: "Please enter a valid URL starting with http:// or https://"
form:
headline1:
type: headline
text: "Content Settings"

topic:
type: text
label: "Topic"

wordCount:
type: number
label: "Word Count"

separator:
type: hr

headline2:
type: headline
text: "Style Settings"

tone:
type: select
label: "Tone"

6. Provide Helpful Instructions

instructions:
type: html
html: |
<p><strong>Please configure your translation:</strong></p>
<ul>
<li>Select target language</li>
<li>Choose formality level</li>
<li>Review before submitting</li>
</ul>

Next Steps