@titan-design/react-ui 0.2.7 → 0.3.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 (82) hide show
  1. package/dist/bodymap-BhG55xHM.d.mts +318 -0
  2. package/dist/bodymap-BhG55xHM.d.ts +318 -0
  3. package/dist/bodymap.d.mts +3 -0
  4. package/dist/bodymap.d.ts +3 -0
  5. package/dist/bodymap.js +2670 -0
  6. package/dist/bodymap.js.map +1 -0
  7. package/dist/bodymap.mjs +1101 -0
  8. package/dist/bodymap.mjs.map +1 -0
  9. package/dist/{chunk-5SSKGS6E.mjs → chunk-7XPB4NAO.mjs} +124 -4
  10. package/dist/chunk-7XPB4NAO.mjs.map +1 -0
  11. package/dist/{chunk-3VLOBS6O.mjs → chunk-P7TSQUN6.mjs} +3 -3
  12. package/dist/{chunk-3VLOBS6O.mjs.map → chunk-P7TSQUN6.mjs.map} +1 -1
  13. package/dist/chunk-TOD2WQBG.mjs +1048 -0
  14. package/dist/chunk-TOD2WQBG.mjs.map +1 -0
  15. package/dist/index.d.mts +375 -249
  16. package/dist/index.d.ts +375 -249
  17. package/dist/index.js +1259 -918
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.mjs +1314 -2107
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/theme/index.d.mts +260 -0
  22. package/dist/theme/index.d.ts +260 -0
  23. package/dist/theme/index.js +122 -2
  24. package/dist/theme/index.js.map +1 -1
  25. package/dist/theme/index.mjs +2 -2
  26. package/dist/theme/tokens-css.d.mts +5 -3
  27. package/dist/theme/tokens-css.d.ts +5 -3
  28. package/dist/theme/tokens-css.js +318 -9
  29. package/dist/theme/tokens-css.js.map +1 -1
  30. package/dist/theme/tokens-css.mjs +1 -1
  31. package/dist/theme/tokens-css.mjs.map +1 -1
  32. package/package.json +10 -1
  33. package/src/bodymap.ts +37 -0
  34. package/src/components/custom/Table/Table.test.tsx +27 -0
  35. package/src/components/custom/Table/Table.tsx +33 -17
  36. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +118 -0
  37. package/src/components/custom/Workout/ActiveWorkoutPage.test.tsx +171 -0
  38. package/src/components/custom/Workout/ActiveWorkoutPage.tsx +447 -0
  39. package/src/components/custom/Workout/BodyMap.tsx +5 -6
  40. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +7 -8
  41. package/src/components/custom/Workout/CapacityBandChart.tsx +1 -1
  42. package/src/components/custom/Workout/ExerciseCard.test.tsx +24 -0
  43. package/src/components/custom/Workout/ExerciseCard.tsx +28 -15
  44. package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +225 -0
  45. package/src/components/custom/Workout/ExerciseDetailPage.test.tsx +128 -0
  46. package/src/components/custom/Workout/ExerciseDetailPage.tsx +630 -0
  47. package/src/components/custom/Workout/InputBar.tsx +6 -7
  48. package/src/components/custom/Workout/MesoCard.tsx +4 -2
  49. package/src/components/custom/Workout/MesoProgressBar.test.tsx +12 -0
  50. package/src/components/custom/Workout/MesoProgressBar.tsx +5 -1
  51. package/src/components/custom/Workout/MesoStatusCard.tsx +5 -5
  52. package/src/components/custom/Workout/PlaceholderStrip.tsx +2 -3
  53. package/src/components/custom/Workout/PrHistoryModal.tsx +5 -5
  54. package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +257 -0
  55. package/src/components/custom/Workout/ProgramPlanningPage.test.tsx +142 -0
  56. package/src/components/custom/Workout/ProgramPlanningPage.tsx +422 -0
  57. package/src/components/custom/Workout/ReadinessCheck.tsx +7 -8
  58. package/src/components/custom/Workout/RestTimer.test.tsx +10 -0
  59. package/src/components/custom/Workout/RestTimer.tsx +11 -9
  60. package/src/components/custom/Workout/SetRow.tsx +13 -19
  61. package/src/components/custom/Workout/StatusDot.tsx +1 -0
  62. package/src/components/custom/Workout/StrengthTrendChart.tsx +5 -5
  63. package/src/components/custom/Workout/TempoDisplay.tsx +3 -1
  64. package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +212 -0
  65. package/src/components/custom/Workout/TrainingStatusPage.test.tsx +131 -0
  66. package/src/components/custom/Workout/TrainingStatusPage.tsx +321 -0
  67. package/src/components/custom/Workout/VelocityStrip.test.tsx +10 -0
  68. package/src/components/custom/Workout/VelocityStrip.tsx +16 -15
  69. package/src/components/custom/Workout/WeekRow.tsx +1 -1
  70. package/src/components/custom/Workout/WorkoutCard.tsx +5 -3
  71. package/src/components/custom/Workout/index.ts +71 -32
  72. package/src/theme/config.completeness.test.ts +77 -0
  73. package/src/theme/config.ts +151 -0
  74. package/src/theme/manifest/design-freeze.test.ts +155 -0
  75. package/src/theme/manifest/extract-css-properties.fixture.html +29 -0
  76. package/src/theme/manifest/extract-css-properties.test.ts +112 -0
  77. package/src/theme/tokens-css.ts +5 -3
  78. package/src/theme/workout-tokens.ts +12 -14
  79. package/src/utils/index.ts +10 -0
  80. package/src/utils/workout-format.test.ts +81 -0
  81. package/src/utils/workout-format.ts +83 -0
  82. package/dist/chunk-5SSKGS6E.mjs.map +0 -1
package/dist/index.js CHANGED
@@ -6,12 +6,10 @@ var tailwindMerge = require('tailwind-merge');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var React24 = require('react');
8
8
  var lucideReact = require('lucide-react');
9
- var BodyHighlighter = require('react-native-body-highlighter');
10
9
 
11
10
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
11
 
13
12
  var React24__default = /*#__PURE__*/_interopDefault(React24);
14
- var BodyHighlighter__default = /*#__PURE__*/_interopDefault(BodyHighlighter);
15
13
 
16
14
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
17
15
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
@@ -1211,6 +1209,28 @@ var PasswordInput = React24.forwardRef(function PasswordInput2({ showIcon, hideI
1211
1209
  });
1212
1210
 
1213
1211
  // src/theme/config.ts
1212
+ var themeIndependentCSSVars = {
1213
+ // RGB decomposed values for glow shadows
1214
+ "--color-brand-primary-rgb": "255, 121, 0",
1215
+ "--color-brand-secondary-rgb": "64, 109, 135",
1216
+ "--color-status-success-rgb": "20, 184, 166",
1217
+ "--color-status-error-rgb": "209, 67, 67",
1218
+ "--color-status-warning-rgb": "255, 176, 32",
1219
+ "--color-status-info-rgb": "33, 150, 243",
1220
+ "--color-background-base-rgb": "16, 16, 16",
1221
+ // Font families
1222
+ "--font-family-sans": "'Inter'",
1223
+ "--font-family-body": "'Nunito Sans'",
1224
+ "--font-family-heading": "'Space Grotesk'",
1225
+ "--font-family-mono": "'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace",
1226
+ // Animation tokens
1227
+ "--ease-out": "cubic-bezier(0.22, 1, 0.36, 1)",
1228
+ "--ease-in-out": "cubic-bezier(0.65, 0, 0.35, 1)",
1229
+ "--ease-spring": "cubic-bezier(0.34, 1.56, 0.64, 1)",
1230
+ "--duration-fast": "150ms",
1231
+ "--duration-normal": "250ms",
1232
+ "--duration-slow": "400ms"
1233
+ };
1214
1234
  var lightThemeCSSVars = {
1215
1235
  "--color-brand-primary": semanticColorsLight["brand-primary"],
1216
1236
  "--color-brand-primary-light": semanticColorsLight["brand-primary-light"],
@@ -1251,7 +1271,56 @@ var lightThemeCSSVars = {
1251
1271
  "--color-interactive-active": semanticColorsLight["interactive-active"],
1252
1272
  "--color-interactive-selected": semanticColorsLight["interactive-selected"],
1253
1273
  "--color-interactive-disabled": semanticColorsLight["interactive-disabled"],
1254
- "--color-divider": semanticColorsLight["divider"]
1274
+ "--color-divider": semanticColorsLight["divider"],
1275
+ "--color-brand-primary-hover": semanticColorsLight["brand-primary-hover"],
1276
+ "--color-brand-primary-active": semanticColorsLight["brand-primary-active"],
1277
+ "--color-brand-secondary-hover": semanticColorsLight["brand-secondary-hover"],
1278
+ "--color-brand-secondary-active": semanticColorsLight["brand-secondary-active"],
1279
+ "--color-status-success-light": semanticColorsLight["status-success-light"],
1280
+ "--color-status-success-dark": semanticColorsLight["status-success-dark"],
1281
+ "--color-status-error-light": semanticColorsLight["status-error-light"],
1282
+ "--color-status-error-dark": semanticColorsLight["status-error-dark"],
1283
+ "--color-status-warning-light": semanticColorsLight["status-warning-light"],
1284
+ "--color-status-warning-dark": semanticColorsLight["status-warning-dark"],
1285
+ "--color-status-info-light": semanticColorsLight["status-info-light"],
1286
+ "--color-status-info-dark": semanticColorsLight["status-info-dark"],
1287
+ "--color-on-status-success": semanticColorsLight["on-status-success"],
1288
+ "--color-on-status-error": semanticColorsLight["on-status-error"],
1289
+ "--color-on-status-warning": semanticColorsLight["on-status-warning"],
1290
+ "--color-on-status-info": semanticColorsLight["on-status-info"],
1291
+ "--color-result-improve": semanticColorsLight["result-improve"],
1292
+ "--color-result-improve-light": semanticColorsLight["result-improve-light"],
1293
+ "--color-result-improve-dark": semanticColorsLight["result-improve-dark"],
1294
+ "--color-result-degrade": semanticColorsLight["result-degrade"],
1295
+ "--color-result-degrade-light": semanticColorsLight["result-degrade-light"],
1296
+ "--color-result-degrade-dark": semanticColorsLight["result-degrade-dark"],
1297
+ "--color-result-inconclusive": semanticColorsLight["result-inconclusive"],
1298
+ "--color-result-inconclusive-light": semanticColorsLight["result-inconclusive-light"],
1299
+ "--color-result-neutral": semanticColorsLight["result-neutral"],
1300
+ "--color-on-result-improve": semanticColorsLight["on-result-improve"],
1301
+ "--color-on-result-degrade": semanticColorsLight["on-result-degrade"],
1302
+ "--color-on-result-inconclusive": semanticColorsLight["on-result-inconclusive"],
1303
+ "--color-data-1": semanticColorsLight["data-1"],
1304
+ "--color-data-2": semanticColorsLight["data-2"],
1305
+ "--color-data-3": semanticColorsLight["data-3"],
1306
+ "--color-data-4": semanticColorsLight["data-4"],
1307
+ "--color-data-5": semanticColorsLight["data-5"],
1308
+ "--color-data-6": semanticColorsLight["data-6"],
1309
+ "--color-data-7": semanticColorsLight["data-7"],
1310
+ "--color-data-8": semanticColorsLight["data-8"],
1311
+ "--color-data-9": semanticColorsLight["data-9"],
1312
+ "--color-data-10": semanticColorsLight["data-10"],
1313
+ "--color-text-link-hover": semanticColorsLight["text-link-hover"],
1314
+ "--color-surface-overlay": semanticColorsLight["surface-overlay"],
1315
+ "--color-surface-input": semanticColorsLight["surface-input"],
1316
+ "--color-border-input": semanticColorsLight["border-input"],
1317
+ "--color-border-input-hover": semanticColorsLight["border-input-hover"],
1318
+ "--color-border-input-focus": semanticColorsLight["border-input-focus"],
1319
+ "--color-border-input-error": semanticColorsLight["border-input-error"],
1320
+ "--color-interactive-disabled-text": semanticColorsLight["interactive-disabled-text"],
1321
+ "--color-avatar-background": semanticColorsLight["avatar-background"],
1322
+ "--color-avatar-text": semanticColorsLight["avatar-text"],
1323
+ ...themeIndependentCSSVars
1255
1324
  };
1256
1325
  var darkThemeCSSVars = {
1257
1326
  "--color-brand-primary": semanticColorsDark["brand-primary"],
@@ -1293,7 +1362,56 @@ var darkThemeCSSVars = {
1293
1362
  "--color-interactive-active": semanticColorsDark["interactive-active"],
1294
1363
  "--color-interactive-selected": semanticColorsDark["interactive-selected"],
1295
1364
  "--color-interactive-disabled": semanticColorsDark["interactive-disabled"],
1296
- "--color-divider": semanticColorsDark["divider"]
1365
+ "--color-divider": semanticColorsDark["divider"],
1366
+ "--color-brand-primary-hover": semanticColorsDark["brand-primary-hover"],
1367
+ "--color-brand-primary-active": semanticColorsDark["brand-primary-active"],
1368
+ "--color-brand-secondary-hover": semanticColorsDark["brand-secondary-hover"],
1369
+ "--color-brand-secondary-active": semanticColorsDark["brand-secondary-active"],
1370
+ "--color-status-success-light": semanticColorsDark["status-success-light"],
1371
+ "--color-status-success-dark": semanticColorsDark["status-success-dark"],
1372
+ "--color-status-error-light": semanticColorsDark["status-error-light"],
1373
+ "--color-status-error-dark": semanticColorsDark["status-error-dark"],
1374
+ "--color-status-warning-light": semanticColorsDark["status-warning-light"],
1375
+ "--color-status-warning-dark": semanticColorsDark["status-warning-dark"],
1376
+ "--color-status-info-light": semanticColorsDark["status-info-light"],
1377
+ "--color-status-info-dark": semanticColorsDark["status-info-dark"],
1378
+ "--color-on-status-success": semanticColorsDark["on-status-success"],
1379
+ "--color-on-status-error": semanticColorsDark["on-status-error"],
1380
+ "--color-on-status-warning": semanticColorsDark["on-status-warning"],
1381
+ "--color-on-status-info": semanticColorsDark["on-status-info"],
1382
+ "--color-result-improve": semanticColorsDark["result-improve"],
1383
+ "--color-result-improve-light": semanticColorsDark["result-improve-light"],
1384
+ "--color-result-improve-dark": semanticColorsDark["result-improve-dark"],
1385
+ "--color-result-degrade": semanticColorsDark["result-degrade"],
1386
+ "--color-result-degrade-light": semanticColorsDark["result-degrade-light"],
1387
+ "--color-result-degrade-dark": semanticColorsDark["result-degrade-dark"],
1388
+ "--color-result-inconclusive": semanticColorsDark["result-inconclusive"],
1389
+ "--color-result-inconclusive-light": semanticColorsDark["result-inconclusive-light"],
1390
+ "--color-result-neutral": semanticColorsDark["result-neutral"],
1391
+ "--color-on-result-improve": semanticColorsDark["on-result-improve"],
1392
+ "--color-on-result-degrade": semanticColorsDark["on-result-degrade"],
1393
+ "--color-on-result-inconclusive": semanticColorsDark["on-result-inconclusive"],
1394
+ "--color-data-1": semanticColorsDark["data-1"],
1395
+ "--color-data-2": semanticColorsDark["data-2"],
1396
+ "--color-data-3": semanticColorsDark["data-3"],
1397
+ "--color-data-4": semanticColorsDark["data-4"],
1398
+ "--color-data-5": semanticColorsDark["data-5"],
1399
+ "--color-data-6": semanticColorsDark["data-6"],
1400
+ "--color-data-7": semanticColorsDark["data-7"],
1401
+ "--color-data-8": semanticColorsDark["data-8"],
1402
+ "--color-data-9": semanticColorsDark["data-9"],
1403
+ "--color-data-10": semanticColorsDark["data-10"],
1404
+ "--color-text-link-hover": semanticColorsDark["text-link-hover"],
1405
+ "--color-surface-overlay": semanticColorsDark["surface-overlay"],
1406
+ "--color-surface-input": semanticColorsDark["surface-input"],
1407
+ "--color-border-input": semanticColorsDark["border-input"],
1408
+ "--color-border-input-hover": semanticColorsDark["border-input-hover"],
1409
+ "--color-border-input-focus": semanticColorsDark["border-input-focus"],
1410
+ "--color-border-input-error": semanticColorsDark["border-input-error"],
1411
+ "--color-interactive-disabled-text": semanticColorsDark["interactive-disabled-text"],
1412
+ "--color-avatar-background": semanticColorsDark["avatar-background"],
1413
+ "--color-avatar-text": semanticColorsDark["avatar-text"],
1414
+ ...themeIndependentCSSVars
1297
1415
  };
1298
1416
  function getThemeCSSVars(mode) {
1299
1417
  return mode === "dark" ? darkThemeCSSVars : lightThemeCSSVars;
@@ -5982,7 +6100,7 @@ function Table({
5982
6100
  selectable,
5983
6101
  allRowIds: rowIds
5984
6102
  },
5985
- children: /* @__PURE__ */ jsx(reactNative.View, { className: cn("w-full", className), ...props, children: /* @__PURE__ */ jsx(reactNative.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, children: /* @__PURE__ */ jsx(reactNative.View, { className: "w-full min-w-full", children: isLoading ? /* @__PURE__ */ jsx(TableLoadingSkeleton, { rowCount: loadingRowCount }) : children }) }) })
6103
+ children: /* @__PURE__ */ jsx(reactNative.View, { className: cn("w-full", className), ...props, children: /* @__PURE__ */ jsx(reactNative.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, children: /* @__PURE__ */ jsx(reactNative.View, { role: "table", className: "w-full min-w-full", children: isLoading ? /* @__PURE__ */ jsx(TableLoadingSkeleton, { rowCount: loadingRowCount }) : children }) }) })
5986
6104
  }
5987
6105
  );
5988
6106
  }
@@ -5990,14 +6108,14 @@ function TableHeader({ children, className }) {
5990
6108
  return /* @__PURE__ */ jsx(
5991
6109
  reactNative.View,
5992
6110
  {
5993
- accessibilityRole: "none",
6111
+ role: "rowgroup",
5994
6112
  className: cn("bg-background-subtle rounded-t-lg", className),
5995
6113
  children
5996
6114
  }
5997
6115
  );
5998
6116
  }
5999
6117
  function TableBody({ children, className }) {
6000
- return /* @__PURE__ */ jsx(reactNative.View, { accessibilityRole: "none", className, children });
6118
+ return /* @__PURE__ */ jsx(reactNative.View, { role: "rowgroup", className, children });
6001
6119
  }
