Actions
Detailed specification of the actions system and built-in actions in Pathway.
Actions are TypeScript functions that define what happens when users interact with slide elements. They receive a constrained context and validated parameters, enabling safe and predictable behavior while keeping implementation flexible.
Actions run when events occur (for example, a block click or a slide lifecycle event). See Events for how events are attached to blocks and slides.
Core Concepts
- Actions are defined using the
defineAction()helper from@pathway/engine - Each action has a unique ID (e.g.,
"core.next","core.link") - Parameters are validated with Zod schemas before execution
- Actions receive an
ActionContextwith a constrained API (no raw DOM access) - All actions are registered in
src/actions/registry.tsfor runtime lookup
Events reference actions by ID and can include optional params (action-specific) and optional conditions. See Events.
Action Anatomy
import { defineAction } from "@pathway/engine";
import { z } from "zod";
export const myAction = defineAction({
id: "namespace.actionName",
schema: z.object({
param1: z.string(),
param2: z.number().optional(),
}),
handler: (context, params) => {
// Action logic here
// context provides: goToSlide(), getOrderedSlides(), openExternal()
},
});
ActionContext API
The context object passed to all action handlers provides these methods:
goToSlide(slideId: string): Navigate to a specific slidegetOrderedSlides(): Get the ordered list of slide IDsopenExternal(url: string): Open an external URL in a new tabsetVariable(name, value): Set a runtime variablegetVariable(name): Read a runtime variablesetState(blockId, state): Show, hide, or disable a block
Future additions:
playAudio(blockId): Start audio playbackpauseAudio(blockId): Pause audio playbackseekAudio(blockId, timestamp): Jump to a specific time
Built-in Actions (Implemented)
core.next
Navigate to the next slide in sequence.
Parameters: None
Schema:
z.object({});
Behavior:
- Gets the ordered list of slides
- Finds the current slide position
- Navigates to the next slide
- Does nothing if already on the last slide
Example usage (JSON):
{
"id": "next-btn",
"kind": "button",
"text": "Next",
"click": [{ "action": "core.next" }]
}
core.prev
Navigate to the previous slide in sequence.
Parameters: None
Schema:
z.object({});
Behavior:
- Gets the ordered list of slides
- Finds the current slide position
- Navigates to the previous slide
- Does nothing if already on the first slide
Example usage (JSON):
{
"id": "prev-btn",
"kind": "button",
"text": "Previous",
"click": [{ "action": "core.prev" }]
}
core.link
Open an external URL in a new browser tab.
Parameters:
href(string, required): The URL to open
Schema:
z.object({
href: z.string().url(),
});
Behavior:
- Validates the URL format
- Opens the URL in a new tab via
context.openExternal()
Example usage (JSON):
{
"id": "learn-more",
"kind": "button",
"text": "Learn More",
"click": [
{
"action": "core.link",
"params": {
"href": "https://example.com"
}
}
]
}
Planned Built-in Actions
core.goTo
Navigate to a specific slide by ID.
Planned parameters:
slideId(string, required): The ID of the target slide
Planned schema:
z.object({
slideId: z.string(),
});
Example usage:
{
"id": "jump-btn",
"kind": "button",
"text": "Go to Summary",
"click": [
{
"action": "core.goTo",
"params": {
"slideId": "summary"
}
}
]
}
core.showBlock / core.hideBlock / core.disableBlock
Control the visibility and interactivity of blocks on the current slide.
Planned parameters:
blockId(string or array, required): ID(s) of the block(s) to affect
Planned schema:
z.object({
blockId: z.union([z.string(), z.array(z.string())]),
});
Use cases:
- Progressive disclosure of content
- Show/hide hints or feedback
- Disable submit buttons after use
- Toggle visibility of layers
Example usage:
{
"id": "reveal-btn",
"kind": "button",
"text": "Show Answer",
"click": [
{
"action": "core.showBlock",
"params": {
"blockId": "answer-layer"
}
}
]
}
core.showLayer / core.hideLayer
Specifically control layer visibility (specialized container blocks).
Planned parameters:
layerId(string, required): ID of the layer to show/hide
Use cases:
- Tooltips
- Modal dialogs
- Overlays
- Pop-up feedback
core.setVariable / core.incrementVariable / core.decrementVariable
Manage runtime variables for tracking state, scores, or progress.
Planned parameters:
name(string, required): Variable namevalue(any, required for set): Value to setamount(number, optional for inc/dec): Amount to change by (default: 1)
Planned schema (setVariable):
z.object({
name: z.string(),
value: z.union([z.string(), z.number(), z.boolean()]),
});
Use cases:
- Quiz scoring
- Progress tracking
- Conditional content based on user choices
- Module state management
Example usage:
{
"id": "correct-answer",
"kind": "button",
"text": "A",
"click": [
{
"action": "core.incrementVariable",
"params": {
"name": "score",
"amount": 10
}
}
]
}
core.playAudio / core.pauseAudio / core.stopAudio
Control audio playback for audio blocks.
Planned parameters:
blockId(string, required): ID of the audio blocktimestamp(number, optional): Jump to specific time (for play/seek)
Planned schema:
z.object({
blockId: z.string(),
timestamp: z.number().optional(),
});
Use cases:
- Narration control
- Background music
- Sound effects
- Audio-synchronized content
core.playVideo / core.pauseVideo / core.stopVideo
Control video playback for video blocks.
Planned parameters:
blockId(string, required): ID of the video blocktimestamp(number, optional): Jump to specific time
Use cases:
- Video-based learning content
- Demonstrations
- Interactive video experiences
core.showTooltip / core.showModal
Display overlay content (tooltips or modals).
Planned parameters:
blockId(string, required): ID of the tooltip/modal block to displayposition(string, optional): Override positioning
Use cases:
- Contextual help
- Additional information
- Feedback messages
- Confirmations
core.submitQuiz
Submit quiz answers and evaluate results.
Planned parameters:
quizId(string, required): ID of the quiz blockanswers(object, required): User’s answer selections
Planned schema:
z.object({
quizId: z.string(),
answers: z.record(z.any()),
});
Use cases:
- Knowledge checks
- Assessments
- Interactive exercises
- SCORM/LMS result reporting
core.triggerAnimation
Apply a CSS animation or transition to a block.
Planned parameters:
blockId(string, required): ID of the block to animateanimation(string, required): Animation type (e.g.,"fadeIn","slideUp")duration(number, optional): Animation duration in milliseconds
Planned schema:
z.object({
blockId: z.string(),
animation: z.enum(["fadeIn", "fadeOut", "slideUp", "slideDown", "scale"]),
duration: z.number().optional(),
});
Use cases:
- Visual feedback
- Emphasis on important content
- Smooth transitions
- Engaging interactions
Conditional Actions (Planned)
core.ifCondition
Execute different actions based on variable values or conditions.
Planned parameters:
condition(object, required): Condition to evaluatethenAction(string, required): Action ID to execute if trueelseAction(string, optional): Action ID to execute if false
Planned schema:
z.object({
condition: z.object({
variable: z.string(),
operator: z.enum(["eq", "neq", "gt", "lt", "gte", "lte"]),
value: z.any(),
}),
thenAction: z.string(),
thenParams: z.record(z.any()).optional(),
elseAction: z.string().optional(),
elseParams: z.record(z.any()).optional(),
});
Use cases:
- Branching based on quiz performance
- Adaptive content
- Personalized learning paths
Global Events (Planned)
Events that trigger automatically without user interaction.
onSlideStart
Triggered when a slide becomes active.
Use cases:
- Auto-play narration
- Initialize slide state
- Track analytics
- Start timers
onSlideEnd
Triggered when leaving a slide.
Use cases:
- Save progress
- Clean up resources
- Track completion
Custom Actions
Users can define custom actions by:
- Creating a TypeScript file in
src/actions/ - Using
defineAction()with a unique ID - Registering it in
src/actions/registry.ts
Example custom action:
// src/actions/custom.ts
import { defineAction } from "@pathway/engine";
import { z } from "zod";
export const logMessage = defineAction({
id: "custom.log",
schema: z.object({
message: z.string(),
}),
handler: (context, params) => {
console.log("[Custom]:", params.message);
},
});
// src/actions/registry.ts
import { logMessage } from "./custom";
export const actionRegistry = new Map([
// ... existing actions
[logMessage.id, logMessage],
]);
Design Principles
- Type Safety: Zod validation ensures parameters are correct before execution
- Constrained Context: Actions cannot manipulate DOM directly, ensuring consistency
- Composability: Actions can be combined (future: action sequences)
- Framework-Agnostic: Action definitions live in
packages/engine/ - Extensibility: Custom actions follow the same pattern as built-in ones
- Predictability: Pure functions with well-defined side effects
Visual Editor Integration
In the planned visual editor:
- Action dropdown shows all registered actions
- Form fields are auto-generated from Zod schemas
- Parameters that reference blocks show a picker UI
- Validation happens before saving
Implementation Status
- ✅ Implemented:
core.next,core.prev,core.link - 🚧 Planned: All other actions listed in this document
Next Steps
- Implement block visibility actions (
showBlock,hideBlock,disableBlock) - Add variable management actions (
setVariable,incrementVariable) - Implement media control actions (audio/video playback)
- Design conditional action system
- Add global slide events (
onSlideStart,onSlideEnd) - Create SCORM/LMS integration actions