@titan-design/react-ui 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/dist/bodymap.js +12 -7
  2. package/dist/bodymap.js.map +1 -1
  3. package/dist/bodymap.mjs +12 -7
  4. package/dist/bodymap.mjs.map +1 -1
  5. package/dist/index.d.mts +855 -124
  6. package/dist/index.d.ts +855 -124
  7. package/dist/index.js +2276 -801
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +2230 -802
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/{pages-MO0JCySL.d.mts → pages-8u_4WbL-.d.mts} +280 -57
  12. package/dist/{pages-D7Jlssvp.d.ts → pages-DaWiBOGa.d.ts} +280 -57
  13. package/dist/pages.d.mts +1 -1
  14. package/dist/pages.d.ts +1 -1
  15. package/dist/pages.js +1255 -798
  16. package/dist/pages.js.map +1 -1
  17. package/dist/pages.mjs +1255 -798
  18. package/dist/pages.mjs.map +1 -1
  19. package/dist/theme/index.d.mts +16 -82
  20. package/dist/theme/index.d.ts +16 -82
  21. package/dist/theme/index.js +13 -115
  22. package/dist/theme/index.js.map +1 -1
  23. package/dist/theme/index.mjs +13 -115
  24. package/dist/theme/index.mjs.map +1 -1
  25. package/dist/theme/tokens-css.js +14 -6
  26. package/dist/theme/tokens-css.js.map +1 -1
  27. package/dist/theme/tokens-css.mjs +14 -6
  28. package/dist/theme/tokens-css.mjs.map +1 -1
  29. package/package.json +1 -1
  30. package/src/arch/Architecture.stories.tsx +870 -0
  31. package/src/arch/annotations.json +48 -0
  32. package/src/arch/arch-graph.json +4301 -0
  33. package/src/components/custom/DateTime/DateTime.stories.tsx +1 -1
  34. package/src/components/custom/EmptyState/EmptyState.stories.tsx +1 -1
  35. package/src/components/custom/Gauge/Gauge.stories.tsx +1 -1
  36. package/src/components/custom/Metric/Metric.stories.tsx +1 -1
  37. package/src/components/custom/Scatter/Scatter.stories.tsx +1 -1
  38. package/src/components/custom/Sidebar/Sidebar.stories.tsx +1 -1
  39. package/src/components/custom/Table/Table.stories.tsx +1 -1
  40. package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +62 -0
  41. package/src/components/custom/TimerReadout/TimerReadout.test.tsx +72 -0
  42. package/src/components/custom/TimerReadout/TimerReadout.tsx +75 -0
  43. package/src/components/custom/TimerReadout/index.ts +1 -0
  44. package/src/components/custom/Treemap/Treemap.stories.tsx +1 -1
  45. package/src/components/custom/Typography/Typography.stories.tsx +1 -1
  46. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +21 -13
  47. package/src/components/custom/Workout/ActiveWorkoutPage.test.tsx +16 -13
  48. package/src/components/custom/Workout/ActiveWorkoutPage.tsx +9 -15
  49. package/src/components/custom/Workout/BaseBadge.stories.tsx +1 -1
  50. package/src/components/custom/Workout/BodyMap.stories.tsx +1 -1
  51. package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +1 -1
  52. package/src/components/custom/Workout/CapacityBandChart.stories.tsx +1 -1
  53. package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
  54. package/src/components/custom/Workout/ExerciseCard.stories.tsx +51 -66
  55. package/src/components/custom/Workout/ExerciseCard.test.tsx +146 -143
  56. package/src/components/custom/Workout/ExerciseCard.tsx +176 -198
  57. package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +99 -0
  58. package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +72 -0
  59. package/src/components/custom/Workout/ExerciseCardHeading.tsx +43 -0
  60. package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +151 -0
  61. package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +5 -3
  62. package/src/components/custom/Workout/ExerciseDetailPage.test.tsx +4 -4
  63. package/src/components/custom/Workout/ExerciseDetailPage.tsx +4 -5
  64. package/src/components/custom/Workout/ExerciseHeading.stories.tsx +81 -0
  65. package/src/components/custom/Workout/ExerciseHeading.test.tsx +77 -0
  66. package/src/components/custom/Workout/ExerciseHeading.tsx +85 -0
  67. package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +110 -0
  68. package/src/components/custom/Workout/ExerciseIndicator.test.tsx +93 -0
  69. package/src/components/custom/Workout/ExerciseIndicator.tsx +120 -0
  70. package/src/components/custom/Workout/FatigueMeter.stories.tsx +51 -0
  71. package/src/components/custom/Workout/FatigueMeter.test.tsx +73 -0
  72. package/src/components/custom/Workout/FatigueMeter.tsx +82 -0
  73. package/src/components/custom/Workout/InputBar.stories.tsx +1 -1
  74. package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
  75. package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +153 -0
  76. package/src/components/custom/Workout/LiveAuraFrame.test.tsx +74 -0
  77. package/src/components/custom/Workout/LiveAuraFrame.tsx +105 -0
  78. package/src/components/custom/Workout/MesoCard.stories.tsx +1 -1
  79. package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
  80. package/src/components/custom/Workout/MesoStatusCard.stories.tsx +1 -1
  81. package/src/components/custom/Workout/MetricTiles.stories.tsx +60 -0
  82. package/src/components/custom/Workout/MetricTiles.test.tsx +58 -0
  83. package/src/components/custom/Workout/MetricTiles.tsx +48 -0
  84. package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +1 -1
  85. package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +1 -1
  86. package/src/components/custom/Workout/PrBadge.stories.tsx +1 -1
  87. package/src/components/custom/Workout/PrHistoryModal.stories.tsx +1 -1
  88. package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +4 -16
  89. package/src/components/custom/Workout/ProgramPlanningPage.tsx +8 -6
  90. package/src/components/custom/Workout/README.md +146 -4
  91. package/src/components/custom/Workout/ReadinessCheck.stories.tsx +1 -1
  92. package/src/components/custom/Workout/RestTimer.stories.tsx +1 -1
  93. package/src/components/custom/Workout/RestTimer.tsx +15 -10
  94. package/src/components/custom/Workout/RowInventory.stories.tsx +405 -0
  95. package/src/components/custom/Workout/S3FullRail.stories.tsx +34 -0
  96. package/src/components/custom/Workout/S3SessionPace.stories.tsx +95 -0
  97. package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +61 -0
  98. package/src/components/custom/Workout/S3SetTypes.stories.tsx +176 -0
  99. package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +240 -0
  100. package/src/components/custom/Workout/ScheduleTiles.stories.tsx +40 -0
  101. package/src/components/custom/Workout/ScheduleTiles.test.tsx +34 -0
  102. package/src/components/custom/Workout/ScheduleTiles.tsx +70 -0
  103. package/src/components/custom/Workout/SegmentedBar.stories.tsx +99 -0
  104. package/src/components/custom/Workout/SegmentedBar.test.tsx +77 -0
  105. package/src/components/custom/Workout/SegmentedBar.tsx +172 -0
  106. package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +50 -0
  107. package/src/components/custom/Workout/SegmentedProgressBar.test.tsx +80 -0
  108. package/src/components/custom/Workout/SegmentedProgressBar.tsx +67 -0
  109. package/src/components/custom/Workout/SessionHeader.stories.tsx +121 -0
  110. package/src/components/custom/Workout/SessionHeader.test.tsx +122 -0
  111. package/src/components/custom/Workout/SessionHeader.tsx +152 -0
  112. package/src/components/custom/Workout/SessionRail.stories.tsx +153 -0
  113. package/src/components/custom/Workout/SessionRail.test.tsx +86 -0
  114. package/src/components/custom/Workout/SessionRail.tsx +126 -0
  115. package/src/components/custom/Workout/SetBar.stories.tsx +124 -0
  116. package/src/components/custom/Workout/SetBar.test.tsx +123 -0
  117. package/src/components/custom/Workout/SetBar.tsx +171 -0
  118. package/src/components/custom/Workout/SetRow.stories.tsx +50 -100
  119. package/src/components/custom/Workout/SetRow.test.tsx +94 -166
  120. package/src/components/custom/Workout/SetRow.tsx +169 -232
  121. package/src/components/custom/Workout/SetStrip.stories.tsx +154 -0
  122. package/src/components/custom/Workout/SetStrip.test.tsx +109 -0
  123. package/src/components/custom/Workout/SetStrip.tsx +71 -0
  124. package/src/components/custom/Workout/SetTableHeader.stories.tsx +66 -0
  125. package/src/components/custom/Workout/SetTableHeader.test.tsx +47 -0
  126. package/src/components/custom/Workout/SetTableHeader.tsx +88 -0
  127. package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +47 -0
  128. package/src/components/custom/Workout/SetsRepsLoad.test.tsx +44 -0
  129. package/src/components/custom/Workout/SetsRepsLoad.tsx +52 -0
  130. package/src/components/custom/Workout/Sparkline.stories.tsx +1 -1
  131. package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
  132. package/src/components/custom/Workout/StatusPill.stories.tsx +59 -0
  133. package/src/components/custom/Workout/StatusPill.test.tsx +63 -0
  134. package/src/components/custom/Workout/StatusPill.tsx +88 -0
  135. package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +1 -1
  136. package/src/components/custom/Workout/SupersetWrapper.stories.tsx +1 -1
  137. package/src/components/custom/Workout/TempoBar.stories.tsx +1 -1
  138. package/src/components/custom/Workout/TempoDisplay.stories.tsx +12 -1
  139. package/src/components/custom/Workout/TempoDisplay.tsx +23 -33
  140. package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +1 -1
  141. package/src/components/custom/Workout/VelocityStrip.modalities.stories.tsx +254 -0
  142. package/src/components/custom/Workout/VelocityStrip.responsive.stories.tsx +145 -0
  143. package/src/components/custom/Workout/VelocityStrip.stories.tsx +129 -66
  144. package/src/components/custom/Workout/VelocityStrip.test.tsx +376 -141
  145. package/src/components/custom/Workout/VelocityStrip.tsx +508 -120
  146. package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +73 -0
  147. package/src/components/custom/Workout/VolumeLandmarkBar.test.tsx +119 -0
  148. package/src/components/custom/Workout/VolumeLandmarkBar.tsx +154 -0
  149. package/src/components/custom/Workout/WeekRow.stories.tsx +1 -1
  150. package/src/components/custom/Workout/WeightBadge.stories.tsx +1 -1
  151. package/src/components/custom/Workout/WorkoutCard.stories.tsx +2 -6
  152. package/src/components/custom/Workout/WorkoutCard.test.tsx +13 -46
  153. package/src/components/custom/Workout/WorkoutPill.stories.tsx +1 -1
  154. package/src/components/custom/Workout/ZoneTrack.stories.tsx +140 -0
  155. package/src/components/custom/Workout/ZoneTrack.test.tsx +200 -0
  156. package/src/components/custom/Workout/ZoneTrack.tsx +315 -0
  157. package/src/components/custom/Workout/index.ts +55 -2
  158. package/src/components/custom/Workout/metricText.stories.tsx +69 -0
  159. package/src/components/custom/Workout/metricText.tsx +34 -0
  160. package/src/components/custom/Workout/paceTone.test.ts +29 -0
  161. package/src/components/custom/Workout/paceTone.ts +27 -0
  162. package/src/components/custom/Workout/setHeadingKit.tsx +177 -0
  163. package/src/components/custom/index.ts +1 -0
  164. package/src/components/custom/stepper/Stepper.stories.tsx +1 -1
  165. package/src/components/icons/icons.stories.tsx +67 -2
  166. package/src/components/icons/icons.test.tsx +32 -2
  167. package/src/components/icons/icons.tsx +108 -0
  168. package/src/components/shell/BrandLockup.stories.tsx +3 -3
  169. package/src/components/shell/DashboardShell.stories.tsx +65 -0
  170. package/src/components/shell/DashboardShell.test.tsx +42 -0
  171. package/src/components/shell/DashboardShell.tsx +74 -0
  172. package/src/components/shell/DeviceIndicator.stories.tsx +2 -2
  173. package/src/components/shell/DeviceMenu.stories.tsx +5 -5
  174. package/src/components/shell/DeviceRow.stories.tsx +3 -3
  175. package/src/components/shell/NavItem.stories.tsx +52 -0
  176. package/src/components/shell/NavItem.test.tsx +35 -0
  177. package/src/components/shell/NavItem.tsx +75 -0
  178. package/src/components/shell/README.md +30 -0
  179. package/src/components/shell/SessionRailSpecimen.stories.tsx +753 -0
  180. package/src/components/shell/SessionStatePill.stories.tsx +3 -3
  181. package/src/components/shell/SessionStatePill.tsx +1 -1
  182. package/src/components/shell/SideNav.stories.tsx +81 -0
  183. package/src/components/shell/SideNav.test.tsx +51 -0
  184. package/src/components/shell/SideNav.tsx +74 -0
  185. package/src/components/shell/TopBar.stories.tsx +5 -5
  186. package/src/components/shell/index.ts +5 -0
  187. package/src/components/ui/alert/Alert.stories.tsx +1 -1
  188. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +1 -1
  189. package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
  190. package/src/components/ui/badge/Badge.stories.tsx +1 -1
  191. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  192. package/src/components/ui/button/Button.stories.tsx +1 -1
  193. package/src/components/ui/card/Card.stories.tsx +1 -1
  194. package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
  195. package/src/components/ui/chip/Chip.stories.tsx +1 -1
  196. package/src/components/ui/collapse/Collapse.stories.tsx +1 -1
  197. package/src/components/ui/data-row/DataRow.stories.tsx +1 -1
  198. package/src/components/ui/divider/Divider.stories.tsx +1 -1
  199. package/src/components/ui/drawer/Drawer.stories.tsx +1 -1
  200. package/src/components/ui/form-field/FormField.stories.tsx +1 -1
  201. package/src/components/ui/help-tip/HelpTip.stories.tsx +1 -1
  202. package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
  203. package/src/components/ui/index.ts +1 -0
  204. package/src/components/ui/indicator/Indicator.stories.tsx +2 -2
  205. package/src/components/ui/indicator/Indicator.test.tsx +1 -0
  206. package/src/components/ui/indicator/Indicator.tsx +6 -1
  207. package/src/components/ui/input/Input.stories.tsx +1 -1
  208. package/src/components/ui/link/Link.stories.tsx +1 -1
  209. package/src/components/ui/list-item/ListItem.stories.tsx +1 -1
  210. package/src/components/ui/menu/Menu.stories.tsx +1 -1
  211. package/src/components/ui/modal/Modal.stories.tsx +1 -1
  212. package/src/components/ui/pill/Pill.stories.tsx +1 -1
  213. package/src/components/ui/popover/Popover.stories.tsx +1 -1
  214. package/src/components/ui/progress/Progress.stories.tsx +1 -1
  215. package/src/components/ui/radio/Radio.stories.tsx +1 -1
  216. package/src/components/ui/section/Section.stories.tsx +1 -1
  217. package/src/components/ui/select/Select.stories.tsx +1 -1
  218. package/src/components/ui/skeleton/Skeleton.stories.tsx +1 -1
  219. package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
  220. package/src/components/ui/stack/Stack.stories.tsx +1 -1
  221. package/src/components/ui/surface/Surface.stories.tsx +1 -1
  222. package/src/components/ui/switch/Switch.stories.tsx +1 -1
  223. package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
  224. package/src/components/ui/tile/Tile.stories.tsx +79 -0
  225. package/src/components/ui/tile/Tile.test.tsx +48 -0
  226. package/src/components/ui/tile/Tile.tsx +64 -0
  227. package/src/components/ui/tile/index.ts +2 -0
  228. package/src/components/ui/toast/Toast.stories.tsx +1 -1
  229. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +1 -1
  230. package/src/components/ui/tooltip/Tooltip.stories.tsx +1 -1
  231. package/src/examples/react-hook-form/ReactHookForm.stories.tsx +1 -1
  232. package/src/hooks/index.ts +2 -0
  233. package/src/hooks/useTimer.test.ts +150 -0
  234. package/src/hooks/useTimer.ts +77 -0
  235. package/src/index.ts +3 -0
  236. package/src/pages.ts +1 -1
  237. package/src/stories/PresetShowcase.stories.tsx +1 -1
  238. package/src/stories/ThemePresets.stories.tsx +1 -1
  239. package/src/theme/ColorSystem.stories.tsx +1 -1
  240. package/src/theme/Colors.stories.tsx +5 -8
  241. package/src/theme/config.ts +4 -0
  242. package/src/theme/elevation.stories.tsx +1 -1
  243. package/src/theme/global.css +5 -1
  244. package/src/theme/shadows.stories.tsx +1 -1
  245. package/src/theme/tokens/primitives.ts +11 -107
  246. package/src/theme/tokens/semantic.ts +8 -0
  247. package/src/theme/workout-tokens.ts +11 -7
  248. package/tailwind.config.js +5 -0
package/dist/index.js CHANGED
@@ -82,6 +82,72 @@ function DumbbellIcon(props) {
82
82
  function StarIcon(props) {
83
83
  return /* @__PURE__ */ jsx(SvgIcon, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z" }) });
84
84
  }
85
+ function ActivityIcon(props) {
86
+ return /* @__PURE__ */ jsx(SvgIcon, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M22 12h-4l-3 9L9 3l-3 9H2" }) });
87
+ }
88
+ function HistoryIcon(props) {
89
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
90
+ /* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 1 0 3-6.7L3 8" }),
91
+ /* @__PURE__ */ jsx("path", { d: "M3 3v5h5" }),
92
+ /* @__PURE__ */ jsx("path", { d: "M12 7v5l4 2" })
93
+ ] });
94
+ }
95
+ function LayersIcon(props) {
96
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
97
+ /* @__PURE__ */ jsx("path", { d: "m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z" }),
98
+ /* @__PURE__ */ jsx("path", { d: "m6.08 9.5-3.49 1.59a1 1 0 0 0 0 1.81l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9a1 1 0 0 0 0-1.83l-3.5-1.59" }),
99
+ /* @__PURE__ */ jsx("path", { d: "m6.08 14.5-3.49 1.59a1 1 0 0 0 0 1.81l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9a1 1 0 0 0 0-1.83l-3.5-1.59" })
100
+ ] });
101
+ }
102
+ function PersonStandingIcon(props) {
103
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
104
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "1" }),
105
+ /* @__PURE__ */ jsx("path", { d: "m9 20 3-6 3 6" }),
106
+ /* @__PURE__ */ jsx("path", { d: "m6 8 6 2 6-2" }),
107
+ /* @__PURE__ */ jsx("path", { d: "M12 10v4" })
108
+ ] });
109
+ }
110
+ function ScaleIcon(props) {
111
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
112
+ /* @__PURE__ */ jsx("path", { d: "m16 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z" }),
113
+ /* @__PURE__ */ jsx("path", { d: "m2 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z" }),
114
+ /* @__PURE__ */ jsx("path", { d: "M7 21h10" }),
115
+ /* @__PURE__ */ jsx("path", { d: "M12 3v18" }),
116
+ /* @__PURE__ */ jsx("path", { d: "M3 7h2c2 0 5-1 7-2 2 1 5 2 7 2h2" })
117
+ ] });
118
+ }
119
+ function AlertTriangleIcon(props) {
120
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
121
+ /* @__PURE__ */ jsx("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" }),
122
+ /* @__PURE__ */ jsx("path", { d: "M12 9v4" }),
123
+ /* @__PURE__ */ jsx("path", { d: "M12 17h.01" })
124
+ ] });
125
+ }
126
+ function TrendingDownIcon(props) {
127
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
128
+ /* @__PURE__ */ jsx("path", { d: "M16 17h6v-6" }),
129
+ /* @__PURE__ */ jsx("path", { d: "m22 17-8.5-8.5-5 5L2 7" })
130
+ ] });
131
+ }
132
+ function CircleSlashIcon(props) {
133
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
134
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
135
+ /* @__PURE__ */ jsx("path", { d: "m9 15 6-6" })
136
+ ] });
137
+ }
138
+ function AwardIcon(props) {
139
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
140
+ /* @__PURE__ */ jsx("path", { d: "m15.477 12.89 1.515 8.526a.5.5 0 0 1-.81.47l-3.58-2.687a1 1 0 0 0-1.197 0l-3.586 2.686a.5.5 0 0 1-.81-.469l1.514-8.526" }),
141
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "8", r: "6" })
142
+ ] });
143
+ }
144
+ function InfoIcon(props) {
145
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
146
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
147
+ /* @__PURE__ */ jsx("path", { d: "M12 16v-4" }),
148
+ /* @__PURE__ */ jsx("path", { d: "M12 8h.01" })
149
+ ] });
150
+ }
85
151
  function cn(...inputs) {
86
152
  return tailwindMerge.twMerge(clsx.clsx(inputs));
87
153
  }