6002
6120
  function TableRow({
6003
6121
  isSelected = false,
@@ -6009,7 +6127,7 @@ function TableRow({
6009
6127
  return /* @__PURE__ */ jsx(
6010
6128
  reactNative.View,
6011
6129
  {
6012
- accessibilityRole: "none",
6130
+ role: "row",
6013
6131
  className: cn(
6014
6132
  "flex-row border-b border-divider",
6015
6133
  isHoverable && "web:hover:bg-interactive-hover",
@@ -6033,6 +6151,7 @@ function TableHeaderCell({
6033
6151
  const { sortColumn, sortDirection, onSort } = React24.useContext(TableContext);
6034
6152
  const isSorted = sortKey && sortColumn === sortKey;
6035
6153
  const isSortable = !!sortKey && !!onSort;
6154
+ const ariaSort = isSorted ? sortDirection === "asc" ? "ascending" : sortDirection === "desc" ? "descending" : "none" : "none";
6036
6155
  const handlePress = (e) => {
6037
6156
  if (isSortable && sortKey) {
6038
6157
  onSort(sortKey);
@@ -6059,26 +6178,34 @@ function TableHeaderCell({
6059
6178
  ] });
6060
6179
  if (isSortable) {
6061
6180
  return /* @__PURE__ */ jsx(
6062
- reactNative.Pressable,
6181
+ reactNative.View,
6063
6182
  {
6064
- accessibilityRole: "button",
6065
- accessibilityLabel: `Sort by ${children}`,
6066
- onPress: handlePress,
6183
+ role: "columnheader",
6184
+ "aria-sort": ariaSort,
6067
6185
  style: width ? { width } : { flex: 1 },
6068
- className: cn(
6069
- "flex-row items-center px-4 py-3",
6070
- alignStyles3[align],
6071
- "web:hover:bg-interactive-hover active:bg-interactive-active",
6072
- className
6073
- ),
6074
- ...props,
6075
- children: content
6186
+ children: /* @__PURE__ */ jsx(
6187
+ reactNative.Pressable,
6188
+ {
6189
+ accessibilityRole: "button",
6190
+ accessibilityLabel: `Sort by ${children}`,
6191
+ onPress: handlePress,
6192
+ className: cn(
6193
+ "flex-row items-center px-4 py-3",
6194
+ alignStyles3[align],
6195
+ "web:hover:bg-interactive-hover active:bg-interactive-active",
6196
+ className
6197
+ ),
6198
+ ...props,
6199
+ children: content
6200
+ }
6201
+ )
6076
6202
  }
6077
6203
  );
6078
6204
  }
6079
6205
  return /* @__PURE__ */ jsx(
6080
6206
  reactNative.View,
6081
6207
  {
6208
+ role: "columnheader",
6082
6209
  style: width ? { width } : { flex: 1 },
6083
6210
  className: cn(
6084
6211
  "flex-row items-center px-4 py-3",
@@ -6104,6 +6231,7 @@ function TableCell({
6104
6231
  return /* @__PURE__ */ jsx(
6105
6232
  reactNative.View,
6106
6233
  {
6234
+ role: "cell",
6107
6235
  style: width ? { width } : { flex: 1 },
6108
6236
  className: cn(
6109
6237
  "justify-center px-4 py-3.5",
@@ -6985,36 +7113,6 @@ function StatusDot({
6985
7113
  }
6986
7114
  return /* @__PURE__ */ jsx(reactNative.View, { className, ...props, children: dot });
6987
7115
  }
6988
-
6989
- // src/theme/workout-tokens.ts
6990
- var WORKOUT_TOKENS = {
6991
- // BodyMap volume heatmap scale (drive dynamic SVG fills, so inline values).
6992
- // Maps weekly-volume status against MEV/MAV/MRV landmarks to a fill color.
6993
- heatmap: {
6994
- none: "#E0E0E0",
6995
- // gray — no training data
6996
- under: "#4A90D9",
6997
- // cool blue — below MEV
6998
- maintenance: "#F5C842",
6999
- // warm yellow — MEV to MAV
7000
- productive: "#4CAF50",
7001
- // green — MAV to MRV
7002
- approaching: "#FFA502",
7003
- // orange — near MRV
7004
- over: "#FF6B35"
7005
- // red-orange — over MRV
7006
- },
7007
- // Border colors (use inline to avoid 'border' class pitfall)
7008
- border: {
7009
- default: "#1F1F1F",
7010
- strong: "#2C2C2C"},
7011
- // Surface colors for inline use
7012
- surface: {
7013
- raised: "#1C1C1C",
7014
- elevated: "#191919"
7015
- }};
7016
-
7017
- // src/components/custom/Workout/PlaceholderStrip.tsx
7018
7116
  function SingleStrip({
7019
7117
  width,
7020
7118
  className,
@@ -7030,7 +7128,7 @@ function SingleStrip({
7030
7128
  backgroundColor: "transparent",
7031
7129
  borderWidth: 1,
7032
7130
  borderStyle: "dashed",
7033
- borderColor: WORKOUT_TOKENS.border.strong,
7131
+ borderColor: "var(--color-border-strong)",
7034
7132
  borderRadius: 1,
7035
7133
  opacity: 0.5
7036
7134
  },
@@ -7070,7 +7168,7 @@ function SegmentedStrip({
7070
7168
  backgroundColor: "transparent",
7071
7169
  borderWidth: 1,
7072
7170
  borderStyle: "dashed",
7073
- borderColor: WORKOUT_TOKENS.border.strong,
7171
+ borderColor: "var(--color-border-strong)",
7074
7172
  borderRadius: 1,
7075
7173
  minWidth: 4
7076
7174
  },
@@ -7092,6 +7190,63 @@ function PlaceholderStrip({
7092
7190
  }
7093
7191
  return /* @__PURE__ */ jsx(SingleStrip, { ...props });
7094
7192
  }
7193
+
7194
+ // src/theme/workout-tokens.ts
7195
+ var WORKOUT_TOKENS = {
7196
+ // Canonical 4-band performance scale — the single source for BOTH the
7197
+ // VelocityStrip zone bars and the SetRow RPE color (TD-03.43). The direction
7198
+ // is intentionally inverted between the two consumers: for velocity, green =
7199
+ // fastest/best; for RPE, green = easiest. Each component maps its own
7200
+ // thresholds onto these four colors. Kept as vivid data-viz values (distinct
7201
+ // from the muted status tokens) and theme-independent, so — like the heatmap
7202
+ // scale below — they are consumed inline as a JS import rather than CSS vars.
7203
+ // A JS import also resolves on native RN, unlike var(--…) strings.
7204
+ scale: {
7205
+ green: "#2ed573",
7206
+ yellow: "#ffd43b",
7207
+ orange: "#ffa502",
7208
+ red: "#ff4757"
7209
+ }};
7210
+
7211
+ // src/utils/workout-format.ts
7212
+ function roundRpe(rpe) {
7213
+ return Math.round(rpe * 2) / 2;
7214
+ }
7215
+ function rpeColor(rpe) {
7216
+ if (rpe >= 9.5) return WORKOUT_TOKENS.scale.red;
7217
+ if (rpe >= 8.5) return WORKOUT_TOKENS.scale.orange;
7218
+ if (rpe >= 7) return WORKOUT_TOKENS.scale.yellow;
7219
+ return WORKOUT_TOKENS.scale.green;
7220
+ }
7221
+ function roundWeight(weight) {
7222
+ return Math.round(weight);
7223
+ }
7224
+ function formatVelocity(velocity) {
7225
+ return velocity.toFixed(2);
7226
+ }
7227
+ function roundTempo(tempo) {
7228
+ return tempo.map((v) => Math.round(v * 10) / 10);
7229
+ }
7230
+ function formatSignedPct(ratio) {
7231
+ const pct = Math.round(ratio * 100);
7232
+ return `${pct > 0 ? "+" : ""}${pct}%`;
7233
+ }
7234
+ function formatPrescription(p) {
7235
+ if (p == null) return null;
7236
+ let reps = null;
7237
+ if (p.repsLow != null && p.repsHigh != null) {
7238
+ reps = p.repsLow === p.repsHigh ? `${p.repsLow}` : `${p.repsLow}\u2013${p.repsHigh}`;
7239
+ } else if (p.repsLow != null) {
7240
+ reps = `${p.repsLow}`;
7241
+ }
7242
+ const weight = p.weightLbs != null ? `${p.weightLbs} lb` : null;
7243
+ const head = [reps, weight].filter((s) => s != null).join(" @ ");
7244
+ const rpe = p.rpe != null ? `RPE ${p.rpe}` : null;
7245
+ const full = [head.length > 0 ? head : null, rpe].filter((s) => s != null).join(" \xB7 ");
7246
+ return full.length > 0 ? full : null;
7247
+ }
7248
+
7249
+ // src/components/custom/Workout/TempoDisplay.tsx
7095
7250
  var INTER = "Inter, sans-serif";
7096
7251
  var TEXT_TERTIARY = "#6B7280";
7097
7252
  var phaseColors = {
@@ -7147,7 +7302,7 @@ function TempoDisplay({
7147
7302
  ...props
7148
7303
  }) {
7149
7304
  const [showTooltip, setShowTooltip] = React24.useState(false);
7150
- const [eccentric, pauseBottom, concentric, pauseTop] = tempo;
7305
+ const [eccentric, pauseBottom, concentric, pauseTop] = roundTempo(tempo);
7151
7306
  const isSm = size === "sm";
7152
7307
  const fontSize = isSm ? 9 : 11;
7153
7308
  const monoColor = TEXT_TERTIARY;
@@ -7655,12 +7810,7 @@ function WorkoutPill({
7655
7810
  }
7656
7811
  return pill;
7657
7812
  }
7658
- var VEL_COLORS = {
7659
- green: "#2ed573",
7660
- yellow: "#ffd43b",
7661
- orange: "#ffa502",
7662
- red: "#ff4757"
7663
- };
7813
+ var VEL_COLORS = WORKOUT_TOKENS.scale;
7664
7814
  function getVelocityZoneColor(velocity) {
7665
7815
  if (velocity >= 1) return "vel-green";
7666
7816
  if (velocity >= 0.75) return "vel-yellow";
@@ -7798,7 +7948,7 @@ function VelocityStrip({
7798
7948
  paddingBottom: expanded ? 24 : 0,
7799
7949
  paddingHorizontal: expanded ? 6 : 0,
7800
7950
  paddingVertical: expanded ? void 0 : 8,
7801
- backgroundColor: expanded ? "#1C1C1C" : "transparent",
7951
+ backgroundColor: expanded ? "var(--color-surface-raised)" : "transparent",
7802
7952
  overflow: expanded ? "visible" : "hidden"
7803
7953
  }
7804
7954
  ],
@@ -7813,7 +7963,7 @@ function VelocityStrip({
7813
7963
  style: { flexDirection: "row", flex: 1, gap: 2, alignItems: "flex-end" },
7814
7964
  children: velocities.map((v, i) => {
7815
7965
  const zone = getVelocityZoneColor(v);
7816
- const barHeightPct = Math.round(v / (maxVelocity * 1.15) * 100);
7966
+ const barHeightPct = maxVelocity > 0 ? Math.round(v / (maxVelocity * 1.15) * 100) : 0;
7817
7967
  const bar = /* @__PURE__ */ jsxs(
7818
7968
  reactNative.View,
7819
7969
  {
@@ -7822,9 +7972,9 @@ function VelocityStrip({
7822
7972
  expanded && /* @__PURE__ */ jsx(reactNative.Animated.View, { style: { opacity: labelOpacity, alignItems: "center", position: "absolute", top: -13, left: 0, right: 0 }, accessibilityElementsHidden: true, children: /* @__PURE__ */ jsx(
7823
7973
  reactNative.Text,
7824
7974
  {
7825
- style: { fontSize: 8, fontWeight: "600", color: "#9CA3AF" },
7975
+ style: { fontSize: 8, fontWeight: "600", color: "var(--color-text-secondary)" },
7826
7976
  testID: `velocity-label-${i}`,
7827
- children: v.toFixed(2)
7977
+ children: formatVelocity(v)
7828
7978
  }
7829
7979
  ) }),
7830
7980
  /* @__PURE__ */ jsx(
@@ -7840,7 +7990,7 @@ function VelocityStrip({
7840
7990
  },
7841
7991
  testID: `velocity-bar-${i}`,
7842
7992
  accessibilityRole: "image",
7843
- accessibilityLabel: `Rep ${i + 1}: ${v.toFixed(2)} meters per second`
7993
+ accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second`
7844
7994
  }
7845
7995
  )
7846
7996
  ]
@@ -7854,7 +8004,7 @@ function VelocityStrip({
7854
8004
  style: { flex: 1, height: "100%" },
7855
8005
  onPress: () => onRepPress(i, v),
7856
8006
  accessibilityRole: "button",
7857
- accessibilityLabel: `Rep ${i + 1}: ${v.toFixed(2)} meters per second, tap for details`,
8007
+ accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second, tap for details`,
7858
8008
  testID: `velocity-bar-pressable-${i}`,
7859
8009
  children: bar
7860
8010
  },
@@ -7884,7 +8034,7 @@ function VelocityStrip({
7884
8034
  {
7885
8035
  style: {
7886
8036
  fontSize: 10,
7887
- color: "#9CA3AF",
8037
+ color: "var(--color-text-secondary)",
7888
8038
  fontFamily: "Inter, sans-serif"
7889
8039
  },
7890
8040
  children: [
@@ -7892,7 +8042,7 @@ function VelocityStrip({
7892
8042
  " ",
7893
8043
  "\xB7",
7894
8044
  " ",
7895
- meanVelocity.toFixed(2),
8045
+ formatVelocity(meanVelocity),
7896
8046
  " m/s"
7897
8047
  ]
7898
8048
  }
@@ -7902,7 +8052,7 @@ function VelocityStrip({
7902
8052
  {
7903
8053
  style: {
7904
8054
  fontSize: 10,
7905
- color: "#9CA3AF",
8055
+ color: "var(--color-text-secondary)",
7906
8056
  fontFamily: "Inter, sans-serif",
7907
8057
  ...getLossStyle(loss)
7908
8058
  },
@@ -8163,15 +8313,9 @@ function Sparkline({
8163
8313
  }
8164
8314
  );
8165
8315
  }
8166
- function getRPEColor(rpe) {
8167
- if (rpe >= 9.5) return "#ff4757";
8168
- if (rpe >= 8.5) return "#ffa502";
8169
- if (rpe >= 7) return "#ffd43b";
8170
- return "#2ed573";
8171
- }
8172
8316
  function formatAccessibilityLabel(setNumber, reps, weight, unit) {
8173
8317
  const repsText = reps != null ? `${reps} reps` : "no reps";
8174
- const weightText = weight != null ? `at ${weight} ${unit}` : "";
8318
+ const weightText = weight != null ? `at ${roundWeight(weight)} ${unit}` : "";
8175
8319
  return `Set ${setNumber}: ${repsText}${weightText ? ` ${weightText}` : ""}`;
8176
8320
  }
8177
8321
  function SetRow({
@@ -8249,7 +8393,7 @@ function SetRow({
8249
8393
  fontSize: 13,
8250
8394
  fontWeight: "600",
8251
8395
  fontFamily: "Inter, sans-serif",
8252
- color: "#9CA3AF"
8396
+ color: "var(--color-text-secondary)"
8253
8397
  },
8254
8398
  children: setNumber
8255
8399
  }
@@ -8267,9 +8411,9 @@ function SetRow({
8267
8411
  style: {
8268
8412
  fontSize: 12,
8269
8413
  fontFamily: "Inter, sans-serif",
8270
- color: "#9CA3AF"
8414
+ color: "var(--color-text-secondary)"
8271
8415
  },
8272
- children: previous ? `${previous.reps} x ${previous.weight}` : "\u2014"
8416
+ children: previous ? `${previous.reps} x ${roundWeight(previous.weight)}` : "\u2014"
8273
8417
  }
8274
8418
  )
8275
8419
  }
@@ -8286,7 +8430,7 @@ function SetRow({
8286
8430
  fontSize: 13,
8287
8431
  fontStyle: "italic",
8288
8432
  fontFamily: "Inter, sans-serif",
8289
- color: "#6B7280"
8433
+ color: "var(--color-text-tertiary)"
8290
8434
  },
8291
8435
  testID: "set-row-target-reps",
8292
8436
  children: targets.reps
@@ -8298,7 +8442,7 @@ function SetRow({
8298
8442
  fontSize: 14,
8299
8443
  fontWeight: "600",
8300
8444
  fontFamily: "Inter, sans-serif",
8301
- color: reps != null ? "#F3F4F6" : "#6B7280"
8445
+ color: reps != null ? "var(--color-text-primary)" : "var(--color-text-tertiary)"
8302
8446
  },
8303
8447
  children: reps != null ? reps : "\u2014"
8304
8448
  }
@@ -8317,10 +8461,10 @@ function SetRow({
8317
8461
  fontSize: 13,
8318
8462
  fontStyle: "italic",
8319
8463
  fontFamily: "Inter, sans-serif",
8320
- color: "#6B7280"
8464
+ color: "var(--color-text-tertiary)"
8321
8465
  },
8322
8466
  testID: "set-row-target-weight",
8323
- children: targets.weight
8467
+ children: roundWeight(targets.weight)
8324
8468
  }
8325
8469
  ) : /* @__PURE__ */ jsx(
8326
8470
  reactNative.Text,
@@ -8329,9 +8473,9 @@ function SetRow({
8329
8473
  fontSize: 14,
8330
8474
  fontWeight: "600",
8331
8475
  fontFamily: "Inter, sans-serif",
8332
- color: weight != null ? "#F3F4F6" : "#6B7280"
8476
+ color: weight != null ? "var(--color-text-primary)" : "var(--color-text-tertiary)"
8333
8477
  },
8334
- children: weight != null ? weight : "\u2014"
8478
+ children: weight != null ? roundWeight(weight) : "\u2014"
8335
8479
  }
8336
8480
  )
8337
8481
  }
@@ -8348,9 +8492,9 @@ function SetRow({
8348
8492
  fontSize: 13,
8349
8493
  fontWeight: "600",
8350
8494
  fontFamily: "Inter, sans-serif",
8351
- color: rpe != null ? getRPEColor(rpe) : "#6B7280"
8495
+ color: rpe != null ? rpeColor(rpe) : "var(--color-text-tertiary)"
8352
8496
  },
8353
- children: rpe != null ? rpe : "\u2014"
8497
+ children: rpe != null ? roundRpe(rpe) : "\u2014"
8354
8498
  }
8355
8499
  )
8356
8500
  }
@@ -8394,15 +8538,15 @@ function SetRow({
8394
8538
  );
8395
8539
  }
8396
8540
  var BRAND_PRIMARY2 = "#FF7900";
8397
- var TEXT_PRIMARY = "#F3F4F6";
8398
- var TEXT_TERTIARY2 = "#6B7280";
8541
+ var TEXT_PRIMARY = "var(--color-text-primary)";
8542
+ var TEXT_TERTIARY2 = "var(--color-text-tertiary)";
8399
8543
  var inputStyle = {
8400
8544
  fontSize: 14,
8401
8545
  fontWeight: "600",
8402
8546
  fontFamily: "Inter, sans-serif",
8403
- backgroundColor: WORKOUT_TOKENS.surface.raised,
8547
+ backgroundColor: "var(--color-surface-raised)",
8404
8548
  borderWidth: 1,
8405
- borderColor: WORKOUT_TOKENS.border.strong,
8549
+ borderColor: "var(--color-border-strong)",
8406
8550
  borderRadius: 6,
8407
8551
  textAlign: "center",
8408
8552
  color: TEXT_PRIMARY,
@@ -8429,9 +8573,9 @@ function InputBar({
8429
8573
  {
8430
8574
  style: {
8431
8575
  width: "100%",
8432
- backgroundColor: WORKOUT_TOKENS.surface.elevated,
8576
+ backgroundColor: "var(--color-surface-elevated)",
8433
8577
  borderTopWidth: 1,
8434
- borderTopColor: WORKOUT_TOKENS.border.default,
8578
+ borderTopColor: "var(--color-border-default)",
8435
8579
  paddingTop: 10,
8436
8580
  paddingHorizontal: 16,
8437
8581
  paddingBottom: 12,
@@ -8566,15 +8710,16 @@ function RestTimer({
8566
8710
  const minutes = Math.floor(remainingSec / 60);
8567
8711
  const seconds = remainingSec % 60;
8568
8712
  const timeDisplay = `${minutes}:${String(seconds).padStart(2, "0")}`;
8569
- const progressPct = Math.min(100, elapsedMs / (totalSeconds * 1e3) * 100);
8713
+ const totalMs = totalSeconds * 1e3;
8714
+ const progressPct = totalMs > 0 ? Math.min(100, elapsedMs / totalMs * 100) : 100;
8570
8715
  return /* @__PURE__ */ jsxs(
8571
8716
  reactNative.View,
8572
8717
  {
8573
8718
  style: {
8574
8719
  width: "100%",
8575
- backgroundColor: WORKOUT_TOKENS.surface.raised,
8720
+ backgroundColor: "var(--color-surface-raised)",
8576
8721
  borderTopWidth: 1,
8577
- borderTopColor: WORKOUT_TOKENS.border.default,
8722
+ borderTopColor: "var(--color-border-default)",
8578
8723
  paddingVertical: 12,
8579
8724
  paddingHorizontal: 16
8580
8725
  },
@@ -8602,7 +8747,7 @@ function RestTimer({
8602
8747
  fontWeight: "600",
8603
8748
  textTransform: "uppercase",
8604
8749
  letterSpacing: 1,
8605
- color: "#9CA3AF"
8750
+ color: "var(--color-text-secondary)"
8606
8751
  },
8607
8752
  testID: "rest-timer-label",
8608
8753
  children: "REST"
@@ -8614,7 +8759,7 @@ function RestTimer({
8614
8759
  style: {
8615
8760
  fontSize: 11,
8616
8761
  fontFamily: "Inter, sans-serif",
8617
- color: "#6B7280",
8762
+ color: "var(--color-text-tertiary)",
8618
8763
  marginTop: 2
8619
8764
  },
8620
8765
  testID: "rest-timer-next-set",
@@ -8629,7 +8774,7 @@ function RestTimer({
8629
8774
  fontSize: 28,
8630
8775
  fontFamily: '"Space Grotesk", sans-serif',
8631
8776
  fontWeight: "700",
8632
- color: "#F3F4F6",
8777
+ color: "var(--color-text-primary)",
8633
8778
  fontVariant: ["tabular-nums"],
8634
8779
  letterSpacing: -0.5
8635
8780
  },
@@ -8645,7 +8790,7 @@ function RestTimer({
8645
8790
  {
8646
8791
  style: {
8647
8792
  height: 3,
8648
- backgroundColor: WORKOUT_TOKENS.border.default,
8793
+ backgroundColor: "var(--color-border-default)",
8649
8794
  borderRadius: 2,
8650
8795
  marginBottom: 12
8651
8796
  },
@@ -8685,7 +8830,7 @@ function RestTimer({
8685
8830
  fontSize: 11,
8686
8831
  fontFamily: "Inter, sans-serif",
8687
8832
  fontWeight: "600",
8688
- color: "#9CA3AF"
8833
+ color: "var(--color-text-secondary)"
8689
8834
  },
8690
8835
  children: "+30s"
8691
8836
  }
@@ -8725,10 +8870,21 @@ function RestTimer({
8725
8870
  );
8726
8871
  }
8727
8872
  var COLORS = {
8728
- textPrimary: "#F3F4F6",
8729
- textSecondary: "#9CA3AF",
8730
- textTertiary: "#6B7280"};
8731
- var COLUMN_HEADERS = ["SET", "PREV", "REPS", "LBS", "RPE"];
8873
+ // Theme-aware tokens: text foregrounds and this card's own surfaces/borders
8874
+ // must flip together so text stays readable in both light and dark. The
8875
+ // expanded/pressed surfaces are self-owned here (not from a themed Card), so
8876
+ // hardcoding dark values would leave dark-on-dark once the text flips.
8877
+ // react-native-web passes the CSS var through to the inline style.
8878
+ textPrimary: "var(--color-text-primary)",
8879
+ textSecondary: "var(--color-text-secondary)",
8880
+ textTertiary: "var(--color-text-tertiary)",
8881
+ surfaceRaised: "var(--color-surface-raised)",
8882
+ surfaceElevated: "var(--color-surface-elevated)",
8883
+ borderDefault: "var(--color-border-default)"
8884
+ };
8885
+ function buildColumnHeaders(unit) {
8886
+ return ["SET", "PREV", "REPS", unit.toUpperCase(), "RPE"];
8887
+ }
8732
8888
  function getSupersetBorderRadius(position) {
8733
8889
  switch (position) {
8734
8890
  case "first":
@@ -8769,7 +8925,7 @@ function getSupersetMargin(position) {
8769
8925
  }
8770
8926
  function formatSummary(summary) {
8771
8927
  if (!summary) return "";
8772
- return `${summary.sets}\xD7${summary.reps} @ ${summary.weight} ${summary.unit}`;
8928
+ return `${summary.sets}\xD7${summary.reps} @ ${roundWeight(summary.weight)} ${summary.unit}`;
8773
8929
  }
8774
8930
  function formatAccessibilityLabel2(name, summary, prescription) {
8775
8931
  if (summary) return `${name}, ${formatSummary(summary)}`;
@@ -8807,7 +8963,7 @@ function CollapsedCard({
8807
8963
  padding: 12,
8808
8964
  paddingHorizontal: 14,
8809
8965
  cursor: "pointer",
8810
- backgroundColor: pressed ? WORKOUT_TOKENS.surface.raised : "transparent",
8966
+ backgroundColor: pressed ? COLORS.surfaceRaised : "transparent",
8811
8967
  ...borderRadius,
8812
8968
  ...supersetMargin
8813
8969
  },
@@ -8848,7 +9004,7 @@ function CollapsedCard({
8848
9004
  e1rm && /* @__PURE__ */ jsx(
8849
9005
  WeightBadge,
8850
9006
  {
8851
- value: e1rm.value,
9007
+ value: roundWeight(e1rm.value),
8852
9008
  unit: e1rm.unit,
8853
9009
  size: "sm",
8854
9010
  showIcon: false,
@@ -8903,13 +9059,15 @@ function ExpandedCard({
8903
9059
  }) {
8904
9060
  const borderRadius = getSupersetBorderRadius(supersetPosition);
8905
9061
  const supersetMargin = getSupersetMargin(supersetPosition);
9062
+ const unit = summary?.unit ?? sets?.[0]?.unit ?? "lbs";
9063
+ const columnHeaders = buildColumnHeaders(unit);
8906
9064
  return /* @__PURE__ */ jsxs(
8907
9065
  reactNative.View,
8908
9066
  {
8909
9067
  style: {
8910
- backgroundColor: WORKOUT_TOKENS.surface.elevated,
9068
+ backgroundColor: COLORS.surfaceElevated,
8911
9069
  borderWidth: 1,
8912
- borderColor: WORKOUT_TOKENS.border.default,
9070
+ borderColor: COLORS.borderDefault,
8913
9071
  ...borderRadius,
8914
9072
  ...supersetMargin
8915
9073
  },
@@ -8932,7 +9090,7 @@ function ExpandedCard({
8932
9090
  paddingHorizontal: 14,
8933
9091
  paddingBottom: 10,
8934
9092
  borderBottomWidth: 1,
8935
- borderBottomColor: WORKOUT_TOKENS.border.default
9093
+ borderBottomColor: COLORS.borderDefault
8936
9094
  },
8937
9095
  children: [
8938
9096
  /* @__PURE__ */ jsx(
@@ -8965,7 +9123,7 @@ function ExpandedCard({
8965
9123
  e1rm && /* @__PURE__ */ jsx(
8966
9124
  WeightBadge,
8967
9125
  {
8968
- value: e1rm.value,
9126
+ value: roundWeight(e1rm.value),
8969
9127
  unit: e1rm.unit,
8970
9128
  size: "sm",
8971
9129
  showIcon: false,
@@ -8989,7 +9147,7 @@ function ExpandedCard({
8989
9147
  paddingBottom: 4
8990
9148
  },
8991
9149
  testID: "exercise-card-column-headers",
8992
- children: COLUMN_HEADERS.map((header, i) => {
9150
+ children: columnHeaders.map((header, i) => {
8993
9151
  const widths = [36, void 0, 44, 56, 36];
8994
9152
  const width = widths[i];
8995
9153
  const isFlex = width === void 0;
@@ -9183,10 +9341,11 @@ function MesoSegment({ meso, isActive, onPress }) {
9183
9341
  }).start();
9184
9342
  };
9185
9343
  const progress = meso.status === "current" ? clampProgress(meso.currentWeek, meso.weekCount) : 0;
9344
+ const segmentFlex = Math.max(1, meso.weekCount);
9186
9345
  return /* @__PURE__ */ jsx(
9187
9346
  reactNative.Pressable,
9188
9347
  {
9189
- style: { flex: meso.weekCount },
9348
+ style: { flex: segmentFlex },
9190
9349
  onPress: () => onPress(meso.id),
9191
9350
  onPressIn: handlePressIn,
9192
9351
  onPressOut: handlePressOut,
@@ -9323,7 +9482,7 @@ function WeekRow({
9323
9482
  fontSize: 13,
9324
9483
  fontWeight: "700",
9325
9484
  fontFamily: "Inter, sans-serif",
9326
- color: isCurrent ? BRAND_PRIMARY5 : "#F3F4F6"
9485
+ color: isCurrent ? BRAND_PRIMARY5 : "var(--color-text-primary)"
9327
9486
  },
9328
9487
  accessibilityElementsHidden: true,
9329
9488
  children: `W${weekNumber}`
@@ -9363,9 +9522,11 @@ function WeekRow({
9363
9522
  );
9364
9523
  }
9365
9524
  var COLORS2 = {
9366
- textPrimary: "#F3F4F6",
9367
- textSecondary: "#9CA3AF",
9368
- textTertiary: "#6B7280",
9525
+ // Theme-aware foregrounds: resolve to a readable color in both light and dark
9526
+ // (react-native-web passes the CSS var through to the inline style).
9527
+ textPrimary: "var(--color-text-primary)",
9528
+ textSecondary: "var(--color-text-secondary)",
9529
+ textTertiary: "var(--color-text-tertiary)",
9369
9530
  statusSuccess: "#14B8A6",
9370
9531
  brandPrimary: "#FF7900",
9371
9532
  borderDefault: "#1F1F1F",
@@ -9513,8 +9674,8 @@ var BRAND_PRIMARY6 = "#FF7900";
9513
9674
  var BRAND_PRIMARY_DARK = "#C45E00";
9514
9675
  var BRAND_PRIMARY_LIGHT = "#FFB066";
9515
9676
  var BORDER_DEFAULT = "#1F1F1F";
9516
- var TEXT_PRIMARY2 = "#F3F4F6";
9517
- var TEXT_SECONDARY = "#9CA3AF";
9677
+ var TEXT_PRIMARY2 = "var(--color-text-primary)";
9678
+ var TEXT_SECONDARY = "var(--color-text-secondary)";
9518
9679
  var ACCENT_STOPS = [BRAND_PRIMARY_DARK, BRAND_PRIMARY6, BRAND_PRIMARY_LIGHT];
9519
9680
  function heatmapColor(percentage) {
9520
9681
  const clamped = Math.max(0, Math.min(100, percentage));
@@ -9670,11 +9831,11 @@ function MesoCard({
9670
9831
  var BRAND_PRIMARY7 = "#FF7900";
9671
9832
  var RECENT_BORDER = "rgba(255,176,32,0.3)";
9672
9833
  var RECENT_GLOW = "0 0 12px rgba(255,176,32,0.06)";
9673
- var SURFACE_RAISED = "#1C1C1C";
9674
- var BORDER_DEFAULT2 = "#1F1F1F";
9675
- var TEXT_PRIMARY3 = "#F3F4F6";
9676
- var TEXT_SECONDARY2 = "#9CA3AF";
9677
- var TEXT_TERTIARY3 = "#6B7280";
9834
+ var SURFACE_RAISED = "var(--color-surface-raised)";
9835
+ var BORDER_DEFAULT2 = "var(--color-border-default)";
9836
+ var TEXT_PRIMARY3 = "var(--color-text-primary)";
9837
+ var TEXT_SECONDARY2 = "var(--color-text-secondary)";
9838
+ var TEXT_TERTIARY3 = "var(--color-text-tertiary)";
9678
9839
  var TYPE_LABELS = {
9679
9840
  e1rm: "e1RM",
9680
9841
  weight: "Weight",
@@ -9863,8 +10024,8 @@ var BRAND_PRIMARY_SUBTLE = "rgba(255,121,0,0.12)";
9863
10024
  var STATUS_SUCCESS = "#14B8A6";
9864
10025
  var STATUS_WARNING = "#FFB020";
9865
10026
  var STATUS_ERROR = "#D14343";
9866
- var TEXT_PRIMARY4 = "#F3F4F6";
9867
- var TEXT_SECONDARY3 = "#9CA3AF";
10027
+ var TEXT_PRIMARY4 = "var(--color-text-primary)";
10028
+ var TEXT_SECONDARY3 = "var(--color-text-secondary)";
9868
10029
  var EMOJI_SETS = {
9869
10030
  sleep: ["\u{1F634}", "\u{1F610}", "\u{1F642}", "\u{1F60A}", "\u{1F929}"],
9870
10031
  soreness: ["\u{1F635}", "\u{1F623}", "\u{1F610}", "\u{1F642}", "\u{1F4AA}"],
@@ -9926,8 +10087,8 @@ function EmojiSlider({ factor }) {
9926
10087
  paddingVertical: 8,
9927
10088
  borderRadius: 8,
9928
10089
  borderWidth: 1,
9929
- borderColor: selected ? BRAND_PRIMARY8 : WORKOUT_TOKENS.border.default,
9930
- backgroundColor: selected ? BRAND_PRIMARY_SUBTLE : WORKOUT_TOKENS.surface.raised,
10090
+ borderColor: selected ? BRAND_PRIMARY8 : "var(--color-border-default)",
10091
+ backgroundColor: selected ? BRAND_PRIMARY_SUBTLE : "var(--color-surface-raised)",
9931
10092
  opacity: selected ? 1 : pressed ? 0.8 : 0.55,
9932
10093
  transform: [{ scale: selected ? 1.06 : 1 }]
9933
10094
  }),
@@ -9954,7 +10115,7 @@ function ScoreGauge({ score }) {
9954
10115
  borderRadius: 30,
9955
10116
  borderWidth: 4,
9956
10117
  borderColor: color,
9957
- backgroundColor: WORKOUT_TOKENS.surface.raised,
10118
+ backgroundColor: "var(--color-surface-raised)",
9958
10119
  alignItems: "center",
9959
10120
  justifyContent: "center"
9960
10121
  },
@@ -9979,8 +10140,8 @@ function WarmUpCard({ validation }) {
9979
10140
  padding: 12,
9980
10141
  borderRadius: 8,
9981
10142
  borderWidth: 1,
9982
- borderColor: WORKOUT_TOKENS.border.default,
9983
- backgroundColor: WORKOUT_TOKENS.surface.raised
10143
+ borderColor: "var(--color-border-default)",
10144
+ backgroundColor: "var(--color-surface-raised)"
9984
10145
  },
9985
10146
  testID: "readiness-check-warmup",
9986
10147
  children: [
@@ -10053,14 +10214,14 @@ function ReadinessCheck({
10053
10214
  var BRAND_PRIMARY9 = "#FF7900";
10054
10215
  var BRAND_PRIMARY_DARK2 = "#C45E00";
10055
10216
  var BRAND_PRIMARY_LIGHT2 = "#FFB066";
10056
- var TEXT_PRIMARY5 = "#F3F4F6";
10057
- var TEXT_SECONDARY4 = "#9CA3AF";
10058
- var TEXT_TERTIARY4 = "#6B7280";
10217
+ var TEXT_PRIMARY5 = "var(--color-text-primary)";
10218
+ var TEXT_SECONDARY4 = "var(--color-text-secondary)";
10219
+ var TEXT_TERTIARY4 = "var(--color-text-tertiary)";
10059
10220
  var SUCCESS = "#14B8A6";
10060
10221
  var WARNING = "#FFB020";
10061
10222
  var ERROR = "#D14343";
10062
10223
  var ACCENT_STOPS2 = [BRAND_PRIMARY_DARK2, BRAND_PRIMARY9, BRAND_PRIMARY_LIGHT2];
10063
- var CARD_GRADIENT = "linear-gradient(135deg, #191919 0%, rgba(25,25,25,0.95) 100%)";
10224
+ var CARD_GRADIENT = "linear-gradient(135deg, var(--color-surface-elevated) 0%, var(--color-surface-raised) 100%)";
10064
10225
  var GAUGE_GRADIENT = "linear-gradient(90deg, rgba(20,184,166,0.25) 0%, rgba(255,176,32,0.25) 50%, rgba(209,67,67,0.25) 100%)";
10065
10226
  var STATUS_VARIANTS = {
10066
10227
  success: { bg: "rgba(20,184,166,0.15)", border: "rgba(20,184,166,0.3)", text: SUCCESS },
@@ -10415,15 +10576,15 @@ function MesoStatusCard({
10415
10576
  );
10416
10577
  }
10417
10578
  var BRAND_PRIMARY10 = "#FF7900";
10418
- var TEXT_PRIMARY6 = "#F3F4F6";
10419
- var TEXT_SECONDARY5 = "#9CA3AF";
10420
- var TEXT_TERTIARY5 = "#6B7280";
10421
- var BORDER_STRONG = "#2C2C2C";
10579
+ var TEXT_PRIMARY6 = "var(--color-text-primary)";
10580
+ var TEXT_SECONDARY5 = "var(--color-text-secondary)";
10581
+ var TEXT_TERTIARY5 = "var(--color-text-tertiary)";
10582
+ var BORDER_STRONG = "var(--color-border-strong)";
10422
10583
  var STATUS_SUCCESS2 = "#14B8A6";
10423
10584
  var STATUS_ERROR2 = "#D14343";
10424
10585
  var STATUS_WARNING2 = "#FFB020";
10425
10586
  var GRID_LINE = "rgba(255,255,255,0.06)";
10426
- var TOOLTIP_BG = "rgba(28,28,28,0.95)";
10587
+ var TOOLTIP_BG = "var(--color-surface-elevated)";
10427
10588
  var SUCCESS_PILL_BG = "rgba(20,184,166,0.10)";
10428
10589
  var SUCCESS_PILL_BORDER = "rgba(20,184,166,0.20)";
10429
10590
  var ERROR_PILL_BG = "rgba(209,67,67,0.10)";
@@ -10926,7 +11087,7 @@ var STATUS_SUCCESS3 = "#14B8A6";
10926
11087
  var STATUS_WARNING3 = "#FFB020";
10927
11088
  var STATUS_INFO = "#2196F3";
10928
11089
  var DOT_BORDER = "#F3F4F6";
10929
- var TEXT_TERTIARY6 = "#6B7280";
11090
+ var TEXT_TERTIARY6 = "var(--color-text-tertiary)";
10930
11091
  var BAND_FILL = "rgba(20,184,166,0.1)";
10931
11092
  var BAND_EDGE = "rgba(20,184,166,0.45)";
10932
11093
  var PROJECTION_FILL = "rgba(20,184,166,0.05)";
@@ -11299,847 +11460,674 @@ function CapacityBandChart({
11299
11460
  }
11300
11461
  );
11301
11462
  }
11302
-
11303
- // src/components/custom/Workout/muscleTaxonomy.ts
11304
- var MuscleGroup = /* @__PURE__ */ ((MuscleGroup4) => {
11305
- MuscleGroup4["CHEST"] = "chest";
11306
- MuscleGroup4["FRONT_DELTS"] = "front_delts";
11307
- MuscleGroup4["SIDE_DELTS"] = "side_delts";
11308
- MuscleGroup4["TRICEPS"] = "triceps";
11309
- MuscleGroup4["LATS"] = "lats";
11310
- MuscleGroup4["UPPER_BACK"] = "upper_back";
11311
- MuscleGroup4["REAR_DELTS"] = "rear_delts";
11312
- MuscleGroup4["BICEPS"] = "biceps";
11313
- MuscleGroup4["FOREARMS"] = "forearms";
11314
- MuscleGroup4["QUADS"] = "quads";
11315
- MuscleGroup4["HAMSTRINGS"] = "hamstrings";
11316
- MuscleGroup4["GLUTES"] = "glutes";
11317
- MuscleGroup4["CALVES"] = "calves";
11318
- MuscleGroup4["ABS"] = "abs";
11319
- MuscleGroup4["OBLIQUES"] = "obliques";
11320
- return MuscleGroup4;
11321
- })(MuscleGroup || {});
11322
- var SimpleMuscleGroup = /* @__PURE__ */ ((SimpleMuscleGroup3) => {
11323
- SimpleMuscleGroup3["CHEST"] = "chest";
11324
- SimpleMuscleGroup3["BACK"] = "back";
11325
- SimpleMuscleGroup3["SHOULDERS"] = "shoulders";
11326
- SimpleMuscleGroup3["BICEPS"] = "biceps";
11327
- SimpleMuscleGroup3["TRICEPS"] = "triceps";
11328
- SimpleMuscleGroup3["LEGS"] = "legs";
11329
- SimpleMuscleGroup3["CORE"] = "core";
11330
- SimpleMuscleGroup3["FOREARMS"] = "forearms";
11331
- return SimpleMuscleGroup3;
11332
- })(SimpleMuscleGroup || {});
11333
- var SIMPLE_TO_DETAILED = {
11334
- ["chest" /* CHEST */]: ["chest" /* CHEST */],
11335
- ["back" /* BACK */]: ["lats" /* LATS */, "upper_back" /* UPPER_BACK */],
11336
- ["shoulders" /* SHOULDERS */]: [
11337
- "front_delts" /* FRONT_DELTS */,
11338
- "side_delts" /* SIDE_DELTS */,
11339
- "rear_delts" /* REAR_DELTS */
11340
- ],
11341
- ["biceps" /* BICEPS */]: ["biceps" /* BICEPS */],
11342
- ["triceps" /* TRICEPS */]: ["triceps" /* TRICEPS */],
11343
- ["legs" /* LEGS */]: [
11344
- "quads" /* QUADS */,
11345
- "hamstrings" /* HAMSTRINGS */,
11346
- "glutes" /* GLUTES */,
11347
- "calves" /* CALVES */
11348
- ],
11349
- ["core" /* CORE */]: ["abs" /* ABS */, "obliques" /* OBLIQUES */],
11350
- ["forearms" /* FOREARMS */]: ["forearms" /* FOREARMS */]
11351
- };
11352
- var DETAILED_TO_SIMPLE = Object.entries(
11353
- SIMPLE_TO_DETAILED
11354
- ).reduce(
11355
- (acc, [simple, groups]) => {
11356
- for (const group of groups) {
11357
- acc[group] = simple;
11358
- }
11359
- return acc;
11360
- },
11361
- {}
11362
- );
11363
- var MUSCLE_TO_CATEGORY = {
11364
- ["chest" /* CHEST */]: "push",
11365
- ["front_delts" /* FRONT_DELTS */]: "push",
11366
- ["side_delts" /* SIDE_DELTS */]: "push",
11367
- ["triceps" /* TRICEPS */]: "push",
11368
- ["lats" /* LATS */]: "pull",
11369
- ["upper_back" /* UPPER_BACK */]: "pull",
11370
- ["rear_delts" /* REAR_DELTS */]: "pull",
11371
- ["biceps" /* BICEPS */]: "pull",
11372
- ["forearms" /* FOREARMS */]: "pull",
11373
- ["quads" /* QUADS */]: "legs",
11374
- ["hamstrings" /* HAMSTRINGS */]: "legs",
11375
- ["glutes" /* GLUTES */]: "legs",
11376
- ["calves" /* CALVES */]: "legs",
11377
- ["abs" /* ABS */]: "core",
11378
- ["obliques" /* OBLIQUES */]: "core"
11379
- };
11380
- var MUSCLE_TO_SVG_SLUGS = {
11381
- ["chest" /* CHEST */]: ["chest"],
11382
- ["front_delts" /* FRONT_DELTS */]: ["deltoids"],
11383
- ["side_delts" /* SIDE_DELTS */]: ["deltoids"],
11384
- ["rear_delts" /* REAR_DELTS */]: ["deltoids"],
11385
- ["triceps" /* TRICEPS */]: ["triceps"],
11386
- ["lats" /* LATS */]: ["upper-back"],
11387
- ["upper_back" /* UPPER_BACK */]: ["upper-back", "trapezius"],
11388
- ["biceps" /* BICEPS */]: ["biceps"],
11389
- ["forearms" /* FOREARMS */]: ["forearm"],
11390
- ["quads" /* QUADS */]: ["quadriceps"],
11391
- ["hamstrings" /* HAMSTRINGS */]: ["hamstring"],
11392
- ["glutes" /* GLUTES */]: ["gluteal"],
11393
- ["calves" /* CALVES */]: ["calves"],
11394
- ["abs" /* ABS */]: ["abs"],
11395
- ["obliques" /* OBLIQUES */]: ["obliques"]
11396
- };
11397
- var MUSCLE_DISPLAY_NAMES = {
11398
- ["chest" /* CHEST */]: "Chest",
11399
- ["front_delts" /* FRONT_DELTS */]: "Front Delts",
11400
- ["side_delts" /* SIDE_DELTS */]: "Side Delts",
11401
- ["rear_delts" /* REAR_DELTS */]: "Rear Delts",
11402
- ["triceps" /* TRICEPS */]: "Triceps",
11403
- ["lats" /* LATS */]: "Lats",
11404
- ["upper_back" /* UPPER_BACK */]: "Upper Back",
11405
- ["biceps" /* BICEPS */]: "Biceps",
11406
- ["forearms" /* FOREARMS */]: "Forearms",
11407
- ["quads" /* QUADS */]: "Quads",
11408
- ["hamstrings" /* HAMSTRINGS */]: "Hamstrings",
11409
- ["glutes" /* GLUTES */]: "Glutes",
11410
- ["calves" /* CALVES */]: "Calves",
11411
- ["abs" /* ABS */]: "Abs",
11412
- ["obliques" /* OBLIQUES */]: "Obliques"
11413
- };
11414
- var SIMPLE_DISPLAY_NAMES = {
11415
- ["chest" /* CHEST */]: "Chest",
11416
- ["back" /* BACK */]: "Back",
11417
- ["shoulders" /* SHOULDERS */]: "Shoulders",
11418
- ["biceps" /* BICEPS */]: "Biceps",
11419
- ["triceps" /* TRICEPS */]: "Triceps",
11420
- ["legs" /* LEGS */]: "Legs",
11421
- ["core" /* CORE */]: "Core",
11422
- ["forearms" /* FOREARMS */]: "Forearms"
11423
- };
11424
- var DEFAULT_VOLUME_LANDMARKS = {
11425
- ["chest" /* CHEST */]: { mev: 8, mav: 14, mrv: 20 },
11426
- ["lats" /* LATS */]: { mev: 8, mav: 14, mrv: 20 },
11427
- ["upper_back" /* UPPER_BACK */]: { mev: 6, mav: 12, mrv: 18 },
11428
- ["front_delts" /* FRONT_DELTS */]: { mev: 2, mav: 6, mrv: 10 },
11429
- ["side_delts" /* SIDE_DELTS */]: { mev: 6, mav: 14, mrv: 22 },
11430
- ["rear_delts" /* REAR_DELTS */]: { mev: 6, mav: 12, mrv: 18 },
11431
- ["biceps" /* BICEPS */]: { mev: 4, mav: 10, mrv: 18 },
11432
- ["triceps" /* TRICEPS */]: { mev: 4, mav: 8, mrv: 14 },
11433
- ["forearms" /* FOREARMS */]: { mev: 2, mav: 6, mrv: 12 },
11434
- ["quads" /* QUADS */]: { mev: 6, mav: 12, mrv: 18 },
11435
- ["hamstrings" /* HAMSTRINGS */]: { mev: 4, mav: 10, mrv: 16 },
11436
- ["glutes" /* GLUTES */]: { mev: 4, mav: 10, mrv: 16 },
11437
- ["calves" /* CALVES */]: { mev: 6, mav: 10, mrv: 16 },
11438
- ["abs" /* ABS */]: { mev: 0, mav: 8, mrv: 16 },
11439
- ["obliques" /* OBLIQUES */]: { mev: 0, mav: 6, mrv: 12 }
11440
- };
11441
- var VOLUME_STATUS_LABELS = {
11442
- under: "under-trained",
11443
- maintenance: "maintenance",
11444
- productive: "productive",
11445
- over: "over-reaching"
11446
- };
11447
- var STATUS_SEVERITY = {
11448
- under: 1,
11449
- maintenance: 2,
11450
- productive: 3,
11451
- over: 4
11452
- };
11453
- function getHeatmapColor(status, intensity = 0) {
11454
- const heatmap = WORKOUT_TOKENS.heatmap;
11455
- switch (status) {
11456
- case "under":
11457
- return heatmap.under;
11458
- case "maintenance":
11459
- return heatmap.maintenance;
11460
- case "productive":
11461
- return intensity >= 0.85 ? heatmap.approaching : heatmap.productive;
11462
- case "over":
11463
- return heatmap.over;
11464
- default:
11465
- return heatmap.none;
11466
- }
11467
- }
11468
- function isMoreSevere(a, b) {
11469
- return STATUS_SEVERITY[a] >= STATUS_SEVERITY[b];
11470
- }
11471
- var SLUG_TO_PRIMARY_MUSCLE = {
11472
- chest: "chest" /* CHEST */,
11473
- deltoids: "side_delts" /* SIDE_DELTS */,
11474
- triceps: "triceps" /* TRICEPS */,
11475
- "upper-back": "lats" /* LATS */,
11476
- trapezius: "upper_back" /* UPPER_BACK */,
11477
- biceps: "biceps" /* BICEPS */,
11478
- forearm: "forearms" /* FOREARMS */,
11479
- quadriceps: "quads" /* QUADS */,
11480
- hamstring: "hamstrings" /* HAMSTRINGS */,
11481
- gluteal: "glutes" /* GLUTES */,
11482
- calves: "calves" /* CALVES */,
11483
- abs: "abs" /* ABS */,
11484
- obliques: "obliques" /* OBLIQUES */
11485
- };
11486
-
11487
- // src/components/custom/Workout/BodyMap.tsx
11488
- var Body = BodyHighlighter__default.default.default ?? BodyHighlighter__default.default;
11489
- var OUTLINE_FILL = "rgba(255,255,255,0.08)";
11490
- var OUTLINE_BORDER = "rgba(255,255,255,0.12)";
11491
- var BODY_SCALE = 0.8;
11492
- var TEXT_PRIMARY7 = "#F3F4F6";
11493
- var TEXT_SECONDARY6 = "#9CA3AF";
11494
- function buildSlugParts(data) {
11495
- const bySlug = /* @__PURE__ */ new Map();
11496
- for (const d of data) {
11497
- for (const slug of MUSCLE_TO_SVG_SLUGS[d.muscleGroup] ?? []) {
11498
- const existing = bySlug.get(slug);
11499
- if (!existing || isMoreSevere(d.volumeStatus, existing.status)) {
11500
- bySlug.set(slug, { status: d.volumeStatus, intensity: d.intensity });
11501
- }
11502
- }
11503
- }
11504
- return Array.from(bySlug.entries()).map(([slug, v]) => ({
11505
- slug,
11506
- color: getHeatmapColor(v.status, v.intensity)
11463
+ var SURFACE_ELEVATED = "var(--color-surface-elevated)";
11464
+ var BORDER_DEFAULT3 = "var(--color-border-default)";
11465
+ var BRAND_PRIMARY11 = "#FF7900";
11466
+ var TEXT_PRIMARY7 = "var(--color-text-primary)";
11467
+ var TEXT_TERTIARY7 = "var(--color-text-tertiary)";
11468
+ var PAGE_BG = "var(--color-background-base)";
11469
+ var WORKOUT_PILL_STATUS = {
11470
+ completed: "completed",
11471
+ today: "current",
11472
+ upcoming: "upcoming"
11473
+ };
11474
+ function deriveNavLevel(selection) {
11475
+ if (selection.workoutId != null) return "workout";
11476
+ if (selection.weekNumber != null) return "week";
11477
+ return "meso";
11478
+ }
11479
+ function toProgressBarMesos(mesos) {
11480
+ return mesos.map(({ id, name, weekCount, status, currentWeek }) => ({
11481
+ id,
11482
+ name,
11483
+ weekCount,
11484
+ status,
11485
+ currentWeek
11507
11486
  }));
11508
11487
  }
11509
- function buildSimpleLegend(data) {
11510
- const bySimple = /* @__PURE__ */ new Map();
11511
- for (const d of data) {
11512
- const simple = DETAILED_TO_SIMPLE[d.muscleGroup];
11513
- const existing = bySimple.get(simple);
11514
- if (!existing) {
11515
- bySimple.set(simple, {
11516
- key: simple,
11517
- name: SIMPLE_DISPLAY_NAMES[simple],
11518
- status: d.volumeStatus,
11519
- intensity: d.intensity,
11520
- sets: d.weeklySets,
11521
- muscle: d.muscleGroup
11522
- });
11523
- continue;
11524
- }
11525
- existing.sets += d.weeklySets;
11526
- existing.intensity = Math.max(existing.intensity, d.intensity);
11527
- if (isMoreSevere(d.volumeStatus, existing.status)) {
11528
- existing.status = d.volumeStatus;
11529
- existing.muscle = d.muscleGroup;
11530
- }
11488
+ function findMeso(mesos, id) {
11489
+ if (id == null) return null;
11490
+ return mesos.find((meso) => meso.id === id) ?? null;
11491
+ }
11492
+ function findWeek(meso, weekNumber) {
11493
+ if (meso == null || weekNumber == null) return null;
11494
+ return meso.weeks.find((week) => week.weekNumber === weekNumber) ?? null;
11495
+ }
11496
+ function findWorkout(week, workoutId) {
11497
+ if (week == null || workoutId == null) return null;
11498
+ return week.workouts.find((workout) => workout.id === workoutId) ?? null;
11499
+ }
11500
+ function buildBreadcrumbs(meso, weekNumber, workout) {
11501
+ const crumbs = [{ key: "meso", label: meso.name, level: "meso" }];
11502
+ if (weekNumber != null) {
11503
+ crumbs.push({ key: "week", label: `Week ${weekNumber}`, level: "week" });
11504
+ }
11505
+ if (workout != null) {
11506
+ crumbs.push({ key: "workout", label: workout.name, level: "workout" });
11531
11507
  }
11532
- return Array.from(bySimple.values());
11508
+ return crumbs;
11533
11509
  }
11534
- function buildDetailedLegend(data) {
11535
- return data.map((d) => ({
11536
- key: d.muscleGroup,
11537
- name: MUSCLE_DISPLAY_NAMES[d.muscleGroup],
11538
- status: d.volumeStatus,
11539
- intensity: d.intensity,
11540
- sets: d.weeklySets,
11541
- muscle: d.muscleGroup
11510
+ function Breadcrumbs2({ crumbs, onNavigate }) {
11511
+ return /* @__PURE__ */ jsx(
11512
+ reactNative.View,
11513
+ {
11514
+ className: "flex-row items-center flex-wrap",
11515
+ style: { gap: 4 },
11516
+ testID: "program-planning-page-breadcrumbs",
11517
+ children: crumbs.map((crumb, index) => {
11518
+ const isLast = index === crumbs.length - 1;
11519
+ return /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", style: { gap: 4 }, children: [
11520
+ index > 0 && /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 12, color: TEXT_TERTIARY7 }, children: "\u203A" }),
11521
+ /* @__PURE__ */ jsx(
11522
+ reactNative.Pressable,
11523
+ {
11524
+ onPress: () => onNavigate(crumb.level),
11525
+ disabled: isLast,
11526
+ accessibilityRole: "button",
11527
+ testID: `program-planning-page-crumb-${crumb.key}`,
11528
+ children: /* @__PURE__ */ jsx(
11529
+ reactNative.Text,
11530
+ {
11531
+ style: {
11532
+ fontSize: 12,
11533
+ fontFamily: "Inter, sans-serif",
11534
+ fontWeight: isLast ? "700" : "500",
11535
+ color: isLast ? TEXT_PRIMARY7 : BRAND_PRIMARY11
11536
+ },
11537
+ children: crumb.label
11538
+ }
11539
+ )
11540
+ }
11541
+ )
11542
+ ] }, crumb.key);
11543
+ })
11544
+ }
11545
+ );
11546
+ }
11547
+ function toWeekRow(week, meso, onSelectWeek) {
11548
+ const workouts = week.workouts.map((workout) => ({
11549
+ name: workout.name,
11550
+ status: WORKOUT_PILL_STATUS[workout.status],
11551
+ onPress: () => onSelectWeek(week.weekNumber)
11542
11552
  }));
11553
+ return {
11554
+ weekNumber: week.weekNumber,
11555
+ totalWeeks: meso.weekCount,
11556
+ workouts,
11557
+ intensityLevel: week.intensityLevel,
11558
+ intensityThreshold: week.intensityThreshold,
11559
+ isCurrent: week.isCurrent ?? week.weekNumber === meso.currentWeek,
11560
+ isDeload: week.isDeload
11561
+ };
11543
11562
  }
11544
- function BodyMap({
11545
- data,
11546
- view,
11547
- onViewChange,
11548
- onMusclePress,
11549
- highlightedMuscle,
11550
- mode = "detailed",
11563
+ function MesoLevel({ mesos, activeMesoId, onSelectMeso, onSelectWeek }) {
11564
+ return /* @__PURE__ */ jsx(reactNative.View, { style: { gap: 12 }, testID: "program-planning-page-meso-level", children: mesos.map((meso) => {
11565
+ const expanded = meso.id === activeMesoId;
11566
+ return /* @__PURE__ */ jsx(
11567
+ MesoCard,
11568
+ {
11569
+ name: meso.name,
11570
+ goal: meso.goal,
11571
+ split: meso.split,
11572
+ weekRange: meso.weekRange,
11573
+ currentWeek: meso.currentWeek,
11574
+ totalWeeks: meso.weekCount,
11575
+ weeks: expanded ? meso.weeks.map((week) => toWeekRow(week, meso, onSelectWeek)) : [],
11576
+ volumeHeatmap: meso.volumeHeatmap,
11577
+ expanded,
11578
+ onToggle: () => onSelectMeso(meso.id),
11579
+ highlighted: expanded
11580
+ },
11581
+ meso.id
11582
+ );
11583
+ }) });
11584
+ }
11585
+ function WeekLevel({ week, onSelectWorkout }) {
11586
+ return /* @__PURE__ */ jsx(reactNative.View, { style: { gap: 10 }, testID: "program-planning-page-week-level", children: week.workouts.map((workout) => /* @__PURE__ */ jsx(
11587
+ WorkoutCard,
11588
+ {
11589
+ name: workout.name,
11590
+ date: workout.date,
11591
+ duration: workout.duration,
11592
+ status: workout.status,
11593
+ muscleGroups: workout.muscleGroups,
11594
+ totalSets: workout.totalSets,
11595
+ totalVolume: workout.totalVolume,
11596
+ unit: workout.unit,
11597
+ exercises: workout.exercises,
11598
+ expanded: false,
11599
+ onToggle: () => onSelectWorkout(workout.id)
11600
+ },
11601
+ workout.id
11602
+ )) });
11603
+ }
11604
+ function WorkoutLevel({ workout, expandedExercise, onToggleExercise }) {
11605
+ const exercises = workout.exercises.map((exercise, index) => ({
11606
+ ...exercise,
11607
+ state: exercise.state === "upcoming" ? exercise.state : index === expandedExercise ? "expanded" : "collapsed",
11608
+ onToggle: () => onToggleExercise(index)
11609
+ }));
11610
+ return /* @__PURE__ */ jsx(reactNative.View, { testID: "program-planning-page-workout-level", children: /* @__PURE__ */ jsx(
11611
+ WorkoutCard,
11612
+ {
11613
+ name: workout.name,
11614
+ date: workout.date,
11615
+ duration: workout.duration,
11616
+ status: workout.status,
11617
+ muscleGroups: workout.muscleGroups,
11618
+ totalSets: workout.totalSets,
11619
+ totalVolume: workout.totalVolume,
11620
+ unit: workout.unit,
11621
+ exercises,
11622
+ expanded: true,
11623
+ onToggle: () => void 0
11624
+ }
11625
+ ) });
11626
+ }
11627
+ function ProgramPlanningPage({
11628
+ title = "Program Plan",
11629
+ mesos,
11551
11630
  className,
11552
11631
  ...props
11553
11632
  }) {
11554
- const slugParts = React24.useMemo(() => buildSlugParts(data), [data]);
11555
- const legend = React24.useMemo(
11556
- () => mode === "simple" ? buildSimpleLegend(data) : buildDetailedLegend(data),
11557
- [data, mode]
11558
- );
11559
- const [highlight] = React24.useState(() => new reactNative.Animated.Value(highlightedMuscle ? 1 : 0));
11560
- React24.useEffect(() => {
11561
- const animation = reactNative.Animated.timing(highlight, {
11562
- toValue: highlightedMuscle ? 1 : 0,
11563
- duration: 250,
11564
- easing: reactNative.Easing.out(reactNative.Easing.ease),
11565
- useNativeDriver: false
11566
- });
11567
- animation.start();
11568
- return () => animation.stop();
11569
- }, [highlightedMuscle, highlight]);
11570
- const scale = highlight.interpolate({ inputRange: [0, 1], outputRange: [1, 1.02] });
11571
- const glowColor = highlightedMuscle ? getHeatmapColor(
11572
- data.find((d) => d.muscleGroup === highlightedMuscle)?.volumeStatus,
11573
- data.find((d) => d.muscleGroup === highlightedMuscle)?.intensity ?? 0
11574
- ) : null;
11575
- const handleBodyPress = (part) => {
11576
- if (!onMusclePress || !part.slug) return;
11577
- const fromData = data.find(
11578
- (d) => (MUSCLE_TO_SVG_SLUGS[d.muscleGroup] ?? []).includes(part.slug)
11579
- );
11580
- const muscle = fromData?.muscleGroup ?? SLUG_TO_PRIMARY_MUSCLE[part.slug];
11581
- if (muscle) onMusclePress(muscle);
11633
+ const [activeMesoId, setActiveMesoId] = React24.useState(mesos[0]?.id ?? null);
11634
+ const [selectedWeek, setSelectedWeek] = React24.useState(null);
11635
+ const [selectedWorkoutId, setSelectedWorkoutId] = React24.useState(null);
11636
+ const [expandedExercise, setExpandedExercise] = React24.useState(null);
11637
+ const progressMesos = React24.useMemo(() => toProgressBarMesos(mesos), [mesos]);
11638
+ const activeMeso = findMeso(mesos, activeMesoId);
11639
+ const level = deriveNavLevel({ weekNumber: selectedWeek, workoutId: selectedWorkoutId });
11640
+ const activeWeek = findWeek(activeMeso, selectedWeek);
11641
+ const activeWorkout = findWorkout(activeWeek, selectedWorkoutId);
11642
+ const selectMeso = (id) => {
11643
+ setActiveMesoId(id);
11644
+ setSelectedWeek(null);
11645
+ setSelectedWorkoutId(null);
11582
11646
  };
11583
- return /* @__PURE__ */ jsxs(reactNative.View, { className: cn(className), testID: "body-map", ...props, children: [
11584
- /* @__PURE__ */ jsx(ViewToggle, { view, onViewChange }),
11585
- /* @__PURE__ */ jsx(
11586
- reactNative.Animated.View,
11587
- {
11588
- style: [
11647
+ const selectWeek = (weekNumber) => {
11648
+ setSelectedWeek(weekNumber);
11649
+ setSelectedWorkoutId(null);
11650
+ setExpandedExercise(null);
11651
+ };
11652
+ const selectWorkout = (id) => {
11653
+ setSelectedWorkoutId(id);
11654
+ setExpandedExercise(null);
11655
+ };
11656
+ const toggleExercise = (index) => setExpandedExercise((current) => current === index ? null : index);
11657
+ const navigateTo = (target) => {
11658
+ if (target === "meso") selectMeso(activeMesoId ?? mesos[0]?.id);
11659
+ else if (target === "week" && selectedWeek != null) selectWeek(selectedWeek);
11660
+ };
11661
+ return /* @__PURE__ */ jsx(
11662
+ reactNative.View,
11663
+ {
11664
+ className,
11665
+ style: { width: 390, backgroundColor: PAGE_BG },
11666
+ accessibilityRole: "main",
11667
+ "aria-label": title,
11668
+ testID: "program-planning-page",
11669
+ ...props,
11670
+ children: /* @__PURE__ */ jsxs(reactNative.View, { style: { padding: 16, gap: 14 }, testID: "program-planning-page-content", children: [
11671
+ /* @__PURE__ */ jsx(
11672
+ reactNative.Text,
11589
11673
  {
11590
- width: 200 * BODY_SCALE,
11591
- alignSelf: "center",
11592
- transform: [{ scale }]
11593
- },
11594
- glowColor ? { boxShadow: `0 0 14px 2px ${glowColor}` } : void 0
11595
- ],
11596
- "aria-hidden": true,
11597
- accessibilityElementsHidden: true,
11598
- importantForAccessibility: "no-hide-descendants",
11599
- testID: "body-map-svg",
11600
- children: /* @__PURE__ */ jsx(
11601
- Body,
11674
+ accessibilityRole: "header",
11675
+ style: {
11676
+ fontSize: 20,
11677
+ fontFamily: '"Space Grotesk", sans-serif',
11678
+ fontWeight: "700",
11679
+ color: TEXT_PRIMARY7
11680
+ },
11681
+ testID: "program-planning-page-title",
11682
+ children: title
11683
+ }
11684
+ ),
11685
+ /* @__PURE__ */ jsx(reactNative.View, { style: { marginHorizontal: -16 }, children: /* @__PURE__ */ jsx(
11686
+ MesoProgressBar,
11602
11687
  {
11603
- side: view,
11604
- data: slugParts,
11605
- scale: BODY_SCALE,
11606
- gender: "male",
11607
- defaultFill: OUTLINE_FILL,
11608
- border: OUTLINE_BORDER,
11609
- onBodyPartPress: handleBodyPress
11688
+ mesos: progressMesos,
11689
+ activeMesoId,
11690
+ onMesoPress: selectMeso
11610
11691
  }
11611
- )
11612
- }
11613
- ),
11614
- legend.length === 0 ? /* @__PURE__ */ jsx(
11615
- reactNative.Text,
11616
- {
11617
- style: {
11618
- marginTop: 8,
11619
- fontSize: 12,
11620
- color: TEXT_SECONDARY6,
11621
- textAlign: "center",
11622
- fontFamily: "Inter, sans-serif"
11623
- },
11624
- testID: "body-map-empty",
11625
- children: "No training data"
11626
- }
11627
- ) : /* @__PURE__ */ jsx(
11628
- reactNative.View,
11629
- {
11630
- className: "flex-row flex-wrap",
11631
- style: { marginTop: 10, gap: 6, justifyContent: "center" },
11632
- testID: "body-map-legend",
11633
- children: legend.map((entry) => /* @__PURE__ */ jsx(
11634
- MuscleButton,
11692
+ ) }),
11693
+ activeMeso != null && /* @__PURE__ */ jsx(
11694
+ Breadcrumbs2,
11635
11695
  {
11636
- entry,
11637
- highlighted: highlightedMuscle === entry.muscle,
11638
- onMusclePress
11639
- },
11640
- entry.key
11641
- ))
11642
- }
11643
- )
11644
- ] });
11696
+ crumbs: buildBreadcrumbs(activeMeso, selectedWeek, activeWorkout),
11697
+ onNavigate: navigateTo
11698
+ }
11699
+ ),
11700
+ /* @__PURE__ */ jsxs(
11701
+ reactNative.View,
11702
+ {
11703
+ style: {
11704
+ backgroundColor: SURFACE_ELEVATED,
11705
+ borderWidth: 1,
11706
+ borderColor: BORDER_DEFAULT3,
11707
+ borderRadius: 12,
11708
+ padding: 12
11709
+ },
11710
+ testID: "program-planning-page-body",
11711
+ children: [
11712
+ level === "meso" && /* @__PURE__ */ jsx(
11713
+ MesoLevel,
11714
+ {
11715
+ mesos,
11716
+ activeMesoId,
11717
+ onSelectMeso: selectMeso,
11718
+ onSelectWeek: selectWeek
11719
+ }
11720
+ ),
11721
+ level === "week" && activeWeek != null && /* @__PURE__ */ jsx(WeekLevel, { week: activeWeek, onSelectWorkout: selectWorkout }),
11722
+ level === "workout" && activeWorkout != null && /* @__PURE__ */ jsx(
11723
+ WorkoutLevel,
11724
+ {
11725
+ workout: activeWorkout,
11726
+ expandedExercise,
11727
+ onToggleExercise: toggleExercise
11728
+ }
11729
+ )
11730
+ ]
11731
+ }
11732
+ )
11733
+ ] })
11734
+ }
11735
+ );
11736
+ }
11737
+ var SURFACE_ELEVATED2 = "var(--color-surface-elevated)";
11738
+ var BORDER_DEFAULT4 = "var(--color-border-default)";
11739
+ var BRAND_PRIMARY12 = "#FF7900";
11740
+ var TEXT_PRIMARY8 = "var(--color-text-primary)";
11741
+ var TEXT_SECONDARY6 = "var(--color-text-secondary)";
11742
+ var TEXT_TERTIARY8 = "var(--color-text-tertiary)";
11743
+ var PAGE_BG2 = "var(--color-background-base)";
11744
+ var CHART_WIDTH = 326;
11745
+ var CHART_HEIGHT = 150;
11746
+ var EXERCISE_DETAIL_TABS = [
11747
+ { key: "progress", label: "Progress" },
11748
+ { key: "history", label: "History" },
11749
+ { key: "advanced", label: "Advanced" }
11750
+ ];
11751
+ function deriveExerciseStats(entries) {
11752
+ let bestE1rm = null;
11753
+ let prCount = 0;
11754
+ for (const entry of entries) {
11755
+ if (entry.e1rm != null && (bestE1rm == null || entry.e1rm.value > bestE1rm)) {
11756
+ bestE1rm = entry.e1rm.value;
11757
+ }
11758
+ if (entry.isPR) prCount += 1;
11759
+ }
11760
+ return { sessions: entries.length, bestE1rm, prCount };
11761
+ }
11762
+ function toExerciseCardProps(entry, expanded, onToggle) {
11763
+ return {
11764
+ name: entry.title,
11765
+ state: expanded ? "expanded" : "collapsed",
11766
+ onToggle,
11767
+ summary: entry.summary,
11768
+ e1rm: entry.e1rm,
11769
+ isPR: entry.isPR,
11770
+ tempo: entry.tempo,
11771
+ sets: expanded ? entry.sets : void 0
11772
+ };
11773
+ }
11774
+ function summarizeVbt(sets) {
11775
+ const allReps = sets.flatMap((set) => set.velocities);
11776
+ const meanVelocity = calculateMeanVelocity(allReps);
11777
+ let velocityLoss = 0;
11778
+ for (const set of sets) {
11779
+ velocityLoss = Math.max(velocityLoss, calculateVelocityLoss(set.velocities));
11780
+ }
11781
+ return { meanVelocity, velocityLoss };
11645
11782
  }
11646
- function ViewToggle({ view, onViewChange }) {
11783
+ function TabBar({ active, onSelect }) {
11647
11784
  return /* @__PURE__ */ jsx(
11648
11785
  reactNative.View,
11649
11786
  {
11650
- className: "flex-row self-center",
11651
- style: { gap: 4, marginBottom: 8 },
11652
- testID: "body-map-view-toggle",
11653
- children: ["front", "back"].map((side) => {
11654
- const active = view === side;
11787
+ className: "flex-row",
11788
+ style: {
11789
+ borderBottomWidth: 1,
11790
+ borderBottomColor: BORDER_DEFAULT4
11791
+ },
11792
+ accessibilityRole: "tablist",
11793
+ testID: "exercise-detail-page-tabs",
11794
+ children: EXERCISE_DETAIL_TABS.map(({ key, label }) => {
11795
+ const isActive = key === active;
11655
11796
  return /* @__PURE__ */ jsx(
11656
11797
  reactNative.Pressable,
11657
11798
  {
11658
- onPress: () => onViewChange?.(side),
11659
- accessibilityRole: "button",
11660
- accessibilityLabel: `Show ${side} view`,
11661
- "aria-pressed": active,
11662
- disabled: !onViewChange,
11799
+ onPress: () => onSelect(key),
11800
+ accessibilityRole: "tab",
11801
+ accessibilityState: { selected: isActive },
11663
11802
  style: {
11664
- paddingHorizontal: 12,
11665
- paddingVertical: 4,
11666
- borderRadius: 9999,
11667
- backgroundColor: active ? "rgba(255,121,0,0.16)" : "transparent",
11668
- borderWidth: 1,
11669
- borderColor: active ? "#FF7900" : WORKOUT_TOKENS.border.strong
11803
+ flex: 1,
11804
+ alignItems: "center",
11805
+ paddingVertical: 10,
11806
+ borderBottomWidth: 2,
11807
+ borderBottomColor: isActive ? BRAND_PRIMARY12 : "transparent"
11670
11808
  },
11671
- testID: `body-map-toggle-${side}`,
11809
+ testID: `exercise-detail-page-tab-${key}`,
11672
11810
  children: /* @__PURE__ */ jsx(
11673
11811
  reactNative.Text,
11674
11812
  {
11675
11813
  style: {
11676
- fontSize: 12,
11814
+ fontSize: 13,
11677
11815
  fontFamily: "Inter, sans-serif",
11678
- fontWeight: active ? "700" : "500",
11679
- color: active ? "#FF7900" : TEXT_SECONDARY6,
11680
- textTransform: "capitalize"
11816
+ fontWeight: isActive ? "700" : "500",
11817
+ color: isActive ? TEXT_PRIMARY8 : TEXT_TERTIARY8
11681
11818
  },
11682
- children: side
11819
+ children: label
11683
11820
  }
11684
11821
  )
11685
11822
  },
11686
- side
11823
+ key
11687
11824
  );
11688
11825
  })
11689
11826
  }
11690
11827
  );
11691
11828
  }
11692
- function MuscleButton({ entry, highlighted, onMusclePress }) {
11693
- const dotColor = getHeatmapColor(entry.status, entry.intensity);
11694
- const label = `${entry.name}, ${VOLUME_STATUS_LABELS[entry.status]}, ${entry.sets} sets this week`;
11829
+ var STAT_CARDS = [
11830
+ { key: "sessions", label: "Sessions" },
11831
+ { key: "bestE1rm", label: "Best e1RM" },
11832
+ { key: "prCount", label: "PRs" }
11833
+ ];
11834
+ function StatStrip({ stats, unit }) {
11835
+ return /* @__PURE__ */ jsx(reactNative.View, { className: "flex-row", style: { gap: 8 }, testID: "exercise-detail-page-stats", children: STAT_CARDS.map(({ key, label }) => {
11836
+ const raw = stats[key];
11837
+ const value = key === "bestE1rm" ? raw == null ? "\u2014" : `${raw} ${unit}` : `${raw}`;
11838
+ return /* @__PURE__ */ jsxs(
11839
+ reactNative.View,
11840
+ {
11841
+ style: {
11842
+ flex: 1,
11843
+ padding: 12,
11844
+ borderRadius: 10,
11845
+ backgroundColor: SURFACE_ELEVATED2,
11846
+ borderWidth: 1,
11847
+ borderColor: BORDER_DEFAULT4
11848
+ },
11849
+ testID: `exercise-detail-page-stat-${key}`,
11850
+ children: [
11851
+ /* @__PURE__ */ jsx(
11852
+ reactNative.Text,
11853
+ {
11854
+ style: {
11855
+ fontSize: 18,
11856
+ fontFamily: '"Space Grotesk", sans-serif',
11857
+ fontWeight: "700",
11858
+ color: TEXT_PRIMARY8
11859
+ },
11860
+ children: value
11861
+ }
11862
+ ),
11863
+ /* @__PURE__ */ jsx(
11864
+ reactNative.Text,
11865
+ {
11866
+ style: {
11867
+ marginTop: 2,
11868
+ fontSize: 10,
11869
+ fontFamily: "Inter, sans-serif",
11870
+ fontWeight: "600",
11871
+ letterSpacing: 0.4,
11872
+ textTransform: "uppercase",
11873
+ color: TEXT_TERTIARY8
11874
+ },
11875
+ children: label
11876
+ }
11877
+ )
11878
+ ]
11879
+ },
11880
+ key
11881
+ );
11882
+ }) });
11883
+ }
11884
+ function SectionCard({
11885
+ title,
11886
+ children,
11887
+ testID
11888
+ }) {
11695
11889
  return /* @__PURE__ */ jsxs(
11696
- reactNative.Pressable,
11890
+ reactNative.View,
11697
11891
  {
11698
- onPress: () => onMusclePress?.(entry.muscle),
11699
- accessibilityRole: "button",
11700
- accessibilityLabel: label,
11701
- "aria-pressed": highlighted,
11702
11892
  style: {
11703
- flexDirection: "row",
11704
- alignItems: "center",
11705
- gap: 5,
11706
- paddingHorizontal: 8,
11707
- paddingVertical: 3,
11708
- borderRadius: 9999,
11893
+ backgroundColor: SURFACE_ELEVATED2,
11709
11894
  borderWidth: 1,
11710
- borderColor: highlighted ? "#FF7900" : WORKOUT_TOKENS.border.default,
11711
- backgroundColor: WORKOUT_TOKENS.surface.raised
11895
+ borderColor: BORDER_DEFAULT4,
11896
+ borderRadius: 12,
11897
+ padding: 12,
11898
+ gap: 10
11712
11899
  },
11713
- testID: `body-map-muscle-${entry.key}`,
11900
+ testID,
11714
11901
  children: [
11715
- /* @__PURE__ */ jsx(
11716
- reactNative.View,
11717
- {
11718
- style: { width: 7, height: 7, borderRadius: 9999, backgroundColor: dotColor },
11719
- accessibilityElementsHidden: true,
11720
- testID: `body-map-muscle-dot-${entry.key}`
11721
- }
11722
- ),
11723
11902
  /* @__PURE__ */ jsx(
11724
11903
  reactNative.Text,
11725
11904
  {
11726
11905
  style: {
11727
11906
  fontSize: 11,
11728
11907
  fontFamily: "Inter, sans-serif",
11729
- fontWeight: "500",
11730
- color: TEXT_PRIMARY7
11908
+ fontWeight: "600",
11909
+ letterSpacing: 0.6,
11910
+ textTransform: "uppercase",
11911
+ color: TEXT_TERTIARY8
11731
11912
  },
11732
- accessibilityElementsHidden: true,
11733
- children: entry.name
11913
+ children: title
11734
11914
  }
11735
- )
11915
+ ),
11916
+ children
11736
11917
  ]
11737
11918
  }
11738
11919
  );
11739
11920
  }
11740
- var SURFACE_ELEVATED = WORKOUT_TOKENS.surface.elevated;
11741
- var SURFACE_RAISED2 = WORKOUT_TOKENS.surface.raised;
11742
- var BORDER_STRONG2 = WORKOUT_TOKENS.border.strong;
11743
- var BORDER_DEFAULT3 = WORKOUT_TOKENS.border.default;
11744
- var BRAND_PRIMARY11 = "#FF7900";
11745
- var TEXT_PRIMARY8 = "#F3F4F6";
11746
- var TEXT_SECONDARY7 = "#9CA3AF";
11747
- var TEXT_TERTIARY7 = "#6B7280";
11748
- var VOLUME_GRADIENT = "linear-gradient(90deg, #2196F3 0%, #14B8A6 50%, #D14343 100%)";
11749
- var SLIDE_OFFSET = 400;
11750
- var BACKDROP_OPACITY = 0.4;
11751
- var STATUS_BADGE_COLOR = {
11752
- under: "info",
11753
- maintenance: "warning",
11754
- productive: "success",
11755
- over: "error"
11756
- };
11757
- function clamp013(value) {
11758
- if (value < 0) return 0;
11759
- if (value > 1) return 1;
11760
- return value;
11921
+ function EntryList({ entries, expandedId, onToggle, emptyLabel, testID }) {
11922
+ if (entries.length === 0) {
11923
+ return /* @__PURE__ */ jsx(
11924
+ reactNative.Text,
11925
+ {
11926
+ style: { fontSize: 12, fontFamily: "Inter, sans-serif", color: TEXT_TERTIARY8 },
11927
+ testID: `${testID}-empty`,
11928
+ children: emptyLabel
11929
+ }
11930
+ );
11931
+ }
11932
+ return /* @__PURE__ */ jsx(reactNative.View, { style: { gap: 8 }, testID, children: entries.map((entry) => /* @__PURE__ */ jsx(
11933
+ ExerciseCard,
11934
+ {
11935
+ ...toExerciseCardProps(entry, entry.id === expandedId, () => onToggle(entry.id))
11936
+ },
11937
+ entry.id
11938
+ )) });
11761
11939
  }
11762
- function markerFraction(weeklySets, { mev, mrv }) {
11763
- const span = mrv - mev;
11764
- if (span <= 0) return weeklySets >= mrv ? 1 : 0;
11765
- return clamp013((weeklySets - mev) / span);
11940
+ function VbtBreakdown({ sets }) {
11941
+ return /* @__PURE__ */ jsx(reactNative.View, { style: { gap: 10 }, testID: "exercise-detail-page-vbt-breakdown", children: sets.map((set) => {
11942
+ const mean = calculateMeanVelocity(set.velocities);
11943
+ const loss = calculateVelocityLoss(set.velocities);
11944
+ return /* @__PURE__ */ jsxs(
11945
+ reactNative.View,
11946
+ {
11947
+ className: "flex-row items-center",
11948
+ style: { gap: 10 },
11949
+ testID: "exercise-detail-page-vbt-set",
11950
+ children: [
11951
+ /* @__PURE__ */ jsx(
11952
+ reactNative.Text,
11953
+ {
11954
+ style: {
11955
+ width: 44,
11956
+ fontSize: 11,
11957
+ fontFamily: "Inter, sans-serif",
11958
+ fontWeight: "600",
11959
+ color: TEXT_SECONDARY6
11960
+ },
11961
+ children: set.label
11962
+ }
11963
+ ),
11964
+ /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1", style: { height: 8, justifyContent: "center" }, children: /* @__PURE__ */ jsx(VelocityStrip, { velocities: set.velocities, variant: "mini" }) }),
11965
+ /* @__PURE__ */ jsx(
11966
+ reactNative.Text,
11967
+ {
11968
+ style: {
11969
+ fontSize: 11,
11970
+ fontFamily: "Inter, sans-serif",
11971
+ fontVariant: ["tabular-nums"],
11972
+ color: TEXT_TERTIARY8
11973
+ },
11974
+ children: `${mean.toFixed(2)} \xB7 -${loss}%`
11975
+ }
11976
+ )
11977
+ ]
11978
+ },
11979
+ set.label
11980
+ );
11981
+ }) });
11766
11982
  }
11767
- function BodyMapDetailPanel({
11768
- muscleGroup,
11769
- displayName,
11770
- weeklySets,
11771
- landmarks,
11772
- volumeStatus,
11773
- lastTrained,
11774
- weeklyHistory,
11775
- contributingExercises,
11776
- upcomingExercises,
11777
- isOpen,
11778
- visible,
11779
- onClose,
11780
- onViewExercises,
11781
- ...props
11782
- }) {
11783
- const open = isOpen ?? visible ?? false;
11784
- const [translateY] = React24.useState(() => new reactNative.Animated.Value(SLIDE_OFFSET));
11785
- const [backdrop] = React24.useState(() => new reactNative.Animated.Value(0));
11786
- React24.useEffect(() => {
11787
- if (!open) return;
11788
- const animation = reactNative.Animated.parallel([
11789
- reactNative.Animated.timing(translateY, {
11790
- toValue: 0,
11791
- duration: 400,
11792
- easing: reactNative.Easing.out(reactNative.Easing.ease),
11793
- useNativeDriver: false
11794
- }),
11795
- reactNative.Animated.timing(backdrop, {
11796
- toValue: BACKDROP_OPACITY,
11797
- duration: 300,
11798
- easing: reactNative.Easing.out(reactNative.Easing.ease),
11799
- useNativeDriver: false
11800
- })
11801
- ]);
11802
- animation.start();
11803
- return () => animation.stop();
11804
- }, [open, translateY, backdrop]);
11805
- if (!open) return null;
11806
- const dialogLabel = `${displayName} volume details`;
11807
- const markerLeft = markerFraction(weeklySets, landmarks) * 100;
11808
- const hasContributing = (contributingExercises?.length ?? 0) > 0;
11809
- const hasUpcoming = (upcomingExercises?.length ?? 0) > 0;
11810
- return /* @__PURE__ */ jsxs(
11983
+ function VbtSummaryRow({ summary }) {
11984
+ const cells = [
11985
+ { label: "Mean Velocity", value: `${summary.meanVelocity.toFixed(2)} m/s` },
11986
+ { label: "Peak Vel. Loss", value: `-${summary.velocityLoss}%` }
11987
+ ];
11988
+ return /* @__PURE__ */ jsx(reactNative.View, { className: "flex-row", style: { gap: 8 }, testID: "exercise-detail-page-vbt-summary", children: cells.map((cell) => /* @__PURE__ */ jsxs(
11811
11989
  reactNative.View,
11812
11990
  {
11813
- style: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, justifyContent: "flex-end" },
11814
- testID: "body-map-detail-panel-root",
11991
+ style: {
11992
+ flex: 1,
11993
+ padding: 12,
11994
+ borderRadius: 10,
11995
+ backgroundColor: SURFACE_ELEVATED2,
11996
+ borderWidth: 1,
11997
+ borderColor: BORDER_DEFAULT4
11998
+ },
11815
11999
  children: [
11816
12000
  /* @__PURE__ */ jsx(
11817
- reactNative.Animated.View,
12001
+ reactNative.Text,
11818
12002
  {
11819
12003
  style: {
11820
- position: "absolute",
11821
- top: 0,
11822
- left: 0,
11823
- right: 0,
11824
- bottom: 0,
11825
- backgroundColor: "#000000",
11826
- opacity: backdrop
12004
+ fontSize: 16,
12005
+ fontFamily: '"Space Grotesk", sans-serif',
12006
+ fontWeight: "700",
12007
+ color: TEXT_PRIMARY8
11827
12008
  },
11828
- testID: "body-map-detail-panel-backdrop-anim",
11829
- children: /* @__PURE__ */ jsx(
11830
- reactNative.Pressable,
11831
- {
11832
- onPress: onClose,
11833
- accessibilityRole: "button",
11834
- accessibilityLabel: `Close ${displayName} details`,
11835
- style: { flex: 1 },
11836
- testID: "body-map-detail-panel-backdrop"
11837
- }
11838
- )
12009
+ children: cell.value
11839
12010
  }
11840
12011
  ),
11841
- /* @__PURE__ */ jsxs(
11842
- reactNative.Animated.View,
12012
+ /* @__PURE__ */ jsx(
12013
+ reactNative.Text,
11843
12014
  {
11844
- accessibilityRole: "dialog",
11845
- accessibilityLabel: dialogLabel,
11846
- "aria-label": dialogLabel,
11847
12015
  style: {
11848
- backgroundColor: SURFACE_ELEVATED,
11849
- borderTopLeftRadius: 16,
11850
- borderTopRightRadius: 16,
11851
- maxHeight: "88%",
11852
- transform: [{ translateY }]
12016
+ marginTop: 2,
12017
+ fontSize: 10,
12018
+ fontFamily: "Inter, sans-serif",
12019
+ fontWeight: "600",
12020
+ letterSpacing: 0.4,
12021
+ textTransform: "uppercase",
12022
+ color: TEXT_TERTIARY8
11853
12023
  },
11854
- testID: "body-map-detail-panel",
11855
- ...props,
12024
+ children: cell.label
12025
+ }
12026
+ )
12027
+ ]
12028
+ },
12029
+ cell.label
12030
+ )) });
12031
+ }
12032
+ function ExerciseDetailPage({
12033
+ exercise,
12034
+ meso,
12035
+ trend,
12036
+ progression,
12037
+ history,
12038
+ vbt,
12039
+ className,
12040
+ ...props
12041
+ }) {
12042
+ const [tab, setTab] = React24.useState("progress");
12043
+ const [expandedProgressId, setExpandedProgressId] = React24.useState(null);
12044
+ const [expandedHistoryId, setExpandedHistoryId] = React24.useState(null);
12045
+ const stats = React24.useMemo(() => deriveExerciseStats(history), [history]);
12046
+ const vbtSummary = React24.useMemo(() => summarizeVbt(vbt.sets), [vbt.sets]);
12047
+ const toggle = (setId) => (id) => setId((current) => current === id ? null : id);
12048
+ return /* @__PURE__ */ jsx(
12049
+ reactNative.View,
12050
+ {
12051
+ className,
12052
+ style: { width: 390, backgroundColor: PAGE_BG2 },
12053
+ accessibilityRole: "main",
12054
+ "aria-label": exercise.name,
12055
+ testID: "exercise-detail-page",
12056
+ ...props,
12057
+ children: /* @__PURE__ */ jsxs(reactNative.View, { style: { padding: 16, gap: 14 }, testID: "exercise-detail-page-content", children: [
12058
+ /* @__PURE__ */ jsxs(
12059
+ reactNative.View,
12060
+ {
12061
+ className: "flex-row items-start justify-between",
12062
+ style: { gap: 8 },
12063
+ testID: "exercise-detail-page-header",
11856
12064
  children: [
11857
- /* @__PURE__ */ jsx(
11858
- reactNative.Pressable,
11859
- {
11860
- onPress: onClose,
11861
- accessibilityRole: "button",
11862
- accessibilityLabel: `Close ${displayName} details`,
11863
- hitSlop: 12,
11864
- style: { alignSelf: "center", paddingVertical: 8 },
11865
- testID: "body-map-detail-panel-handle",
11866
- children: /* @__PURE__ */ jsx(
11867
- reactNative.View,
11868
- {
11869
- style: { width: 40, height: 4, borderRadius: 2, backgroundColor: BORDER_STRONG2 },
11870
- accessibilityElementsHidden: true
11871
- }
11872
- )
11873
- }
11874
- ),
11875
- /* @__PURE__ */ jsxs(
11876
- reactNative.ScrollView,
12065
+ /* @__PURE__ */ jsxs(reactNative.View, { style: { flexShrink: 1 }, children: [
12066
+ /* @__PURE__ */ jsx(
12067
+ reactNative.Text,
12068
+ {
12069
+ accessibilityRole: "header",
12070
+ style: {
12071
+ fontSize: 20,
12072
+ fontFamily: '"Space Grotesk", sans-serif',
12073
+ fontWeight: "700",
12074
+ color: TEXT_PRIMARY8
12075
+ },
12076
+ testID: "exercise-detail-page-title",
12077
+ children: exercise.name
12078
+ }
12079
+ ),
12080
+ /* @__PURE__ */ jsx(
12081
+ reactNative.Text,
12082
+ {
12083
+ style: {
12084
+ marginTop: 2,
12085
+ fontSize: 12,
12086
+ fontFamily: "Inter, sans-serif",
12087
+ color: TEXT_SECONDARY6
12088
+ },
12089
+ testID: "exercise-detail-page-subtitle",
12090
+ children: exercise.subtitle
12091
+ }
12092
+ )
12093
+ ] }),
12094
+ exercise.currentE1rm != null && /* @__PURE__ */ jsxs(
12095
+ reactNative.View,
11877
12096
  {
11878
- style: { paddingHorizontal: 16 },
11879
- contentContainerStyle: { paddingBottom: 20 },
11880
- testID: "body-map-detail-panel-scroll",
12097
+ style: {
12098
+ paddingVertical: 4,
12099
+ paddingHorizontal: 10,
12100
+ borderRadius: 6,
12101
+ backgroundColor: "rgba(255,121,0,0.10)",
12102
+ borderWidth: 1,
12103
+ borderColor: "rgba(255,121,0,0.25)"
12104
+ },
12105
+ testID: "exercise-detail-page-e1rm",
11881
12106
  children: [
11882
- /* @__PURE__ */ jsxs(
11883
- reactNative.View,
11884
- {
11885
- className: "flex-row items-center justify-between",
11886
- style: { gap: 8, paddingTop: 4, paddingBottom: 4 },
11887
- testID: "body-map-detail-panel-header",
11888
- children: [
11889
- /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", style: { gap: 8, flexShrink: 1 }, children: [
11890
- /* @__PURE__ */ jsx(
11891
- reactNative.Text,
11892
- {
11893
- accessibilityRole: "header",
11894
- style: {
11895
- fontSize: 16,
11896
- fontFamily: '"Space Grotesk", sans-serif',
11897
- fontWeight: "700",
11898
- color: TEXT_PRIMARY8
11899
- },
11900
- testID: "body-map-detail-panel-title",
11901
- children: displayName
11902
- }
11903
- ),
11904
- /* @__PURE__ */ jsx(
11905
- Badge,
11906
- {
11907
- variant: "subtle",
11908
- color: STATUS_BADGE_COLOR[volumeStatus],
11909
- size: "sm",
11910
- testID: "body-map-detail-panel-status-badge",
11911
- children: VOLUME_STATUS_LABELS[volumeStatus]
11912
- }
11913
- )
11914
- ] }),
11915
- /* @__PURE__ */ jsx(
11916
- reactNative.Pressable,
11917
- {
11918
- onPress: onClose,
11919
- accessibilityRole: "button",
11920
- accessibilityLabel: `Close ${displayName} details`,
11921
- hitSlop: 8,
11922
- style: { padding: 4, margin: -4 },
11923
- testID: "body-map-detail-panel-close",
11924
- children: /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 22, lineHeight: 22, color: TEXT_SECONDARY7 }, children: "\xD7" })
11925
- }
11926
- )
11927
- ]
11928
- }
11929
- ),
11930
- lastTrained != null && /* @__PURE__ */ jsx(
12107
+ /* @__PURE__ */ jsx(
11931
12108
  reactNative.Text,
11932
12109
  {
11933
- style: { fontSize: 12, fontFamily: "Inter, sans-serif", color: TEXT_SECONDARY7 },
11934
- testID: "body-map-detail-panel-last-trained",
11935
- children: `Last trained ${lastTrained}`
12110
+ style: {
12111
+ fontSize: 9,
12112
+ fontFamily: "Inter, sans-serif",
12113
+ fontWeight: "600",
12114
+ letterSpacing: 0.4,
12115
+ textTransform: "uppercase",
12116
+ color: TEXT_TERTIARY8
12117
+ },
12118
+ children: "e1RM"
11936
12119
  }
11937
12120
  ),
11938
- /* @__PURE__ */ jsxs(reactNative.View, { style: { marginTop: 16 }, testID: "body-map-detail-panel-volume", children: [
11939
- /* @__PURE__ */ jsxs(
11940
- reactNative.View,
11941
- {
11942
- className: "flex-row items-center justify-between",
11943
- style: { marginBottom: 6 },
11944
- accessibilityElementsHidden: true,
11945
- children: [
11946
- /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 10, fontFamily: "Inter, sans-serif", fontWeight: "600", color: TEXT_TERTIARY7 }, children: `MEV ${landmarks.mev}` }),
11947
- /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 10, fontFamily: "Inter, sans-serif", fontWeight: "600", color: TEXT_TERTIARY7 }, children: `MRV ${landmarks.mrv}` })
11948
- ]
11949
- }
11950
- ),
11951
- /* @__PURE__ */ jsxs(
11952
- reactNative.View,
11953
- {
11954
- style: { height: 14, justifyContent: "center" },
11955
- accessibilityRole: "progressbar",
11956
- accessibilityValue: { min: landmarks.mev, max: landmarks.mrv, now: weeklySets },
11957
- accessibilityLabel: `${weeklySets} weekly sets, between MEV ${landmarks.mev} and MRV ${landmarks.mrv}`,
11958
- "aria-valuenow": weeklySets,
11959
- "aria-valuemin": landmarks.mev,
11960
- "aria-valuemax": landmarks.mrv,
11961
- testID: "body-map-detail-panel-volume-bar",
11962
- children: [
11963
- /* @__PURE__ */ jsx(
11964
- reactNative.View,
11965
- {
11966
- style: { height: 8, borderRadius: 4, backgroundImage: VOLUME_GRADIENT },
11967
- accessibilityElementsHidden: true
11968
- }
11969
- ),
11970
- /* @__PURE__ */ jsx(
11971
- reactNative.View,
11972
- {
11973
- style: {
11974
- position: "absolute",
11975
- left: `${markerLeft}%`,
11976
- marginLeft: -7,
11977
- width: 14,
11978
- height: 14,
11979
- borderRadius: 7,
11980
- borderWidth: 2,
11981
- borderColor: TEXT_PRIMARY8,
11982
- backgroundColor: SURFACE_ELEVATED,
11983
- boxShadow: "0 0 4px rgba(0,0,0,0.5)"
11984
- },
11985
- accessibilityElementsHidden: true,
11986
- testID: "body-map-detail-panel-volume-marker"
11987
- }
11988
- )
11989
- ]
11990
- }
11991
- )
11992
- ] }),
11993
- /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-baseline", style: { marginTop: 14, gap: 4 }, children: [
11994
- /* @__PURE__ */ jsx(
11995
- reactNative.Text,
11996
- {
11997
- style: {
11998
- fontSize: 24,
11999
- fontFamily: '"Space Grotesk", sans-serif',
12000
- fontWeight: "700",
12001
- color: TEXT_PRIMARY8
12002
- },
12003
- testID: "body-map-detail-panel-set-count",
12004
- children: weeklySets
12005
- }
12006
- ),
12007
- /* @__PURE__ */ jsx(
12008
- reactNative.Text,
12009
- {
12010
- style: { fontSize: 12, fontFamily: "Inter, sans-serif", color: TEXT_SECONDARY7 },
12011
- testID: "body-map-detail-panel-mrv-suffix",
12012
- children: `/ ${landmarks.mrv} MRV`
12013
- }
12014
- )
12015
- ] }),
12016
- weeklyHistory != null && weeklyHistory.length > 0 && /* @__PURE__ */ jsxs(reactNative.View, { style: { marginTop: 14 }, testID: "body-map-detail-panel-sparkline", children: [
12017
- /* @__PURE__ */ jsx(
12018
- reactNative.Text,
12019
- {
12020
- style: {
12021
- fontSize: 10,
12022
- fontFamily: "Inter, sans-serif",
12023
- fontWeight: "600",
12024
- color: TEXT_TERTIARY7,
12025
- marginBottom: 6
12026
- },
12027
- accessibilityElementsHidden: true,
12028
- children: "VOLUME TREND"
12029
- }
12030
- ),
12031
- /* @__PURE__ */ jsx(Sparkline, { data: weeklyHistory, width: 80, height: 30, highlightLast: true })
12032
- ] }),
12033
- hasContributing && /* @__PURE__ */ jsxs(reactNative.View, { style: { marginTop: 16 }, testID: "body-map-detail-panel-contributing", children: [
12034
- /* @__PURE__ */ jsx(
12035
- reactNative.Text,
12036
- {
12037
- style: {
12038
- fontSize: 10,
12039
- fontFamily: "Inter, sans-serif",
12040
- fontWeight: "600",
12041
- color: TEXT_TERTIARY7,
12042
- marginBottom: 8
12043
- },
12044
- children: "CONTRIBUTING EXERCISES"
12045
- }
12046
- ),
12047
- contributingExercises.map((exercise, index) => /* @__PURE__ */ jsxs(
12048
- reactNative.View,
12049
- {
12050
- className: "flex-row items-center justify-between",
12051
- style: {
12052
- gap: 10,
12053
- paddingVertical: 8,
12054
- paddingHorizontal: 12,
12055
- marginBottom: 6,
12056
- borderRadius: 8,
12057
- backgroundColor: SURFACE_RAISED2,
12058
- borderWidth: 1,
12059
- borderColor: BORDER_DEFAULT3
12060
- },
12061
- testID: `body-map-detail-panel-contributing-${index}`,
12062
- children: [
12063
- /* @__PURE__ */ jsx(
12064
- reactNative.Text,
12065
- {
12066
- style: { flex: 1, fontSize: 13, fontFamily: "Inter, sans-serif", fontWeight: "600", color: TEXT_PRIMARY8 },
12067
- testID: `body-map-detail-panel-contributing-${index}-name`,
12068
- children: exercise.name
12069
- }
12070
- ),
12071
- /* @__PURE__ */ jsx(
12072
- reactNative.Text,
12073
- {
12074
- style: { fontSize: 12, fontFamily: "Inter, sans-serif", color: TEXT_SECONDARY7 },
12075
- testID: `body-map-detail-panel-contributing-${index}-detail`,
12076
- children: `${exercise.sets} sets \xB7 ${Math.round(exercise.contributionWeight * 100)}%`
12077
- }
12078
- )
12079
- ]
12080
- },
12081
- `${exercise.name}-${index}`
12082
- ))
12083
- ] }),
12084
- hasUpcoming && /* @__PURE__ */ jsxs(reactNative.View, { style: { marginTop: 16 }, testID: "body-map-detail-panel-upcoming", children: [
12085
- /* @__PURE__ */ jsx(
12086
- reactNative.Text,
12087
- {
12088
- style: {
12089
- fontSize: 10,
12090
- fontFamily: "Inter, sans-serif",
12091
- fontWeight: "600",
12092
- color: TEXT_TERTIARY7,
12093
- marginBottom: 8
12094
- },
12095
- children: "UPCOMING"
12096
- }
12097
- ),
12098
- upcomingExercises.map((exercise, index) => /* @__PURE__ */ jsxs(
12099
- reactNative.View,
12100
- {
12101
- className: "flex-row items-center justify-between",
12102
- style: { gap: 10, paddingVertical: 6 },
12103
- testID: `body-map-detail-panel-upcoming-${index}`,
12104
- children: [
12105
- /* @__PURE__ */ jsx(
12106
- reactNative.Text,
12107
- {
12108
- style: { flex: 1, fontSize: 13, fontFamily: "Inter, sans-serif", fontWeight: "600", color: TEXT_PRIMARY8 },
12109
- testID: `body-map-detail-panel-upcoming-${index}-name`,
12110
- children: exercise.name
12111
- }
12112
- ),
12113
- /* @__PURE__ */ jsx(
12114
- reactNative.Text,
12115
- {
12116
- style: { fontSize: 12, fontFamily: "Inter, sans-serif", color: TEXT_TERTIARY7 },
12117
- testID: `body-map-detail-panel-upcoming-${index}-detail`,
12118
- children: `${exercise.workoutName} \xB7 ${exercise.sets} sets`
12119
- }
12120
- )
12121
- ]
12122
- },
12123
- `${exercise.name}-${index}`
12124
- ))
12125
- ] }),
12126
- onViewExercises != null && /* @__PURE__ */ jsx(
12127
- reactNative.Pressable,
12121
+ /* @__PURE__ */ jsx(
12122
+ reactNative.Text,
12128
12123
  {
12129
- onPress: onViewExercises,
12130
- accessibilityRole: "button",
12131
- accessibilityLabel: `View ${displayName} exercises`,
12132
12124
  style: {
12133
- marginTop: 16,
12134
- paddingVertical: 10,
12135
- borderRadius: 8,
12136
- alignItems: "center",
12137
- backgroundColor: "rgba(255,121,0,0.12)",
12138
- borderWidth: 1,
12139
- borderColor: "rgba(255,121,0,0.3)"
12125
+ fontSize: 15,
12126
+ fontFamily: '"Space Grotesk", sans-serif',
12127
+ fontWeight: "700",
12128
+ color: BRAND_PRIMARY12
12140
12129
  },
12141
- testID: "body-map-detail-panel-view-exercises",
12142
- children: /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 13, fontFamily: "Inter, sans-serif", fontWeight: "700", color: BRAND_PRIMARY11 }, children: "View exercises" })
12130
+ children: `${exercise.currentE1rm} ${exercise.unit}`
12143
12131
  }
12144
12132
  )
12145
12133
  ]
@@ -12147,6 +12135,346 @@ function BodyMapDetailPanel({
12147
12135
  )
12148
12136
  ]
12149
12137
  }
12138
+ ),
12139
+ /* @__PURE__ */ jsx(TabBar, { active: tab, onSelect: setTab }),
12140
+ tab === "progress" && /* @__PURE__ */ jsxs(reactNative.View, { style: { gap: 14 }, testID: "exercise-detail-page-panel-progress", children: [
12141
+ /* @__PURE__ */ jsx(MesoStatusCard, { ...meso }),
12142
+ /* @__PURE__ */ jsx(StatStrip, { stats, unit: exercise.unit }),
12143
+ /* @__PURE__ */ jsx(SectionCard, { title: "Strength Trend", testID: "exercise-detail-page-trend", children: /* @__PURE__ */ jsx(
12144
+ StrengthTrendChart,
12145
+ {
12146
+ data: trend.data,
12147
+ projection: trend.projection,
12148
+ mesoBoundaries: trend.mesoBoundaries,
12149
+ width: CHART_WIDTH,
12150
+ height: CHART_HEIGHT,
12151
+ unit: exercise.unit,
12152
+ animateOnMount: false
12153
+ }
12154
+ ) }),
12155
+ /* @__PURE__ */ jsx(SectionCard, { title: "Week-by-Week", testID: "exercise-detail-page-progression", children: /* @__PURE__ */ jsx(
12156
+ EntryList,
12157
+ {
12158
+ entries: progression,
12159
+ expandedId: expandedProgressId,
12160
+ onToggle: toggle(setExpandedProgressId),
12161
+ emptyLabel: "No progression logged yet",
12162
+ testID: "exercise-detail-page-progression-list"
12163
+ }
12164
+ ) })
12165
+ ] }),
12166
+ tab === "history" && /* @__PURE__ */ jsx(reactNative.View, { style: { gap: 14 }, testID: "exercise-detail-page-panel-history", children: /* @__PURE__ */ jsx(SectionCard, { title: "All Sessions", testID: "exercise-detail-page-history", children: /* @__PURE__ */ jsx(
12167
+ EntryList,
12168
+ {
12169
+ entries: history,
12170
+ expandedId: expandedHistoryId,
12171
+ onToggle: toggle(setExpandedHistoryId),
12172
+ emptyLabel: "No sessions logged yet",
12173
+ testID: "exercise-detail-page-history-list"
12174
+ }
12175
+ ) }) }),
12176
+ tab === "advanced" && /* @__PURE__ */ jsxs(reactNative.View, { style: { gap: 14 }, testID: "exercise-detail-page-panel-advanced", children: [
12177
+ /* @__PURE__ */ jsx(SectionCard, { title: "Capacity Band", testID: "exercise-detail-page-capacity", children: /* @__PURE__ */ jsx(
12178
+ CapacityBandChart,
12179
+ {
12180
+ band: vbt.band,
12181
+ workouts: vbt.workouts,
12182
+ projection: vbt.projection,
12183
+ width: CHART_WIDTH,
12184
+ height: CHART_HEIGHT
12185
+ }
12186
+ ) }),
12187
+ /* @__PURE__ */ jsx(VbtSummaryRow, { summary: vbtSummary }),
12188
+ /* @__PURE__ */ jsx(SectionCard, { title: "Velocity Breakdown", testID: "exercise-detail-page-velocity", children: /* @__PURE__ */ jsx(VbtBreakdown, { sets: vbt.sets }) })
12189
+ ] })
12190
+ ] })
12191
+ }
12192
+ );
12193
+ }
12194
+ var SURFACE_ELEVATED3 = "var(--color-surface-elevated)";
12195
+ var BORDER_DEFAULT5 = "var(--color-border-default)";
12196
+ var BRAND_PRIMARY13 = "#FF7900";
12197
+ var TEXT_PRIMARY9 = "var(--color-text-primary)";
12198
+ var TEXT_SECONDARY7 = "var(--color-text-secondary)";
12199
+ var TEXT_TERTIARY9 = "var(--color-text-tertiary)";
12200
+ var PAGE_BG3 = "var(--color-background-base)";
12201
+ function countCompletedSets(exercise) {
12202
+ if (exercise.setVelocities != null) return exercise.setVelocities.length;
12203
+ if (exercise.status === "completed") return exercise.totalPlannedSets;
12204
+ return 0;
12205
+ }
12206
+ function deriveWorkoutProgress(exercises) {
12207
+ let completedExercises = 0;
12208
+ let completedSets = 0;
12209
+ let totalSets = 0;
12210
+ for (const exercise of exercises) {
12211
+ if (exercise.status === "completed") completedExercises += 1;
12212
+ completedSets += countCompletedSets(exercise);
12213
+ totalSets += exercise.totalPlannedSets;
12214
+ }
12215
+ return {
12216
+ completedExercises,
12217
+ totalExercises: exercises.length,
12218
+ completedSets,
12219
+ totalSets,
12220
+ fraction: totalSets === 0 ? 0 : completedSets / totalSets
12221
+ };
12222
+ }
12223
+ function findActiveExercise(exercises) {
12224
+ return exercises.find((exercise) => exercise.status === "active") ?? null;
12225
+ }
12226
+ function statusToCardState(status, focused) {
12227
+ if (status === "upcoming") return "upcoming";
12228
+ return focused ? "expanded" : "collapsed";
12229
+ }
12230
+ function toActiveCardProps(exercise, focused, onToggle, supersetPosition = null, supersetColor) {
12231
+ const state = statusToCardState(exercise.status, focused);
12232
+ return {
12233
+ name: exercise.name,
12234
+ state,
12235
+ onToggle,
12236
+ summary: exercise.summary,
12237
+ e1rm: exercise.e1rm,
12238
+ isPR: exercise.isPR,
12239
+ tempo: exercise.tempo,
12240
+ setVelocities: exercise.setVelocities,
12241
+ totalPlannedSets: exercise.totalPlannedSets,
12242
+ sets: state === "expanded" ? exercise.sets : void 0,
12243
+ prescription: exercise.prescription,
12244
+ previousBest: exercise.previousBest,
12245
+ supersetPosition,
12246
+ supersetColor
12247
+ };
12248
+ }
12249
+ function groupExercises(exercises, supersets) {
12250
+ const byId = new Map(supersets.map((superset) => [superset.id, superset]));
12251
+ const groups = [];
12252
+ for (const exercise of exercises) {
12253
+ const superset = exercise.supersetId != null ? byId.get(exercise.supersetId) : void 0;
12254
+ const last = groups[groups.length - 1];
12255
+ if (superset != null && last?.type === "superset" && last.superset.id === superset.id) {
12256
+ last.exercises.push(exercise);
12257
+ } else if (superset != null) {
12258
+ groups.push({ type: "superset", superset, exercises: [exercise] });
12259
+ } else {
12260
+ groups.push({ type: "single", exercise });
12261
+ }
12262
+ }
12263
+ return groups;
12264
+ }
12265
+ function supersetPositionAt(index, length) {
12266
+ if (length === 1) return null;
12267
+ if (index === 0) return "first";
12268
+ if (index === length - 1) return "last";
12269
+ return "middle";
12270
+ }
12271
+ function WorkoutHeader({
12272
+ title,
12273
+ subtitle,
12274
+ progress
12275
+ }) {
12276
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: { gap: 10 }, testID: "active-workout-page-header", children: [
12277
+ /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-start justify-between", style: { gap: 8 }, children: [
12278
+ /* @__PURE__ */ jsxs(reactNative.View, { style: { flexShrink: 1 }, children: [
12279
+ /* @__PURE__ */ jsx(
12280
+ reactNative.Text,
12281
+ {
12282
+ accessibilityRole: "header",
12283
+ style: {
12284
+ fontSize: 20,
12285
+ fontFamily: '"Space Grotesk", sans-serif',
12286
+ fontWeight: "700",
12287
+ color: TEXT_PRIMARY9
12288
+ },
12289
+ testID: "active-workout-page-title",
12290
+ children: title
12291
+ }
12292
+ ),
12293
+ subtitle != null && /* @__PURE__ */ jsx(
12294
+ reactNative.Text,
12295
+ {
12296
+ style: {
12297
+ marginTop: 2,
12298
+ fontSize: 12,
12299
+ fontFamily: "Inter, sans-serif",
12300
+ color: TEXT_SECONDARY7
12301
+ },
12302
+ testID: "active-workout-page-subtitle",
12303
+ children: subtitle
12304
+ }
12305
+ )
12306
+ ] }),
12307
+ /* @__PURE__ */ jsxs(reactNative.View, { style: { alignItems: "flex-end" }, testID: "active-workout-page-progress-count", children: [
12308
+ /* @__PURE__ */ jsx(
12309
+ reactNative.Text,
12310
+ {
12311
+ style: {
12312
+ fontSize: 15,
12313
+ fontFamily: '"Space Grotesk", sans-serif',
12314
+ fontWeight: "700",
12315
+ color: BRAND_PRIMARY13,
12316
+ fontVariant: ["tabular-nums"]
12317
+ },
12318
+ children: `${progress.completedExercises}/${progress.totalExercises}`
12319
+ }
12320
+ ),
12321
+ /* @__PURE__ */ jsx(
12322
+ reactNative.Text,
12323
+ {
12324
+ style: {
12325
+ fontSize: 10,
12326
+ fontFamily: "Inter, sans-serif",
12327
+ fontWeight: "600",
12328
+ letterSpacing: 0.4,
12329
+ textTransform: "uppercase",
12330
+ color: TEXT_TERTIARY9
12331
+ },
12332
+ children: `${progress.completedSets}/${progress.totalSets} sets`
12333
+ }
12334
+ )
12335
+ ] })
12336
+ ] }),
12337
+ /* @__PURE__ */ jsx(
12338
+ reactNative.View,
12339
+ {
12340
+ style: { height: 4, backgroundColor: BORDER_DEFAULT5, borderRadius: 2 },
12341
+ testID: "active-workout-page-progress-track",
12342
+ children: /* @__PURE__ */ jsx(
12343
+ reactNative.View,
12344
+ {
12345
+ style: {
12346
+ height: "100%",
12347
+ width: `${Math.round(progress.fraction * 100)}%`,
12348
+ backgroundColor: BRAND_PRIMARY13,
12349
+ borderRadius: 2
12350
+ },
12351
+ testID: "active-workout-page-progress-fill"
12352
+ }
12353
+ )
12354
+ }
12355
+ )
12356
+ ] });
12357
+ }
12358
+ function ExerciseList({ groups, focusedId, onToggle }) {
12359
+ return /* @__PURE__ */ jsx(reactNative.View, { style: { gap: 10 }, testID: "active-workout-page-exercises", children: groups.map((group) => {
12360
+ if (group.type === "single") {
12361
+ return /* @__PURE__ */ jsx(
12362
+ reactNative.View,
12363
+ {
12364
+ style: {
12365
+ backgroundColor: SURFACE_ELEVATED3,
12366
+ borderWidth: 1,
12367
+ borderColor: BORDER_DEFAULT5,
12368
+ borderRadius: 12
12369
+ },
12370
+ children: /* @__PURE__ */ jsx(
12371
+ ExerciseCard,
12372
+ {
12373
+ ...toActiveCardProps(
12374
+ group.exercise,
12375
+ group.exercise.id === focusedId,
12376
+ () => onToggle(group.exercise.id)
12377
+ )
12378
+ }
12379
+ )
12380
+ },
12381
+ group.exercise.id
12382
+ );
12383
+ }
12384
+ return /* @__PURE__ */ jsx(
12385
+ SupersetWrapper,
12386
+ {
12387
+ label: group.superset.label,
12388
+ color: group.superset.color,
12389
+ children: group.exercises.map((exercise, index) => /* @__PURE__ */ jsx(
12390
+ ExerciseCard,
12391
+ {
12392
+ ...toActiveCardProps(
12393
+ exercise,
12394
+ exercise.id === focusedId,
12395
+ () => onToggle(exercise.id),
12396
+ supersetPositionAt(index, group.exercises.length),
12397
+ group.superset.color
12398
+ )
12399
+ },
12400
+ exercise.id
12401
+ ))
12402
+ },
12403
+ group.superset.id
12404
+ );
12405
+ }) });
12406
+ }
12407
+ function ActiveWorkoutPage({
12408
+ title = "Active Workout",
12409
+ subtitle,
12410
+ exercises,
12411
+ supersets = [],
12412
+ input,
12413
+ rest,
12414
+ initialResting = false,
12415
+ className,
12416
+ ...props
12417
+ }) {
12418
+ const active = findActiveExercise(exercises);
12419
+ const [focusedId, setFocusedId] = React24.useState(active?.id ?? null);
12420
+ const [resting, setResting] = React24.useState(initialResting);
12421
+ const [reps, setReps] = React24.useState(input?.reps ?? "");
12422
+ const [weight, setWeight] = React24.useState(input?.weight ?? "");
12423
+ const progress = React24.useMemo(() => deriveWorkoutProgress(exercises), [exercises]);
12424
+ const groups = React24.useMemo(() => groupExercises(exercises, supersets), [exercises, supersets]);
12425
+ const toggleFocus = (id) => setFocusedId((current) => current === id ? null : id);
12426
+ const nextSetNumber = active != null ? countCompletedSets(active) + 1 : 1;
12427
+ const canRecord = reps.trim() !== "" && weight.trim() !== "";
12428
+ const showRest = resting && rest != null;
12429
+ const showInput = !showRest && active != null;
12430
+ return /* @__PURE__ */ jsxs(
12431
+ reactNative.View,
12432
+ {
12433
+ className,
12434
+ style: { position: "relative", width: 390, backgroundColor: PAGE_BG3 },
12435
+ accessibilityRole: "main",
12436
+ "aria-label": title,
12437
+ testID: "active-workout-page",
12438
+ ...props,
12439
+ children: [
12440
+ /* @__PURE__ */ jsxs(reactNative.View, { style: { padding: 16, gap: 14 }, testID: "active-workout-page-content", children: [
12441
+ /* @__PURE__ */ jsx(WorkoutHeader, { title, subtitle, progress }),
12442
+ /* @__PURE__ */ jsx(ExerciseList, { groups, focusedId, onToggle: toggleFocus })
12443
+ ] }),
12444
+ (showRest || showInput) && /* @__PURE__ */ jsx(
12445
+ reactNative.View,
12446
+ {
12447
+ style: { borderTopWidth: 1, borderTopColor: BORDER_DEFAULT5 },
12448
+ testID: "active-workout-page-bottom-bar",
12449
+ children: showRest && rest != null ? /* @__PURE__ */ jsx(
12450
+ RestTimer,
12451
+ {
12452
+ totalSeconds: rest.totalSeconds,
12453
+ elapsedMs: rest.elapsedMs,
12454
+ nextSetInfo: rest.nextSetInfo,
12455
+ onSkip: () => setResting(false),
12456
+ onAddTime: () => void 0,
12457
+ visible: true
12458
+ }
12459
+ ) : active != null && /* @__PURE__ */ jsx(
12460
+ InputBar,
12461
+ {
12462
+ exerciseName: active.name,
12463
+ setNumber: nextSetNumber,
12464
+ totalSets: active.totalPlannedSets,
12465
+ reps,
12466
+ weight,
12467
+ unit: active.unit,
12468
+ onRepsChange: setReps,
12469
+ onWeightChange: setWeight,
12470
+ onRecord: () => {
12471
+ if (rest != null) setResting(true);
12472
+ },
12473
+ canRecord,
12474
+ visible: true
12475
+ }
12476
+ )
12477
+ }
12150
12478
  )
12151
12479
  ]
12152
12480
  }
@@ -12469,7 +12797,7 @@ var DEFAULT_THRESHOLDS = [
12469
12797
  var ARC_START = 135;
12470
12798
  var ARC_SWEEP = 270;
12471
12799
  var SEGMENTS = 40;
12472
- function clamp014(n) {
12800
+ function clamp013(n) {
12473
12801
  if (Number.isNaN(n)) return 0;
12474
12802
  return Math.max(0, Math.min(1, n));
12475
12803
  }
@@ -12514,7 +12842,7 @@ function Gauge2({
12514
12842
  ...props
12515
12843
  }) {
12516
12844
  const span = max - min || 1;
12517
- const fill = clamp014((value - min) / span);
12845
+ const fill = clamp013((value - min) / span);
12518
12846
  const activeColor = (f) => color ?? bandColor(f, thresholds, min, max);
12519
12847
  const ticks = buildTicks(fill, size, activeColor);
12520
12848
  const displayColor = color ?? bandColor(fill, thresholds, min, max);
@@ -12712,6 +13040,7 @@ exports.Accordion = Accordion;
12712
13040
  exports.AccordionButton = AccordionButton;
12713
13041
  exports.AccordionItem = AccordionItem;
12714
13042
  exports.AccordionPanel = AccordionPanel;
13043
+ exports.ActiveWorkoutPage = ActiveWorkoutPage;
12715
13044
  exports.Alert = Alert;
12716
13045
  exports.AlertDescription = AlertDescription;
12717
13046
  exports.AlertTitle = AlertTitle;
@@ -12722,8 +13051,6 @@ exports.AvatarGroup = AvatarGroup;
12722
13051
  exports.Badge = Badge;
12723
13052
  exports.BadgeText = BadgeText;
12724
13053
  exports.BaseBadge = BaseBadge;
12725
- exports.BodyMap = BodyMap;
12726
- exports.BodyMapDetailPanel = BodyMapDetailPanel;
12727
13054
  exports.BreadcrumbItem = BreadcrumbItem;
12728
13055
  exports.Breadcrumbs = Breadcrumbs;
12729
13056
  exports.Button = Button;
@@ -12746,8 +13073,6 @@ exports.CircularProgress = CircularProgress;
12746
13073
  exports.Collapse = Collapse;
12747
13074
  exports.CollapseButton = CollapseButton;
12748
13075
  exports.CollapseContent = CollapseContent;
12749
- exports.DEFAULT_VOLUME_LANDMARKS = DEFAULT_VOLUME_LANDMARKS;
12750
- exports.DETAILED_TO_SIMPLE = DETAILED_TO_SIMPLE;
12751
13076
  exports.DataRow = DataRow;
12752
13077
  exports.DateTime = DateTime;
12753
13078
  exports.DeviationBar = DeviationBar;
@@ -12756,8 +13081,10 @@ exports.Drawer = Drawer;
12756
13081
  exports.DrawerBody = DrawerBody;
12757
13082
  exports.DrawerFooter = DrawerFooter;
12758
13083
  exports.DrawerHeader = DrawerHeader;
13084
+ exports.EXERCISE_DETAIL_TABS = EXERCISE_DETAIL_TABS;
12759
13085
  exports.EmptyState = EmptyState;
12760
13086
  exports.ExerciseCard = ExerciseCard;
13087
+ exports.ExerciseDetailPage = ExerciseDetailPage;
12761
13088
  exports.FormActions = FormActions;
12762
13089
  exports.FormField = FormField;
12763
13090
  exports.FormRow = FormRow;
@@ -12780,9 +13107,6 @@ exports.ListItemContent = ListItemContent;
12780
13107
  exports.ListItemDivider = ListItemDivider;
12781
13108
  exports.ListItemIcon = ListItemIcon;
12782
13109
  exports.ListItemTrailing = ListItemTrailing;
12783
- exports.MUSCLE_DISPLAY_NAMES = MUSCLE_DISPLAY_NAMES;
12784
- exports.MUSCLE_TO_CATEGORY = MUSCLE_TO_CATEGORY;
12785
- exports.MUSCLE_TO_SVG_SLUGS = MUSCLE_TO_SVG_SLUGS;
12786
13110
  exports.Menu = Menu;
12787
13111
  exports.MenuDivider = MenuDivider;
12788
13112
  exports.MenuGroup = MenuGroup;
@@ -12801,7 +13125,6 @@ exports.ModalContent = ModalContent;
12801
13125
  exports.ModalFooter = ModalFooter;
12802
13126
  exports.ModalHeader = ModalHeader;
12803
13127
  exports.ModalTitle = ModalTitle;
12804
- exports.MuscleGroup = MuscleGroup;
12805
13128
  exports.MuscleGroupChip = MuscleGroupChip;
12806
13129
  exports.Overline = Overline;
12807
13130
  exports.Paragraph = Paragraph;
@@ -12814,14 +13137,13 @@ exports.PopoverContent = PopoverContent;
12814
13137
  exports.PopoverTrigger = PopoverTrigger;
12815
13138
  exports.PrBadge = PrBadge;
12816
13139
  exports.PrHistoryModal = PrHistoryModal;
13140
+ exports.ProgramPlanningPage = ProgramPlanningPage;
12817
13141
  exports.Progress = Progress;
12818
13142
  exports.ProgressSteps = ProgressSteps;
12819
13143
  exports.Radio = Radio;
12820
13144
  exports.RadioGroup = RadioGroup;
12821
13145
  exports.ReadinessCheck = ReadinessCheck;
12822
13146
  exports.RestTimer = RestTimer;
12823
- exports.SIMPLE_DISPLAY_NAMES = SIMPLE_DISPLAY_NAMES;
12824
- exports.SIMPLE_TO_DETAILED = SIMPLE_TO_DETAILED;
12825
13147
  exports.Scatter = Scatter;
12826
13148
  exports.Section = Section;
12827
13149
  exports.SectionContent = SectionContent;
@@ -12835,7 +13157,6 @@ exports.SidebarFooter = SidebarFooter;
12835
13157
  exports.SidebarHeader = SidebarHeader;
12836
13158
  exports.SidebarItem = SidebarItem;
12837
13159
  exports.SidebarSection = SidebarSection;
12838
- exports.SimpleMuscleGroup = SimpleMuscleGroup;
12839
13160
  exports.Skeleton = Skeleton;
12840
13161
  exports.SkeletonCard = SkeletonCard;
12841
13162
  exports.SkeletonCircle = SkeletonCircle;
@@ -12874,7 +13195,6 @@ exports.ToolbarButtonGroup = ToolbarButtonGroup;
12874
13195
  exports.Tooltip = Tooltip;
12875
13196
  exports.Treemap = Treemap;
12876
13197
  exports.Typography = Typography;
12877
- exports.VOLUME_STATUS_LABELS = VOLUME_STATUS_LABELS;
12878
13198
  exports.VStack = VStack;
12879
13199
  exports.VelocityStrip = VelocityStrip;
12880
13200
  exports.WeekRow = WeekRow;
@@ -12884,12 +13204,14 @@ exports.WorkoutPill = WorkoutPill;
12884
13204
  exports.alpha = alpha;
12885
13205
  exports.applyThemePreset = applyThemePreset;
12886
13206
  exports.audiobookPreset = audiobookPreset;
13207
+ exports.buildBreadcrumbs = buildBreadcrumbs;
12887
13208
  exports.buttonSizes = buttonSizes;
12888
13209
  exports.calculateMeanVelocity = calculateMeanVelocity;
12889
13210
  exports.calculateVelocityLoss = calculateVelocityLoss;
12890
13211
  exports.cn = cn;
12891
13212
  exports.componentElevationRanges = componentElevationRanges;
12892
13213
  exports.composeValidators = composeValidators;
13214
+ exports.countCompletedSets = countCompletedSets;
12893
13215
  exports.createFieldId = createFieldId;
12894
13216
  exports.createFlatShadow = createFlatShadow;
12895
13217
  exports.createPressedHoverShadow = createPressedHoverShadow;
@@ -12899,9 +13221,19 @@ exports.createRaisedShadow = createRaisedShadow;
12899
13221
  exports.darkThemeCSSVars = darkThemeCSSVars;
12900
13222
  exports.darken = darken;
12901
13223
  exports.defaultPreset = defaultPreset;
13224
+ exports.deriveExerciseStats = deriveExerciseStats;
13225
+ exports.deriveNavLevel = deriveNavLevel;
13226
+ exports.deriveWorkoutProgress = deriveWorkoutProgress;
12902
13227
  exports.discreteRainbow = discreteRainbow;
12903
13228
  exports.elevationSystem = elevationSystem;
13229
+ exports.findActiveExercise = findActiveExercise;
13230
+ exports.findMeso = findMeso;
13231
+ exports.findWeek = findWeek;
13232
+ exports.findWorkout = findWorkout;
12904
13233
  exports.formatDateTime = formatDateTime;
13234
+ exports.formatPrescription = formatPrescription;
13235
+ exports.formatSignedPct = formatSignedPct;
13236
+ exports.formatVelocity = formatVelocity;
12905
13237
  exports.getBaseSurfaceColor = getBaseSurfaceColor;
12906
13238
  exports.getContrastText = getContrastText;
12907
13239
  exports.getDiscreteRainbowColor = getDiscreteRainbowColor;
@@ -12911,7 +13243,6 @@ exports.getElevationSurface = getElevationSurface;
12911
13243
  exports.getFieldAriaProps = getFieldAriaProps;
12912
13244
  exports.getFieldValidationProps = getFieldValidationProps;
12913
13245
  exports.getGlowShadow = getGlowShadow;
12914
- exports.getHeatmapColor = getHeatmapColor;
12915
13246
  exports.getHoverColors = getHoverColors;
12916
13247
  exports.getLuminance = getLuminance;
12917
13248
  exports.getResultColor = getResultColor;
@@ -12922,6 +13253,7 @@ exports.getValidatedElevation = getValidatedElevation;
12922
13253
  exports.getVelocityZoneColor = getVelocityZoneColor;
12923
13254
  exports.getVelocityZoneName = getVelocityZoneName;
12924
13255
  exports.gluestackConfig = gluestackConfig;
13256
+ exports.groupExercises = groupExercises;
12925
13257
  exports.hasMaxLength = hasMaxLength;
12926
13258
  exports.hasMinLength = hasMinLength;
12927
13259
  exports.hexToRgb = hexToRgb;
@@ -12942,10 +13274,19 @@ exports.primitiveTypography = primitiveTypography;
12942
13274
  exports.primitiveZIndex = primitiveZIndex;
12943
13275
  exports.rgbToHex = rgbToHex;
12944
13276
  exports.rgbToHsv = rgbToHsv;
13277
+ exports.roundRpe = roundRpe;
13278
+ exports.roundTempo = roundTempo;
13279
+ exports.roundWeight = roundWeight;
13280
+ exports.rpeColor = rpeColor;
12945
13281
  exports.semanticColorsDark = semanticColorsDark;
12946
13282
  exports.semanticColorsLight = semanticColorsLight;
12947
13283
  exports.semanticTypography = semanticTypography;
12948
13284
  exports.shadowColors = shadowColors;
13285
+ exports.statusToCardState = statusToCardState;
13286
+ exports.summarizeVbt = summarizeVbt;
13287
+ exports.toActiveCardProps = toActiveCardProps;
13288
+ exports.toExerciseCardProps = toExerciseCardProps;
13289
+ exports.toProgressBarMesos = toProgressBarMesos;
12949
13290
  exports.useTable = useTable;
12950
13291
  exports.useToast = useToast;
12951
13292
  exports.useToolbarButton = useToolbarButton;