Skip to content

Workout Creator Integration/Standardization#225

Merged
JaapvanEkris merged 9 commits intoJaapvanEkris:0.9.7-(under-construction)from
DXCanas:workout-creator-standardization
Apr 2, 2026
Merged

Workout Creator Integration/Standardization#225
JaapvanEkris merged 9 commits intoJaapvanEkris:0.9.7-(under-construction)from
DXCanas:workout-creator-standardization

Conversation

@DXCanas
Copy link
Copy Markdown

@DXCanas DXCanas commented Mar 28, 2026

First off, thank you @NickOldfield for this contribution! 🎉 The workout goal feature is a great addition to the UI, and we (all users as well as the excellent maintainers) really appreciate you taking the time to implement it. This was clearly a thoughtful piece of work.

What this PR does

This PR refactors the contributed workout goal popup from workout-bar.js into the project's Lit component architecture. The functionality remains identical—users can still click Distance/Timer/Calories tiles to set workout goals with the same increments and behavior.

Why the refactor?

While the original implementation worked, integrating it into the existing component patterns should help with long-term maintainability. Here's what changed and why:

1. Component architecture alignment

  • Before: Vanilla DOM manipulation with direct document.createElement() calls
  • After: Proper Lit component (<workout-dialog>) extending AppElement
  • Why: The other UI components use Lit's reactive property system. This makes the code more maintainable and consistent with components like SettingsDialog, DashboardToolbar, etc.

Even if we eventually drop lit as a framework, the standardization of each component would make it much easier to port.

2. Event flow standardization

  • Before: window.dispatchEvent() bypassing the component tree
  • After: Bubbling composed events (bubbles: true, composed: true) that traverse shadow DOM
  • Why: The architecture (ideally) uses "properties down, events up" — child components communicate with parents via bubbling events. Window-level events break this pattern and make it harder to reason about data flow.

3. Centralized WebSocket communication

  • Before: This feature creating was its own WebSocket connection
  • After: All commands route through handleAction() in app.js
  • Why: The app uses a single shared WebSocket connection. This, along with the centralized handleAction pattern used by other features (reset, upload, shutdown, etc.), simplifies things with standards.

4. Dialog reuse

  • Before: Custom modal implementation
  • After: Uses existing <app-dialog> component
  • Why: There's already a dialog component with proper theming, keyboard handling, and accessibility. Reusing it ensures consistent UX and reduces code duplication. Future improvements to the dialog component will benefit all consumers 😄

5. Theme compatibility

  • Before: Hardcoded colors that didn't work with true-black theme
  • After: CSS variables (--theme-font-color, --theme-widget-color, etc.)
  • Why: Personal testing revealed visibility issues using the True Black theme. Using CSS variables ensures the dialog adapts to all theme variants.

6. Future compatibility

  • Why now: There's upcoming work on the retile-mode branch (commit 7bfbb31) that adds interactive dashboard customization. This refactor places dialog management on PerformanceDashboard using the same pattern (_dialog state + connectedCallback listeners), which should make that merge nearly conflict-free.

What stayed the same

✅ All 3 workout types (distance, time, calories)
✅ All increment values and formatting
✅ User-facing behavior and UX
✅ WebSocket message format (updateIntervalSettings)

Technical changes

  • New: app/client/components/WorkoutDialog.js — Lit component with reactive state
  • Modified: app/client/components/PerformanceDashboard.js — Dialog host with event listener
  • Modified: app/client/lib/app.js — Added updateIntervalSettings to handleAction
  • Modified: app/client/store/dashboardMetrics.js — Bubbling events instead of window events
  • Removed: app/client/workout-bar.js — Replaced by WorkoutDialog component

Relevant Notes


Again, huge thanks for the contribution!

DXCanas added 5 commits March 28, 2026 01:10
New <workout-dialog> component wrapping <app-dialog> for the workout goal
picker. Manages distance/time/calories workout types with increment buttons
and running total. Uses CSS variables for proper theming support.

AI-assisted-by: Claude (Anthropic)
Route workout interval settings through the shared WebSocket connection
instead of creating a separate socket per command.

AI-assisted-by: Claude (Anthropic)
Add _dialog state and workout-open event listener following the
connectedCallback/disconnectedCallback pattern for retile-mode
branch compatibility.

AI-assisted-by: Claude (Anthropic)
Metric tile clicks now dispatch proper DOM events that bubble through
shadow DOM boundaries instead of bypassing the component tree via
window-level events.

AI-assisted-by: Claude (Anthropic)
Replaced by the WorkoutDialog Lit component with proper AppDialog
integration, event bubbling, and theme support.

AI-assisted-by: Claude (Anthropic)
@DXCanas DXCanas changed the base branch from main to 0.9.7-(under-construction) March 28, 2026 08:39
@Abasz
Copy link
Copy Markdown
Collaborator

Abasz commented Mar 28, 2026

@JaapvanEkris I will test this new, can we include it in 0.9.7 please?

Rework the dialog structure and CSS for better responsiveness and
cleaner markup. Use CSS grid for button layout (to ensure euqal size) with automatic wrapping
on small screens, and simplify the HTML by removing wrapper elements.

Key changes:
- Add :host { position: absolute } for proper dialog positioning
- Switch increment buttons from flexbox to CSS grid with 4-column layout
- Add responsive 2-column grid for screens below 425px
- Replace popup-title div with semantic legend element
- Remove wrapper div, flatten dialog content structure
- Move reset button into grid with full-row span
- Apply button styles via element selector instead of classes
- Add hover and active states to all buttons
- Add user-select: none and white-space: nowrap to buttons
@Abasz Abasz marked this pull request as ready for review March 28, 2026 11:18
@Abasz Abasz self-assigned this Mar 28, 2026
@JaapvanEkris
Copy link
Copy Markdown
Owner