@@ -180,7 +246,7 @@ function AlertDescription({ children, className }) {
180
246
 
181
247
  // src/theme/tokens/primitives.ts
182
248
  var primitiveColors = {
183
- // Blue scale (primary brand)
249
+ // Blue scale
184
250
  blue: {
185
251
  50: "#EFF6FF",
186
252
  100: "#DBEAFE",
@@ -189,124 +255,36 @@ var primitiveColors = {
189
255
  400: "#60A5FA",
190
256
  500: "#3B82F6",
191
257
  600: "#5048E5",
192
- // Primary main
193
258
  700: "#3832A0",
194
- // Primary dark
195
259
  800: "#1E40AF",
196
260
  900: "#1E3A8A"
197
261
  },
198
- // Green scale (secondary brand)
199
- green: {
200
- 50: "#ECFDF5",
201
- 100: "#D1FAE5",
202
- 200: "#A7F3D0",
203
- 300: "#6EE7B7",
204
- 400: "#3FC79A",
205
- // Secondary light
206
- 500: "#10B981",
207
- // Secondary main
208
- 600: "#059669",
209
- 700: "#0B815A",
210
- // Secondary dark
211
- 800: "#065F46",
212
- 900: "#064E3B"
213
- },
214
- // Teal scale (success)
215
- teal: {
216
- 50: "#F0FDFA",
217
- 100: "#CCFBF1",
218
- 200: "#99F6E4",
219
- 300: "#5EEAD4",
220
- 400: "#43C6B7",
221
- // Success light
222
- 500: "#14B8A6",
223
- // Success main
224
- 600: "#0D9488",
225
- 700: "#0E8074",
226
- // Success dark
227
- 800: "#115E59",
228
- 900: "#134E4A"
229
- },
230
- // Red scale (error)
262
+ // Red scale
231
263
  red: {
232
264
  50: "#FEF2F2",
233
265
  100: "#FEE2E2",
234
266
  200: "#FECACA",
235
267
  300: "#FCA5A5",
236
268
  400: "#DA6868",
237
- // Error light
238
269
  500: "#EF4444",
239
270
  600: "#D14343",
240
- // Error main
241
271
  700: "#922E2E",
242
- // Error dark
243
272
  800: "#991B1B",
244
273
  900: "#7F1D1D"
245
274
  },
246
- // Vivid green scale (live/go success)
247
- greenVivid: {
248
- 50: "#E9FBF0",
249
- 100: "#C8F7DA",
250
- 200: "#9FF0BF",
251
- 300: "#6BE79D",
252
- 400: "#47DE84",
253
- // Success vivid light
254
- 500: "#2ED573",
255
- // Success vivid main
256
- 600: "#22B85F",
257
- 700: "#1A9950",
258
- // Success vivid dark
259
- 800: "#157A40",
260
- 900: "#105C30"
261
- },
262
- // Vivid red scale (alert)
275
+ // Vivid red scale
263
276
  redVivid: {
264
277
  50: "#FFECEE",
265
278
  100: "#FFD6DB",
266
279
  200: "#FFB3BC",
267
280
  300: "#FF8593",
268
281
  400: "#FF6070",
269
- // Error vivid light
270
282
  500: "#FF4757",
271
- // Error vivid main
272
283
  600: "#E63548",
273
284
  700: "#C42539",
274
- // Error vivid dark
275
285
  800: "#9E1C2C",
276
286
  900: "#7A1520"
277
287
  },
278
- // Amber scale (warning)
279
- amber: {
280
- 50: "#FFFBEB",
281
- 100: "#FEF3C7",
282
- 200: "#FDE68A",
283
- 300: "#FCD34D",
284
- 400: "#FFBF4C",
285
- // Warning light
286
- 500: "#FFB020",
287
- // Warning main
288
- 600: "#B27B16",
289
- // Warning dark
290
- 700: "#B45309",
291
- 800: "#92400E",
292
- 900: "#78350F"
293
- },
294
- // Sky/Blue scale (info)
295
- sky: {
296
- 50: "#F0F9FF",
297
- 100: "#E0F2FE",
298
- 200: "#BAE6FD",
299
- 300: "#7DD3FC",
300
- 400: "#64B6F7",
301
- // Info light
302
- 500: "#2196F3",
303
- // Info main
304
- 600: "#0284C7",
305
- 700: "#0B79D0",
306
- // Info dark
307
- 800: "#075985",
308
- 900: "#0C4A6E"
309
- },
310
288
  // Neutral/Gray scale
311
289
  neutral: {
312
290
  50: "#FAFAFA",
@@ -321,18 +299,6 @@ var primitiveColors = {
321
299
  900: "#111827",
322
300
  950: "#030712"
323
301
  },
324
- // Orange scale (accent/brand color)
325
- accent: {
326
- 100: "#FFA860",
327
- 200: "#FF9630",
328
- 300: "#FF8500",
329
- 400: "#FF7900",
330
- 500: "#F56C00",
331
- 600: "#E06D10",
332
- 700: "#D0620C",
333
- 800: "#B75500",
334
- 900: "#A34900"
335
- },
336
302
  // Charcoal scale (dark backgrounds)
337
303
  charcoal: {
338
304
  0: "#6E6E6E",
@@ -347,18 +313,6 @@ var primitiveColors = {
347
313
  800: "#131313",
348
314
  900: "#101010"
349
315
  },
350
- // Steel scale (cool accent)
351
- steel: {
352
- 100: "#8AA4B8",
353
- 200: "#7894A8",
354
- 300: "#678498",
355
- 400: "#557488",
356
- 500: "#406D87",
357
- 600: "#39617A",
358
- 700: "#32556D",
359
- 800: "#243D53",
360
- 900: "#1D3146"
361
- },
362
316
  // Pure colors
363
317
  white: "#FFFFFF",
364
318
  black: "#000000",
@@ -749,6 +703,9 @@ var semanticColorsLight = {
749
703
  "status-success-light": primitiveRamps.green[200],
750
704
  "status-success-dark": primitiveRamps.green[600],
751
705
  "status-success-subtle": primitiveRamps.green[50],
706
+ // Live-session accent — its OWN role, decoupled from success so the two can diverge
707
+ "status-live": primitiveRamps.green[300],
708
+ "status-live-muted": primitiveRamps.green[500],
752
709
  "status-error": primitiveRamps.red[600],
753
710
  "status-error-light": primitiveRamps.red[500],
754
711
  "status-error-dark": primitiveRamps.red[700],
@@ -882,6 +839,9 @@ var semanticColorsDark = {
882
839
  "status-success-light": primitiveRamps.green[200],
883
840
  "status-success-dark": primitiveRamps.green[600],
884
841
  "status-success-subtle": "rgba(46, 213, 115, 0.12)",
842
+ // Live-session accent — its OWN role, decoupled from success so the two can diverge
843
+ "status-live": primitiveRamps.green[300],
844
+ "status-live-muted": primitiveRamps.green[500],
885
845
  "status-error": primitiveRamps.red[600],
886
846
  "status-error-light": primitiveRamps.red[500],
887
847
  "status-error-dark": primitiveRamps.red[700],
@@ -1476,6 +1436,8 @@ var lightThemeCSSVars = {
1476
1436
  "--color-on-brand-secondary": semanticColorsLight["on-brand-secondary"],
1477
1437
  "--color-status-success": semanticColorsLight["status-success"],
1478
1438
  "--color-status-success-subtle": semanticColorsLight["status-success-subtle"],
1439
+ "--color-status-live": semanticColorsLight["status-live"],
1440
+ "--color-status-live-muted": semanticColorsLight["status-live-muted"],
1479
1441
  "--color-status-error": semanticColorsLight["status-error"],
1480
1442
  "--color-status-error-subtle": semanticColorsLight["status-error-subtle"],
1481
1443
  "--color-status-warning": semanticColorsLight["status-warning"],
@@ -1572,6 +1534,8 @@ var darkThemeCSSVars = {
1572
1534
  "--color-on-brand-secondary": semanticColorsDark["on-brand-secondary"],
1573
1535
  "--color-status-success": semanticColorsDark["status-success"],
1574
1536
  "--color-status-success-subtle": semanticColorsDark["status-success-subtle"],
1537
+ "--color-status-live": semanticColorsDark["status-live"],
1538
+ "--color-status-live-muted": semanticColorsDark["status-live-muted"],
1575
1539
  "--color-status-error": semanticColorsDark["status-error"],
1576
1540
  "--color-status-error-subtle": semanticColorsDark["status-error-subtle"],
1577
1541
  "--color-status-warning": semanticColorsDark["status-warning"],
@@ -2789,6 +2753,7 @@ var colorStyles = {
2789
2753
  default: "bg-text-tertiary",
2790
2754
  primary: "bg-brand-primary",
2791
2755
  success: "bg-status-success",
2756
+ live: "bg-status-live",
2792
2757
  error: "bg-status-error",
2793
2758
  warning: "bg-status-warning",
2794
2759
  info: "bg-status-info",
@@ -2845,7 +2810,7 @@ function Indicator({
2845
2810
  colorClass,
2846
2811
  pulseMode === "opacity" && "animate-pulse",
2847
2812
  ring && "border-2 border-background-base",
2848
- glow && !customColor && color === "success" && "shadow-[0_0_6px_rgba(46,213,115,0.6)]",
2813
+ glow && !customColor && (color === "success" || color === "live") && "shadow-[0_0_6px_rgba(46,213,115,0.6)]",
2849
2814
  glow && !customColor && color === "error" && "shadow-[0_0_6px_rgba(239,68,68,0.6)]",
2850
2815
  glow && !customColor && color === "warning" && "shadow-[0_0_6px_rgba(245,158,11,0.6)]",
2851
2816
  glow && !customColor && color === "primary" && "shadow-[0_0_6px_rgba(255,121,0,0.6)]",
@@ -5269,7 +5234,7 @@ function ToastProvider({
5269
5234
  return id;
5270
5235
  }, [defaultDuration, maxToasts]);
5271
5236
  const removeToast = React24.useCallback((id) => {
5272
- setToasts((prev) => prev.filter((t12) => t12.id !== id));
5237
+ setToasts((prev) => prev.filter((t18) => t18.id !== id));
5273
5238
  }, []);
5274
5239
  const removeAllToasts = React24.useCallback(() => {
5275
5240
  setToasts([]);
@@ -6138,6 +6103,50 @@ function Pill({
6138
6103
  }
6139
6104
  return /* @__PURE__ */ jsx(reactNative.View, { className: containerClasses, ...props, children: content });
6140
6105
  }
6106
+ function Tile({
6107
+ label,
6108
+ value,
6109
+ valueColor,
6110
+ align = "center",
6111
+ className,
6112
+ ...props
6113
+ }) {
6114
+ const isCenter = align === "center";
6115
+ return /* @__PURE__ */ jsxs(
6116
+ reactNative.View,
6117
+ {
6118
+ className: cn(
6119
+ "flex-1 rounded-md bg-surface-raised px-1.5 py-2",
6120
+ isCenter ? "items-center" : "items-start",
6121
+ className
6122
+ ),
6123
+ ...props,
6124
+ children: [
6125
+ /* @__PURE__ */ jsx(
6126
+ reactNative.Text,
6127
+ {
6128
+ className: cn(
6129
+ "text-[10px] font-bold uppercase tracking-wider text-text-tertiary",
6130
+ isCenter ? "text-center" : "text-left"
6131
+ ),
6132
+ children: label
6133
+ }
6134
+ ),
6135
+ /* @__PURE__ */ jsx(
6136
+ reactNative.Text,
6137
+ {
6138
+ className: cn(
6139
+ "mt-0.5 font-mono text-sm font-bold text-text-primary",
6140
+ isCenter ? "text-center" : "text-left"
6141
+ ),
6142
+ style: valueColor ? { color: valueColor } : void 0,
6143
+ children: value
6144
+ }
6145
+ )
6146
+ ]
6147
+ }
6148
+ );
6149
+ }
6141
6150
  var variantStyles4 = {
6142
6151
  h1: "font-heading text-5xl font-bold leading-tight",
6143
6152
  h2: "font-heading text-4xl font-bold leading-tight",
@@ -6997,6 +7006,79 @@ function formatDateTime(value, format = "datetime", isUTC = false, fallback = "-
6997
7006
  }
6998
7007
  return formatDate(value, format, isUTC);
6999
7008
  }
7009
+ function formatDuration(ms) {
7010
+ const totalSeconds = Math.max(0, Math.ceil(ms / 1e3));
7011
+ const minutes = Math.floor(totalSeconds / 60);
7012
+ const seconds = totalSeconds % 60;
7013
+ return `${minutes}:${String(seconds).padStart(2, "0")}`;
7014
+ }
7015
+ function useTimer(options = {}) {
7016
+ const {
7017
+ mode = "up",
7018
+ durationMs,
7019
+ elapsedMs: controlledElapsedMs,
7020
+ running = false,
7021
+ autoTick = false,
7022
+ tickMs = 1e3
7023
+ } = options;
7024
+ const isControlled = controlledElapsedMs != null;
7025
+ const [internalElapsedMs, setInternalElapsedMs] = React24.useState(0);
7026
+ React24.useEffect(() => {
7027
+ if (isControlled || !autoTick || !running) return;
7028
+ const id = setInterval(() => {
7029
+ setInternalElapsedMs((prev) => prev + tickMs);
7030
+ }, tickMs);
7031
+ return () => clearInterval(id);
7032
+ }, [isControlled, autoTick, running, tickMs]);
7033
+ const elapsedMs = isControlled ? controlledElapsedMs : internalElapsedMs;
7034
+ const hasDuration = durationMs != null && durationMs > 0;
7035
+ const remainingMs = durationMs != null ? Math.max(0, durationMs - elapsedMs) : 0;
7036
+ let progress;
7037
+ if (hasDuration) {
7038
+ progress = Math.min(1, Math.max(0, elapsedMs / durationMs));
7039
+ } else {
7040
+ progress = mode === "down" ? 1 : 0;
7041
+ }
7042
+ const label = formatDuration(mode === "down" ? remainingMs : elapsedMs);
7043
+ const done = durationMs != null && elapsedMs >= durationMs;
7044
+ return { elapsedMs, remainingMs, progress, label, done };
7045
+ }
7046
+
7047
+ // src/components/custom/TimerReadout/TimerReadout.tsx
7048
+ var semantic = getSemanticColors("dark");
7049
+ var LIVE_GREEN = primitiveRamps.green[500];
7050
+ var SECONDARY = semantic["text-secondary"];
7051
+ var TERTIARY = semantic["text-tertiary"];
7052
+ var DEFAULT_GLYPH = "\u23F1";
7053
+ function TimerReadout({
7054
+ mode = "up",
7055
+ elapsedMs,
7056
+ durationMs,
7057
+ running = false,
7058
+ autoTick = false,
7059
+ showTotal = false,
7060
+ glyph = DEFAULT_GLYPH,
7061
+ className,
7062
+ ...rest
7063
+ }) {
7064
+ const { label } = useTimer({ mode, durationMs, elapsedMs, running, autoTick });
7065
+ const currentColor = running ? LIVE_GREEN : SECONDARY;
7066
+ const total = showTotal && durationMs != null ? formatDuration(durationMs) : null;
7067
+ return /* @__PURE__ */ jsx(
7068
+ reactNative.View,
7069
+ {
7070
+ accessibilityRole: "timer",
7071
+ accessibilityLabel: total != null ? `${label} of ${total}` : label,
7072
+ className: cn("flex-row items-baseline justify-end", className),
7073
+ ...rest,
7074
+ children: /* @__PURE__ */ jsxs(Typography, { variant: "mono", style: { fontSize: 11, textAlign: "right" }, children: [
7075
+ /* @__PURE__ */ jsx(reactNative.Text, { testID: "timer-readout-glyph", style: { color: currentColor, marginRight: 3 }, children: glyph }),
7076
+ /* @__PURE__ */ jsx(reactNative.Text, { testID: "timer-readout-current", style: { color: currentColor }, children: label }),
7077
+ total != null ? /* @__PURE__ */ jsx(reactNative.Text, { testID: "timer-readout-total", style: { color: TERTIARY }, children: ` / ${total}` }) : null
7078
+ ] })
7079
+ }
7080
+ );
7081
+ }
7000
7082
  var sizeConfig2 = {
7001
7083
  sm: { value: "text-lg font-bold", label: "text-xs", unit: "text-xs" },
7002
7084
  md: { value: "text-2xl font-bold", label: "text-xs", unit: "text-sm" },
@@ -7426,15 +7508,22 @@ var WORKOUT_TOKENS = {
7426
7508
  // Canonical 4-band performance scale — the single source for BOTH the
7427
7509
  // VelocityStrip zone bars and the SetRow RPE color (TD-03.43). The direction
7428
7510
  // is intentionally inverted between the two consumers: for velocity, green =
7429
- // fastest/best; for RPE, green = easiest. This is the [0,1,2,4] subsample of
7430
- // the canonical `sequentialEffort` primitive. The `orange` key is the band
7431
- // label. Consumed inline (RN).
7511
+ // fastest/best; for RPE, green = easiest. This is the [0,2,3,4] subsample of
7512
+ // the canonical `sequentialEffort` primitive: an EVEN hue walk across the ramp
7513
+ // (green gold → orange → red). The earlier [0,1,2,4] spent two of its four
7514
+ // bands on adjacent yellows (amber-200 + amber-300) and skipped orange, reading
7515
+ // yellow-heavy with an abrupt jump to red; sampling amber-300 + orange-400
7516
+ // instead gives four cleanly separable bands that hold at 3px strip scale.
7517
+ // Consumed inline (RN).
7432
7518
  scale: {
7433
7519
  green: sequentialEffort[0],
7434
- yellow: sequentialEffort[1],
7435
- orange: sequentialEffort[2],
7436
- // gold band
7520
+ // green-300
7521
+ yellow: sequentialEffort[2],
7522
+ // amber-300 (gold)
7523
+ orange: sequentialEffort[3],
7524
+ // orange-400 (true orange)
7437
7525
  red: sequentialEffort[4]
7526
+ // red-600
7438
7527
  },
7439
7528
  // BodyMap volume heatmap — the canonical `divergingScale` (under → optimal →
7440
7529
  // over): a true diverging shape with a light green center, cool-blue under-
@@ -7559,8 +7648,8 @@ function roundTempo(tempo) {
7559
7648
  return tempo.map((v) => Math.round(v * 10) / 10);
7560
7649
  }
7561
7650
  function formatSignedPct(ratio) {
7562
- const pct = Math.round(ratio * 100);
7563
- return `${pct > 0 ? "+" : ""}${pct}%`;
7651
+ const pct2 = Math.round(ratio * 100);
7652
+ return `${pct2 > 0 ? "+" : ""}${pct2}%`;
7564
7653
  }
7565
7654
  function formatPrescription(p) {
7566
7655
  if (p == null) return null;
@@ -7706,7 +7795,7 @@ function ActiveSegment({
7706
7795
  const pacing = getTempoPacingState(phaseElapsedMs, targetMs);
7707
7796
  const barColor = pacing === "behind" ? t2["status-error"] : config.color;
7708
7797
  const fillPct = getTempoFillPct(phaseElapsedMs, targetMs);
7709
- const label = targetMs ? `${formatDuration(phaseElapsedMs)} / ${formatDuration(targetMs)}` : formatDuration(phaseElapsedMs);
7798
+ const label = targetMs ? `${formatDuration2(phaseElapsedMs)} / ${formatDuration2(targetMs)}` : formatDuration2(phaseElapsedMs);
7710
7799
  return /* @__PURE__ */ jsxs(reactNative.View, { style: { height: "100%", position: "relative" }, testID: `tempo-segment-active-${config.label}`, children: [
7711
7800
  /* @__PURE__ */ jsx(
7712
7801
  reactNative.View,
@@ -7756,16 +7845,32 @@ function CompletedSegment({
7756
7845
  },
7757
7846
  testID: `tempo-segment-completed-${config.label}`,
7758
7847
  children: /* @__PURE__ */ jsxs(reactNative.Text, { style: { fontSize: 12, fontWeight: "500", color: alpha(config.color, 0.7) }, children: [
7759
- formatDuration(completedMs),
7848
+ formatDuration2(completedMs),
7760
7849
  indicator
7761
7850
  ] })
7762
7851
  }
7763
7852
  );
7764
7853
  }
7765
- function formatDuration(ms) {
7854
+ function formatDuration2(ms) {
7766
7855
  const s = ms / 1e3;
7767
7856
  return s < 10 ? s.toFixed(1) : Math.round(s).toString();
7768
7857
  }
7858
+ var METRIC_FONT = "Inter, sans-serif";
7859
+ function MetricCell({ children, color, fontSize = 11 }) {
7860
+ return /* @__PURE__ */ jsx(
7861
+ reactNative.Text,
7862
+ {
7863
+ style: {
7864
+ fontFamily: METRIC_FONT,
7865
+ fontSize,
7866
+ color,
7867
+ fontWeight: "600",
7868
+ letterSpacing: 1
7869
+ },
7870
+ children
7871
+ }
7872
+ );
7873
+ }
7769
7874
 
7770
7875
  // src/components/custom/Workout/TempoDisplay.tsx
7771
7876
  var t3 = getSemanticColors("dark");
@@ -7784,34 +7889,10 @@ function TempoValue({
7784
7889
  color,
7785
7890
  fontSize
7786
7891
  }) {
7787
- return /* @__PURE__ */ jsx(
7788
- reactNative.Text,
7789
- {
7790
- style: {
7791
- fontFamily: INTER,
7792
- fontSize,
7793
- color,
7794
- fontWeight: "600",
7795
- letterSpacing: 1
7796
- },
7797
- children: value
7798
- }
7799
- );
7892
+ return /* @__PURE__ */ jsx(MetricCell, { color, fontSize, children: value });
7800
7893
  }
7801
7894
  function TempoSeparator({ color, fontSize }) {
7802
- return /* @__PURE__ */ jsx(
7803
- reactNative.Text,
7804
- {
7805
- style: {
7806
- fontFamily: INTER,
7807
- fontSize,
7808
- color,
7809
- fontWeight: "600",
7810
- letterSpacing: 1
7811
- },
7812
- children: "-"
7813
- }
7814
- );
7895
+ return /* @__PURE__ */ jsx(MetricCell, { color, fontSize, children: "-" });
7815
7896
  }
7816
7897
  var LIVE_PHASES = [
7817
7898
  { key: "eccentric", color: phaseColors.eccentric },
@@ -7873,6 +7954,7 @@ function TempoDisplay({
7873
7954
  tempo,
7874
7955
  size = "md",
7875
7956
  colored = true,
7957
+ showLabel = true,
7876
7958
  showInfo = true,
7877
7959
  live,
7878
7960
  onPress,
@@ -7906,7 +7988,7 @@ function TempoDisplay({
7906
7988
  },
7907
7989
  ...props,
7908
7990
  children: [
7909
- /* @__PURE__ */ jsx(
7991
+ showLabel && /* @__PURE__ */ jsx(
7910
7992
  reactNative.Text,
7911
7993
  {
7912
7994
  style: {
@@ -8134,12 +8216,12 @@ var ZONE = {
8134
8216
  // severe over-target
8135
8217
  };
8136
8218
  var BULGE_SIZE = { 1: 8, 2: 10, 3: 11 };
8137
- function zoneForPct(pct) {
8138
- if (pct >= 125) return { color: ZONE.over3, over: 3 };
8139
- if (pct >= 115) return { color: ZONE.over2, over: 2 };
8140
- if (pct > 100) return { color: ZONE.over1, over: 1 };
8141
- if (pct === 100) return { color: ZONE.target, over: 0 };
8142
- if (pct >= 75) return { color: ZONE.approaching, over: 0 };
8219
+ function zoneForPct(pct2) {
8220
+ if (pct2 >= 125) return { color: ZONE.over3, over: 3 };
8221
+ if (pct2 >= 115) return { color: ZONE.over2, over: 2 };
8222
+ if (pct2 > 100) return { color: ZONE.over1, over: 1 };
8223
+ if (pct2 === 100) return { color: ZONE.target, over: 0 };
8224
+ if (pct2 >= 75) return { color: ZONE.approaching, over: 0 };
8143
8225
  return { color: ZONE.building, over: 0 };
8144
8226
  }
8145
8227
  function clampFill(value) {
@@ -8147,9 +8229,9 @@ function clampFill(value) {
8147
8229
  if (value > 1) return 1;
8148
8230
  return value;
8149
8231
  }
8150
- function isAtTarget(pct, threshold) {
8232
+ function isAtTarget(pct2, threshold) {
8151
8233
  if (threshold == null) return false;
8152
- return Math.abs(pct - Math.round(threshold * 100)) <= 5;
8234
+ return Math.abs(pct2 - Math.round(threshold * 100)) <= 5;
8153
8235
  }
8154
8236
  function IntensityBar({
8155
8237
  level,
@@ -8163,9 +8245,9 @@ function IntensityBar({
8163
8245
  }) {
8164
8246
  const isVertical = orientation === "vertical";
8165
8247
  const fillLevel = clampFill(level);
8166
- const pct = Math.round(Math.max(0, level) * 100);
8167
- const zone = zoneForPct(pct);
8168
- const atTarget = isAtTarget(pct, threshold);
8248
+ const pct2 = Math.round(Math.max(0, level) * 100);
8249
+ const zone = zoneForPct(pct2);
8250
+ const atTarget = isAtTarget(pct2, threshold);
8169
8251
  const [fillAnim] = React24.useState(() => new reactNative.Animated.Value(fillLevel));
8170
8252
  React24.useEffect(() => {
8171
8253
  const animation = reactNative.Animated.timing(fillAnim, {
@@ -8216,8 +8298,8 @@ function IntensityBar({
8216
8298
  className,
8217
8299
  style: { alignItems: "center" },
8218
8300
  accessibilityRole: "progressbar",
8219
- accessibilityValue: { min: 0, max: 100, now: pct },
8220
- accessibilityLabel: `Intensity level: ${pct}%`,
8301
+ accessibilityValue: { min: 0, max: 100, now: pct2 },
8302
+ accessibilityLabel: `Intensity level: ${pct2}%`,
8221
8303
  testID: "intensity-bar",
8222
8304
  ...props,
8223
8305
  children: [
@@ -8263,7 +8345,7 @@ function IntensityBar({
8263
8345
  accessibilityElementsHidden: true,
8264
8346
  testID: "intensity-label",
8265
8347
  children: [
8266
- pct,
8348
+ pct2,
8267
8349
  "%"
8268
8350
  ]
8269
8351
  }
@@ -8448,13 +8530,257 @@ function WorkoutPill({
8448
8530
  }
8449
8531
  return pill;
8450
8532
  }
8533
+ var PULSE_HALF_MS = 950;
8534
+ var PULSE_MIN_OPACITY = 0.45;
8535
+ var SEGMENTED_BAR_GAP = 5;
8536
+ function usePulse2(active) {
8537
+ const [value] = React24.useState(() => new reactNative.Animated.Value(0));
8538
+ React24.useEffect(() => {
8539
+ if (!active) return;
8540
+ const loop = reactNative.Animated.loop(
8541
+ reactNative.Animated.sequence([
8542
+ reactNative.Animated.timing(value, {
8543
+ toValue: 1,
8544
+ duration: PULSE_HALF_MS,
8545
+ easing: reactNative.Easing.inOut(reactNative.Easing.ease),
8546
+ useNativeDriver: false
8547
+ }),
8548
+ reactNative.Animated.timing(value, {
8549
+ toValue: 0,
8550
+ duration: PULSE_HALF_MS,
8551
+ easing: reactNative.Easing.inOut(reactNative.Easing.ease),
8552
+ useNativeDriver: false
8553
+ })
8554
+ ])
8555
+ );
8556
+ loop.start();
8557
+ return () => loop.stop();
8558
+ }, [active, value]);
8559
+ return value;
8560
+ }
8561
+ function SegmentedBar({
8562
+ segments,
8563
+ height = 8,
8564
+ gap = SEGMENTED_BAR_GAP,
8565
+ radius = 2,
8566
+ marker = null,
8567
+ segmentTestID,
8568
+ style,
8569
+ ...props
8570
+ }) {
8571
+ const pulse = usePulse2(segments.some((s) => s.pulse));
8572
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: [{ flexDirection: "row", height, gap, position: "relative" }, style], ...props, children: [
8573
+ segments.map((seg, i) => {
8574
+ const fillStyle = {
8575
+ width: `${(seg.fill ?? 1) * 100}%`,
8576
+ height: "100%",
8577
+ backgroundColor: seg.color,
8578
+ opacity: seg.opacity
8579
+ };
8580
+ const testID = segmentTestID?.(seg, i) ?? "segmented-bar-segment";
8581
+ return /* @__PURE__ */ jsx(
8582
+ reactNative.View,
8583
+ {
8584
+ style: {
8585
+ flex: seg.weight ?? 1,
8586
+ minWidth: 0,
8587
+ height: "100%",
8588
+ marginLeft: seg.leadingGap,
8589
+ borderRadius: radius,
8590
+ overflow: "hidden"
8591
+ },
8592
+ children: seg.pulse ? /* @__PURE__ */ jsx(
8593
+ reactNative.Animated.View,
8594
+ {
8595
+ style: {
8596
+ ...fillStyle,
8597
+ ...seg.pulseColor ? {
8598
+ backgroundColor: pulse.interpolate({
8599
+ inputRange: [0, 1],
8600
+ outputRange: seg.pulseColor
8601
+ })
8602
+ } : {
8603
+ opacity: pulse.interpolate({
8604
+ inputRange: [0, 1],
8605
+ outputRange: [PULSE_MIN_OPACITY, 1]
8606
+ })
8607
+ }
8608
+ },
8609
+ accessibilityElementsHidden: true,
8610
+ testID
8611
+ }
8612
+ ) : /* @__PURE__ */ jsx(reactNative.View, { style: fillStyle, accessibilityElementsHidden: true, testID })
8613
+ },
8614
+ i
8615
+ );
8616
+ }),
8617
+ marker ? /* @__PURE__ */ jsx(
8618
+ reactNative.View,
8619
+ {
8620
+ style: {
8621
+ position: "absolute",
8622
+ left: `${marker.position * 100}%`,
8623
+ top: 0,
8624
+ bottom: 0,
8625
+ width: 2,
8626
+ backgroundColor: marker.color
8627
+ },
8628
+ accessibilityElementsHidden: true,
8629
+ testID: "segmented-bar-marker"
8630
+ }
8631
+ ) : null
8632
+ ] });
8633
+ }
8634
+
8635
+ // src/components/custom/Workout/SetBar.tsx
8636
+ var SET_STRIP_ZONES = {
8637
+ slow: primitiveRamps.red[600],
8638
+ moderate: primitiveRamps.orange[400],
8639
+ fast: primitiveRamps.amber[300],
8640
+ fastest: primitiveRamps.green[300]
8641
+ };
8642
+ var TODO_COLOR = primitiveColors.charcoal[300];
8643
+ var SET_STRIP_VARIABLE_COLOR = primitiveRamps.cyan[900];
8644
+ var NOTCH_GAP = 2;
8645
+ var CLUSTER_GAP = 3;
8646
+ var MYO_UPCOMING_CLUSTERS = 3;
8647
+ var MYO_UPCOMING_CLUSTER_LEN = 5;
8648
+ var MYO_UPCOMING_FADE = 0.6;
8649
+ var PULSE_RANGE = {
8650
+ [SET_STRIP_ZONES.slow]: [primitiveRamps.red[500], primitiveRamps.red[700]],
8651
+ [SET_STRIP_ZONES.moderate]: [primitiveRamps.orange[300], primitiveRamps.orange[500]],
8652
+ [SET_STRIP_ZONES.fast]: [primitiveRamps.amber[200], primitiveRamps.amber[400]],
8653
+ [SET_STRIP_ZONES.fastest]: [primitiveRamps.green[200], primitiveRamps.green[400]]
8654
+ };
8655
+ function velocityZoneColor(v) {
8656
+ if (v < 0.5) return SET_STRIP_ZONES.slow;
8657
+ if (v < 0.75) return SET_STRIP_ZONES.moderate;
8658
+ if (v < 1) return SET_STRIP_ZONES.fast;
8659
+ return SET_STRIP_ZONES.fastest;
8660
+ }
8661
+ function chunkedSegments(chunks, gap) {
8662
+ return chunks.flatMap(
8663
+ (vels, ci) => vels.map((v, ri) => ({
8664
+ color: velocityZoneColor(v),
8665
+ ...ci > 0 && ri === 0 ? { leadingGap: gap } : {}
8666
+ }))
8667
+ );
8668
+ }
8669
+ function rangeSegments(floor, max, doneVels) {
8670
+ return Array.from({ length: max }, (_, i) => {
8671
+ if (i < doneVels.length) return { color: velocityZoneColor(doneVels[i]) };
8672
+ return { color: i < floor ? TODO_COLOR : SET_STRIP_VARIABLE_COLOR };
8673
+ });
8674
+ }
8675
+ function myoUpcomingSegments(activationLen, clusterCount) {
8676
+ const activation = Array.from({ length: activationLen }, () => ({ color: TODO_COLOR }));
8677
+ const trail = Array.from(
8678
+ { length: clusterCount },
8679
+ (_, ci) => Array.from({ length: MYO_UPCOMING_CLUSTER_LEN }, (_2, ri) => ({
8680
+ color: SET_STRIP_VARIABLE_COLOR,
8681
+ opacity: MYO_UPCOMING_FADE ** ci,
8682
+ ...ri === 0 ? { leadingGap: CLUSTER_GAP } : {}
8683
+ }))
8684
+ ).flat();
8685
+ return [...activation, ...trail];
8686
+ }
8687
+ function setSegments(set) {
8688
+ if (set.status === "todo") {
8689
+ return [{ color: TODO_COLOR }];
8690
+ }
8691
+ if (set.status === "done") {
8692
+ return set.velocities.map((v) => ({ color: velocityZoneColor(v) }));
8693
+ }
8694
+ if (set.status === "range") {
8695
+ return rangeSegments(set.floor, set.max, set.doneVels);
8696
+ }
8697
+ if (set.status === "drop") {
8698
+ return chunkedSegments(set.subloads, NOTCH_GAP);
8699
+ }
8700
+ if (set.status === "myo") {
8701
+ return chunkedSegments([set.activation, ...set.clusters], CLUSTER_GAP);
8702
+ }
8703
+ if (set.status === "myo-upcoming") {
8704
+ return myoUpcomingSegments(set.activationLen, set.clusterCount ?? MYO_UPCOMING_CLUSTERS);
8705
+ }
8706
+ const performed = set.velocities.map((v) => {
8707
+ const color = velocityZoneColor(v);
8708
+ return { color, pulse: true, pulseColor: PULSE_RANGE[color] };
8709
+ });
8710
+ const remaining = Math.max(0, set.planned - set.velocities.length);
8711
+ return [...performed, ...Array.from({ length: remaining }, () => ({ color: TODO_COLOR }))];
8712
+ }
8713
+ function setBarSegmentTestID(seg) {
8714
+ if (seg.pulse) return "set-strip-pulse";
8715
+ if (seg.color === SET_STRIP_VARIABLE_COLOR) return "set-strip-variable";
8716
+ return seg.color === TODO_COLOR ? "set-strip-empty" : "set-strip-fill";
8717
+ }
8718
+ function SetBar({ set, height = 8 }) {
8719
+ return /* @__PURE__ */ jsx(
8720
+ SegmentedBar,
8721
+ {
8722
+ segments: setSegments(set),
8723
+ height,
8724
+ gap: 0,
8725
+ radius: 0,
8726
+ segmentTestID: setBarSegmentTestID,
8727
+ style: { flex: 1, minWidth: 0, borderRadius: 2, overflow: "hidden" },
8728
+ testID: "set-strip-set"
8729
+ }
8730
+ );
8731
+ }
8732
+
8733
+ // src/components/custom/Workout/SetStrip.tsx
8734
+ var SET_STRIP_GAP = 5;
8735
+ function setCategory(set) {
8736
+ switch (set.status) {
8737
+ case "done":
8738
+ case "drop":
8739
+ case "myo":
8740
+ return "done";
8741
+ case "active":
8742
+ return "active";
8743
+ case "range":
8744
+ return set.doneVels.length > 0 ? "active" : "upcoming";
8745
+ case "todo":
8746
+ case "myo-upcoming":
8747
+ return "upcoming";
8748
+ }
8749
+ }
8750
+ function describeSets(sets) {
8751
+ const done = sets.filter((s) => setCategory(s) === "done").length;
8752
+ const active = sets.filter((s) => setCategory(s) === "active").length;
8753
+ const upcoming = sets.filter((s) => setCategory(s) === "upcoming").length;
8754
+ return `Set progress: ${done} done, ${active} in progress, ${upcoming} upcoming`;
8755
+ }
8756
+ function SetStrip({ sets, height = 8, className, ...props }) {
8757
+ return /* @__PURE__ */ jsx(
8758
+ reactNative.View,
8759
+ {
8760
+ className,
8761
+ style: { flexDirection: "row", width: "100%", height, gap: SET_STRIP_GAP },
8762
+ accessibilityRole: "image",
8763
+ accessibilityLabel: describeSets(sets),
8764
+ testID: "set-strip",
8765
+ ...props,
8766
+ children: sets.map((set, i) => /* @__PURE__ */ jsx(SetBar, { set, height }, i))
8767
+ }
8768
+ );
8769
+ }
8770
+
8771
+ // src/components/custom/Workout/VelocityStrip.tsx
8451
8772
  var VEL_COLORS = WORKOUT_TOKENS.scale;
8452
- var zoneIdToScaleToken = {
8453
- speed: "green",
8454
- power: "yellow",
8455
- strengthSpeed: "orange",
8456
- maximalStrength: "red",
8457
- grinding: "red"
8773
+ var bandIdToEffortColor = {
8774
+ speed: sequentialEffort[0],
8775
+ // green-300
8776
+ power: sequentialEffort[2],
8777
+ // amber-300 (gold)
8778
+ strengthSpeed: sequentialEffort[3],
8779
+ // orange-400
8780
+ maximalStrength: sequentialEffort[4],
8781
+ // red-600
8782
+ grinding: sequentialEffort[5]
8783
+ // red-700 — the distinct 5th band
8458
8784
  };
8459
8785
  function getVelocityZoneColor(velocity) {
8460
8786
  if (velocity >= 1) return "vel-green";
@@ -8493,14 +8819,132 @@ function classifyBand(velocity, bands) {
8493
8819
  return bands[bands.length - 1];
8494
8820
  }
8495
8821
  function bandColor(band) {
8496
- const token = zoneIdToScaleToken[band.id];
8497
- return token ? VEL_COLORS[token] : VEL_COLORS.green;
8822
+ return bandIdToEffortColor[band.id] ?? VEL_COLORS.green;
8498
8823
  }
8499
8824
  function getLossStyle(loss) {
8500
8825
  if (loss > 25) return { color: VEL_COLORS.red };
8501
8826
  if (loss > 20) return { color: VEL_COLORS.orange };
8502
8827
  return null;
8503
8828
  }
8829
+ var REP_GAP = 2;
8830
+ var WIDE_GAP = 8;
8831
+ var TODO_COLOR2 = primitiveColors.charcoal[300];
8832
+ var CONTINUE_OUTLINE = primitiveRamps.cyan[800];
8833
+ function deriveDoneVelocities(set) {
8834
+ switch (set.type) {
8835
+ case "drop":
8836
+ return set.subloads.flat();
8837
+ case "myo":
8838
+ return [...set.activation, ...set.clusters.flat()];
8839
+ default:
8840
+ return set.velocities;
8841
+ }
8842
+ }
8843
+ function chunkedRepSlots(chunks) {
8844
+ const slots = [];
8845
+ chunks.forEach((chunk) => {
8846
+ chunk.forEach((velocity, ri) => {
8847
+ const first = slots.length === 0;
8848
+ slots.push({ kind: "rep", velocity, leadingGap: first ? 0 : ri === 0 ? WIDE_GAP : REP_GAP });
8849
+ });
8850
+ });
8851
+ return slots;
8852
+ }
8853
+ function buildSlots(set) {
8854
+ switch (set.type) {
8855
+ case "straight": {
8856
+ const total = Math.max(set.velocities.length, set.planned ?? set.velocities.length);
8857
+ return Array.from({ length: total }, (_, i) => {
8858
+ const done = i < set.velocities.length;
8859
+ return {
8860
+ kind: done ? "rep" : "todo",
8861
+ velocity: done ? set.velocities[i] : void 0,
8862
+ leadingGap: i === 0 ? 0 : REP_GAP
8863
+ };
8864
+ });
8865
+ }
8866
+ case "range": {
8867
+ const total = Math.max(set.max, set.velocities.length);
8868
+ return Array.from({ length: total }, (_, i) => {
8869
+ const kind = i < set.velocities.length ? "rep" : i < set.floor ? "todo" : "variable";
8870
+ return {
8871
+ kind,
8872
+ velocity: kind === "rep" ? set.velocities[i] : void 0,
8873
+ leadingGap: i === 0 ? 0 : REP_GAP
8874
+ };
8875
+ });
8876
+ }
8877
+ case "amrap": {
8878
+ const reps = set.velocities.map((velocity, i) => ({
8879
+ kind: "rep",
8880
+ velocity,
8881
+ leadingGap: i === 0 ? 0 : REP_GAP
8882
+ }));
8883
+ reps.push({ kind: "continue", leadingGap: reps.length === 0 ? 0 : REP_GAP });
8884
+ return reps;
8885
+ }
8886
+ case "drop":
8887
+ return chunkedRepSlots(set.subloads);
8888
+ case "myo": {
8889
+ const slots = chunkedRepSlots([set.activation, ...set.clusters]);
8890
+ if (set.open) slots.push({ kind: "continue", leadingGap: slots.length === 0 ? 0 : REP_GAP });
8891
+ return slots;
8892
+ }
8893
+ case "cluster": {
8894
+ const total = Math.max(set.velocities.length, set.planned ?? set.velocities.length);
8895
+ return Array.from({ length: total }, (_, i) => {
8896
+ const done = i < set.velocities.length;
8897
+ const boundary = i > 0 && i % set.groupSize === 0;
8898
+ return {
8899
+ kind: done ? "rep" : "todo",
8900
+ velocity: done ? set.velocities[i] : void 0,
8901
+ leadingGap: i === 0 ? 0 : boundary ? WIDE_GAP : REP_GAP
8902
+ };
8903
+ });
8904
+ }
8905
+ }
8906
+ }
8907
+ function setAccessibilityLabel(set, repCount) {
8908
+ switch (set.type) {
8909
+ case "straight":
8910
+ return `Velocity strip, straight set, ${repCount} reps`;
8911
+ case "range":
8912
+ return `Velocity strip, rep-range set, floor ${set.floor} to ${set.max}, ${repCount} reps done`;
8913
+ case "amrap":
8914
+ return `Velocity strip, AMRAP set, ${repCount} reps and counting`;
8915
+ case "drop":
8916
+ return `Velocity strip, drop set, ${set.subloads.length} loads, ${repCount} reps`;
8917
+ case "myo":
8918
+ return `Velocity strip, myo-reps set, ${set.clusters.length} clusters${set.open ? ", open" : ""}, ${repCount} reps`;
8919
+ case "cluster":
8920
+ return `Velocity strip, cluster set, groups of ${set.groupSize}, ${repCount} reps`;
8921
+ }
8922
+ }
8923
+ function slotAccessibilityLabel(slot, index) {
8924
+ switch (slot.kind) {
8925
+ case "rep":
8926
+ return `Rep ${index + 1}: ${formatVelocity(slot.velocity ?? 0)} meters per second`;
8927
+ case "todo":
8928
+ return `Rep ${index + 1}: planned`;
8929
+ case "variable":
8930
+ return `Rep ${index + 1}: variable`;
8931
+ case "continue":
8932
+ return "Keep going";
8933
+ }
8934
+ }
8935
+ var EXPANDED_TODO_STUB_PCT = 16;
8936
+ var EXPANDED_ENCODED_PCT = 45;
8937
+ var EXPANDED_HEIGHT = 60;
8938
+ var FIXED_MAX_VELOCITY = 1.15;
8939
+ var BARE_STUB_HEIGHT = 3;
8940
+ function scaleDenominator(scale, maxVelocity) {
8941
+ return scale === "fixed" ? FIXED_MAX_VELOCITY : maxVelocity * 1.15;
8942
+ }
8943
+ function bareSlotHeight(slot, height, denom) {
8944
+ if (slot.kind !== "rep" || denom <= 0) return BARE_STUB_HEIGHT;
8945
+ const ratio = Math.min(1, (slot.velocity ?? 0) / denom);
8946
+ return Math.max(BARE_STUB_HEIGHT, ratio * height);
8947
+ }
8504
8948
  function getReducedMotionPreference() {
8505
8949
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
8506
8950
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
@@ -8522,31 +8966,48 @@ var ANIMATION_EASING = reactNative.Easing.bezier(0.22, 1, 0.36, 1);
8522
8966
  var POP_EASING = reactNative.Easing.bezier(0.34, 1.56, 0.64, 1);
8523
8967
  function VelocityStrip({
8524
8968
  velocities,
8969
+ set,
8525
8970
  zones,
8526
8971
  liveRepIndex,
8527
- expanded = false,
8972
+ expanded = true,
8528
8973
  onToggle,
8529
8974
  onRepPress,
8530
- variant = "full",
8975
+ variant = "expanded",
8976
+ height = EXPANDED_HEIGHT,
8977
+ scale = "peak",
8978
+ showNumbers = true,
8531
8979
  showInfo = true,
8532
8980
  className,
8533
8981
  ...props
8534
8982
  }) {
8535
- const maxVelocity = Math.max(...velocities, 0);
8536
- const meanVelocity = calculateMeanVelocity(velocities);
8537
- const loss = calculateVelocityLoss(velocities);
8983
+ const doneVelocities = set ? deriveDoneVelocities(set) : velocities ?? [];
8984
+ const slots = set ? buildSlots(set) : doneVelocities.map((v, i) => ({
8985
+ kind: "rep",
8986
+ velocity: v,
8987
+ leadingGap: i === 0 ? 0 : REP_GAP
8988
+ }));
8989
+ const maxVelocity = Math.max(...doneVelocities, 0);
8990
+ const meanVelocity = calculateMeanVelocity(doneVelocities);
8991
+ const loss = calculateVelocityLoss(doneVelocities);
8992
+ const framed = showNumbers || showInfo;
8993
+ const scaleDenom = scaleDenominator(scale, maxVelocity);
8538
8994
  const hasZones = zones != null && zones.length > 0;
8539
8995
  const barColorFor = (v) => hasZones ? bandColor(classifyBand(v, zones)) : zoneHexMap[getVelocityZoneColor(v)];
8996
+ const slotColor = (slot) => {
8997
+ if (slot.kind === "rep") return barColorFor(slot.velocity ?? 0);
8998
+ if (slot.kind === "todo") return TODO_COLOR2;
8999
+ return SET_STRIP_VARIABLE_COLOR;
9000
+ };
8540
9001
  const meanZone = hasZones ? classifyBand(meanVelocity, zones)?.label ?? "" : getVelocityZoneName(meanVelocity);
8541
9002
  const prefersReducedMotion = usePrefersReducedMotion();
8542
- const [heightAnim] = React24.useState(() => new reactNative.Animated.Value(expanded ? 60 : 3));
9003
+ const [heightAnim] = React24.useState(() => new reactNative.Animated.Value(expanded ? height : 3));
8543
9004
  const [labelOpacity] = React24.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
8544
9005
  const [infoOpacity] = React24.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
8545
9006
  const [liveScale] = React24.useState(() => new reactNative.Animated.Value(1));
8546
- const liveVelocity = liveRepIndex != null ? velocities[liveRepIndex] : void 0;
9007
+ const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
8547
9008
  const isNewPeak = liveVelocity != null && maxVelocity > 0 && liveVelocity === maxVelocity;
8548
9009
  React24.useEffect(() => {
8549
- if (variant === "mini" || liveRepIndex == null || liveVelocity == null) return;
9010
+ if (variant !== "expanded" || !framed || liveRepIndex == null || liveVelocity == null) return;
8550
9011
  if (prefersReducedMotion) {
8551
9012
  liveScale.setValue(1);
8552
9013
  return;
@@ -8576,12 +9037,12 @@ function VelocityStrip({
8576
9037
  useNativeDriver: true
8577
9038
  }).start();
8578
9039
  }
8579
- }, [variant, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
9040
+ }, [variant, framed, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
8580
9041
  React24.useEffect(() => {
8581
- if (variant === "mini") return;
9042
+ if (variant !== "expanded" || !framed) return;
8582
9043
  reactNative.Animated.parallel([
8583
9044
  reactNative.Animated.timing(heightAnim, {
8584
- toValue: expanded ? 60 : 3,
9045
+ toValue: expanded ? height : 3,
8585
9046
  duration: ANIMATION_DURATION,
8586
9047
  easing: ANIMATION_EASING,
8587
9048
  useNativeDriver: false
@@ -8599,52 +9060,133 @@ function VelocityStrip({
8599
9060
  useNativeDriver: false
8600
9061
  })
8601
9062
  ]).start();
8602
- }, [expanded, variant, heightAnim, labelOpacity, infoOpacity]);
9063
+ }, [expanded, variant, framed, height, heightAnim, labelOpacity, infoOpacity]);
9064
+ if (set == null && velocities == null) return null;
9065
+ const repCount = doneVelocities.length;
9066
+ const miniLabel = set ? setAccessibilityLabel(set, repCount) : `Velocity strip, ${repCount} reps`;
8603
9067
  if (variant === "mini") {
8604
9068
  const { style: externalStyle, ...restProps } = props;
8605
9069
  return /* @__PURE__ */ jsx(
8606
9070
  reactNative.View,
8607
9071
  {
8608
9072
  className,
8609
- style: [{ flexDirection: "row", height: 3, gap: 2, borderRadius: 2, overflow: "hidden" }, externalStyle],
9073
+ style: [
9074
+ { flexDirection: "row", height: 3, gap: REP_GAP, borderRadius: 2, overflow: "hidden" },
9075
+ externalStyle
9076
+ ],
8610
9077
  accessibilityRole: "image",
8611
- accessibilityLabel: `Velocity strip, ${velocities.length} reps`,
9078
+ accessibilityLabel: miniLabel,
8612
9079
  testID: "velocity-strip-mini",
8613
9080
  ...restProps,
8614
- children: velocities.map((v, i) => /* @__PURE__ */ jsx(
9081
+ children: slots.map((slot, i) => /* @__PURE__ */ jsx(
8615
9082
  reactNative.View,
8616
9083
  {
8617
9084
  style: {
8618
9085
  flex: 1,
8619
- backgroundColor: barColorFor(v),
9086
+ backgroundColor: slotColor(slot),
8620
9087
  borderRadius: 1,
8621
9088
  minWidth: 4,
8622
- height: "100%"
9089
+ height: "100%",
9090
+ // The container's uniform 2px gap covers rep spacing; a wide slot adds
9091
+ // only the EXTRA (WIDE_GAP − REP_GAP) so the notch totals WIDE_GAP.
9092
+ marginLeft: Math.max(0, slot.leadingGap - REP_GAP),
9093
+ ...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
9094
+ },
9095
+ accessibilityElementsHidden: true,
9096
+ testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`
9097
+ },
9098
+ i
9099
+ ))
9100
+ }
9101
+ );
9102
+ }
9103
+ if (!framed) {
9104
+ const { style: externalStyle, ...restProps } = props;
9105
+ return /* @__PURE__ */ jsx(
9106
+ reactNative.View,
9107
+ {
9108
+ className,
9109
+ style: [
9110
+ { flexDirection: "row", height, gap: REP_GAP, alignItems: "flex-end" },
9111
+ externalStyle
9112
+ ],
9113
+ accessibilityRole: "image",
9114
+ accessibilityLabel: miniLabel,
9115
+ testID: "velocity-strip-compact",
9116
+ ...restProps,
9117
+ children: slots.map((slot, i) => /* @__PURE__ */ jsx(
9118
+ reactNative.View,
9119
+ {
9120
+ style: {
9121
+ flex: 1,
9122
+ minWidth: 4,
9123
+ height: bareSlotHeight(slot, height, scaleDenom),
9124
+ // Top-rounded to match the framed chart's bars (the "rounded tops from
9125
+ // the numbers one"); square bottoms since bars sit on the baseline.
9126
+ borderTopLeftRadius: 2,
9127
+ borderTopRightRadius: 2,
9128
+ backgroundColor: slotColor(slot),
9129
+ marginLeft: Math.max(0, slot.leadingGap - REP_GAP),
9130
+ ...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
8623
9131
  },
8624
9132
  accessibilityElementsHidden: true,
8625
- testID: `velocity-bar-${i}`
9133
+ testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`
8626
9134
  },
8627
9135
  i
8628
9136
  ))
8629
9137
  }
8630
9138
  );
8631
9139
  }
8632
- const stripLabel = `Velocity chart for set, ${velocities.length} reps, tap to ${expanded ? "collapse" : "expand"}`;
9140
+ const stripLabel = set ? `${setAccessibilityLabel(set, repCount)}, tap to ${expanded ? "collapse" : "expand"}` : `Velocity chart for set, ${repCount} reps, tap to ${expanded ? "collapse" : "expand"}`;
8633
9141
  const hasInteractiveContainer = onToggle != null;
8634
9142
  const hasInteractiveReps = onRepPress != null && expanded;
8635
- const stripContent = /* @__PURE__ */ jsxs(
8636
- reactNative.Animated.View,
8637
- {
8638
- className: [className, expanded ? "bg-surface-raised" : "bg-transparent"].filter(Boolean).join(" "),
8639
- style: [
8640
- {
8641
- height: heightAnim,
8642
- width: "100%",
8643
- gap: 2,
9143
+ const setBars = set != null && slots.map((slot, i) => {
9144
+ const isStraightRep = set.type === "straight" && slot.kind === "rep";
9145
+ const heightPct = isStraightRep ? scaleDenom > 0 ? Math.round((slot.velocity ?? 0) / scaleDenom * 100) : 0 : set.type === "straight" ? EXPANDED_TODO_STUB_PCT : EXPANDED_ENCODED_PCT;
9146
+ return /* @__PURE__ */ jsx(
9147
+ reactNative.View,
9148
+ {
9149
+ style: {
9150
+ flex: 1,
9151
+ height: "100%",
9152
+ justifyContent: "flex-end",
9153
+ marginLeft: slot.leadingGap
9154
+ },
9155
+ children: /* @__PURE__ */ jsx(
9156
+ reactNative.View,
9157
+ {
9158
+ style: {
9159
+ height: `${heightPct}%`,
9160
+ minHeight: 2,
9161
+ borderTopLeftRadius: 2,
9162
+ borderTopRightRadius: 2,
9163
+ backgroundColor: slotColor(slot),
9164
+ ...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
9165
+ },
9166
+ testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`,
9167
+ accessibilityRole: "image",
9168
+ accessibilityLabel: slotAccessibilityLabel(slot, i)
9169
+ }
9170
+ )
9171
+ },
9172
+ i
9173
+ );
9174
+ });
9175
+ const stripContent = /* @__PURE__ */ jsxs(
9176
+ reactNative.Animated.View,
9177
+ {
9178
+ className: [className, expanded ? "bg-surface-raised" : "bg-transparent"].filter(Boolean).join(" "),
9179
+ style: [
9180
+ {
9181
+ height: heightAnim,
9182
+ width: "100%",
9183
+ gap: 2,
8644
9184
  borderRadius: expanded ? 6 : 2,
8645
9185
  paddingTop: expanded ? 16 : 0,
8646
9186
  paddingBottom: expanded ? 24 : 0,
8647
- paddingHorizontal: expanded ? 6 : 0,
9187
+ // No horizontal inset the framed chart's bars span full width, matching
9188
+ // the bare spotlight and mini. Numbers/info sit in the vertical padding.
9189
+ paddingHorizontal: 0,
8648
9190
  paddingVertical: expanded ? void 0 : 8,
8649
9191
  overflow: expanded ? "visible" : "hidden"
8650
9192
  }
@@ -8654,46 +9196,65 @@ function VelocityStrip({
8654
9196
  testID: "velocity-strip",
8655
9197
  ...props,
8656
9198
  children: [
8657
- /* @__PURE__ */ jsx(
8658
- reactNative.View,
8659
- {
8660
- style: { flexDirection: "row", flex: 1, gap: 2, alignItems: "flex-end" },
8661
- children: velocities.map((v, i) => {
8662
- const barBackground = barColorFor(v);
8663
- const barHeightPct = maxVelocity > 0 ? Math.round(v / (maxVelocity * 1.15) * 100) : 0;
8664
- const isLive = liveRepIndex === i;
8665
- const liveLabelSuffix = isLive ? isNewPeak ? ", latest rep, new set peak" : ", latest rep" : "";
8666
- const barStyle = expanded ? { height: `${barHeightPct}%`, minHeight: 2, borderTopLeftRadius: 2, borderTopRightRadius: 2, backgroundColor: barBackground } : {
8667
- minHeight: "100%",
8668
- borderTopLeftRadius: 2,
8669
- borderTopRightRadius: 2,
8670
- borderBottomLeftRadius: 0,
8671
- borderBottomRightRadius: 0,
8672
- backgroundColor: barBackground
8673
- };
8674
- const barInner = isLive ? /* @__PURE__ */ jsx(
8675
- reactNative.Animated.View,
8676
- {
8677
- style: [barStyle, { transform: [{ scale: liveScale }] }],
8678
- testID: `velocity-bar-${i}`,
8679
- accessibilityRole: "image",
8680
- accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second${liveLabelSuffix}`
8681
- }
8682
- ) : /* @__PURE__ */ jsx(
8683
- reactNative.View,
8684
- {
8685
- style: barStyle,
8686
- testID: `velocity-bar-${i}`,
8687
- accessibilityRole: "image",
8688
- accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second`
8689
- }
8690
- );
8691
- const bar = /* @__PURE__ */ jsxs(
8692
- reactNative.View,
8693
- {
8694
- style: { flex: 1, height: "100%", justifyContent: "flex-end", position: "relative" },
8695
- children: [
8696
- expanded && /* @__PURE__ */ jsx(reactNative.Animated.View, { style: { opacity: labelOpacity, alignItems: "center", position: "absolute", top: -13, left: 0, right: 0 }, accessibilityElementsHidden: true, children: /* @__PURE__ */ jsx(
9199
+ /* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", flex: 1, gap: set ? 0 : 2, alignItems: "flex-end" }, children: set ? setBars : doneVelocities.map((v, i) => {
9200
+ const barBackground = barColorFor(v);
9201
+ const barHeightPct = scaleDenom > 0 ? Math.round(v / scaleDenom * 100) : 0;
9202
+ const isLive = liveRepIndex === i;
9203
+ const liveLabelSuffix = isLive ? isNewPeak ? ", latest rep, new set peak" : ", latest rep" : "";
9204
+ const barStyle = expanded ? {
9205
+ height: `${barHeightPct}%`,
9206
+ minHeight: 2,
9207
+ borderTopLeftRadius: 2,
9208
+ borderTopRightRadius: 2,
9209
+ backgroundColor: barBackground
9210
+ } : {
9211
+ minHeight: "100%",
9212
+ borderTopLeftRadius: 2,
9213
+ borderTopRightRadius: 2,
9214
+ borderBottomLeftRadius: 0,
9215
+ borderBottomRightRadius: 0,
9216
+ backgroundColor: barBackground
9217
+ };
9218
+ const barInner = isLive ? /* @__PURE__ */ jsx(
9219
+ reactNative.Animated.View,
9220
+ {
9221
+ style: [barStyle, { transform: [{ scale: liveScale }] }],
9222
+ testID: `velocity-bar-${i}`,
9223
+ accessibilityRole: "image",
9224
+ accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second${liveLabelSuffix}`
9225
+ }
9226
+ ) : /* @__PURE__ */ jsx(
9227
+ reactNative.View,
9228
+ {
9229
+ style: barStyle,
9230
+ testID: `velocity-bar-${i}`,
9231
+ accessibilityRole: "image",
9232
+ accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second`
9233
+ }
9234
+ );
9235
+ const bar = /* @__PURE__ */ jsxs(
9236
+ reactNative.View,
9237
+ {
9238
+ style: {
9239
+ flex: 1,
9240
+ height: "100%",
9241
+ justifyContent: "flex-end",
9242
+ position: "relative"
9243
+ },
9244
+ children: [
9245
+ expanded && showNumbers && /* @__PURE__ */ jsx(
9246
+ reactNative.Animated.View,
9247
+ {
9248
+ style: {
9249
+ opacity: labelOpacity,
9250
+ alignItems: "center",
9251
+ position: "absolute",
9252
+ top: -13,
9253
+ left: 0,
9254
+ right: 0
9255
+ },
9256
+ accessibilityElementsHidden: true,
9257
+ children: /* @__PURE__ */ jsx(
8697
9258
  reactNative.Text,
8698
9259
  {
8699
9260
  className: "text-text-secondary",
@@ -8701,30 +9262,30 @@ function VelocityStrip({
8701
9262
  testID: `velocity-label-${i}`,
8702
9263
  children: formatVelocity(v)
8703
9264
  }
8704
- ) }),
8705
- barInner
8706
- ]
8707
- },
8708
- i
8709
- );
8710
- if (onRepPress && expanded) {
8711
- return /* @__PURE__ */ jsx(
8712
- reactNative.Pressable,
8713
- {
8714
- style: { flex: 1, height: "100%" },
8715
- onPress: () => onRepPress(i, v),
8716
- accessibilityRole: "button",
8717
- accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second, tap for details`,
8718
- testID: `velocity-bar-pressable-${i}`,
8719
- children: bar
8720
- },
8721
- i
8722
- );
8723
- }
8724
- return bar;
8725
- })
9265
+ )
9266
+ }
9267
+ ),
9268
+ barInner
9269
+ ]
9270
+ },
9271
+ i
9272
+ );
9273
+ if (onRepPress && expanded) {
9274
+ return /* @__PURE__ */ jsx(
9275
+ reactNative.Pressable,
9276
+ {
9277
+ style: { flex: 1, height: "100%" },
9278
+ onPress: () => onRepPress(i, v),
9279
+ accessibilityRole: "button",
9280
+ accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second, tap for details`,
9281
+ testID: `velocity-bar-pressable-${i}`,
9282
+ children: bar
9283
+ },
9284
+ i
9285
+ );
8726
9286
  }
8727
- ),
9287
+ return bar;
9288
+ }) }),
8728
9289
  expanded && showInfo && /* @__PURE__ */ jsxs(
8729
9290
  reactNative.Animated.View,
8730
9291
  {
@@ -9022,233 +9583,90 @@ function Sparkline({
9022
9583
  }
9023
9584
  );
9024
9585
  }
9025
- function formatAccessibilityLabel(setNumber, reps, weight, unit) {
9026
- const repsText = reps != null ? `${reps} reps` : "no reps";
9027
- const weightText = weight != null ? `at ${roundWeight(weight)} ${unit}` : "";
9028
- return `Set ${setNumber}: ${repsText}${weightText ? ` ${weightText}` : ""}`;
9586
+ var DARK = getSemanticColors("dark");
9587
+ var TEXT_ACTIVE = DARK["text-primary"];
9588
+ var TEXT_MUTED = DARK["text-secondary"];
9589
+ var COL = { set: 36, reps: 44, load: 56, rpe: 36 };
9590
+ var cellText = { fontFamily: "Inter, sans-serif", fontSize: 13, fontWeight: "600" };
9591
+ var typeBadge = {
9592
+ fontSize: 10,
9593
+ fontWeight: "700",
9594
+ fontFamily: "Inter, sans-serif",
9595
+ color: WORKOUT_TOKENS.scale.orange,
9596
+ backgroundColor: "rgba(255,165,2,0.12)",
9597
+ paddingVertical: 1,
9598
+ paddingHorizontal: 5,
9599
+ borderRadius: 3,
9600
+ overflow: "hidden"
9601
+ };
9602
+ function displayReps(set) {
9603
+ return set.state === "done" ? set.reps : set.target.reps;
9604
+ }
9605
+ function displayWeight(set) {
9606
+ return set.state === "done" ? set.weight : set.target.weight;
9607
+ }
9608
+ function plannedReps(set) {
9609
+ if (set.state === "done") return set.velocities.length;
9610
+ if (set.state === "live") return set.target.reps;
9611
+ return set.planned ?? set.target.reps;
9612
+ }
9613
+ function accessibilityLabel(set) {
9614
+ const base = `Set ${set.setNumber}: ${displayReps(set)} reps at ${roundWeight(displayWeight(set))} ${set.unit}`;
9615
+ if (set.state === "live") return `${base}, in progress`;
9616
+ if (set.state === "todo") return `${base}, upcoming`;
9617
+ return base;
9618
+ }
9619
+ function Cell({
9620
+ width,
9621
+ children,
9622
+ testID
9623
+ }) {
9624
+ return /* @__PURE__ */ jsx(reactNative.View, { style: { width, alignItems: "center", justifyContent: "center" }, testID, children });
9029
9625
  }
9030
- function SetRow({
9031
- mode,
9032
- setNumber,
9033
- previous,
9034
- reps,
9035
- weight,
9036
- rpe,
9037
- unit,
9038
- velocities,
9039
- velocityZones,
9040
- velocityLiveRepIndex,
9041
- velocityExpanded,
9042
- onVelocityToggle,
9043
- setType,
9044
- prBadges,
9045
- isNextSet,
9046
- targets
9047
- }) {
9048
- const isActive = mode === "active";
9049
- const isCompleted = mode === "completed";
9050
- const rowStyle = {
9051
- flexDirection: "row",
9052
- alignItems: "center",
9053
- padding: 7,
9054
- paddingHorizontal: 8,
9055
- gap: 8,
9056
- borderRadius: 8
9057
- };
9058
- if (isActive && isNextSet) {
9059
- rowStyle.backgroundColor = "rgba(255,121,0,0.06)";
9060
- rowStyle.borderWidth = 1;
9061
- rowStyle.borderColor = "rgba(255,121,0,0.15)";
9062
- }
9063
- if (isCompleted && !isNextSet) {
9064
- rowStyle.opacity = 0.55;
9626
+ function RowStrip({ set }) {
9627
+ const zones = set.velocityZones;
9628
+ if (set.state === "live") {
9629
+ return /* @__PURE__ */ jsx(
9630
+ VelocityStrip,
9631
+ {
9632
+ variant: "expanded",
9633
+ showNumbers: false,
9634
+ showInfo: false,
9635
+ height: 24,
9636
+ scale: "fixed",
9637
+ set: { type: "straight", velocities: set.velocities, planned: set.target.reps },
9638
+ zones
9639
+ }
9640
+ );
9065
9641
  }
9642
+ const velocities = set.state === "done" ? set.velocities : [];
9643
+ return /* @__PURE__ */ jsx(
9644
+ VelocityStrip,
9645
+ {
9646
+ variant: "mini",
9647
+ set: { type: "straight", velocities, planned: plannedReps(set) },
9648
+ zones
9649
+ }
9650
+ );
9651
+ }
9652
+ function SetRow(set) {
9653
+ const live = set.state === "live";
9654
+ const valueColor = live ? TEXT_ACTIVE : TEXT_MUTED;
9655
+ const rpe = set.state === "todo" ? null : set.rpe;
9066
9656
  return /* @__PURE__ */ jsxs(
9067
9657
  reactNative.View,
9068
9658
  {
9069
- style: rowStyle,
9070
- accessibilityLabel: formatAccessibilityLabel(
9071
- setNumber,
9072
- reps,
9073
- weight,
9074
- unit
9075
- ),
9659
+ style: { paddingVertical: 6, paddingHorizontal: 8 },
9660
+ accessibilityLabel: accessibilityLabel(set),
9076
9661
  testID: "set-row",
9077
9662
  children: [
9078
- /* @__PURE__ */ jsx(
9079
- reactNative.View,
9080
- {
9081
- style: { width: 36, alignItems: "center", justifyContent: "center" },
9082
- testID: "set-row-set-number",
9083
- children: setType ? /* @__PURE__ */ jsx(
9084
- reactNative.Text,
9085
- {
9086
- style: {
9087
- fontSize: 10,
9088
- fontWeight: "700",
9089
- fontFamily: "Inter, sans-serif",
9090
- color: WORKOUT_TOKENS.scale.orange,
9091
- backgroundColor: "rgba(255,165,2,0.12)",
9092
- paddingVertical: 1,
9093
- paddingHorizontal: 5,
9094
- borderRadius: 3,
9095
- overflow: "hidden"
9096
- },
9097
- testID: "set-row-type-badge",
9098
- children: setType
9099
- }
9100
- ) : /* @__PURE__ */ jsx(
9101
- reactNative.Text,
9102
- {
9103
- className: "text-text-secondary",
9104
- style: {
9105
- fontSize: 13,
9106
- fontWeight: "600",
9107
- fontFamily: "Inter, sans-serif"
9108
- },
9109
- children: setNumber
9110
- }
9111
- )
9112
- }
9113
- ),
9114
- /* @__PURE__ */ jsx(
9115
- reactNative.View,
9116
- {
9117
- className: "flex-1",
9118
- testID: "set-row-previous",
9119
- children: /* @__PURE__ */ jsx(
9120
- reactNative.Text,
9121
- {
9122
- className: "text-text-secondary",
9123
- style: {
9124
- fontSize: 12,
9125
- fontFamily: "Inter, sans-serif"
9126
- },
9127
- children: previous ? `${previous.reps} x ${roundWeight(previous.weight)}` : "\u2014"
9128
- }
9129
- )
9130
- }
9131
- ),
9132
- /* @__PURE__ */ jsx(
9133
- reactNative.View,
9134
- {
9135
- style: { width: 44, alignItems: "center", justifyContent: "center" },
9136
- testID: "set-row-reps",
9137
- children: isActive && reps == null && targets ? /* @__PURE__ */ jsx(
9138
- reactNative.Text,
9139
- {
9140
- className: "text-text-tertiary",
9141
- style: {
9142
- fontSize: 13,
9143
- fontStyle: "italic",
9144
- fontFamily: "Inter, sans-serif"
9145
- },
9146
- testID: "set-row-target-reps",
9147
- children: targets.reps
9148
- }
9149
- ) : /* @__PURE__ */ jsx(
9150
- reactNative.Text,
9151
- {
9152
- className: reps != null ? "text-text-primary" : "text-text-tertiary",
9153
- style: {
9154
- fontSize: 14,
9155
- fontWeight: "600",
9156
- fontFamily: "Inter, sans-serif"
9157
- },
9158
- children: reps != null ? reps : "\u2014"
9159
- }
9160
- )
9161
- }
9162
- ),
9163
- /* @__PURE__ */ jsx(
9164
- reactNative.View,
9165
- {
9166
- style: { width: 56, alignItems: "center", justifyContent: "center" },
9167
- testID: "set-row-weight",
9168
- children: isActive && weight == null && targets ? /* @__PURE__ */ jsx(
9169
- reactNative.Text,
9170
- {
9171
- className: "text-text-tertiary",
9172
- style: {
9173
- fontSize: 13,
9174
- fontStyle: "italic",
9175
- fontFamily: "Inter, sans-serif"
9176
- },
9177
- testID: "set-row-target-weight",
9178
- children: roundWeight(targets.weight)
9179
- }
9180
- ) : /* @__PURE__ */ jsx(
9181
- reactNative.Text,
9182
- {
9183
- className: weight != null ? "text-text-primary" : "text-text-tertiary",
9184
- style: {
9185
- fontSize: 14,
9186
- fontWeight: "600",
9187
- fontFamily: "Inter, sans-serif"
9188
- },
9189
- children: weight != null ? roundWeight(weight) : "\u2014"
9190
- }
9191
- )
9192
- }
9193
- ),
9194
- /* @__PURE__ */ jsx(
9195
- reactNative.View,
9196
- {
9197
- style: { width: 36, alignItems: "center", justifyContent: "center" },
9198
- testID: "set-row-rpe",
9199
- children: /* @__PURE__ */ jsx(
9200
- reactNative.Text,
9201
- {
9202
- className: rpe != null ? void 0 : "text-text-tertiary",
9203
- style: {
9204
- fontSize: 13,
9205
- fontWeight: "600",
9206
- fontFamily: "Inter, sans-serif",
9207
- // rpeColor returns a concrete hex (native-safe); only the tertiary
9208
- // fallback needs the className token.
9209
- color: rpe != null ? rpeColor(rpe) : void 0
9210
- },
9211
- children: rpe != null ? roundRpe(rpe) : "\u2014"
9212
- }
9213
- )
9214
- }
9215
- ),
9216
- prBadges && prBadges.length > 0 && /* @__PURE__ */ jsx(
9217
- reactNative.View,
9218
- {
9219
- className: "flex-row items-center",
9220
- style: { gap: 4 },
9221
- testID: "set-row-pr-badges",
9222
- children: prBadges.map((badge, i) => /* @__PURE__ */ jsx(
9223
- PrBadge,
9224
- {
9225
- type: badge.type,
9226
- label: badge.label,
9227
- animate: false,
9228
- compact: true
9229
- },
9230
- i
9231
- ))
9232
- }
9233
- ),
9234
- velocities && velocities.length > 0 && /* @__PURE__ */ jsx(
9235
- reactNative.View,
9236
- {
9237
- style: { position: "absolute", bottom: 0, left: 8, right: 8 },
9238
- testID: "set-row-velocity-strip",
9239
- children: /* @__PURE__ */ jsx(
9240
- VelocityStrip,
9241
- {
9242
- velocities,
9243
- zones: velocityZones,
9244
- liveRepIndex: velocityLiveRepIndex,
9245
- expanded: velocityExpanded,
9246
- onToggle: onVelocityToggle,
9247
- variant: onVelocityToggle ? "full" : "mini"
9248
- }
9249
- )
9250
- }
9251
- )
9663
+ /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", style: { justifyContent: "space-between" }, children: [
9664
+ /* @__PURE__ */ jsx(Cell, { width: COL.set, testID: "set-row-set-number", children: set.setType ? /* @__PURE__ */ jsx(reactNative.Text, { style: typeBadge, testID: "set-row-type-badge", children: set.setType }) : /* @__PURE__ */ jsx(reactNative.Text, { style: { ...cellText, color: valueColor, fontWeight: live ? "700" : "600" }, children: set.setNumber }) }),
9665
+ /* @__PURE__ */ jsx(Cell, { width: COL.reps, testID: "set-row-reps", children: /* @__PURE__ */ jsx(reactNative.Text, { style: { ...cellText, color: valueColor }, children: displayReps(set) }) }),
9666
+ /* @__PURE__ */ jsx(Cell, { width: COL.load, testID: "set-row-weight", children: /* @__PURE__ */ jsx(reactNative.Text, { style: { ...cellText, color: valueColor }, children: roundWeight(displayWeight(set)) }) }),
9667
+ /* @__PURE__ */ jsx(Cell, { width: COL.rpe, testID: "set-row-rpe", children: /* @__PURE__ */ jsx(reactNative.Text, { style: { ...cellText, color: TEXT_MUTED }, children: rpe != null ? roundRpe(rpe) : "\u2014" }) })
9668
+ ] }),
9669
+ /* @__PURE__ */ jsx(reactNative.View, { style: { marginTop: live ? 6 : 4 }, testID: "set-row-strip", children: /* @__PURE__ */ jsx(RowStrip, { set }) })
9252
9670
  ]
9253
9671
  }
9254
9672
  );
@@ -9419,14 +9837,18 @@ function RestTimer({
9419
9837
  visible,
9420
9838
  displayOnly = false
9421
9839
  }) {
9422
- if (!visible) return null;
9423
- const remainingMs = Math.max(0, totalSeconds * 1e3 - elapsedMs);
9840
+ const {
9841
+ remainingMs,
9842
+ progress,
9843
+ label: timeDisplay
9844
+ } = useTimer({
9845
+ mode: "down",
9846
+ durationMs: totalSeconds * 1e3,
9847
+ elapsedMs
9848
+ });
9424
9849
  const remainingSec = Math.ceil(remainingMs / 1e3);
9425
- const minutes = Math.floor(remainingSec / 60);
9426
- const seconds = remainingSec % 60;
9427
- const timeDisplay = `${minutes}:${String(seconds).padStart(2, "0")}`;
9428
- const totalMs = totalSeconds * 1e3;
9429
- const progressPct = totalMs > 0 ? Math.min(100, elapsedMs / totalMs * 100) : 100;
9850
+ const progressPct = progress * 100;
9851
+ if (!visible) return null;
9430
9852
  return /* @__PURE__ */ jsxs(
9431
9853
  reactNative.View,
9432
9854
  {
@@ -9584,15 +10006,319 @@ function RestTimer({
9584
10006
  }
9585
10007
  );
9586
10008
  }
9587
- function buildColumnHeaders(unit) {
9588
- return ["SET", "PREV", "REPS", unit.toUpperCase(), "RPE"];
10009
+ var t8 = getSemanticColors("dark");
10010
+ var TIMES = "\xD7";
10011
+ var AT = "@";
10012
+ function SetsRepsLoad({
10013
+ sets,
10014
+ reps,
10015
+ load,
10016
+ unit = "lb",
10017
+ className,
10018
+ ...props
10019
+ }) {
10020
+ const value = t8["text-primary"];
10021
+ const sep = t8["text-tertiary"];
10022
+ return /* @__PURE__ */ jsxs(
10023
+ reactNative.View,
10024
+ {
10025
+ className,
10026
+ style: { flexDirection: "row", alignItems: "baseline" },
10027
+ accessibilityLabel: `${sets} sets of ${reps} reps at ${load} ${unit}`,
10028
+ testID: "sets-reps-load",
10029
+ ...props,
10030
+ children: [
10031
+ /* @__PURE__ */ jsx(MetricCell, { color: value, children: sets }),
10032
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${TIMES} ` }),
10033
+ /* @__PURE__ */ jsx(MetricCell, { color: value, children: reps }),
10034
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${AT} ` }),
10035
+ /* @__PURE__ */ jsx(MetricCell, { color: value, children: load }),
10036
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${unit}` })
10037
+ ]
10038
+ }
10039
+ );
9589
10040
  }
9590
- function getSupersetBorderRadius(position) {
9591
- switch (position) {
9592
- case "first":
9593
- return {
9594
- borderTopLeftRadius: 12,
9595
- borderTopRightRadius: 12,
10041
+
10042
+ // src/components/custom/Workout/paceTone.ts
10043
+ function paceTone(progress, target) {
10044
+ if (target === void 0) return "neutral";
10045
+ return progress >= target ? "ahead" : "behind";
10046
+ }
10047
+ var PACE_TONE_COLORS = {
10048
+ ahead: getSemanticColors("dark")["status-success"],
10049
+ behind: getSemanticColors("dark")["status-warning"],
10050
+ neutral: primitiveRamps.cyan[400]
10051
+ };
10052
+ function paceToneColor(tone) {
10053
+ return PACE_TONE_COLORS[tone];
10054
+ }
10055
+
10056
+ // src/components/custom/Workout/SegmentedProgressBar.tsx
10057
+ var DEFAULT_MARKER_COLOR = primitiveRamps.green[500];
10058
+ function SegmentedProgressBar({
10059
+ segments,
10060
+ value,
10061
+ target,
10062
+ markerColor = DEFAULT_MARKER_COLOR,
10063
+ color,
10064
+ height,
10065
+ gap,
10066
+ ...props
10067
+ }) {
10068
+ const total = segments.reduce((sum, s) => sum + s.weight, 0);
10069
+ const fillColor = color ?? paceToneColor(paceTone(total > 0 ? value / total : 0, target));
10070
+ const barSegments = segments.map((seg, i) => {
10071
+ const cumulativeBefore = segments.slice(0, i).reduce((sum, s) => sum + s.weight, 0);
10072
+ const fill = Math.max(0, Math.min(1, (value - cumulativeBefore) / seg.weight));
10073
+ return { weight: seg.weight, fill, color: fillColor };
10074
+ });
10075
+ return /* @__PURE__ */ jsx(
10076
+ SegmentedBar,
10077
+ {
10078
+ segments: barSegments,
10079
+ marker: target != null ? { position: target, color: markerColor } : null,
10080
+ height,
10081
+ gap,
10082
+ ...props
10083
+ }
10084
+ );
10085
+ }
10086
+
10087
+ // src/components/custom/Workout/MetricTiles.tsx
10088
+ function MetricTiles({ metrics, gap = 1, ...props }) {
10089
+ return /* @__PURE__ */ jsx(HStack, { gap, align: "stretch", ...props, children: metrics.map((metric) => /* @__PURE__ */ jsx(
10090
+ Tile,
10091
+ {
10092
+ label: metric.label,
10093
+ value: metric.value,
10094
+ valueColor: metric.valueColor
10095
+ },
10096
+ metric.label
10097
+ )) });
10098
+ }
10099
+ var DAY_MS = 24 * 60 * 60 * 1e3;
10100
+ var SOON_ACCENT = primitiveRamps.orange[400];
10101
+ function compactUntil(ms) {
10102
+ if (ms <= 0) return "now";
10103
+ const min = Math.round(ms / 6e4);
10104
+ if (min < 60) return `${min}m`;
10105
+ const hr = Math.round(min / 60);
10106
+ if (hr < 24) return `${hr}h`;
10107
+ const d = Math.round(hr / 24);
10108
+ if (d < 7) return `${d}d`;
10109
+ return `${Math.round(d / 7)}w`;
10110
+ }
10111
+ function ScheduleTiles({ when, now, gap = 1, ...props }) {
10112
+ const [mountNow] = React24.useState(() => Date.now());
10113
+ const reference = now ?? mountNow;
10114
+ const whenMs = when instanceof Date ? when.getTime() : when;
10115
+ const soon = whenMs - reference < DAY_MS;
10116
+ const time = new Date(whenMs).toLocaleTimeString(void 0, {
10117
+ hour: "numeric",
10118
+ minute: "2-digit",
10119
+ hour12: true
10120
+ });
10121
+ return /* @__PURE__ */ jsxs(HStack, { gap, ...props, children: [
10122
+ /* @__PURE__ */ jsx(Tile, { label: "Date", value: formatDateTime(when, "short") }),
10123
+ /* @__PURE__ */ jsx(Tile, { label: "Time", value: time }),
10124
+ /* @__PURE__ */ jsx(
10125
+ Tile,
10126
+ {
10127
+ label: "Until",
10128
+ value: compactUntil(whenMs - reference),
10129
+ valueColor: soon ? SOON_ACCENT : void 0
10130
+ }
10131
+ )
10132
+ ] });
10133
+ }
10134
+ var t9 = getSemanticColors("dark");
10135
+ var INDICATOR_CONFIG = {
10136
+ imbalance: { Icon: ScaleIcon, color: t9["status-error"], label: "Left/right imbalance" },
10137
+ overshoot: { Icon: AlertTriangleIcon, color: t9["status-error"], label: "Load overshoot" },
10138
+ "velocity-loss": { Icon: TrendingDownIcon, color: t9["status-warning"], label: "Velocity loss" },
10139
+ "missed-reps": { Icon: CircleSlashIcon, color: t9["status-warning"], label: "Missed reps" },
10140
+ pr: { Icon: AwardIcon, color: t9["status-success"], label: "Personal record" },
10141
+ info: { Icon: InfoIcon, color: t9["status-info"], label: "More info" }
10142
+ };
10143
+ var INDICATOR_PRECEDENCE = [
10144
+ "imbalance",
10145
+ "overshoot",
10146
+ "velocity-loss",
10147
+ "missed-reps",
10148
+ "pr",
10149
+ "info"
10150
+ ];
10151
+ function resolveIndicator(candidates) {
10152
+ return INDICATOR_PRECEDENCE.find((kind) => candidates.includes(kind));
10153
+ }
10154
+ function ExerciseIndicator({ kind, onPress, className, ...props }) {
10155
+ const { Icon, color, label } = INDICATOR_CONFIG[kind];
10156
+ const chip = /* @__PURE__ */ jsx(
10157
+ reactNative.View,
10158
+ {
10159
+ className,
10160
+ style: {
10161
+ width: 16,
10162
+ height: 16,
10163
+ borderRadius: 8,
10164
+ borderWidth: 1,
10165
+ borderColor: color,
10166
+ alignItems: "center",
10167
+ justifyContent: "center"
10168
+ },
10169
+ accessibilityRole: "image",
10170
+ accessibilityLabel: label,
10171
+ testID: "exercise-indicator",
10172
+ ...props,
10173
+ children: /* @__PURE__ */ jsx(Icon, { size: 11, color })
10174
+ }
10175
+ );
10176
+ if (onPress) {
10177
+ return /* @__PURE__ */ jsx(reactNative.Pressable, { onPress, accessibilityRole: "button", accessibilityLabel: label, children: chip });
10178
+ }
10179
+ return chip;
10180
+ }
10181
+ function headingLabel(name, sets, reps, load, unit) {
10182
+ return `${name}, ${sets}\xD7${reps} @ ${roundWeight(load)} ${unit}`;
10183
+ }
10184
+ function ExerciseHeading({
10185
+ name,
10186
+ sets,
10187
+ reps,
10188
+ load,
10189
+ unit = "lbs",
10190
+ tempo,
10191
+ indicator,
10192
+ dimmed,
10193
+ onPress
10194
+ }) {
10195
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: { opacity: dimmed ? 0.55 : 1 }, testID: "exercise-heading", children: [
10196
+ /* @__PURE__ */ jsxs(
10197
+ reactNative.Pressable,
10198
+ {
10199
+ onPress,
10200
+ accessibilityRole: "button",
10201
+ accessibilityLabel: headingLabel(name, sets, reps, load, unit),
10202
+ className: "flex-row items-center",
10203
+ style: { gap: 8 },
10204
+ testID: "exercise-card-header",
10205
+ children: [
10206
+ /* @__PURE__ */ jsx(
10207
+ reactNative.Text,
10208
+ {
10209
+ className: "text-text-primary",
10210
+ style: { fontSize: 14, fontFamily: '"Space Grotesk", sans-serif', fontWeight: "700" },
10211
+ testID: "exercise-card-name",
10212
+ children: name
10213
+ }
10214
+ ),
10215
+ /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
10216
+ indicator && /* @__PURE__ */ jsx(ExerciseIndicator, { kind: indicator })
10217
+ ]
10218
+ }
10219
+ ),
10220
+ /* @__PURE__ */ jsxs(
10221
+ reactNative.View,
10222
+ {
10223
+ className: "flex-row items-center",
10224
+ style: { gap: 8, marginTop: 1 },
10225
+ testID: "exercise-card-summary",
10226
+ children: [
10227
+ /* @__PURE__ */ jsx(SetsRepsLoad, { sets, reps, load, unit }),
10228
+ /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
10229
+ tempo && /* @__PURE__ */ jsx(TempoDisplay, { tempo, size: "sm", showLabel: false, showInfo: false })
10230
+ ]
10231
+ }
10232
+ )
10233
+ ] });
10234
+ }
10235
+ function ExerciseCardHeading({
10236
+ setStates,
10237
+ stripHeight = 8,
10238
+ dimmed,
10239
+ testID = "exercise-card",
10240
+ ...heading
10241
+ }) {
10242
+ return /* @__PURE__ */ jsxs(
10243
+ reactNative.View,
10244
+ {
10245
+ style: { paddingVertical: 9, paddingHorizontal: 12, opacity: dimmed ? 0.55 : 1 },
10246
+ testID,
10247
+ children: [
10248
+ /* @__PURE__ */ jsx(ExerciseHeading, { ...heading }),
10249
+ setStates.length > 0 && /* @__PURE__ */ jsx(reactNative.View, { style: { marginTop: 7 }, testID: "exercise-card-strip", children: /* @__PURE__ */ jsx(SetStrip, { sets: setStates, height: stripHeight }) })
10250
+ ]
10251
+ }
10252
+ );
10253
+ }
10254
+ var PREV_COLUMN = { label: "PREV", width: void 0 };
10255
+ function buildColumns(unit, showPrevious) {
10256
+ return [
10257
+ { label: "SET", width: 36 },
10258
+ ...showPrevious ? [PREV_COLUMN] : [],
10259
+ { label: "REPS", width: 44 },
10260
+ { label: unit.toUpperCase(), width: 56 },
10261
+ { label: "RPE", width: 36 }
10262
+ ];
10263
+ }
10264
+ function SetTableHeader({
10265
+ unit = "lbs",
10266
+ showPrevious = true,
10267
+ testID = "table-header"
10268
+ }) {
10269
+ const columns = buildColumns(unit, showPrevious);
10270
+ return /* @__PURE__ */ jsx(
10271
+ reactNative.View,
10272
+ {
10273
+ className: "flex-row",
10274
+ style: {
10275
+ paddingVertical: 8,
10276
+ paddingHorizontal: 8,
10277
+ paddingBottom: 4,
10278
+ ...showPrevious ? {} : { justifyContent: "space-between" }
10279
+ },
10280
+ testID,
10281
+ children: columns.map(({ label, width }) => {
10282
+ const isFlex = width === void 0;
10283
+ return /* @__PURE__ */ jsx(
10284
+ reactNative.View,
10285
+ {
10286
+ style: {
10287
+ ...isFlex ? { minWidth: 44 } : { width, flexShrink: 1 },
10288
+ alignItems: "center",
10289
+ justifyContent: "center",
10290
+ gap: 8
10291
+ },
10292
+ ...isFlex ? { className: "flex-1" } : {},
10293
+ children: /* @__PURE__ */ jsx(
10294
+ reactNative.Text,
10295
+ {
10296
+ className: "text-text-tertiary",
10297
+ style: {
10298
+ fontSize: 10,
10299
+ fontWeight: "600",
10300
+ fontFamily: "Inter, sans-serif",
10301
+ textTransform: "uppercase",
10302
+ letterSpacing: 0.8
10303
+ },
10304
+ children: label
10305
+ }
10306
+ )
10307
+ },
10308
+ label
10309
+ );
10310
+ })
10311
+ }
10312
+ );
10313
+ }
10314
+
10315
+ // src/components/custom/Workout/ExerciseCard.tsx
10316
+ function getSupersetBorderRadius(position) {
10317
+ switch (position) {
10318
+ case "first":
10319
+ return {
10320
+ borderTopLeftRadius: 12,
10321
+ borderTopRightRadius: 12,
9596
10322
  borderBottomLeftRadius: 8,
9597
10323
  borderBottomRightRadius: 8
9598
10324
  };
@@ -9629,7 +10355,7 @@ function formatSummary(summary) {
9629
10355
  if (!summary) return "";
9630
10356
  return `${summary.sets}\xD7${summary.reps} @ ${roundWeight(summary.weight)} ${summary.unit}`;
9631
10357
  }
9632
- function formatAccessibilityLabel2(name, summary, prescription) {
10358
+ function formatAccessibilityLabel(name, summary, prescription) {
9633
10359
  if (summary) return `${name}, ${formatSummary(summary)}`;
9634
10360
  if (prescription) return `${name}, ${prescription}`;
9635
10361
  return name;
@@ -9638,7 +10364,6 @@ function CollapsedCard({
9638
10364
  name,
9639
10365
  onToggle,
9640
10366
  summary,
9641
- e1rm,
9642
10367
  isPR,
9643
10368
  setVelocities,
9644
10369
  velocityZones,
@@ -9657,7 +10382,7 @@ function CollapsedCard({
9657
10382
  onPressIn: () => setPressed(true),
9658
10383
  onPressOut: () => setPressed(false),
9659
10384
  accessibilityRole: "button",
9660
- accessibilityLabel: formatAccessibilityLabel2(name, summary, void 0),
10385
+ accessibilityLabel: formatAccessibilityLabel(name, summary, void 0),
9661
10386
  testID: "exercise-card",
9662
10387
  children: /* @__PURE__ */ jsxs(
9663
10388
  reactNative.View,
@@ -9671,100 +10396,93 @@ function CollapsedCard({
9671
10396
  ...supersetMargin
9672
10397
  },
9673
10398
  children: [
9674
- /* @__PURE__ */ jsxs(
9675
- reactNative.View,
9676
- {
9677
- className: "flex-row items-center",
9678
- testID: "exercise-card-header",
9679
- children: [
9680
- /* @__PURE__ */ jsx(
9681
- reactNative.Text,
9682
- {
9683
- className: "text-text-primary",
9684
- style: {
9685
- fontSize: 14,
9686
- fontFamily: '"Space Grotesk", sans-serif',
9687
- fontWeight: "700"
9688
- },
9689
- testID: "exercise-card-name",
9690
- children: name
9691
- }
9692
- ),
9693
- /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
9694
- summary && /* @__PURE__ */ jsx(
9695
- reactNative.Text,
9696
- {
9697
- className: "text-text-secondary",
9698
- style: {
9699
- fontSize: 12,
9700
- fontFamily: "Inter, sans-serif",
9701
- marginRight: 8
9702
- },
9703
- testID: "exercise-card-summary",
9704
- children: formatSummary(summary)
9705
- }
9706
- ),
9707
- e1rm && /* @__PURE__ */ jsx(
9708
- WeightBadge,
9709
- {
9710
- value: roundWeight(e1rm.value),
9711
- unit: e1rm.unit,
9712
- size: "sm",
9713
- showIcon: false,
9714
- testID: "exercise-card-e1rm"
9715
- }
9716
- ),
9717
- isPR && /* @__PURE__ */ jsx(reactNative.View, { style: { marginLeft: 4 }, children: /* @__PURE__ */ jsx(PrBadge, { type: "e1rm", compact: true, animate: false }) })
9718
- ]
9719
- }
9720
- ),
9721
- (completedSets > 0 || remaining > 0) && /* @__PURE__ */ jsxs(
9722
- reactNative.View,
9723
- {
9724
- className: "flex-row",
9725
- style: { marginTop: 6, gap: 4 },
9726
- testID: "exercise-card-strips",
9727
- children: [
9728
- setVelocities?.map((velocities, i) => /* @__PURE__ */ jsx(
9729
- VelocityStrip,
9730
- {
9731
- velocities,
9732
- zones: velocityZones,
9733
- variant: "mini",
9734
- testID: `exercise-card-velocity-strip-${i}`
9735
- },
9736
- i
9737
- )),
9738
- Array.from({ length: remaining }, (_, i) => /* @__PURE__ */ jsx(
9739
- PlaceholderStrip,
9740
- {
9741
- testID: `exercise-card-placeholder-${i}`
9742
- },
9743
- `placeholder-${i}`
9744
- ))
9745
- ]
9746
- }
9747
- )
10399
+ /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", testID: "exercise-card-header", children: [
10400
+ /* @__PURE__ */ jsx(
10401
+ reactNative.Text,
10402
+ {
10403
+ className: "text-text-primary",
10404
+ style: {
10405
+ fontSize: 14,
10406
+ fontFamily: '"Space Grotesk", sans-serif',
10407
+ fontWeight: "700"
10408
+ },
10409
+ testID: "exercise-card-name",
10410
+ children: name
10411
+ }
10412
+ ),
10413
+ /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
10414
+ summary && /* @__PURE__ */ jsx(
10415
+ reactNative.Text,
10416
+ {
10417
+ className: "text-text-secondary",
10418
+ style: {
10419
+ fontSize: 12,
10420
+ fontFamily: "Inter, sans-serif",
10421
+ marginRight: 8
10422
+ },
10423
+ testID: "exercise-card-summary",
10424
+ children: formatSummary(summary)
10425
+ }
10426
+ ),
10427
+ isPR && /* @__PURE__ */ jsx(reactNative.View, { style: { marginLeft: 4 }, children: /* @__PURE__ */ jsx(PrBadge, { type: "e1rm", compact: true, animate: false }) })
10428
+ ] }),
10429
+ (completedSets > 0 || remaining > 0) && /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row", style: { marginTop: 6, gap: 4 }, testID: "exercise-card-strips", children: [
10430
+ setVelocities?.map((velocities, i) => /* @__PURE__ */ jsx(
10431
+ VelocityStrip,
10432
+ {
10433
+ velocities,
10434
+ zones: velocityZones,
10435
+ variant: "mini",
10436
+ testID: `exercise-card-velocity-strip-${i}`
10437
+ },
10438
+ i
10439
+ )),
10440
+ Array.from({ length: remaining }, (_, i) => /* @__PURE__ */ jsx(
10441
+ PlaceholderStrip,
10442
+ {
10443
+ testID: `exercise-card-placeholder-${i}`
10444
+ },
10445
+ `placeholder-${i}`
10446
+ ))
10447
+ ] })
9748
10448
  ]
9749
10449
  }
9750
10450
  )
9751
10451
  }
9752
10452
  );
9753
10453
  }
10454
+ var DARK2 = getSemanticColors("dark");
10455
+ var BODY_DIVIDER = DARK2["border-subtle"];
10456
+ function deriveHeaderSetStates(sets) {
10457
+ return sets.map((set) => {
10458
+ switch (set.state) {
10459
+ case "live":
10460
+ return { status: "active", velocities: set.velocities, planned: set.target.reps };
10461
+ case "done":
10462
+ return { status: "done", velocities: set.velocities };
10463
+ case "todo":
10464
+ return { status: "todo", planned: set.planned ?? set.target.reps };
10465
+ }
10466
+ });
10467
+ }
9754
10468
  function ExpandedCard({
9755
10469
  name,
9756
10470
  onToggle,
9757
10471
  summary,
9758
- e1rm,
9759
10472
  isPR,
9760
10473
  sets,
9761
10474
  tempo,
10475
+ indicator,
10476
+ setStates,
10477
+ stripHeight = 8,
10478
+ velocityZones,
9762
10479
  supersetPosition
9763
10480
  }) {
9764
10481
  const borderRadius = getSupersetBorderRadius(supersetPosition);
9765
10482
  const supersetMargin = getSupersetMargin(supersetPosition);
9766
10483
  const unit = summary?.unit ?? sets?.[0]?.unit ?? "lbs";
9767
- const columnHeaders = buildColumnHeaders(unit);
10484
+ const headerStates = setStates ?? (sets ? deriveHeaderSetStates(sets) : []);
10485
+ const headerIndicator = indicator ?? (isPR ? "pr" : void 0);
9768
10486
  return /* @__PURE__ */ jsxs(
9769
10487
  reactNative.View,
9770
10488
  {
@@ -9777,114 +10495,32 @@ function ExpandedCard({
9777
10495
  testID: "exercise-card",
9778
10496
  children: [
9779
10497
  /* @__PURE__ */ jsx(
9780
- reactNative.Pressable,
10498
+ ExerciseCardHeading,
9781
10499
  {
10500
+ name,
10501
+ sets: summary?.sets ?? sets?.length ?? 0,
10502
+ reps: summary?.reps ?? 0,
10503
+ load: summary?.weight ?? 0,
10504
+ unit,
10505
+ tempo,
10506
+ indicator: headerIndicator,
10507
+ setStates: headerStates,
10508
+ stripHeight,
9782
10509
  onPress: onToggle,
9783
- accessibilityRole: "button",
9784
- accessibilityLabel: formatAccessibilityLabel2(name, summary, void 0),
9785
- "aria-expanded": true,
9786
- testID: "exercise-card-header",
9787
- children: /* @__PURE__ */ jsxs(
9788
- reactNative.View,
9789
- {
9790
- className: "flex-row items-center",
9791
- style: {
9792
- padding: 12,
9793
- paddingHorizontal: 14,
9794
- paddingBottom: 10,
9795
- borderBottomWidth: 1,
9796
- borderBottomColor: resolveColor("border-default")
9797
- },
9798
- children: [
9799
- /* @__PURE__ */ jsx(
9800
- reactNative.Text,
9801
- {
9802
- className: "text-text-primary",
9803
- style: {
9804
- fontSize: 14,
9805
- fontFamily: '"Space Grotesk", sans-serif',
9806
- fontWeight: "700"
9807
- },
9808
- testID: "exercise-card-name",
9809
- children: name
9810
- }
9811
- ),
9812
- /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
9813
- summary && /* @__PURE__ */ jsx(
9814
- reactNative.Text,
9815
- {
9816
- className: "text-text-secondary",
9817
- style: {
9818
- fontSize: 12,
9819
- fontFamily: "Inter, sans-serif",
9820
- marginRight: 8
9821
- },
9822
- testID: "exercise-card-summary",
9823
- children: formatSummary(summary)
9824
- }
9825
- ),
9826
- e1rm && /* @__PURE__ */ jsx(
9827
- WeightBadge,
9828
- {
9829
- value: roundWeight(e1rm.value),
9830
- unit: e1rm.unit,
9831
- size: "sm",
9832
- showIcon: false,
9833
- testID: "exercise-card-e1rm"
9834
- }
9835
- ),
9836
- isPR && /* @__PURE__ */ jsx(reactNative.View, { style: { marginLeft: 4 }, children: /* @__PURE__ */ jsx(PrBadge, { type: "e1rm", compact: true, animate: false }) })
9837
- ]
9838
- }
9839
- )
10510
+ testID: "exercise-card-heading"
9840
10511
  }
9841
10512
  ),
9842
- tempo && /* @__PURE__ */ jsx(reactNative.View, { style: { marginTop: 8, paddingHorizontal: 14 }, testID: "exercise-card-tempo", children: /* @__PURE__ */ jsx(TempoDisplay, { tempo, size: "sm" }) }),
9843
- /* @__PURE__ */ jsx(
10513
+ /* @__PURE__ */ jsxs(
9844
10514
  reactNative.View,
9845
10515
  {
9846
- className: "flex-row",
9847
- style: {
9848
- paddingVertical: 8,
9849
- paddingHorizontal: 8,
9850
- paddingBottom: 4
9851
- },
9852
- testID: "exercise-card-column-headers",
9853
- children: columnHeaders.map((header, i) => {
9854
- const widths = [36, void 0, 44, 56, 36];
9855
- const width = widths[i];
9856
- const isFlex = width === void 0;
9857
- return /* @__PURE__ */ jsx(
9858
- reactNative.View,
9859
- {
9860
- style: {
9861
- ...isFlex ? {} : { width },
9862
- alignItems: "center",
9863
- justifyContent: "center",
9864
- gap: 8
9865
- },
9866
- ...isFlex ? { className: "flex-1" } : {},
9867
- children: /* @__PURE__ */ jsx(
9868
- reactNative.Text,
9869
- {
9870
- className: "text-text-tertiary",
9871
- style: {
9872
- fontSize: 10,
9873
- fontWeight: "600",
9874
- fontFamily: "Inter, sans-serif",
9875
- textTransform: "uppercase",
9876
- letterSpacing: 0.8
9877
- },
9878
- children: header
9879
- }
9880
- )
9881
- },
9882
- header
9883
- );
9884
- })
10516
+ style: { borderTopWidth: 1, borderTopColor: BODY_DIVIDER, paddingBottom: 6 },
10517
+ testID: "exercise-card-body",
10518
+ children: [
10519
+ /* @__PURE__ */ jsx(SetTableHeader, { unit, showPrevious: false, testID: "exercise-card-column-headers" }),
10520
+ sets && /* @__PURE__ */ jsx(reactNative.View, { testID: "exercise-card-sets", children: sets.map((set, i) => /* @__PURE__ */ jsx(SetRow, { ...set, velocityZones: set.velocityZones ?? velocityZones }, i)) })
10521
+ ]
9885
10522
  }
9886
- ),
9887
- sets && /* @__PURE__ */ jsx(reactNative.View, { testID: "exercise-card-sets", children: sets.map((setProps, i) => /* @__PURE__ */ jsx(SetRow, { ...setProps }, i)) })
10523
+ )
9888
10524
  ]
9889
10525
  }
9890
10526
  );
@@ -9893,11 +10529,12 @@ function UpcomingCard({
9893
10529
  name,
9894
10530
  prescription,
9895
10531
  previousBest,
9896
- supersetPosition
10532
+ supersetPosition,
10533
+ onToggle
9897
10534
  }) {
9898
10535
  const borderRadius = getSupersetBorderRadius(supersetPosition);
9899
10536
  const supersetMargin = getSupersetMargin(supersetPosition);
9900
- return /* @__PURE__ */ jsx(
10537
+ return /* @__PURE__ */ jsx(reactNative.Pressable, { onPress: onToggle, accessibilityRole: "button", children: /* @__PURE__ */ jsx(
9901
10538
  reactNative.View,
9902
10539
  {
9903
10540
  style: {
@@ -9907,7 +10544,7 @@ function UpcomingCard({
9907
10544
  ...borderRadius,
9908
10545
  ...supersetMargin
9909
10546
  },
9910
- accessibilityLabel: formatAccessibilityLabel2(name, void 0, prescription),
10547
+ accessibilityLabel: formatAccessibilityLabel(name, void 0, prescription),
9911
10548
  testID: "exercise-card",
9912
10549
  children: /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", children: [
9913
10550
  /* @__PURE__ */ jsx(
@@ -9915,6 +10552,7 @@ function UpcomingCard({
9915
10552
  {
9916
10553
  className: "text-text-primary",
9917
10554
  style: {
10555
+ flexShrink: 0,
9918
10556
  fontSize: 14,
9919
10557
  fontFamily: '"Space Grotesk", sans-serif',
9920
10558
  fontWeight: "700"
@@ -9927,7 +10565,9 @@ function UpcomingCard({
9927
10565
  reactNative.Text,
9928
10566
  {
9929
10567
  className: "text-text-secondary",
10568
+ numberOfLines: 1,
9930
10569
  style: {
10570
+ flexShrink: 1,
9931
10571
  fontSize: 12,
9932
10572
  fontFamily: "Inter, sans-serif",
9933
10573
  marginLeft: 8
@@ -9936,12 +10576,14 @@ function UpcomingCard({
9936
10576
  children: prescription
9937
10577
  }
9938
10578
  ),
9939
- /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
10579
+ /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1", style: { minWidth: 8 } }),
9940
10580
  previousBest && /* @__PURE__ */ jsx(
9941
10581
  reactNative.Text,
9942
10582
  {
9943
10583
  className: "text-text-tertiary",
10584
+ numberOfLines: 1,
9944
10585
  style: {
10586
+ flexShrink: 1,
9945
10587
  fontSize: 11,
9946
10588
  fontFamily: "Inter, sans-serif"
9947
10589
  },
@@ -9951,17 +10593,208 @@ function UpcomingCard({
9951
10593
  )
9952
10594
  ] })
9953
10595
  }
10596
+ ) });
10597
+ }
10598
+ function ExerciseCard({
10599
+ upcoming,
10600
+ expanded,
10601
+ defaultExpanded,
10602
+ onExpandedChange,
10603
+ ...rest
10604
+ }) {
10605
+ const [internalExpanded, setInternalExpanded] = React24.useState(defaultExpanded ?? false);
10606
+ const isControlled = expanded !== void 0;
10607
+ const isExpanded = isControlled ? expanded : internalExpanded;
10608
+ const onToggle = () => {
10609
+ const next = !isExpanded;
10610
+ onExpandedChange?.(next);
10611
+ if (!isControlled) setInternalExpanded(next);
10612
+ };
10613
+ if (upcoming) return /* @__PURE__ */ jsx(UpcomingCard, { ...rest, onToggle });
10614
+ return isExpanded ? /* @__PURE__ */ jsx(ExpandedCard, { ...rest, onToggle }) : /* @__PURE__ */ jsx(CollapsedCard, { ...rest, onToggle });
10615
+ }
10616
+ var t10 = getSemanticColors("dark");
10617
+ var RAISED = primitiveColors.charcoal[400];
10618
+ var BAR_HEIGHT = 9;
10619
+ var BAR_GAP = 3;
10620
+ function SessionHeader({
10621
+ title,
10622
+ plan,
10623
+ setsDone = 0,
10624
+ elapsedMs,
10625
+ budgetMs,
10626
+ running = true,
10627
+ metrics = [],
10628
+ next,
10629
+ className,
10630
+ style,
10631
+ ...props
10632
+ }) {
10633
+ const upcoming = next != null;
10634
+ const totalSets = plan.reduce((sum, e) => sum + e.sets, 0);
10635
+ const hasPace = !upcoming && budgetMs != null && elapsedMs != null;
10636
+ const target = hasPace ? elapsedMs / budgetMs : void 0;
10637
+ const setsLabel = upcoming ? `${totalSets} sets` : `${Math.floor(setsDone)}/${totalSets} sets`;
10638
+ const setsLabelColor = upcoming ? t10["text-secondary"] : paceToneColor(paceTone(totalSets > 0 ? setsDone / totalSets : 0, target));
10639
+ return /* @__PURE__ */ jsxs(
10640
+ reactNative.View,
10641
+ {
10642
+ className,
10643
+ style: [
10644
+ {
10645
+ backgroundColor: RAISED,
10646
+ paddingTop: 11,
10647
+ paddingRight: 12,
10648
+ paddingBottom: 12,
10649
+ paddingLeft: 12,
10650
+ zIndex: 2
10651
+ },
10652
+ style
10653
+ ],
10654
+ testID: "session-rail-header",
10655
+ ...props,
10656
+ children: [
10657
+ /* @__PURE__ */ jsx(
10658
+ reactNative.Text,
10659
+ {
10660
+ accessibilityRole: "header",
10661
+ style: {
10662
+ fontSize: 16,
10663
+ lineHeight: 18,
10664
+ fontWeight: "700",
10665
+ fontFamily: '"Space Grotesk", sans-serif',
10666
+ color: t10["text-primary"],
10667
+ marginBottom: 10
10668
+ },
10669
+ testID: "session-rail-title",
10670
+ children: title
10671
+ }
10672
+ ),
10673
+ upcoming ? /* @__PURE__ */ jsx(ScheduleTiles, { when: next }) : /* @__PURE__ */ jsx(MetricTiles, { metrics }),
10674
+ /* @__PURE__ */ jsxs(reactNative.View, { style: { marginTop: 10 }, children: [
10675
+ /* @__PURE__ */ jsx(
10676
+ SegmentedProgressBar,
10677
+ {
10678
+ segments: plan.map((e) => ({ weight: e.sets })),
10679
+ value: upcoming ? 0 : setsDone,
10680
+ target,
10681
+ height: BAR_HEIGHT,
10682
+ gap: BAR_GAP
10683
+ }
10684
+ ),
10685
+ /* @__PURE__ */ jsxs(
10686
+ reactNative.View,
10687
+ {
10688
+ style: {
10689
+ flexDirection: "row",
10690
+ justifyContent: "space-between",
10691
+ alignItems: "baseline",
10692
+ marginTop: 5
10693
+ },
10694
+ children: [
10695
+ /* @__PURE__ */ jsx(
10696
+ Typography,
10697
+ {
10698
+ variant: "mono",
10699
+ style: { fontSize: 10, color: setsLabelColor },
10700
+ testID: "session-rail-sets",
10701
+ children: setsLabel
10702
+ }
10703
+ ),
10704
+ /* @__PURE__ */ jsx(
10705
+ TimerReadout,
10706
+ {
10707
+ mode: "up",
10708
+ elapsedMs: upcoming ? budgetMs : elapsedMs,
10709
+ durationMs: budgetMs,
10710
+ running: running && !upcoming,
10711
+ showTotal: budgetMs != null && !upcoming
10712
+ }
10713
+ )
10714
+ ]
10715
+ }
10716
+ )
10717
+ ] })
10718
+ ]
10719
+ }
9954
10720
  );
9955
10721
  }
9956
- function ExerciseCard(props) {
9957
- switch (props.state) {
9958
- case "collapsed":
9959
- return /* @__PURE__ */ jsx(CollapsedCard, { ...props });
9960
- case "expanded":
9961
- return /* @__PURE__ */ jsx(ExpandedCard, { ...props });
9962
- case "upcoming":
9963
- return /* @__PURE__ */ jsx(UpcomingCard, { ...props });
9964
- }
10722
+ var INSET = primitiveColors.charcoal[800];
10723
+ var DIVIDER = primitiveColors.charcoal[500];
10724
+ var DEFAULT_WIDTH = 246;
10725
+ function SessionRail({
10726
+ title,
10727
+ exercises,
10728
+ setsDone,
10729
+ elapsedMs,
10730
+ budgetMs,
10731
+ running,
10732
+ metrics,
10733
+ next,
10734
+ stripHeight = 8,
10735
+ width = DEFAULT_WIDTH,
10736
+ onExercisePress,
10737
+ className,
10738
+ style,
10739
+ ...props
10740
+ }) {
10741
+ return /* @__PURE__ */ jsxs(
10742
+ reactNative.View,
10743
+ {
10744
+ className,
10745
+ style: [{ width, flexDirection: "column", backgroundColor: INSET }, style],
10746
+ testID: "session-rail",
10747
+ ...props,
10748
+ children: [
10749
+ /* @__PURE__ */ jsx(
10750
+ SessionHeader,
10751
+ {
10752
+ title,
10753
+ plan: exercises.map((ex) => ({ name: ex.name, sets: ex.summary.sets })),
10754
+ setsDone,
10755
+ elapsedMs,
10756
+ budgetMs,
10757
+ running,
10758
+ metrics,
10759
+ next
10760
+ }
10761
+ ),
10762
+ /* @__PURE__ */ jsx(
10763
+ reactNative.View,
10764
+ {
10765
+ style: [{ flex: 1, backgroundColor: INSET }, neumorphicShadows.charcoal.pressed.subtle],
10766
+ testID: "session-rail-list",
10767
+ children: exercises.map((ex, i) => /* @__PURE__ */ jsx(
10768
+ reactNative.View,
10769
+ {
10770
+ style: {
10771
+ borderBottomWidth: i < exercises.length - 1 ? 1 : 0,
10772
+ borderBottomColor: DIVIDER
10773
+ },
10774
+ children: /* @__PURE__ */ jsx(
10775
+ ExerciseCardHeading,
10776
+ {
10777
+ name: ex.name,
10778
+ sets: ex.summary.sets,
10779
+ reps: ex.summary.reps,
10780
+ load: ex.summary.weight,
10781
+ unit: ex.summary.unit,
10782
+ tempo: ex.tempo,
10783
+ indicator: ex.indicator,
10784
+ setStates: ex.setStates,
10785
+ stripHeight,
10786
+ dimmed: ex.upcoming,
10787
+ onPress: () => onExercisePress?.(ex, i)
10788
+ }
10789
+ )
10790
+ },
10791
+ ex.id ?? i
10792
+ ))
10793
+ }
10794
+ )
10795
+ ]
10796
+ }
10797
+ );
9965
10798
  }
9966
10799
  var DEFAULTS = {
9967
10800
  label: "SS",
@@ -10225,10 +11058,10 @@ function WeekRow({
10225
11058
  }
10226
11059
  );
10227
11060
  }
10228
- var t8 = getSemanticColors("dark");
11061
+ var t11 = getSemanticColors("dark");
10229
11062
  var COLORS = {
10230
- statusSuccess: t8["status-success"],
10231
- brandPrimary: t8["brand-primary"],
11063
+ statusSuccess: t11["status-success"],
11064
+ brandPrimary: t11["brand-primary"],
10232
11065
  borderDefault: "#1F1F1F",
10233
11066
  brandPrimarySubtle: "rgba(255,121,0,0.06)"
10234
11067
  };
@@ -10712,12 +11545,12 @@ function PrHistoryModal({
10712
11545
  }
10713
11546
  );
10714
11547
  }
10715
- var t9 = getSemanticColors("dark");
10716
- var BRAND_PRIMARY9 = t9["brand-primary"];
11548
+ var t12 = getSemanticColors("dark");
11549
+ var BRAND_PRIMARY9 = t12["brand-primary"];
10717
11550
  var BRAND_PRIMARY_SUBTLE = "rgba(255,121,0,0.12)";
10718
- var STATUS_SUCCESS = t9["status-success"];
10719
- var STATUS_WARNING = t9["status-warning"];
10720
- var STATUS_ERROR2 = t9["status-error"];
11551
+ var STATUS_SUCCESS = t12["status-success"];
11552
+ var STATUS_WARNING = t12["status-warning"];
11553
+ var STATUS_ERROR2 = t12["status-error"];
10721
11554
  var EMOJI_SETS = {
10722
11555
  sleep: ["\u{1F634}", "\u{1F610}", "\u{1F642}", "\u{1F60A}", "\u{1F929}"],
10723
11556
  soreness: ["\u{1F635}", "\u{1F623}", "\u{1F610}", "\u{1F642}", "\u{1F4AA}"],
@@ -10907,13 +11740,13 @@ function ReadinessCheck({
10907
11740
  )
10908
11741
  ] }) });
10909
11742
  }
10910
- var t10 = getSemanticColors("dark");
10911
- var BRAND_PRIMARY10 = t10["brand-primary"];
11743
+ var t13 = getSemanticColors("dark");
11744
+ var BRAND_PRIMARY10 = t13["brand-primary"];
10912
11745
  var BRAND_PRIMARY_DARK2 = MESO_ACCENT_GRADIENT_DARK;
10913
11746
  var BRAND_PRIMARY_LIGHT2 = MESO_ACCENT_GRADIENT_LIGHT;
10914
- var SUCCESS = t10["status-success"];
10915
- var WARNING = t10["status-warning"];
10916
- var ERROR = t10["status-error"];
11747
+ var SUCCESS = t13["status-success"];
11748
+ var WARNING = t13["status-warning"];
11749
+ var ERROR = t13["status-error"];
10917
11750
  var ACCENT_STOPS2 = [BRAND_PRIMARY_DARK2, BRAND_PRIMARY10, BRAND_PRIMARY_LIGHT2];
10918
11751
  var CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor("surface-elevated")} 0%, ${resolveColor("surface-raised")} 100%)`;
10919
11752
  var GAUGE_GRADIENT = "linear-gradient(90deg, rgba(46,213,115,0.25) 0%, rgba(249,180,21,0.25) 50%, rgba(209,67,67,0.25) 100%)";
@@ -10985,7 +11818,7 @@ function StatusPill({ badge }) {
10985
11818
  }
10986
11819
  );
10987
11820
  }
10988
- function MetricCell({ metric }) {
11821
+ function MetricCell2({ metric }) {
10989
11822
  return /* @__PURE__ */ jsxs(reactNative.View, { style: { width: "48%" }, testID: "meso-status-card-metric", children: [
10990
11823
  /* @__PURE__ */ jsx(
10991
11824
  reactNative.Text,
@@ -11203,7 +12036,7 @@ function MesoStatusCard({
11203
12036
  className: "flex-row flex-wrap",
11204
12037
  style: { gap: 8 },
11205
12038
  testID: "meso-status-card-metrics",
11206
- children: metrics.map((metric, index) => /* @__PURE__ */ jsx(MetricCell, { metric }, `${metric.label}-${index}`))
12039
+ children: metrics.map((metric, index) => /* @__PURE__ */ jsx(MetricCell2, { metric }, `${metric.label}-${index}`))
11207
12040
  }
11208
12041
  ),
11209
12042
  gauges.length > 0 && /* @__PURE__ */ jsx(reactNative.View, { style: { gap: 12 }, testID: "meso-status-card-gauges", children: gauges.map((gauge, index) => /* @__PURE__ */ jsx(Gauge, { gauge }, `${gauge.label}-${index}`)) }),
@@ -11284,8 +12117,8 @@ var ERROR_PILL_BORDER = "rgba(209,67,67,0.20)";
11284
12117
  var PLOT_LEFT = 26;
11285
12118
  var TOOLTIP_WIDTH = 124;
11286
12119
  function timeOf(dateStr) {
11287
- const t12 = Date.parse(dateStr);
11288
- return Number.isNaN(t12) ? 0 : t12;
12120
+ const t18 = Date.parse(dateStr);
12121
+ return Number.isNaN(t18) ? 0 : t18;
11289
12122
  }
11290
12123
  function formatValue(value) {
11291
12124
  return `${Math.round(value)}`;
@@ -11774,10 +12607,10 @@ function StrengthTrendChart({
11774
12607
  }
11775
12608
  );
11776
12609
  }
11777
- var t11 = getSemanticColors("dark");
11778
- var STATUS_SUCCESS3 = t11["status-success"];
11779
- var STATUS_WARNING3 = t11["status-warning"];
11780
- var STATUS_INFO = t11["status-info"];
12610
+ var t14 = getSemanticColors("dark");
12611
+ var STATUS_SUCCESS3 = t14["status-success"];
12612
+ var STATUS_WARNING3 = t14["status-warning"];
12613
+ var STATUS_INFO = t14["status-info"];
11781
12614
  var DOT_BORDER = "#F3F4F6";
11782
12615
  var BAND_FILL = "rgba(46,213,115,0.1)";
11783
12616
  var BAND_EDGE = "rgba(46,213,115,0.45)";
@@ -11820,13 +12653,13 @@ function interpEdge(pixels, x, key) {
11820
12653
  const a = pixels[i];
11821
12654
  const b = pixels[i + 1];
11822
12655
  if (x >= a.x && x <= b.x) {
11823
- const t12 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
11824
- return a[key] + t12 * (b[key] - a[key]);
12656
+ const t18 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
12657
+ return a[key] + t18 * (b[key] - a[key]);
11825
12658
  }
11826
12659
  }
11827
12660
  return pixels[pixels.length - 1][key];
11828
12661
  }
11829
- function buildColumns(pixels, step) {
12662
+ function buildColumns2(pixels, step) {
11830
12663
  if (pixels.length < 2) return [];
11831
12664
  const start = pixels[0].x;
11832
12665
  const end = pixels[pixels.length - 1].x;
@@ -11938,7 +12771,7 @@ function CapacityBandChart({
11938
12771
  }
11939
12772
  const { toX, toY } = scale;
11940
12773
  const bandPixels = toPixels(band, toX, toY);
11941
- const columns = buildColumns(bandPixels, COLUMN_STEP);
12774
+ const columns = buildColumns2(bandPixels, COLUMN_STEP);
11942
12775
  const topEdge = buildEdges(bandPixels, "yHigh");
11943
12776
  const bottomEdge = buildEdges(bandPixels, "yLow");
11944
12777
  const lastBandPoint = band[band.length - 1];
@@ -12013,8 +12846,8 @@ function CapacityBandChart({
12013
12846
  testID: "capacity-band-chart-reveal",
12014
12847
  children: /* @__PURE__ */ jsxs(reactNative.View, { style: { width, height, position: "relative" }, children: [
12015
12848
  projection && /* @__PURE__ */ jsxs(reactNative.View, { testID: "capacity-band-chart-projection", children: [
12016
- renderColumns(buildColumns(trainingPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
12017
- renderColumns(buildColumns(restPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
12849
+ renderColumns(buildColumns2(trainingPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
12850
+ renderColumns(buildColumns2(restPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
12018
12851
  renderEdges(buildEdges(trainingPixels, "yHigh"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
12019
12852
  renderEdges(buildEdges(trainingPixels, "yLow"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
12020
12853
  renderEdges(buildEdges(restPixels, "yHigh"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
@@ -12081,72 +12914,546 @@ function CapacityBandChart({
12081
12914
  return /* @__PURE__ */ jsx(
12082
12915
  reactNative.Text,
12083
12916
  {
12084
- className: "text-text-tertiary",
12917
+ className: "text-text-tertiary",
12918
+ style: {
12919
+ position: "absolute",
12920
+ left: toX(point.date) - 14,
12921
+ top: height - PADDING_BOTTOM + 2,
12922
+ width: 28,
12923
+ textAlign: "center",
12924
+ fontSize: 10,
12925
+ fontFamily: "Inter, sans-serif"
12926
+ },
12927
+ accessibilityElementsHidden: true,
12928
+ importantForAccessibility: "no-hide-descendants",
12929
+ testID: "capacity-band-chart-x-label",
12930
+ children: formatDate2(point.date)
12931
+ },
12932
+ `x-${point.date}`
12933
+ );
12934
+ })
12935
+ ]
12936
+ }
12937
+ ),
12938
+ workouts.map((workout, i) => {
12939
+ const cx = toX(workout.date);
12940
+ const cy = toY(workout.load);
12941
+ const color = DOT_COLORS[workout.status];
12942
+ const dotStyle = {
12943
+ width: DOT_SIZE,
12944
+ height: DOT_SIZE,
12945
+ borderRadius: DOT_SIZE / 2,
12946
+ backgroundColor: color,
12947
+ borderWidth: 2,
12948
+ borderColor: DOT_BORDER
12949
+ };
12950
+ const label = `Workout on ${formatDate2(workout.date)}, load ${workout.load}, ${STATUS_PHRASES[workout.status]}`;
12951
+ return /* @__PURE__ */ jsx(
12952
+ reactNative.Animated.View,
12953
+ {
12954
+ style: {
12955
+ position: "absolute",
12956
+ left: cx - DOT_SIZE / 2,
12957
+ top: cy - DOT_SIZE / 2,
12958
+ transform: [{ scale: dotAnims[i] ?? 1 }]
12959
+ },
12960
+ testID: "capacity-band-chart-dot-wrapper",
12961
+ children: onWorkoutPress ? /* @__PURE__ */ jsx(
12962
+ reactNative.Pressable,
12963
+ {
12964
+ onPress: () => onWorkoutPress(workout),
12965
+ accessibilityRole: "button",
12966
+ accessibilityLabel: label,
12967
+ style: dotStyle,
12968
+ testID: "capacity-band-chart-dot"
12969
+ }
12970
+ ) : /* @__PURE__ */ jsx(
12971
+ reactNative.View,
12972
+ {
12973
+ accessibilityElementsHidden: true,
12974
+ importantForAccessibility: "no-hide-descendants",
12975
+ style: dotStyle,
12976
+ testID: "capacity-band-chart-dot"
12977
+ }
12978
+ )
12979
+ },
12980
+ `dot-${workout.date}-${i}`
12981
+ );
12982
+ })
12983
+ ]
12984
+ }
12985
+ );
12986
+ }
12987
+ var t15 = getSemanticColors("dark");
12988
+ function glowShadow(color) {
12989
+ return `0 0 5px 1px ${alpha(color, 0.3)}, 0 0 10px 3px ${alpha(color, 0.12)}`;
12990
+ }
12991
+ var DEFAULT_TRACK_COLOR = primitiveColors.charcoal[200];
12992
+ var DEFAULT_MARKER_COLOR2 = primitiveColors.white;
12993
+ var TICK_COLOR = t15["text-tertiary"];
12994
+ var DEFAULT_TRACK_HEIGHT = 14;
12995
+ var DEFAULT_NEEDLE_OVERHANG = 6;
12996
+ var NEEDLE_WIDTH = 4;
12997
+ function fraction(value, min, max) {
12998
+ if (max <= min) return 0;
12999
+ return Math.max(0, Math.min(1, (value - min) / (max - min)));
13000
+ }
13001
+ function pct(frac) {
13002
+ return `${frac * 100}%`;
13003
+ }
13004
+ function ZoneTrack({
13005
+ zones,
13006
+ max,
13007
+ min = 0,
13008
+ marker = null,
13009
+ ticks,
13010
+ band = null,
13011
+ trackHeight = DEFAULT_TRACK_HEIGHT,
13012
+ needleOverhang = DEFAULT_NEEDLE_OVERHANG,
13013
+ trackColor = DEFAULT_TRACK_COLOR,
13014
+ className,
13015
+ style,
13016
+ accessibilityLabel: accessibilityLabel2,
13017
+ ...props
13018
+ }) {
13019
+ const isNeedle = marker?.type === "needle";
13020
+ const markerHeight = isNeedle ? trackHeight + needleOverhang * 2 : trackHeight;
13021
+ const markerFrac = marker != null ? fraction(marker.value, min, max) : 0;
13022
+ const bands = zones.map((zone, i) => {
13023
+ const prev = i === 0 ? min : zones[i - 1].upTo;
13024
+ const start = Math.max(min, Math.min(max, prev));
13025
+ const end = Math.max(min, Math.min(max, zone.upTo));
13026
+ return { color: zone.color, weight: Math.max(0, end - start) };
13027
+ });
13028
+ return /* @__PURE__ */ jsxs(
13029
+ reactNative.View,
13030
+ {
13031
+ className,
13032
+ style: [{ width: "100%" }, style],
13033
+ accessibilityRole: "progressbar",
13034
+ accessibilityValue: marker != null ? { min, max, now: marker.value } : { min, max, now: min },
13035
+ accessibilityLabel: accessibilityLabel2 ?? `Zone track: ${marker?.value ?? min}`,
13036
+ testID: "zone-track",
13037
+ ...props,
13038
+ children: [
13039
+ /* @__PURE__ */ jsxs(reactNative.View, { style: { position: "relative", height: markerHeight }, children: [
13040
+ /* @__PURE__ */ jsxs(
13041
+ reactNative.View,
13042
+ {
13043
+ testID: "zone-track-track",
13044
+ accessibilityElementsHidden: true,
13045
+ style: {
13046
+ position: "absolute",
13047
+ top: (markerHeight - trackHeight) / 2,
13048
+ left: 0,
13049
+ right: 0,
13050
+ height: trackHeight,
13051
+ borderRadius: trackHeight / 2,
13052
+ backgroundColor: trackColor,
13053
+ overflow: "hidden",
13054
+ flexDirection: "row",
13055
+ // The pill's own box-shadow renders outside its overflow:hidden, so
13056
+ // `glow` haloes the whole track in the marker colour.
13057
+ ...marker?.glow ? { boxShadow: glowShadow(marker.color ?? DEFAULT_MARKER_COLOR2) } : null
13058
+ },
13059
+ children: [
13060
+ bands.map((b, i) => /* @__PURE__ */ jsx(
13061
+ reactNative.View,
13062
+ {
13063
+ testID: "zone-track-band",
13064
+ style: {
13065
+ flexGrow: b.weight,
13066
+ flexShrink: 1,
13067
+ flexBasis: 0,
13068
+ minWidth: 0,
13069
+ height: "100%",
13070
+ backgroundColor: b.color
13071
+ }
13072
+ },
13073
+ i
13074
+ )),
13075
+ band != null && /* @__PURE__ */ jsx(
13076
+ reactNative.View,
13077
+ {
13078
+ testID: "zone-track-band-highlight",
13079
+ style: {
13080
+ position: "absolute",
13081
+ top: 0,
13082
+ bottom: 0,
13083
+ left: pct(fraction(band.from, min, max)),
13084
+ width: pct(fraction(band.to, min, max) - fraction(band.from, min, max)),
13085
+ backgroundColor: band.color
13086
+ }
13087
+ }
13088
+ ),
13089
+ marker?.type === "fill" && /* @__PURE__ */ jsx(
13090
+ reactNative.View,
13091
+ {
13092
+ testID: "zone-track-unfilled",
13093
+ style: {
13094
+ position: "absolute",
13095
+ top: 0,
13096
+ bottom: 0,
13097
+ left: pct(markerFrac),
13098
+ right: 0,
13099
+ backgroundColor: trackColor
13100
+ }
13101
+ }
13102
+ ),
13103
+ marker?.type === "fill" && marker.color != null && /* @__PURE__ */ jsx(
13104
+ reactNative.View,
13105
+ {
13106
+ testID: "zone-track-fill",
12085
13107
  style: {
12086
13108
  position: "absolute",
12087
- left: toX(point.date) - 14,
12088
- top: height - PADDING_BOTTOM + 2,
12089
- width: 28,
12090
- textAlign: "center",
12091
- fontSize: 10,
12092
- fontFamily: "Inter, sans-serif"
12093
- },
12094
- accessibilityElementsHidden: true,
12095
- importantForAccessibility: "no-hide-descendants",
12096
- testID: "capacity-band-chart-x-label",
12097
- children: formatDate2(point.date)
12098
- },
12099
- `x-${point.date}`
12100
- );
12101
- })
12102
- ]
12103
- }
12104
- ),
12105
- workouts.map((workout, i) => {
12106
- const cx = toX(workout.date);
12107
- const cy = toY(workout.load);
12108
- const color = DOT_COLORS[workout.status];
12109
- const dotStyle = {
12110
- width: DOT_SIZE,
12111
- height: DOT_SIZE,
12112
- borderRadius: DOT_SIZE / 2,
12113
- backgroundColor: color,
12114
- borderWidth: 2,
12115
- borderColor: DOT_BORDER
12116
- };
12117
- const label = `Workout on ${formatDate2(workout.date)}, load ${workout.load}, ${STATUS_PHRASES[workout.status]}`;
13109
+ top: 0,
13110
+ bottom: 0,
13111
+ left: 0,
13112
+ width: pct(markerFrac),
13113
+ backgroundColor: marker.color
13114
+ }
13115
+ }
13116
+ )
13117
+ ]
13118
+ }
13119
+ ),
13120
+ isNeedle && /* @__PURE__ */ jsx(
13121
+ reactNative.View,
13122
+ {
13123
+ testID: "zone-track-needle",
13124
+ style: {
13125
+ position: "absolute",
13126
+ top: 0,
13127
+ left: pct(markerFrac),
13128
+ width: NEEDLE_WIDTH,
13129
+ height: markerHeight,
13130
+ borderRadius: NEEDLE_WIDTH / 2,
13131
+ backgroundColor: marker.color ?? DEFAULT_MARKER_COLOR2,
13132
+ transform: [{ translateX: -NEEDLE_WIDTH / 2 }]
13133
+ }
13134
+ }
13135
+ ),
13136
+ ticks?.map((tick, i) => {
13137
+ const emphasized = tick.emphasized === true;
13138
+ const lineColor = tick.color ?? (emphasized ? t15["brand-primary"] : TICK_COLOR);
13139
+ const lineWidth = emphasized ? 2 : 1.5;
13140
+ return /* @__PURE__ */ jsx(
13141
+ reactNative.View,
13142
+ {
13143
+ testID: "zone-track-tick-line",
13144
+ accessibilityElementsHidden: true,
13145
+ style: {
13146
+ position: "absolute",
13147
+ top: (markerHeight - trackHeight) / 2 - 2,
13148
+ left: pct(fraction(tick.value, min, max)),
13149
+ width: lineWidth,
13150
+ height: trackHeight + 4,
13151
+ borderRadius: lineWidth / 2,
13152
+ backgroundColor: lineColor,
13153
+ transform: [{ translateX: -lineWidth / 2 }],
13154
+ zIndex: 3
13155
+ }
13156
+ },
13157
+ `line-${i}`
13158
+ );
13159
+ })
13160
+ ] }),
13161
+ ticks != null && ticks.length > 0 && /* @__PURE__ */ jsx(reactNative.View, { style: { position: "relative", height: 16, marginTop: 5 }, children: ticks.map((tick, i) => {
13162
+ if (tick.label == null) return null;
13163
+ const emphasized = tick.emphasized === true;
13164
+ const labelColor = tick.color ?? (emphasized ? t15["brand-primary"] : TICK_COLOR);
13165
+ const labelNode = /* @__PURE__ */ jsx(
13166
+ reactNative.Text,
13167
+ {
13168
+ testID: "zone-track-tick-label",
13169
+ style: {
13170
+ fontSize: 9,
13171
+ letterSpacing: 0.5,
13172
+ color: labelColor,
13173
+ fontFamily: "monospace",
13174
+ fontWeight: emphasized ? "700" : "400"
13175
+ },
13176
+ children: tick.label
13177
+ }
13178
+ );
12118
13179
  return /* @__PURE__ */ jsx(
12119
- reactNative.Animated.View,
13180
+ reactNative.View,
12120
13181
  {
13182
+ testID: "zone-track-tick",
12121
13183
  style: {
12122
13184
  position: "absolute",
12123
- left: cx - DOT_SIZE / 2,
12124
- top: cy - DOT_SIZE / 2,
12125
- transform: [{ scale: dotAnims[i] ?? 1 }]
13185
+ left: pct(fraction(tick.value, min, max)),
13186
+ transform: [{ translateX: -14 }],
13187
+ width: 28,
13188
+ alignItems: "center"
12126
13189
  },
12127
- testID: "capacity-band-chart-dot-wrapper",
12128
- children: onWorkoutPress ? /* @__PURE__ */ jsx(
12129
- reactNative.Pressable,
12130
- {
12131
- onPress: () => onWorkoutPress(workout),
12132
- accessibilityRole: "button",
12133
- accessibilityLabel: label,
12134
- style: dotStyle,
12135
- testID: "capacity-band-chart-dot"
12136
- }
12137
- ) : /* @__PURE__ */ jsx(
12138
- reactNative.View,
12139
- {
12140
- accessibilityElementsHidden: true,
12141
- importantForAccessibility: "no-hide-descendants",
12142
- style: dotStyle,
12143
- testID: "capacity-band-chart-dot"
12144
- }
12145
- )
13190
+ children: tick.tooltip != null ? /* @__PURE__ */ jsx(Tooltip, { label: tick.tooltip, placement: "top", children: labelNode }) : labelNode
12146
13191
  },
12147
- `dot-${workout.date}-${i}`
13192
+ `label-${i}`
12148
13193
  );
12149
- })
13194
+ }) })
13195
+ ]
13196
+ }
13197
+ );
13198
+ }
13199
+
13200
+ // src/components/custom/Workout/FatigueMeter.tsx
13201
+ var { green, yellow, orange, red } = WORKOUT_TOKENS.scale;
13202
+ var DEFAULT_ZONE_COLORS = [green, yellow, orange, red];
13203
+ var DEFAULT_THRESHOLDS = [10, 20, 30];
13204
+ var DEFAULT_LABELS = ["fresh", "VL10", "VL20", "VL30", "stop"];
13205
+ var DEFAULT_MAX = 40;
13206
+ function FatigueMeter({
13207
+ value,
13208
+ max = DEFAULT_MAX,
13209
+ thresholds = DEFAULT_THRESHOLDS,
13210
+ zoneColors = DEFAULT_ZONE_COLORS,
13211
+ labels = DEFAULT_LABELS,
13212
+ trackHeight = 14,
13213
+ needleColor,
13214
+ className,
13215
+ ...props
13216
+ }) {
13217
+ const [t1, t22, t32] = thresholds;
13218
+ const zones = [
13219
+ { upTo: t1, color: zoneColors[0] },
13220
+ { upTo: t22, color: zoneColors[1] },
13221
+ { upTo: t32, color: zoneColors[2] },
13222
+ { upTo: max, color: zoneColors[3] }
13223
+ ];
13224
+ const tickValues = [0, t1, t22, t32, max];
13225
+ const ticks = tickValues.map((v, i) => ({ value: v, label: labels[i] }));
13226
+ return /* @__PURE__ */ jsx(
13227
+ ZoneTrack,
13228
+ {
13229
+ className,
13230
+ zones,
13231
+ min: 0,
13232
+ max,
13233
+ trackHeight,
13234
+ ticks,
13235
+ marker: { type: "needle", value, color: needleColor },
13236
+ accessibilityLabel: `Fatigue: ${value}% velocity loss`,
13237
+ testID: "fatigue-meter",
13238
+ ...props
13239
+ }
13240
+ );
13241
+ }
13242
+ var HEAT = WORKOUT_TOKENS.heatmap;
13243
+ var NEUTRAL_TRACK = primitiveColors.charcoal[200];
13244
+ var MONO = "monospace";
13245
+ var HEAT_BY_ZONE = {
13246
+ under: HEAT.under,
13247
+ maintenance: HEAT.maintenance,
13248
+ productive: HEAT.productive,
13249
+ approaching: HEAT.approaching,
13250
+ over: HEAT.over
13251
+ };
13252
+ var ZONE_DESCRIPTION = {
13253
+ under: "below MEV",
13254
+ maintenance: "building toward MAV",
13255
+ productive: "in the productive zone",
13256
+ approaching: "approaching MRV",
13257
+ over: "over MRV"
13258
+ };
13259
+ var LANDMARK_NAME = {
13260
+ MEV: "Minimum Effective Volume",
13261
+ MAV: "Maximum Adaptive Volume",
13262
+ MRV: "Maximum Recoverable Volume"
13263
+ };
13264
+ function zoneForSets(sets, { mev, mav, mrv }) {
13265
+ if (sets < mev) return "under";
13266
+ if (sets < mav) return "maintenance";
13267
+ if (sets >= mrv) return "over";
13268
+ const midpoint = (mav + mrv) / 2;
13269
+ return sets < midpoint ? "productive" : "approaching";
13270
+ }
13271
+ function VolumeLandmarkBar({
13272
+ muscle,
13273
+ currentSets,
13274
+ landmarks,
13275
+ width = 220,
13276
+ trackHeight = 10,
13277
+ scaleMax,
13278
+ className,
13279
+ style,
13280
+ ...props
13281
+ }) {
13282
+ const { mev, mav, mrv } = landmarks;
13283
+ const max = scaleMax ?? mrv * 1.2;
13284
+ const zone = zoneForSets(currentSets, landmarks);
13285
+ const fillColor = HEAT_BY_ZONE[zone];
13286
+ const pct2 = mav > 0 ? Math.round(currentSets / mav * 100) : 0;
13287
+ return /* @__PURE__ */ jsxs(
13288
+ reactNative.View,
13289
+ {
13290
+ className,
13291
+ style: [{ width, gap: 3 }, style],
13292
+ testID: "volume-landmark-bar",
13293
+ ...props,
13294
+ children: [
13295
+ /* @__PURE__ */ jsx(
13296
+ DataRow,
13297
+ {
13298
+ label: muscle,
13299
+ value: /* @__PURE__ */ jsxs(
13300
+ reactNative.Text,
13301
+ {
13302
+ testID: "volume-landmark-pct",
13303
+ style: { fontSize: 14, fontWeight: "700", fontFamily: MONO, color: fillColor },
13304
+ children: [
13305
+ pct2,
13306
+ "%"
13307
+ ]
13308
+ }
13309
+ ),
13310
+ className: "py-0",
13311
+ testID: "volume-landmark-header"
13312
+ }
13313
+ ),
13314
+ /* @__PURE__ */ jsx(
13315
+ ZoneTrack,
13316
+ {
13317
+ zones: [{ upTo: max, color: NEUTRAL_TRACK }],
13318
+ max,
13319
+ marker: {
13320
+ type: "fill",
13321
+ value: currentSets,
13322
+ color: fillColor,
13323
+ // Glow when in the optimal productive band — the "sweet spot" cue.
13324
+ glow: zone === "productive"
13325
+ },
13326
+ trackColor: NEUTRAL_TRACK,
13327
+ trackHeight,
13328
+ ticks: [
13329
+ { value: mev, label: "MEV", tooltip: `${LANDMARK_NAME.MEV} \xB7 ${mev} sets/wk` },
13330
+ {
13331
+ value: mav,
13332
+ label: "MAV",
13333
+ emphasized: true,
13334
+ tooltip: `${LANDMARK_NAME.MAV} (target) \xB7 ${mav} sets/wk`
13335
+ },
13336
+ { value: mrv, label: "MRV", tooltip: `${LANDMARK_NAME.MRV} \xB7 ${mrv} sets/wk` }
13337
+ ],
13338
+ accessibilityLabel: `${muscle} weekly volume: ${currentSets} sets, ${pct2}% of MAV target, ${ZONE_DESCRIPTION[zone]}`,
13339
+ testID: "volume-landmark-track"
13340
+ }
13341
+ )
13342
+ ]
13343
+ }
13344
+ );
13345
+ }
13346
+ var t16 = getSemanticColors("dark");
13347
+ var statusToken = {
13348
+ productive: "status-success",
13349
+ threshold: "status-warning",
13350
+ stop: "status-error"
13351
+ };
13352
+ var statusDotVariant = {
13353
+ productive: "success",
13354
+ threshold: "warning",
13355
+ stop: "error"
13356
+ };
13357
+ var defaultLabel = {
13358
+ productive: "Productive",
13359
+ threshold: "Threshold",
13360
+ stop: "Stop"
13361
+ };
13362
+ function statusPillColor(status) {
13363
+ return t16[statusToken[status]];
13364
+ }
13365
+ function StatusPill2({ status, label, className, style, ...props }) {
13366
+ const color = statusPillColor(status);
13367
+ const text = label ?? defaultLabel[status];
13368
+ return /* @__PURE__ */ jsxs(
13369
+ reactNative.View,
13370
+ {
13371
+ testID: "status-pill",
13372
+ className,
13373
+ style: [
13374
+ {
13375
+ flexDirection: "row",
13376
+ alignItems: "center",
13377
+ gap: 7,
13378
+ alignSelf: "flex-start",
13379
+ paddingVertical: 7,
13380
+ paddingHorizontal: 13,
13381
+ borderRadius: 9,
13382
+ backgroundColor: alpha(color, 0.14),
13383
+ borderWidth: 1,
13384
+ borderColor: alpha(color, 0.4)
13385
+ },
13386
+ style
13387
+ ],
13388
+ ...props,
13389
+ children: [
13390
+ /* @__PURE__ */ jsx(reactNative.View, { accessibilityElementsHidden: true, children: /* @__PURE__ */ jsx(StatusDot, { variant: statusDotVariant[status], glow: true }) }),
13391
+ /* @__PURE__ */ jsx(reactNative.Text, { testID: "status-pill-label", style: { fontSize: 13, fontWeight: "800", color }, children: text })
13392
+ ]
13393
+ }
13394
+ );
13395
+ }
13396
+ var t17 = getSemanticColors("dark");
13397
+ var categoryToken = {
13398
+ productive: null,
13399
+ threshold: "status-warning",
13400
+ stop: "status-error"
13401
+ };
13402
+ var floodOpacity = {
13403
+ productive: 0,
13404
+ threshold: 0.12,
13405
+ stop: 0.18
13406
+ };
13407
+ function liveAuraColor(category) {
13408
+ const token = categoryToken[category];
13409
+ return token ? t17[token] : null;
13410
+ }
13411
+ function LiveAuraFrame({
13412
+ category,
13413
+ pulse = true,
13414
+ className,
13415
+ children,
13416
+ style,
13417
+ ...props
13418
+ }) {
13419
+ const color = liveAuraColor(category);
13420
+ const tint = color ? alpha(color, floodOpacity[category]) : "transparent";
13421
+ const floodStyle = {
13422
+ ...reactNative.StyleSheet.absoluteFillObject,
13423
+ ...reactNative.Platform.OS === "web" ? (
13424
+ // backgroundImage is a runtime RNW style, not in RN ViewStyle types.
13425
+ { backgroundImage: `radial-gradient(130% 95% at 50% 0%, ${tint}, transparent 62%)` }
13426
+ ) : { backgroundColor: tint }
13427
+ };
13428
+ return /* @__PURE__ */ jsxs(
13429
+ reactNative.View,
13430
+ {
13431
+ testID: "live-aura-frame",
13432
+ className,
13433
+ style: [
13434
+ {
13435
+ position: "relative",
13436
+ overflow: "hidden",
13437
+ borderRadius: 10,
13438
+ backgroundColor: t17["background-base"],
13439
+ borderWidth: 1,
13440
+ borderColor: t17["border-default"]
13441
+ },
13442
+ style
13443
+ ],
13444
+ ...props,
13445
+ children: [
13446
+ color && /* @__PURE__ */ jsx(
13447
+ reactNative.View,
13448
+ {
13449
+ testID: "live-aura-flood",
13450
+ accessibilityElementsHidden: true,
13451
+ pointerEvents: "none",
13452
+ className: category === "stop" && pulse ? "animate-pulse" : void 0,
13453
+ style: floodStyle
13454
+ }
13455
+ ),
13456
+ children
12150
13457
  ]
12151
13458
  }
12152
13459
  );
@@ -12357,28 +13664,28 @@ function Scatter({
12357
13664
  testID: "scatter-canvas",
12358
13665
  style: { position: "absolute", top: 0, left: 0, width, height, overflow: "hidden" },
12359
13666
  children: [
12360
- ticksOf(yd, TICK_COUNT).map((t12, i) => {
12361
- const y = toY(t12);
13667
+ ticksOf(yd, TICK_COUNT).map((t18, i) => {
13668
+ const y = toY(t18);
12362
13669
  return /* @__PURE__ */ jsx(
12363
13670
  reactNative.View,
12364
13671
  {
12365
13672
  accessibilityElementsHidden: true,
12366
13673
  testID: "scatter-gridline-y",
12367
13674
  style: { position: "absolute", left: PLOT_LEFT2, width: innerW, top: y, height: 1, backgroundColor: GRID_LINE2 },
12368
- children: /* @__PURE__ */ jsx(reactNative.Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", left: -PLOT_LEFT2, top: -6, width: PLOT_LEFT2 - 6, textAlign: "right" }, children: formatTick(t12) })
13675
+ children: /* @__PURE__ */ jsx(reactNative.Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", left: -PLOT_LEFT2, top: -6, width: PLOT_LEFT2 - 6, textAlign: "right" }, children: formatTick(t18) })
12369
13676
  },
12370
13677
  `y-${i}`
12371
13678
  );
12372
13679
  }),
12373
- ticksOf(xd, TICK_COUNT).map((t12, i) => {
12374
- const x = toX(t12);
13680
+ ticksOf(xd, TICK_COUNT).map((t18, i) => {
13681
+ const x = toX(t18);
12375
13682
  return /* @__PURE__ */ jsx(
12376
13683
  reactNative.View,
12377
13684
  {
12378
13685
  accessibilityElementsHidden: true,
12379
13686
  testID: "scatter-gridline-x",
12380
13687
  style: { position: "absolute", top: PLOT_TOP, height: innerH, left: x, width: 1, backgroundColor: GRID_LINE2 },
12381
- children: /* @__PURE__ */ jsx(reactNative.Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", top: innerH + 4, left: -16, width: 32, textAlign: "center" }, children: formatTick(t12) })
13688
+ children: /* @__PURE__ */ jsx(reactNative.Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", top: innerH + 4, left: -16, width: 32, textAlign: "center" }, children: formatTick(t18) })
12382
13689
  },
12383
13690
  `x-${i}`
12384
13691
  );
@@ -12443,7 +13750,7 @@ var STATUS_SUCCESS4 = sem2["status-success"];
12443
13750
  var STATUS_WARNING4 = sem2["status-warning"];
12444
13751
  var STATUS_ERROR4 = sem2["status-error"];
12445
13752
  var TRACK = "rgba(255,255,255,0.08)";
12446
- var DEFAULT_THRESHOLDS = [
13753
+ var DEFAULT_THRESHOLDS2 = [
12447
13754
  { value: 0, color: STATUS_ERROR4 },
12448
13755
  { value: 60, color: STATUS_WARNING4 },
12449
13756
  { value: 80, color: STATUS_SUCCESS4 }
@@ -12458,8 +13765,8 @@ function clamp012(n) {
12458
13765
  function formatValue2(v) {
12459
13766
  return Number.isInteger(v) ? `${v}` : v.toFixed(1);
12460
13767
  }
12461
- function bandColor2(fraction, bands, min, max) {
12462
- const value = min + fraction * (max - min);
13768
+ function bandColor2(fraction2, bands, min, max) {
13769
+ const value = min + fraction2 * (max - min);
12463
13770
  const sorted = [...bands].sort((a, b) => a.value - b.value);
12464
13771
  let color = sorted[0]?.color ?? STATUS_SUCCESS4;
12465
13772
  for (const band of sorted) {
@@ -12471,15 +13778,15 @@ function buildTicks(fill, size, activeColor) {
12471
13778
  const center = size / 2;
12472
13779
  const radius = center - size * 0.08;
12473
13780
  return Array.from({ length: SEGMENTS }, (_, i) => {
12474
- const fraction = (i + 0.5) / SEGMENTS;
12475
- const deg = ARC_START + fraction * ARC_SWEEP;
13781
+ const fraction2 = (i + 0.5) / SEGMENTS;
13782
+ const deg = ARC_START + fraction2 * ARC_SWEEP;
12476
13783
  const rad = deg * Math.PI / 180;
12477
13784
  return {
12478
13785
  key: i,
12479
13786
  left: center + radius * Math.cos(rad),
12480
13787
  top: center + radius * Math.sin(rad),
12481
13788
  rotate: `${deg - 90}deg`,
12482
- color: fraction <= fill ? activeColor(fraction) : TRACK
13789
+ color: fraction2 <= fill ? activeColor(fraction2) : TRACK
12483
13790
  };
12484
13791
  });
12485
13792
  }
@@ -12490,7 +13797,7 @@ function Gauge2({
12490
13797
  size = 160,
12491
13798
  label,
12492
13799
  unit,
12493
- thresholds = DEFAULT_THRESHOLDS,
13800
+ thresholds = DEFAULT_THRESHOLDS2,
12494
13801
  color,
12495
13802
  className,
12496
13803
  ...props
@@ -12513,23 +13820,23 @@ function Gauge2({
12513
13820
  testID: "gauge",
12514
13821
  ...props,
12515
13822
  children: [
12516
- ticks.map((t12) => /* @__PURE__ */ jsx(
13823
+ ticks.map((t18) => /* @__PURE__ */ jsx(
12517
13824
  reactNative.View,
12518
13825
  {
12519
13826
  accessibilityElementsHidden: true,
12520
13827
  testID: "gauge-segment",
12521
13828
  style: {
12522
13829
  position: "absolute",
12523
- left: t12.left - tickThickness / 2,
12524
- top: t12.top - tickLength / 2,
13830
+ left: t18.left - tickThickness / 2,
13831
+ top: t18.top - tickLength / 2,
12525
13832
  width: tickThickness,
12526
13833
  height: tickLength,
12527
13834
  borderRadius: tickThickness / 2,
12528
- backgroundColor: t12.color,
12529
- transform: [{ rotate: t12.rotate }]
13835
+ backgroundColor: t18.color,
13836
+ transform: [{ rotate: t18.rotate }]
12530
13837
  }
12531
13838
  },
12532
- t12.key
13839
+ t18.key
12533
13840
  )),
12534
13841
  /* @__PURE__ */ jsxs(reactNative.View, { style: { position: "absolute", top: 0, left: 0, width: size, height: size }, className: "items-center justify-center", children: [
12535
13842
  /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-baseline gap-0.5", children: [
@@ -12575,7 +13882,7 @@ function BrandLockup({
12575
13882
  }
12576
13883
  var stateConfig = {
12577
13884
  // live = vivid green with an expanding ring; rest = solid amber (no pulse — operator); idle is dim.
12578
- live: { label: "LIVE", color: "success", pulse: "ping", textColor: "primary" },
13885
+ live: { label: "LIVE", color: "live", pulse: "ping", textColor: "primary" },
12579
13886
  rest: { label: "REST", color: "warning", pulse: false, textColor: "primary" },
12580
13887
  idle: { label: "IDLE", color: "default", pulse: false, textColor: "secondary" }
12581
13888
  };
@@ -12739,6 +14046,127 @@ function TopBar({
12739
14046
  }
12740
14047
  );
12741
14048
  }
14049
+ function NavItem({
14050
+ icon,
14051
+ label,
14052
+ active = false,
14053
+ live = false,
14054
+ onPress,
14055
+ className
14056
+ }) {
14057
+ const glyphColor = active ? "text-brand-primary" : "text-text-tertiary";
14058
+ const labelColor = active ? "text-brand-primary" : live ? "text-status-live-muted" : "text-text-tertiary";
14059
+ return /* @__PURE__ */ jsxs(
14060
+ reactNative.Pressable,
14061
+ {
14062
+ accessibilityRole: "tab",
14063
+ accessibilityState: { selected: active },
14064
+ accessibilityLabel: label,
14065
+ onPress,
14066
+ className: cn("relative h-[46px] w-[60px] items-center justify-center", className),
14067
+ children: [
14068
+ active ? /* @__PURE__ */ jsx(
14069
+ reactNative.View,
14070
+ {
14071
+ testID: "nav-item-accent",
14072
+ className: "absolute left-0 top-[13px] bottom-[13px] w-[3px] rounded-r-[3px] bg-brand-primary"
14073
+ }
14074
+ ) : null,
14075
+ /* @__PURE__ */ jsxs(
14076
+ reactNative.View,
14077
+ {
14078
+ className: cn(
14079
+ "h-[46px] w-[46px] items-center justify-center gap-[3px] rounded-[11px]",
14080
+ glyphColor
14081
+ ),
14082
+ children: [
14083
+ icon,
14084
+ /* @__PURE__ */ jsx(
14085
+ Typography,
14086
+ {
14087
+ variant: "button",
14088
+ color: "inherit",
14089
+ className: cn("text-[8.5px] font-bold uppercase tracking-[0.4px]", labelColor),
14090
+ children: label
14091
+ }
14092
+ )
14093
+ ]
14094
+ }
14095
+ )
14096
+ ]
14097
+ }
14098
+ );
14099
+ }
14100
+ var defaultNavItems = [
14101
+ { key: "live", label: "Live", icon: /* @__PURE__ */ jsx(ActivityIcon, { size: 20, color: "currentColor" }) },
14102
+ { key: "review", label: "Review", icon: /* @__PURE__ */ jsx(HistoryIcon, { size: 20, color: "currentColor" }) },
14103
+ { key: "program", label: "Plan", icon: /* @__PURE__ */ jsx(LayersIcon, { size: 20, color: "currentColor" }) },
14104
+ { key: "body", label: "Body", icon: /* @__PURE__ */ jsx(PersonStandingIcon, { size: 20, color: "currentColor" }) }
14105
+ ];
14106
+ function SideNav({
14107
+ items = defaultNavItems,
14108
+ activeKey,
14109
+ onNavigate,
14110
+ liveKey = null,
14111
+ className
14112
+ }) {
14113
+ return /* @__PURE__ */ jsx(
14114
+ reactNative.View,
14115
+ {
14116
+ accessibilityRole: "tablist",
14117
+ className: cn(
14118
+ "w-[60px] items-center gap-[6px] border-r border-border bg-background-base py-3",
14119
+ className
14120
+ ),
14121
+ children: items.map((item) => /* @__PURE__ */ jsx(
14122
+ NavItem,
14123
+ {
14124
+ icon: item.icon,
14125
+ label: item.label,
14126
+ active: item.key === activeKey,
14127
+ live: item.key === liveKey && item.key !== activeKey,
14128
+ onPress: () => onNavigate?.(item.key)
14129
+ },
14130
+ item.key
14131
+ ))
14132
+ }
14133
+ );
14134
+ }
14135
+ var DEMO_DEVICES = [
14136
+ { id: "Voltra-A3F2", nickname: "Left Cable", slot: "L", state: "connected" },
14137
+ { id: "Voltra-9B1C", nickname: "Right Cable", slot: "R", state: "connected" }
14138
+ ];
14139
+ function ContentPlaceholder() {
14140
+ return /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1 items-center justify-center", children: /* @__PURE__ */ jsx(reactNative.Text, { className: "font-heading text-sm text-text-tertiary", children: "main content region" }) });
14141
+ }
14142
+ function DashboardShell({
14143
+ activeKey = "live",
14144
+ navItems,
14145
+ liveKey = null,
14146
+ state = "live",
14147
+ devices = DEMO_DEVICES,
14148
+ subtitle = "wall dashboard",
14149
+ onNavigate,
14150
+ onSelectDevice,
14151
+ children,
14152
+ className
14153
+ }) {
14154
+ return /* @__PURE__ */ jsxs(reactNative.View, { className: cn("flex-1 flex-row bg-surface-base", className), children: [
14155
+ /* @__PURE__ */ jsx(SideNav, { activeKey, items: navItems, liveKey, onNavigate }),
14156
+ /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-1", children: [
14157
+ /* @__PURE__ */ jsx(
14158
+ TopBar,
14159
+ {
14160
+ state,
14161
+ devices,
14162
+ subtitle,
14163
+ onSelectDevice
14164
+ }
14165
+ ),
14166
+ /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1", children: children ?? /* @__PURE__ */ jsx(ContentPlaceholder, {}) })
14167
+ ] })
14168
+ ] });
14169
+ }
12742
14170
 
12743
14171
  // src/utils/form.ts
12744
14172
  function getFieldValidationProps(state) {
@@ -12832,13 +14260,16 @@ exports.Accordion = Accordion;
12832
14260
  exports.AccordionButton = AccordionButton;
12833
14261
  exports.AccordionItem = AccordionItem;
12834
14262
  exports.AccordionPanel = AccordionPanel;
14263
+ exports.ActivityIcon = ActivityIcon;
12835
14264
  exports.Alert = Alert;
12836
14265
  exports.AlertDescription = AlertDescription;
12837
14266
  exports.AlertTitle = AlertTitle;
14267
+ exports.AlertTriangleIcon = AlertTriangleIcon;
12838
14268
  exports.Autocomplete = Autocomplete;
12839
14269
  exports.Avatar = Avatar;
12840
14270
  exports.AvatarBadge = AvatarBadge;
12841
14271
  exports.AvatarGroup = AvatarGroup;
14272
+ exports.AwardIcon = AwardIcon;
12842
14273
  exports.Badge = Badge;
12843
14274
  exports.BadgeText = BadgeText;
12844
14275
  exports.BaseBadge = BaseBadge;
@@ -12863,10 +14294,12 @@ exports.CardTitle = CardTitle;
12863
14294
  exports.Checkbox = Checkbox;
12864
14295
  exports.CheckboxGroup = CheckboxGroup;
12865
14296
  exports.Chip = Chip;
14297
+ exports.CircleSlashIcon = CircleSlashIcon;
12866
14298
  exports.CircularProgress = CircularProgress;
12867
14299
  exports.Collapse = Collapse;
12868
14300
  exports.CollapseButton = CollapseButton;
12869
14301
  exports.CollapseContent = CollapseContent;
14302
+ exports.DashboardShell = DashboardShell;
12870
14303
  exports.DataRow = DataRow;
12871
14304
  exports.DateTime = DateTime;
12872
14305
  exports.DeviationBar = DeviationBar;
@@ -12881,6 +14314,10 @@ exports.DrawerHeader = DrawerHeader;
12881
14314
  exports.DumbbellIcon = DumbbellIcon;
12882
14315
  exports.EmptyState = EmptyState;
12883
14316
  exports.ExerciseCard = ExerciseCard;
14317
+ exports.ExerciseCardHeading = ExerciseCardHeading;
14318
+ exports.ExerciseHeading = ExerciseHeading;
14319
+ exports.ExerciseIndicator = ExerciseIndicator;
14320
+ exports.FatigueMeter = FatigueMeter;
12884
14321
  exports.FormActions = FormActions;
12885
14322
  exports.FormField = FormField;
12886
14323
  exports.FormRow = FormRow;
@@ -12889,20 +14326,26 @@ exports.Gauge = Gauge2;
12889
14326
  exports.HStack = HStack;
12890
14327
  exports.Heading = Heading;
12891
14328
  exports.HelpTip = HelpTip;
14329
+ exports.HistoryIcon = HistoryIcon;
14330
+ exports.INDICATOR_PRECEDENCE = INDICATOR_PRECEDENCE;
12892
14331
  exports.IconBox = IconBox;
12893
14332
  exports.Indicator = Indicator;
14333
+ exports.InfoIcon = InfoIcon;
12894
14334
  exports.Input = Input;
12895
14335
  exports.InputBar = InputBar;
12896
14336
  exports.InputGroup = InputGroup;
12897
14337
  exports.IntensityBar = IntensityBar;
12898
14338
  exports.Label = Label;
12899
14339
  exports.LabelWithHelp = LabelWithHelp;
14340
+ exports.LayersIcon = LayersIcon;
12900
14341
  exports.Link = Link;
12901
14342
  exports.ListItem = ListItem;
12902
14343
  exports.ListItemContent = ListItemContent;
12903
14344
  exports.ListItemDivider = ListItemDivider;
12904
14345
  exports.ListItemIcon = ListItemIcon;
12905
14346
  exports.ListItemTrailing = ListItemTrailing;
14347
+ exports.LiveAuraFrame = LiveAuraFrame;
14348
+ exports.METRIC_FONT = METRIC_FONT;
12906
14349
  exports.Menu = Menu;
12907
14350
  exports.MenuDivider = MenuDivider;
12908
14351
  exports.MenuGroup = MenuGroup;
@@ -12913,7 +14356,9 @@ exports.MesoCard = MesoCard;
12913
14356
  exports.MesoProgressBar = MesoProgressBar;
12914
14357
  exports.MesoStatusCard = MesoStatusCard;
12915
14358
  exports.Metric = Metric;
14359
+ exports.MetricCell = MetricCell;
12916
14360
  exports.MetricGroup = MetricGroup;
14361
+ exports.MetricTiles = MetricTiles;
12917
14362
  exports.Modal = Modal;
12918
14363
  exports.ModalBody = ModalBody;
12919
14364
  exports.ModalCloseButton = ModalCloseButton;
@@ -12922,9 +14367,11 @@ exports.ModalFooter = ModalFooter;
12922
14367
  exports.ModalHeader = ModalHeader;
12923
14368
  exports.ModalTitle = ModalTitle;
12924
14369
  exports.MuscleGroupChip = MuscleGroupChip;
14370
+ exports.NavItem = NavItem;
12925
14371
  exports.Overline = Overline;
12926
14372
  exports.Paragraph = Paragraph;
12927
14373
  exports.PasswordInput = PasswordInput;
14374
+ exports.PersonStandingIcon = PersonStandingIcon;
12928
14375
  exports.Pill = Pill;
12929
14376
  exports.PlaceholderStrip = PlaceholderStrip;
12930
14377
  exports.Popover = Popover;
@@ -12939,13 +14386,26 @@ exports.Radio = Radio;
12939
14386
  exports.RadioGroup = RadioGroup;
12940
14387
  exports.ReadinessCheck = ReadinessCheck;
12941
14388
  exports.RestTimer = RestTimer;
14389
+ exports.SET_STRIP_VARIABLE_COLOR = SET_STRIP_VARIABLE_COLOR;
14390
+ exports.SET_STRIP_ZONES = SET_STRIP_ZONES;
14391
+ exports.ScaleIcon = ScaleIcon;
12942
14392
  exports.Scatter = Scatter;
14393
+ exports.ScheduleTiles = ScheduleTiles;
12943
14394
  exports.Section = Section;
12944
14395
  exports.SectionContent = SectionContent;
12945
14396
  exports.SectionHeader = SectionHeader;
14397
+ exports.SegmentedBar = SegmentedBar;
14398
+ exports.SegmentedProgressBar = SegmentedProgressBar;
12946
14399
  exports.Select = Select;
14400
+ exports.SessionHeader = SessionHeader;
14401
+ exports.SessionRail = SessionRail;
12947
14402
  exports.SessionStatePill = SessionStatePill;
14403
+ exports.SetBar = SetBar;
12948
14404
  exports.SetRow = SetRow;
14405
+ exports.SetStrip = SetStrip;
14406
+ exports.SetTableHeader = SetTableHeader;
14407
+ exports.SetsRepsLoad = SetsRepsLoad;
14408
+ exports.SideNav = SideNav;
12949
14409
  exports.Sidebar = Sidebar;
12950
14410
  exports.SidebarContent = SidebarContent;
12951
14411
  exports.SidebarDivider = SidebarDivider;
@@ -12963,6 +14423,7 @@ exports.Spinner = Spinner;
12963
14423
  exports.Stack = Stack;
12964
14424
  exports.StarIcon = StarIcon;
12965
14425
  exports.StatusDot = StatusDot;
14426
+ exports.StatusPill = StatusPill2;
12966
14427
  exports.StepContent = StepContent;
12967
14428
  exports.StepIndicator = StepIndicator;
12968
14429
  exports.StepLabel = StepLabel;
@@ -12988,6 +14449,8 @@ exports.TableRow = TableRow;
12988
14449
  exports.Tabs = Tabs;
12989
14450
  exports.TempoBar = TempoBar;
12990
14451
  exports.TempoDisplay = TempoDisplay;
14452
+ exports.Tile = Tile;
14453
+ exports.TimerReadout = TimerReadout;
12991
14454
  exports.Toast = Toast;
12992
14455
  exports.ToastProvider = ToastProvider;
12993
14456
  exports.ToolbarButton = ToolbarButton;
@@ -12995,14 +14458,17 @@ exports.ToolbarButtonGroup = ToolbarButtonGroup;
12995
14458
  exports.Tooltip = Tooltip;
12996
14459
  exports.TopBar = TopBar;
12997
14460
  exports.Treemap = Treemap;
14461
+ exports.TrendingDownIcon = TrendingDownIcon;
12998
14462
  exports.Typography = Typography;
12999
14463
  exports.VStack = VStack;
13000
14464
  exports.VelocityStrip = VelocityStrip;
13001
14465
  exports.VoltrasMark = VoltrasMark;
14466
+ exports.VolumeLandmarkBar = VolumeLandmarkBar;
13002
14467
  exports.WeekRow = WeekRow;
13003
14468
  exports.WeightBadge = WeightBadge;
13004
14469
  exports.WorkoutCard = WorkoutCard;
13005
14470
  exports.WorkoutPill = WorkoutPill;
14471
+ exports.ZoneTrack = ZoneTrack;
13006
14472
  exports.alpha = alpha;
13007
14473
  exports.applyThemePreset = applyThemePreset;
13008
14474
  exports.audiobookPreset = audiobookPreset;
@@ -13022,11 +14488,13 @@ exports.createRaisedHoverShadow = createRaisedHoverShadow;
13022
14488
  exports.createRaisedShadow = createRaisedShadow;
13023
14489
  exports.darkThemeCSSVars = darkThemeCSSVars;
13024
14490
  exports.darken = darken;
14491
+ exports.defaultNavItems = defaultNavItems;
13025
14492
  exports.defaultPreset = defaultPreset;
13026
14493
  exports.discreteRainbow = discreteRainbow;
13027
14494
  exports.divergingScale = divergingScale;
13028
14495
  exports.elevationSystem = elevationSystem;
13029
14496
  exports.formatDateTime = formatDateTime;
14497
+ exports.formatDuration = formatDuration;
13030
14498
  exports.formatPrescription = formatPrescription;
13031
14499
  exports.formatSignedPct = formatSignedPct;
13032
14500
  exports.formatVelocity = formatVelocity;
@@ -13063,7 +14531,10 @@ exports.isValidEmail = isValidEmail;
13063
14531
  exports.lightThemeCSSVars = lightThemeCSSVars;
13064
14532
  exports.lighten = lighten;
13065
14533
  exports.linearGradient = linearGradient;
14534
+ exports.liveAuraColor = liveAuraColor;
13066
14535
  exports.neumorphicShadows = neumorphicShadows;
14536
+ exports.paceTone = paceTone;
14537
+ exports.paceToneColor = paceToneColor;
13067
14538
  exports.primitiveBorderRadius = primitiveBorderRadius;
13068
14539
  exports.primitiveBreakpoints = primitiveBreakpoints;
13069
14540
  exports.primitiveColors = primitiveColors;
@@ -13073,6 +14544,7 @@ exports.primitiveSpacing = primitiveSpacing;
13073
14544
  exports.primitiveTypography = primitiveTypography;
13074
14545
  exports.primitiveZIndex = primitiveZIndex;
13075
14546
  exports.resolveColor = resolveColor;
14547
+ exports.resolveIndicator = resolveIndicator;
13076
14548
  exports.rgbToHex = rgbToHex;
13077
14549
  exports.rgbToHsv = rgbToHsv;
13078
14550
  exports.roundRpe = roundRpe;
@@ -13084,11 +14556,14 @@ exports.semanticColorsLight = semanticColorsLight;
13084
14556
  exports.semanticTypography = semanticTypography;
13085
14557
  exports.sequentialEffort = sequentialEffort;
13086
14558
  exports.shadowColors = shadowColors;
14559
+ exports.statusPillColor = statusPillColor;
13087
14560
  exports.surfaceGradient = surfaceGradient;
13088
14561
  exports.useTable = useTable;
14562
+ exports.useTimer = useTimer;
13089
14563
  exports.useToast = useToast;
13090
14564
  exports.useToolbarButton = useToolbarButton;
13091
14565
  exports.validateCssPropertyManifest = validateCssPropertyManifest;
13092
14566
  exports.validationRules = validationRules;
14567
+ exports.velocityZoneColor = velocityZoneColor;
13093
14568
  //# sourceMappingURL=index.js.map
13094
14569
  //# sourceMappingURL=index.js.map