-
Notifications
You must be signed in to change notification settings - Fork 0
Timeline Commands Reference
Command type IDs are defined in TimelineCommandFactory.Create(). Each command serializes to JSON in timeline.json with a "type" field matching these IDs.
Every entry also has an "enabled" flag (default true). Disabled rows are skipped at run time but stay in the list for organization.
Invalid configuration is highlighted on the row (red) with a tooltip from GetValidationError. Run is blocked while any enabled command is invalid.
Commands can be edited from Studio or via the Timeline Web Remote browser UI (structured fields per command type).
- Input simulation
- Flow control
- Sub-timelines
- CopyScript
- Screenshots
- Studio / scene
- Pose
- Variables & logic
- VNGE
- Other
- Adding new commands
| Type ID | Command | Purpose |
|---|---|---|
simulate_key |
SimulateKeyCommand | Send key combo (Ctrl+A, F5, etc.) |
simulate_mouse |
SimulateMouseCommand | Mouse button click |
move_mouse |
MoveMouseCommand | Move cursor to position |
scroll |
ScrollCommand | Mouse wheel scroll |
| Type ID | Command | Purpose |
|---|---|---|
pause |
PauseCommand | Wait for duration (ms; interpolatable) |
loop |
LoopCommand | Repeat section |
checkpoint |
CheckpointCommand | Mark checkpoint |
jump |
JumpToCheckpointCommand | Jump to checkpoint |
confirm |
ConfirmCommand | Wait for user Confirm in the list |
resolve_on_issue |
ResolveOnIssueCommand | Wait if CopyScript has open issues |
resolve_on_count |
ResolveOnCountCommand | Wait if tracked file count ≠ expected |
sub_timeline |
SubTimelineCommand | Run nested timeline (see Sub-timelines) |
sub_timeline_param |
SubTimelineParamCommand | Declare one parameter for a sub-timeline |
sub_if |
SubBlockIfCommand | Start of a block-if (main timeline or sub-timeline) |
sub_elseif |
SubBlockElseIfCommand | Additional conditional branch |
sub_else |
SubBlockElseCommand | Default branch |
sub_endif |
SubBlockEndIfCommand | End of the block-if |
return |
ReturnCommand | Exit sub-timeline, or stop the run at root |
label |
LabelCommand | Visual separator / section label (no-op at run time) |
pause— duration in milliseconds. The field accepts a literal or a variable/expression (same int resolution ascalc). If the value cannot be resolved, a Resolve button appears on the row.
confirm— blocks until you click Confirm on that row while the timeline is running. Refocus (default on) simulates a click at(0,0)afterward to release UI focus.
resolve_on_issue— calls CopyScriptGET /api/issues. Ifissues_count > 0, shows Resolve; clicking it releases focus and continues.
resolve_on_count— calls CopyScriptGET /api/trackingand comparestotal_countto the Expected field (interpolatable int). On mismatch, waits for Resolve likeresolve_on_issue.
return— inside a sub-timeline, pops back to the parent list after thesub_timelinerow. On the main timeline, stops execution (same as Stop).
label— draws a horizontal rule and optional centered text for organization. Lv (level0= darkest) controls row shading. Does nothing when executed.
loop— jumps to a named checkpoint up to Repeat times per run (interpolatable). The row label shows the current iteration while running, e.g.Loop (2).
A structural if / else if* / else / end construct that works on the main timeline and inside sub-timelines (the add buttons live in the Subtimeline category). Blocks may be repeated and nested freely. Unlike the checkpoint-based while, these branch by position in the list rather than by jumping to a named checkpoint.
Rules
- Every
sub_ifmust be balanced by a matchingsub_endif(End If is mandatory). Between them: any number ofsub_elseif, then at most onesub_else. Order within a block must beIf→Else If* →Else→End If. Nested blocks are matched by depth like brackets. - Violations are shown on the offending row and block Start.
- The conditions in
sub_if/sub_elseifbehave exactly like thewhilecommand (same operands, operators,Not). -
sub_elseandsub_endifhave no condition.
Execution
-
Iftrue → run the commands directly below it; when the block reaches the next same-level marker (Else If/Else), jump to its matchingEnd If. -
Iffalse → evaluate eachElse Ifin turn; the first true one runs its block. If none match, theElseblock runs. - After a taken branch finishes, execution continues after
End If.
Example "CheckMood" (works the same on the main timeline or in a sub-timeline)
1. Set mood = "happy" (normal command)
2. If mood == "happy"
3. Pose Apply "smile"
4. Else If mood == "sad"
5. Pose Apply "cry"
6. Else
7. Pose Apply "neutral"
8. End If
9. Screenshot (runs after the block)
-
mood = "happy"→ 1 → 2 (true) → 3 → jump to 8 → 9 -
mood = "sad"→ 1 → 2 (false) → 4 (true) → 5 → jump to 8 → 9 -
mood = "angry"→ 1 → 2 (false) → 4 (false) → 6 (else) → 7 → 8 → 9
Sub-timeline bodies are stored in a root-level subtimelines array in timeline.json (keyed by stable id). Each sub_timeline row references one body by id and shows its title, command count, and an → button to edit the nested list.
Templates — mark a definition as Tpl (template). Other rows can Ref-link to the same body by matching title, so multiple timeline rows share one nested script. Only one Param command is allowed per sub-timeline.
Parameters — add sub_timeline_param once inside the sub-timeline (types: str, int, bool, list, dict). The parent sub_timeline row shows inputs for that parameter; values are applied when execution enters the sub-timeline (before other sub-commands). Variables set inside a sub-timeline remain visible in the parent after it returns.
Validation — sub-command errors bubble up to the parent sub_timeline row. Block-if rules inside sub-timelines use the same structure as on the main timeline.
| Type ID | Command | Purpose |
|---|---|---|
start_tracking |
StartTrackingCommand | Start file tracking |
stop_tracking |
StopTrackingCommand | Stop tracking |
copy_rename |
CopyRenameCommand | Copy/rename tracked files |
clear_tracked |
ClearTrackedFilesCommand | Clear tracked list |
set_source_path |
SetSourcePathCommand | Set source path |
set_destination_path |
SetDestinationPathCommand | Set destination |
set_name_pattern |
SetNamePatternCommand | Set rename pattern |
set_rule_counter |
SetCounterRuleCommand | Counter rule |
set_rule_list |
SetListRuleCommand | List rule |
set_rule_batch |
SetBatchRuleCommand | Batch rule |
copy_rename— Overwrite (off by default) is passed to CopyScriptPOST /api/copy_rename. On API failure the row shows Resolve and retries when clicked.
set_rule_list— tag + step like other rules. Values come from an inline list (Edit list…) or, when the var toggle is on, from a list variable name at run time.
Requires the BepInEx ScreenshotManager plugin (com.bepis.bepinex.screenshotmanager). The obsolete wait_screenshot command was removed; old timelines drop those rows automatically on load.
| Type ID | Command | Purpose |
|---|---|---|
screenshot |
ScreenshotCommand | Trigger capture and wait for a confirmed new file |
wait_empty_screenshots |
WaitForEmptyScreenshotsCommand | Wait until CopyScript tracking queue is empty |
screenshot_alpha |
ScreenshotAlphaModeCommand | Alpha mode |
screenshot_resolution |
ScreenshotResolutionCommand | Resolution |
screenshot_save_path |
ScreenshotSavePathCommand | Save path |
screenshot_alt_path_var |
ScreenshotAltPathVarCommand | Set alt save path variable |
screenshot— triggersTakeRenderScreenshotvia ScreenshotManager, then waits until a new PNG/JPEG appears in the active save folder (plugin completion API and/or filesystem watch with stable file-size checks). Retries the trigger up to 12 times, then keeps polling and slow-retriggering until a file is confirmed or you click Continue on the row (logged as a sequence error — timeline proceeds without a verified file).
Alt path — when enabled, temporarily sets the plugin save folder from the timeline variable
ScreenshotAltRelPathfor this shot only (set beforehand withscreenshot_alt_path_varor anysetstring). Restores the previous path afterward. Requires the Screenshot plugin static path API.
CopyScript Check — label shows Screenshot CS. After each capture, verifies CopyScript tracking: the newest tracked file must match the screenshot and exist on disk (up to 3 attempts per round; failed shots are removed from tracking and deleted). On failure, Resolve retries the round. Requires the CopyScript API client.
Successful captures notify Studio Live Screenshot-Follow (
/api/studio/screenshot-latest).
wait_empty_screenshots— polls CopyScriptGET /api/tracking?count=1every second untilreturned_countis0(queue drained). Use aftercopy_renamebatches before the next capture wave.
screenshot_alt_path_var— writes an interpolatable game-relative folder path intoScreenshotAltRelPath(e.g.UserData/cap_paperdoll). Used whenscreenshotruns with Alt path enabled.
| Type ID | Command | Purpose |
|---|---|---|
clothing_state |
ClothingStateCommand | Per-part clothing On / Half / Off |
accessory_state |
AccessoryStateCommand | Accessory states |
gdc_wardrobe_state |
GdcWardrobeStateCommand | Enable/disable all GDC Wardrobe layers |
outfit_rotate |
OutfitRotateCommand | Rotate outfit |
outfit_by_name |
OutfitByNameCommand | Select outfit by name |
set_camera_by_name |
SetCameraByNameCommand | Select camera |
select_object_by_name |
SelectObjectByNameCommand | Select workspace object |
set_object_visible_by_name |
SetObjectVisibleByNameCommand | Toggle visibility |
replace_chara_card |
ReplaceCharaCardCommand | Load chara from UserData/chara/
|
load_coordinate_card |
LoadCoordinateCardCommand | Load coordinate card |
pose_library |
PoseLibraryCommand | Pose library interaction |
sound |
SoundCommand | Play sound |
clothing_state— Part cycles cached Studio clothing parts (or free text if the cache is empty). State is On / Half / Off (three-state parts) or On / Off (two-state parts). Applied directly to currently selected character(s) viaOCIChar; the manipulate panel does not need to be open. No selection → warning, no-op.
accessory_state— Slot cycles Studio accessory slots (All Slots first when available). State is On or Off. Same selection rules as clothing.
gdc_wardrobe_stateenables (On) or disables (Off) all layers of the GDC Wardrobe plugin at once. Each GDC layer is a GameObject on the character namedGDCLayer_*; the plugin's per-layer toggle just callsSetActiveon it, so the command does the same directly on every such GameObject (across all characters, including currently inactive ones). The GDC window does not need to be open, but it also won't refresh its own checkboxes when toggled this way.
outfit_by_name— calls FashionLineWearFashionByNameForActiveCharacters(requires prolo.fashionline). File (default on): name is a FashionLine outfit file; off = outfit stored on the character card. Reload (default off): fullChaControlreload after apply — enable only when needed; leaving it off avoids FK/IK corruption over many outfit changes.
select_object_by_name— resolves a workspace object by name (same tree as other object commands). Mode: Select (deselect others, then select), Deselect, or Toggle (default).
set_object_visible_by_name— Visible acceptstrue/false, a bool variable, or interpolation (same rules as bool operands elsewhere).
| Type ID | Command | Purpose |
|---|---|---|
pose_apply |
PoseApplyCommand | Apply a Pose Browser pose by path (interpolatable). Optionally target a single character by name |
pose_apply_group |
PoseApplyGroupCommand | Apply a Pose Browser pose group by path. Optionally target a set of characters by name |
Path for
pose_applyis the pose file's path relative tostudio/pose(e.g.MyFolder/cool pose.png); an absolute path also works. Path forpose_apply_groupis the group name (the group id also works). Both are obtained via the Copy path button in the Pose Browser (on a pose or on a group). Requires the Pose Browser plugin to be installed.
pose_apply: with Character off it applies to the workspace selection; with Character on it looks up the single scene character with the given (interpolatable) name and applies the pose to that character.pose_apply_group: with Characters off it applies to the workspace selection; with Characters on the character set comes either from a list variable (whose items are character names) or from an inline list edited in the command. Names are resolved to scene characters. The Pose Browser priority list is applied in all cases.
| Type ID | Command | Purpose |
|---|---|---|
set |
SetVariableCommand | Set scalar variable (str / int / bool kind selector) |
set_string |
SetStringCommand | (legacy) Set string; loads old timelines |
set_integer |
SetIntegerCommand | (legacy) Set integer; loads old timelines |
get |
GetVariableCommand | Copy variable with optional type conversion |
calc |
CalcCommand | Arithmetic |
if |
WhileCommand | Conditional jump to checkpoint (do-while loop) |
str_replace |
StrReplaceCommand | String replace |
str_split |
StrSplitCommand | Split a string into a list variable |
list |
ListCommand | List operations |
range |
RangeCommand | Cycle a variable through a set of numbers/ranges |
set_list |
SetListCommand | Set list variable |
list_insert |
(legacy) | Folded into list (insert mode); auto-migrated on load |
list_remove |
(legacy) | Folded into list (remove mode); auto-migrated on load |
list_apply_dict |
ListApplyDictCommand | Apply dict to list |
dict_set |
DictSetCommand | Dictionary set |
dict_get |
DictGetCommand | Dictionary get |
set— Kind cyclesstr/int/bool. Value supports interpolation; int/bool operands that cannot be resolved wait for Resolve at run time. Legacyset_string/set_integertimelines still load; new saves useset.
get— copies From → To with As (str/int/bool) for conversion.
Condition operators (used by
whileand the block-if commands):==,≠,<,≤,>,≥, plus three membership operators where the right field is a variable name (a list, a dict, or a string holding arangeexpression):
∈F— left equals the first element (list) / first number (range).∈L— left equals the last element (list) / last number (range).in— left is contained in the right: any element of a list, any number of a range, or a key or value of a dict.
Unresolved condition operands at run time show Resolve on the row (same as
pause/setint).
listis the all-in-one list command. The first field is the Variable for read modes (the result is written there) or the List variable for mutate modes (it is changed in place). A mode button cycles through:
- Read modes (write a result into the variable, sourced from an inline list or a list variable):
- next (default) — advance through the values, cycling.
- rev — advance from last to first.
- first / last — always the first / last value.
- get — read the value at a specific Idx (always reads from a list variable; the inline-list option is hidden). A 1-based toggle switches between 0-based (default) and 1-based indexing; an out-of-range index is skipped with a warning.
- length — store the number of items as a number (works on an empty list too, stores
0).- find — store the index of the value typed in the Find field (interpolatable), or
-1if absent. The 1-based toggle offsets a found index by one.- Mutate modes (change the list variable named in the first field, in place):
- set — replace the element at Idx with the Value (1-based toggle supported); out-of-range is skipped with a warning.
- insert — add a value, with a sub-mode button: At index (insert at an interpolatable/variable Idx, clamped to bounds), Append (end), Prepend (front), or Extend (the value field, labelled From, names another list variable whose contents are appended in order).
- remove — remove elements, with a sub-mode button: By Index (interpolatable/variable Idx, out-of-range skipped with a warning), By Value (remove the first matching Value), or Clear (empty the whole list).
The legacy
list_insertandlist_removecommands are the insert and remove modes; old timelines that use them are migrated tolistautomatically on load. List/variable names, indices and values all support interpolation.
rangedrives a variable from a sequence of numbers each time it runs. The sequence comes from an interpolatable text field of comma-separated single numbers and/or inclusive ranges:1-10,1,2,3,5,1-3,6,1-10,12,14-20. A descending range like5-1counts down. A mode button selects the behaviour:
- next (default) — write the next value, cycling.
1-5→1, 2, 3, 4, 5, 1, …(stored asint).- rev — like next but from the last value to the first.
1-5→5, 4, 3, 2, 1, 5, …(int).- first — always the first value of the whole range (
int).- last — always the last value of the whole range (
int).- list — write the whole expanded range into a list variable, e.g.
1-3,6→[1, 2, 3, 6].The target variable is created if it doesn't exist yet (an
intfor next/rev/first/last, a list for list mode).
str_splitsplits a Source string by a Separator and writes the parts into a List var (created if absent). Source and separator both support interpolation (e.g.{myVar}). An empty separator yields a single-element list containing the whole source. Pairs naturally withlistin get mode to read individual parts.
str_replace— replaces Find with Replace inside a string variable (all fields interpolatable). All (default) replaces every match; First replaces only the first.
| Type ID | Command | Purpose |
|---|---|---|
vnge_scene_next |
VngeSceneNextCommand | Next scene |
vnge_scene_prev |
VngeScenePrevCommand | Previous scene |
vnge_next_scene |
VngeNextSceneCommand | Next scene alt |
vnge_prev_scene |
VngePrevSceneCommand | Prev scene alt |
vnge_load_scene |
VngeLoadSceneByIndexCommand | Load scene by index |
| Type ID | Command | Purpose |
|---|---|---|
video_record |
VideoRecordCommand | Video recording |
get_fashion |
GetFashionCommand | FashionLine data |
display |
DisplayCommand | Open/close a floating window showing a variable's live value |
displaytakes a variable name (typestring,int, orbool) and a mode button: open (default) or close. In open mode it opens a movable, resizable window with the variable name (top) and its current value (large, below); the value updates live while the timeline runs. Each window has a close button and a pin button (top-right) — pinning persists that window's position/size for the variable across runs and sessions. Opening a window for a variable that already has one is a no-op. In close mode it closes the window for that variable. All Display windows also close automatically when the timeline ends. Open cards are mirrored on the Web Remote Dashboard.Instead of a variable name you can enter an override token. Currently supported:
%TIME— elapsed run time of the timeline, formattedHH:MM:SS.Overrides are defined in
DisplayOverrides.cs; add an entry there to extend the set.
See .cursor/skills/create-timeline-command/ in the repository:
- Create
src/Timeline/YourCommand.csextendingTimelineCommand - Register type ID in
TimelineCommandFactory.cs - Add UI in
ActionTimelineWindowif needed - Optional:
AppendEditFieldsinsrc/Timeline/Web/CommandWebFields.*.csfor Web Remote editors
Navigation: ← Timeline · Timeline commands · Web Remote · Next: SearchBarManager →
- Overview
- Folders & library
- Search, filters & sort
- Grid & selection
- Pose groups
- Multi-character apply
- Pose stash
- Pose items
- Import/export ZIP
- Thumbnails
- Options & data