Skip to content

Add action-tracker input type for timestamped event logging#94

Merged
luanzeba merged 24 commits intomainfrom
add-timed-action-input
Feb 5, 2026
Merged

Add action-tracker input type for timestamped event logging#94
luanzeba merged 24 commits intomainfrom
add-timed-action-input

Conversation

@luanzeba
Copy link
Collaborator

@luanzeba luanzeba commented Jan 24, 2026

Summary

This PR introduces a new action-tracker input type that allows scouts to record timestamped robot actions during a match. Rather than just counting events, scouts can now tap action buttons as they happen, building a timeline of what the robot did and when.

Demo with tapping: https://drive.google.com/file/d/1ZOORtvebjrV7ntk0YtEJNCypNdfhZHAD/view?usp=sharing
Demo with holding: https://drive.google.com/file/d/1mpLHSRhjhV06cgtykoCf8Ck12Y7Edtl-/view?usp=sharing

Motivation

Traditional counter inputs capture how many times something happened, but not when. For post-match analysis, knowing that a robot scored fuel at 2.3s, 8.1s, and 14.5s during auto tells a much richer story than knowing it scored 3 times. This timing data enables analysis of cycle times, action sequences, and phase-specific performance.

Design Decisions

Tap vs Hold modes (experimental). The action-tracker supports two recording modes via the mode property:

  • tap: Records an instant timestamp when the button is tapped. Best for discrete events like scoring or picking up game pieces.
  • hold: Records both start and end timestamps while the button is held down. Best for continuous actions like playing defense or climbing. Supports multi-touch for tracking overlapping actions.

Both modes are included temporarily for field testing during practice matches. After evaluating usability with our scouts, we'll likely standardize on a single mode in a follow-up PR.

Separate trackers for Auto and Teleop. We opted for distinct action-tracker instances per game phase rather than one combined tracker. This keeps the timer durations phase-appropriate and makes the data easier to parse—auto actions don't need to be filtered out of a 150-second teleop timeline.

Auto-start on first tap. While scouts should ideally start the timer manually when the phase begins, tapping any action button will start the timer automatically if it isn't already running. This prevents data loss when scouts forget to hit Start, and we display an "(auto-started)" indicator so they're aware.

Human-friendly column names. When copying column names, action-tracker fields expand into readable headers like "Fuel Scored in Auto (count)" and "Fuel Scored in Auto (timestamps)" rather than raw field codes. The phase name is extracted from the field title (e.g., "Auto Actions" → "Auto").

Expanded field output. Each action generates two output columns: {code}_{actionCode}_count (integer) and {code}_{actionCode}_times (comma-separated timestamps in seconds). For example, an auto tracker with code autoAct and a "fuel" action produces autoAct_fuel_count and autoAct_fuel_times. This keeps the data flat and spreadsheet-friendly.

Optional Lucide icons. Action buttons can display icons from the Lucide icon library. Icons are lazy-loaded on demand using dynamic imports, so they add zero bytes to the initial bundle regardless of how many different icons are used across configs.

Example Configuration

{
  "title": "Auto Actions",
  "type": "action-tracker",
  "code": "autoAct",
  "required": false,
  "mode": "tap",
  "actions": [
    { "label": "Fuel Scored", "code": "fuel", "icon": "circle-dot" },
    { "label": "Crossed Bump", "code": "bump", "icon": "chevrons-up" }
  ]
}

This generates column headers "Fuel Scored in Auto (count)", "Fuel Scored in Auto (timestamps)", etc., and output data like 2 0,7.8 1 4.7 (2 fuel at 0s and 7.8s, 1 bump at 4.7s).

- Add 'action-tracker' to inputTypeSchema enum
- Create actionSchema for individual action buttons (label + code)
- Create actionTrackerInputSchema with actions array and optional timerDuration
- Add to sectionSchema discriminated union
- Export ActionTrackerInputData and ActionData types
- Add to InputPropsMap
- Timer with start/stop controls using requestAnimationFrame
- Auto-start on first action tap if timer not running
- Grid of action buttons with tap counts
- Undo last action functionality
- Recent action log display (last 5 entries)
- Outputs _count and _times fields for each action via updateValue()
- Import ActionTrackerInput component
- Add case for 'action-tracker' type in switch statement
- Add generateFieldValues() helper to create field values from config
- For action-tracker inputs, generates _count and _times fields per action
- Update initialState and setFormData to use generateFieldValues()
@github-actions
Copy link