@JaapvanEkris I will test this new, can we include it in 0.9.7 please?

No problem. 0.9.7 is closing Sunday evening, and this seems a good idea to include. Can wait a day or two if needed, but Abasz also wants to move to the new CI/CD pipeline.

For me 0.9.7 will close with a large addittion to the fit-files. End this year RowsAndAll will close (the only site capable of showing all our metrics) and intervals.icu will take over (RowsAndAll's owner is porting the functionality) As that creates an externally imposed deadline to keep data flowing, I want to make sure that we are 99% ready for that move now, as it otherwise would put a lot of pressure on 0.9.8 to be ready when RowsAndAll really pulls the plug. The FIT definition had a lot of moving parts on all side, but it seems to have come together yesterday (already got a working prototype).

@JaapvanEkris JaapvanEkris added this to the 0.9.7 milestone Mar 28, 2026
@Abasz
Copy link
Copy Markdown
Collaborator

Abasz commented Mar 28, 2026

I made some changes this is how this looks like:

image

Small screen

image

Button hover and click:

image

Unless @DXCanas or @NickOldfield has any comments this can be merged tomorrow.

@DXCanas
Copy link
Copy Markdown
Author

DXCanas commented Mar 28, 2026

I made some changes this is how this looks like:

image Small screen image Button hover and click: image Unless @DXCanas or @NickOldfield has any comments this can be merged tomorrow.

Always a fan of less wrappers, personally. Lgtm 😊

@NickOldfield
Copy link
Copy Markdown

It looks good! Thanks for fine tuning and the improvements.

@JaapvanEkris
Copy link
Copy Markdown
Owner

Guys, one kind of bug: when you set a time/distance/calorie target, the large number line has a unit (m/m/kcal), but this unit is also noted below it. I propose to remove it from the number-line, and onlu show it below the number line,

@DXCanas
Copy link
Copy Markdown
Author

DXCanas commented Mar 31, 2026

Guys, one kind of bug: when you set a time/distance/calorie target, the large number line has a unit (m/m/kcal), but this unit is also noted below it. I propose to remove it from the number-line, and onlu show it below the number line,

Ha, I just caught that while trying it out yesterday during a workout. I’m running around most of today but I’ll try and get that in before EOD

@DXCanas
Copy link
Copy Markdown
Author

DXCanas commented Mar 31, 2026

@JaapvanEkris Actually, I'm seeing the reasoning

there's logic in place to change between "m" and "k" once the workout goes hits 10k:

https://github.com/JaapvanEkris/openrowingmonitor/pull/225/changes#diff-9dd1b4b472a6aa49669206847857bdef90841ebc196f0a86fd1278076755a8d9R22

If we want to keep that logic, we could just remove the "m" and keep "metres". Or we can just remove "metres" and assume that the user knows what they're signing up for (which. I think is a fair assumption).

Going to stick with "metres" to keep the standard going -- kcal and minutes don't have units in the number display.

DXCanas added 2 commits March 31, 2026 15:36
Replace manual CustomEvent dispatching with handler function passed through
template factory. Now follows the same pattern as DashboardForceCurve where
click handlers are methods, not inline event creation.

- Handler passed as third parameter to metric templates
- Clean @click syntax: @click=${() => onWorkoutOpen?.('distance')}
- Removes connectedCallback/disconnectedCallback event listeners

AI-assisted-by: Claude (Anthropic)
Change distance display from 99999.5m to 999.5m threshold for switching to
kilometer notation.
Now displays "1K" instead of "1000m" consistency with the rest of the UI and general rowing parlance.

AI-assisted-by: Claude (Anthropic)
@DXCanas DXCanas force-pushed the workout-creator-standardization branch from df28e6f to ec345bb Compare March 31, 2026 22:40
@JaapvanEkris
Copy link
Copy Markdown
Owner

@JaapvanEkris Actually, I'm seeing the reasoning

there's logic in place to change between "m" and "k" once the workout goes hits 10k:

In rowing people generally talk about meters. Even on large distances like the marathon or the half century, it is still displayed in meters by design. Our GUI starts switching to kilometers above 99,999.5 meters....

@DXCanas
Copy link
Copy Markdown
Author

DXCanas commented Mar 31, 2026

Yeah — so the unit below is always going to say meters below the number line, as described. And the metric square will also always say it in raw meters.

I think the “k” behavior was intended for visual space saving for kcal or when the number gets up there (100,000m vs 100k)

You would rather leave it as the raw number no matter what?

@JaapvanEkris
Copy link
Copy Markdown
Owner

You would rather leave it as the raw number no matter what?

That is the behaviour of every monitor on the market today, including ORM. We only start trimming at 100K as it physically doesn't fit the tile.

Per Jaap feedback, large numbers now show raw values only with
units displayed separately below (except for 100k+). Matches standard behaviour.

AI-assisted-by: Claude (Anthropic)
Comment thread app/client/components/WorkoutDialog.js
@DXCanas DXCanas force-pushed the workout-creator-standardization branch 2 times, most recently from c4c05fc to af34e30 Compare April 2, 2026 03:27
Comment thread app/client/components/PerformanceDashboard.js Outdated
@DXCanas DXCanas force-pushed the workout-creator-standardization branch from af34e30 to de9cc05 Compare April 2, 2026 18:23
@JaapvanEkris JaapvanEkris merged commit b57141b into JaapvanEkris:0.9.7-(under-construction) Apr 2, 2026
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.

4 participants