pywry-orientation
When to reach for PyWry MCP tools (native webview rendering, Plotly, TradingView, AgGrid, chat) instead of writing Flask/Streamlit/Dash/Electron code. Use at…
Building interactive forms with validation and collecting user input.
$ npx -y skills add deeleeramone/PyWry --skill forms_and_inputs --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/forms_and_inputsContext preview
The summary Claude sees to decide when to auto-load this skill.
Building interactive forms with validation and collecting user input.
> Building interactive forms with validation and collecting user input.
| Type | Component | Use Case | |------|-----------|----------| | `text` | TextInput | Single-line text (names, emails) | | `textarea` | TextareaInput | Multi-line text (comments, descriptions) | | `number` | NumberInput | Numeric values with min/max/step | | `date` | DateInput | Date selection | | `search` | SearchInput | Search with debounce | | `secret` | SecretInput | Passwords, API keys (value hidden) | | `select` | Select | Single choice dropdown | | `multiselect` | MultiSelect | Multiple choices | | `radio` | RadioGroup | Visible single choice (small set) | | `checkbox` | Checkbox | Boolean toggle | | `toggle` | Toggle | On/off switch | | `slider` | SliderInput | Single value in range | | `range` | RangeInput | Two values (min/max range) |
create_widget(
html='<div id="results">Fill out the form and click Submit</div>',
toolbars=[{
"position": "left",
"items": [
{"type": "text", "label": "Name", "event": "form:name", "placeholder": "Enter your name"},
{"type": "text", "label": "Email", "event": "form:email", "placeholder": "email@example.com"},
{"type": "number", "label": "Age", "event": "form:age", "min": 0, "max": 120, "value": 25},
{"type": "select", "label": "Country", "event": "form:country",
"options": [
{"label": "United States", "value": "us"},
{"label": "United Kingdom", "value": "uk"},
{"label": "Canada", "value": "ca"},
]},
{"type": "button", "label": "Submit", "event": "form:submit", "variant": "primary"},
]
}]
)events = get_events(widget_id, clear=True)
# Collect latest values
form_data = {}
for e in events:
if e["event_type"].startswith("form:"):
field = e["event_type"].replace("form:", "")
form_data[field] = e["data"].get("value")
# On submit
if "submit" in form_data:
process_form(form_data)# Keep running state across event polls
form_state = {}
while True:
events = get_events(widget_id, clear=True)
for e in events:
if e["event_type"].startswith("form:"):
field = e["event_type"].replace("form:", "")
if field != "submit":
form_state[field] = e["data"].get("value")
else:
# Submit with current state
result = process_form(form_state)
set_content(widget_id, component_id="results", text=result)
time.sleep(0.1)def validate_form(data):
if not data.get("name"):
show_toast(widget_id, message="Name is required", type="error")
return False
if not data.get("email") or "@" not in data["email"]:
show_toast(widget_id, message="Valid email is required", type="error")
return False
return Truedef highlight_error(component_id):
set_style(widget_id, component_id=component_id, styles={
"borderColor": "var(--error-color)",
"boxShadow": "0 0 0 2px rgba(239, 68, 68, 0.2)",
})
def clear_error(component_id):
set_style(widget_id, component_id=component_id, styles={
"borderColor": "var(--border-color)",
"boxShadow": "none",
})# Create form with error placeholders
html = """
<div class="form-group">
<div id="name-error" class="error-message" style="display: none; color: var(--error-color);"></div>
</div>
"""
# Show error
set_content(widget_id, component_id="name-error", text="Name is required")
set_style(widget_id, component_id="name-error", styles={"display": "block"})
# Clear error
set_style(widget_id, component_id="name-error", styles={"display": "none"}){"type": "text",
"label": "Username",
"event": "username",
"placeholder": "Enter username",
"value": "", # Default value
}
# Event data: {"value": "entered_text"}{"type": "number",
"label": "Quantity",
"event": "quantity",
"min": 1,
"max": 100,
"step": 1,
"value": 10,
}
# Event data: {"value": 10} # Always a number{"type": "select",
"label": "Category",
"event": "category",
"options": [
{"label": "Option A", "value": "a"},
{"label": "Option B", "value": "b"},
{"label": "Option C", "value": "c"},
],
"value": "a", # Default selection
}
# Event data: {"value": "a", "label": "Option A"}{"type": "multiselect",
"label": "Tags",
"event": "tags",
"options": [
{"label": "Important", "value": "important"},
{"label": "Urgent", "value": "urgent"},
{"label": "Review", "value": "review"},
],
"value": ["important"], # Default selections (array)
}
# Event data: {"value": ["important", "urgent"]}{"type": "radio",
"label": "Priority",
"event": "priority",
"options": [
{"label": "Low", "value": "low"},
{"label": "Medium", "value": "medium"},
{"label": "High", "value": "high"},
],
"value": "medium",
}
# Event data: {"value": "medium"}{"type": "toggle", # or "checkbox"
"label": "Enable notifications",
"event": "notifications",
"value": True, # Default state
}
# Event data: {"value": true} # Boolean{"type": "slider",
"label": "Volume",
"event": "volume",
"min": 0,
"max": 100,
"step": 5,
"value": 50,
}
# Event data: {"value": 50}{"type": "range",
"label": "Price Range",
"event": "price_range",
"min": 0,
"max": 1000,
"step": 10,
"value": [100, 500], # [min_value, max_value]
}
# Event data: {PyWry is a cross-platform app factory, rendering engine and UI toolkit for Python that produces native desktop, web, and notebook experiences from a single API.
Repo: deeleeramone/PyWry
When to reach for PyWry MCP tools (native webview rendering, Plotly, TradingView, AgGrid, chat) instead of writing Flask/Streamlit/Dash/Electron code. Use at…
Add OAuth2 authentication to PyWry apps — Google, GitHub, Microsoft, or any OIDC provider.
Autonomous PyWry application building using LLM sampling, elicitation, and progress reporting.
How an agent operates inside a PyWry chat widget — reading user messages, attachments, @-context, tool-call result cards, edit/resend, settings changes.
**STOP. THIS IS THE AUTHORITATIVE REFERENCE FOR ALL COMPONENTS.** **YOU MUST USE THE EXACT EVENT SIGNATURES DOCUMENTED HERE.** **THERE ARE NO EXCEPTIONS. NO…