github-actions bot commented Jan 24, 2026

PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://FRC2713.github.io/QRScout/pr-preview/pr-94/

Built to branch gh-pages at 2026-02-05 18:42 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Comment on lines 160 to 163
{ "label": "Fuel Scored", "code": "fuel" },
{ "label": "Crossed Bump", "code": "bump" },
{ "label": "Through Trench", "code": "trench" },
{ "label": "Climb Started", "code": "climbStart" }
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are just examples, we'll review them on Monday

Reverts to using formData.sections (preserving behavior for all other
input types) while adding special handling for action-tracker fields.
Action tracker columns now display as 'Fuel Scored in Auto (count)'
instead of 'autoAct_fuel_count'.
Allows specifying a Lucide icon name for action buttons.
See https://lucide.dev/icons for available icons.
Extract DynamicIcon to a separate module that's only loaded when
action-tracker buttons have icons configured. This reduces the main
bundle from 821KB to 724KB (~97KB savings) by deferring the 184KB
dynamicIconImports map until needed.
When action labels wrap to multiple lines, the content height exceeds
the fixed button height, causing icons to render above the button
boundary. Adding h-auto allows buttons to grow to fit their content
while min-h-16 maintains minimum sizing for short labels.
Add 'mode' property to ActionTrackerInputData that allows choosing between:
- 'tap': records instant timestamps on click (existing behavior)
- 'hold': records duration while button is pressed (new, default)

This prepares the schema for implementing press-and-hold recording.
Add press-and-hold recording mode that tracks action durations:
- Pointer events (pointerdown/up/cancel/leave) for multi-touch support
- Active pointer tracking with Map<pointerId, {actionCode, startTime}>
- Visual feedback: ring effect and pulse animation while holding
- Haptic feedback: vibrate on press (50ms) and release (30-20-30ms pattern)
- Times stored as 'start-end' pairs (e.g., '5.2-8.1,12.0-15.3')
- Window blur handler ends all active holds gracefully
- touch-none and select-none prevent browser interference

Tap mode (mode: 'tap') preserves existing click-to-record behavior.
Hold mode is the default when mode is not specified.
Explicitly set hold mode on Auto Actions and Teleop Actions inputs
and update descriptions to reflect press-and-hold behavior.
- Add 200ms intent delay before registering hold (prevents accidental triggers)
- Add 10px movement threshold to detect scroll vs intentional hold
- Ignore pointercancel/pointerleave for confirmed holds (fixes jitter cancellation)
- Delay setPointerCapture until intent is confirmed
- Fix background animation on held buttons with !important modifier

Based on React Native / @use-gesture best practices for touch handling.
Resolve conflicts by accepting both feature sets:
- Keep action-tracker with tap/hold modes (our feature)
- Add TBA integration types from main (TBA-team-and-robot, TBA-match-number)
- Keep generateFieldValues() for action-tracker dynamic field support
- Add MatchDataFetcher component and matchData state from main
- Add comprehensive action-tracker documentation to README including
  tap vs hold modes, properties, data format, and best practices
- Remove action-tracker fields from sample config (actions TBD in follow-up PR)
- Add action-tracker to Table of Contents and input types list
- Update README example to use 'hold' mode (preferred) and 'autoAction' code
- Remove unnecessary formResetBehavior comment in ActionTrackerInput
@luanzeba luanzeba force-pushed the add-timed-action-input branch from 335cdb2 to 0b99e17 Compare February 5, 2026 18:42
@luanzeba luanzeba marked this pull request as ready for review February 5, 2026 18:43
@luanzeba luanzeba requested a review from tytremblay February 5, 2026 18:43
Copy link
Collaborator

@tytremblay tytremblay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks awesome! Can't wait to show this to the other teams! I vote we merge sooner than later and we can update with our chosen actions later.

@luanzeba luanzeba merged commit b1aeb0b into main Feb 5, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants