@titan-design/react-ui 0.7.0 → 0.9.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 (52) hide show
  1. package/dist/index.d.mts +112 -55
  2. package/dist/index.d.ts +112 -55
  3. package/dist/index.js +795 -501
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +795 -498
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/{pages-8u_4WbL-.d.mts → pages-D7hOD4Vj.d.mts} +17 -8
  8. package/dist/{pages-DaWiBOGa.d.ts → pages-DyEx-lBf.d.ts} +17 -8
  9. package/dist/pages.d.mts +1 -1
  10. package/dist/pages.d.ts +1 -1
  11. package/dist/pages.js +691 -250
  12. package/dist/pages.js.map +1 -1
  13. package/dist/pages.mjs +692 -251
  14. package/dist/pages.mjs.map +1 -1
  15. package/package.json +1 -1
  16. package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +90 -0
  17. package/src/components/custom/CircularTimer/CircularTimer.test.tsx +62 -0
  18. package/src/components/custom/CircularTimer/CircularTimer.tsx +112 -0
  19. package/src/components/custom/CircularTimer/index.ts +1 -0
  20. package/src/components/custom/Workout/ExerciseIndicator.tsx +1 -1
  21. package/src/components/custom/Workout/FatigueMeter.stories.tsx +2 -2
  22. package/src/components/custom/Workout/FatigueMeter.test.tsx +12 -0
  23. package/src/components/custom/Workout/FatigueMeter.tsx +7 -3
  24. package/src/components/custom/Workout/README.md +70 -3
  25. package/src/components/custom/Workout/RestTimer.stories.tsx +132 -2
  26. package/src/components/custom/Workout/RestTimer.test.tsx +73 -0
  27. package/src/components/custom/Workout/RestTimer.tsx +141 -50
  28. package/src/components/custom/Workout/SetsRepsLoad.test.tsx +6 -0
  29. package/src/components/custom/Workout/SetsRepsLoad.tsx +15 -6
  30. package/src/components/custom/Workout/TempoDisplay.stories.tsx +240 -65
  31. package/src/components/custom/Workout/TempoDisplay.test.tsx +61 -16
  32. package/src/components/custom/Workout/TempoDisplay.tsx +173 -60
  33. package/src/components/custom/Workout/VelocityStrip.stories.tsx +152 -9
  34. package/src/components/custom/Workout/VelocityStrip.test.tsx +67 -3
  35. package/src/components/custom/Workout/VelocityStrip.tsx +323 -42
  36. package/src/components/custom/Workout/ZoneTrack.stories.tsx +2 -3
  37. package/src/components/custom/Workout/ZoneTrack.test.tsx +34 -0
  38. package/src/components/custom/Workout/ZoneTrack.tsx +91 -19
  39. package/src/components/custom/Workout/index.ts +1 -9
  40. package/src/components/custom/index.ts +1 -0
  41. package/src/components/ui/alert/Alert.stories.tsx +85 -2
  42. package/src/components/ui/alert/Alert.test.tsx +52 -0
  43. package/src/components/ui/alert/Alert.tsx +54 -20
  44. package/src/components/ui/progress/Progress.tsx +47 -22
  45. package/src/lab/north-star/LivePage.tsx +101 -0
  46. package/src/lab/north-star/LiveView.tsx +409 -0
  47. package/src/lab/north-star/LiveWallDashboard.stories.tsx +129 -0
  48. package/src/lab/north-star/RestView.tsx +140 -0
  49. package/src/lab/north-star/fixtures.ts +224 -0
  50. package/src/components/custom/Workout/TempoBar.stories.tsx +0 -95
  51. package/src/components/custom/Workout/TempoBar.test.tsx +0 -103
  52. package/src/components/custom/Workout/TempoBar.tsx +0 -215
package/dist/index.mjs CHANGED
@@ -152,6 +152,7 @@ var statusColors = {
152
152
  subtle: "bg-status-success-subtle",
153
153
  outline: "border-2 border-status-success bg-transparent",
154
154
  solid: "bg-status-success",
155
+ border: "border-status-success",
155
156
  icon: "text-status-success",
156
157
  text: "text-status-success"
157
158
  },
@@ -159,6 +160,7 @@ var statusColors = {
159
160
  subtle: "bg-status-info-subtle",
160
161
  outline: "border-2 border-status-info bg-transparent",
161
162
  solid: "bg-status-info",
163
+ border: "border-status-info",
162
164
  icon: "text-status-info",
163
165
  text: "text-status-info"
164
166
  },
@@ -166,6 +168,7 @@ var statusColors = {
166
168
  subtle: "bg-status-warning-subtle",
167
169
  outline: "border-2 border-status-warning bg-transparent",
168
170
  solid: "bg-status-warning",
171
+ border: "border-status-warning",
169
172
  icon: "text-status-warning",
170
173
  text: "text-status-warning"
171
174
  },
@@ -173,6 +176,7 @@ var statusColors = {
173
176
  subtle: "bg-status-error-subtle",
174
177
  outline: "border-2 border-status-error bg-transparent",
175
178
  solid: "bg-status-error",
179
+ border: "border-status-error",
176
180
  icon: "text-status-error",
177
181
  text: "text-status-error"
178
182
  }
@@ -186,6 +190,8 @@ var defaultIcons = {
186
190
  function Alert({
187
191
  status = "info",
188
192
  variant = "subtle",
193
+ size = "default",
194
+ message,
189
195
  icon,
190
196
  showIcon = true,
191
197
  onClose,
@@ -195,28 +201,43 @@ function Alert({
195
201
  }) {
196
202
  const colors = statusColors[status];
197
203
  const isSolid = variant === "solid";
204
+ const isCompact = size === "compact";
198
205
  return /* @__PURE__ */ jsxs(
199
206
  View,
200
207
  {
201
208
  accessibilityRole: "alert",
202
209
  className: cn(
203
- "flex-row items-start p-4 rounded-lg",
210
+ isCompact ? "flex-row items-center px-3 py-2 rounded-lg" : "flex-row items-start p-4 rounded-lg",
204
211
  colors[variant],
212
+ // A compact `subtle` pill gets a hairline status border so it reads as a
213
+ // defined cue on a dark wall (the CueFlag look); other variants carry their own.
214
+ isCompact && variant === "subtle" && cn("border", colors.border),
205
215
  className
206
216
  ),
207
217
  ...props,
208
218
  children: [
209
- showIcon && /* @__PURE__ */ jsx(View, { className: "mr-3 mt-0.5", children: icon || /* @__PURE__ */ jsx(
219
+ showIcon && /* @__PURE__ */ jsx(View, { className: isCompact ? "mr-2" : "mr-3 mt-0.5", children: icon || /* @__PURE__ */ jsx(
210
220
  Text,
211
221
  {
212
222
  className: cn(
213
- "text-lg font-bold",
223
+ "font-bold",
224
+ isCompact ? "text-base" : "text-lg",
214
225
  isSolid ? "text-white" : colors.icon
215
226
  ),
216
227
  children: defaultIcons[status]
217
228
  }
218
229
  ) }),
219
- /* @__PURE__ */ jsx(View, { className: "flex-1", children }),
230
+ /* @__PURE__ */ jsxs(View, { className: "flex-1", children: [
231
+ message != null && /* @__PURE__ */ jsx(
232
+ Text,
233
+ {
234
+ className: cn("text-sm font-semibold", isSolid ? "text-white" : colors.text),
235
+ testID: "alert-message",
236
+ children: message
237
+ }
238
+ ),
239
+ children
240
+ ] }),
220
241
  onClose && /* @__PURE__ */ jsx(
221
242
  Pressable,
222
243
  {
@@ -5066,6 +5087,8 @@ function CircularProgress({
5066
5087
  color = "primary",
5067
5088
  showValue = false,
5068
5089
  formatValue: formatValue3 = (v, m) => `${Math.round(v / m * 100)}%`,
5090
+ children,
5091
+ fill = false,
5069
5092
  className,
5070
5093
  ...props
5071
5094
  }) {
@@ -5074,11 +5097,12 @@ function CircularProgress({
5074
5097
  const circumference = 2 * Math.PI * radius;
5075
5098
  const strokeDashoffset = circumference - percentage / 100 * circumference;
5076
5099
  const center = size / 2;
5100
+ const boxStyle = fill ? { width: "100%", aspectRatio: 1 } : { width: size, height: size };
5077
5101
  return /* @__PURE__ */ jsxs(
5078
5102
  View,
5079
5103
  {
5080
5104
  className: cn("items-center justify-center", className),
5081
- style: { width: size, height: size },
5105
+ style: boxStyle,
5082
5106
  accessibilityRole: "progressbar",
5083
5107
  accessibilityValue: {
5084
5108
  min: 0,
@@ -5087,52 +5111,56 @@ function CircularProgress({
5087
5111
  },
5088
5112
  ...props,
5089
5113
  children: [
5090
- /* @__PURE__ */ jsx(
5114
+ /* @__PURE__ */ jsx(View, { className: cn(isIndeterminate && "animate-spin"), style: boxStyle, children: /* @__PURE__ */ jsxs(
5115
+ "svg",
5116
+ {
5117
+ width: "100%",
5118
+ height: "100%",
5119
+ viewBox: `0 0 ${size} ${size}`,
5120
+ style: { position: "absolute" },
5121
+ children: [
5122
+ /* @__PURE__ */ jsx(
5123
+ "circle",
5124
+ {
5125
+ cx: center,
5126
+ cy: center,
5127
+ r: radius,
5128
+ fill: "none",
5129
+ stroke: "var(--color-border-default)",
5130
+ strokeWidth
5131
+ }
5132
+ ),
5133
+ /* @__PURE__ */ jsx(
5134
+ "circle",
5135
+ {
5136
+ cx: center,
5137
+ cy: center,
5138
+ r: radius,
5139
+ fill: "none",
5140
+ stroke: colorVarMap[color],
5141
+ strokeWidth,
5142
+ strokeLinecap: "round",
5143
+ strokeDasharray: circumference,
5144
+ strokeDashoffset: isIndeterminate ? circumference * 0.75 : strokeDashoffset,
5145
+ transform: `rotate(-90 ${center} ${center})`,
5146
+ style: { transition: "stroke-dashoffset 300ms cubic-bezier(0.22, 1, 0.36, 1)" }
5147
+ }
5148
+ )
5149
+ ]
5150
+ }
5151
+ ) }),
5152
+ children != null ? /* @__PURE__ */ jsx(
5091
5153
  View,
5092
5154
  {
5093
- className: cn(isIndeterminate && "animate-spin"),
5094
- style: { width: size, height: size },
5095
- children: /* @__PURE__ */ jsxs(
5096
- "svg",
5097
- {
5098
- width: size,
5099
- height: size,
5100
- viewBox: `0 0 ${size} ${size}`,
5101
- style: { position: "absolute" },
5102
- children: [
5103
- /* @__PURE__ */ jsx(
5104
- "circle",
5105
- {
5106
- cx: center,
5107
- cy: center,
5108
- r: radius,
5109
- fill: "none",
5110
- stroke: "var(--color-border-default)",
5111
- strokeWidth
5112
- }
5113
- ),
5114
- /* @__PURE__ */ jsx(
5115
- "circle",
5116
- {
5117
- cx: center,
5118
- cy: center,
5119
- r: radius,
5120
- fill: "none",
5121
- stroke: colorVarMap[color],
5122
- strokeWidth,
5123
- strokeLinecap: "round",
5124
- strokeDasharray: circumference,
5125
- strokeDashoffset: isIndeterminate ? circumference * 0.75 : strokeDashoffset,
5126
- transform: `rotate(-90 ${center} ${center})`,
5127
- style: { transition: "stroke-dashoffset 300ms cubic-bezier(0.22, 1, 0.36, 1)" }
5128
- }
5129
- )
5130
- ]
5131
- }
5132
- )
5155
+ style: {
5156
+ position: "absolute",
5157
+ alignItems: "center",
5158
+ justifyContent: "center",
5159
+ pointerEvents: "none"
5160
+ },
5161
+ children
5133
5162
  }
5134
- ),
5135
- showValue && !isIndeterminate && /* @__PURE__ */ jsx(
5163
+ ) : showValue && !isIndeterminate && /* @__PURE__ */ jsx(
5136
5164
  Text,
5137
5165
  {
5138
5166
  className: "text-xs font-semibold text-text-primary absolute",
@@ -5228,7 +5256,7 @@ function ToastProvider({
5228
5256
  return id;
5229
5257
  }, [defaultDuration, maxToasts]);
5230
5258
  const removeToast = useCallback((id) => {
5231
- setToasts((prev) => prev.filter((t18) => t18.id !== id));
5259
+ setToasts((prev) => prev.filter((t17) => t17.id !== id));
5232
5260
  }, []);
5233
5261
  const removeAllToasts = useCallback(() => {
5234
5262
  setToasts([]);
@@ -7073,6 +7101,70 @@ function TimerReadout({
7073
7101
  }
7074
7102
  );
7075
7103
  }
7104
+ function CircularTimer({
7105
+ durationMs,
7106
+ elapsedMs,
7107
+ mode = "down",
7108
+ size = 160,
7109
+ strokeWidth = 8,
7110
+ fill = false,
7111
+ color = "primary",
7112
+ doneLabel,
7113
+ doneColor = "success",
7114
+ controls,
7115
+ accessibilityLabel: accessibilityLabel2,
7116
+ testID
7117
+ }) {
7118
+ const { remainingMs, progress, label, done } = useTimer({ mode, durationMs, elapsedMs });
7119
+ const isDone = done && mode === "down";
7120
+ const showDone = isDone && doneLabel != null;
7121
+ const remainingFrac = durationMs > 0 ? Math.max(0, Math.min(1, remainingMs / durationMs)) : 0;
7122
+ const arc = mode === "down" ? showDone ? 1 : remainingFrac : progress;
7123
+ const ringColor = showDone ? doneColor : color;
7124
+ const remainingSec = Math.ceil(Math.max(0, remainingMs) / 1e3);
7125
+ const a11y = accessibilityLabel2 ?? (isDone ? "Timer complete" : `${remainingSec} seconds remaining`);
7126
+ return /* @__PURE__ */ jsxs(
7127
+ View,
7128
+ {
7129
+ style: { alignItems: "center", gap: 16 },
7130
+ accessibilityRole: "timer",
7131
+ accessibilityLabel: a11y,
7132
+ testID: testID ?? "circular-timer",
7133
+ children: [
7134
+ /* @__PURE__ */ jsx(
7135
+ CircularProgress,
7136
+ {
7137
+ value: arc,
7138
+ max: 1,
7139
+ size,
7140
+ strokeWidth,
7141
+ fill,
7142
+ color: ringColor,
7143
+ accessibilityLabel: "Timer ring",
7144
+ testID: "circular-timer-ring",
7145
+ children: /* @__PURE__ */ jsx(
7146
+ Text,
7147
+ {
7148
+ className: showDone ? void 0 : "text-text-primary",
7149
+ style: {
7150
+ fontSize: Math.round(size * (showDone ? 0.28 : 0.24)),
7151
+ fontFamily: '"Space Grotesk", sans-serif',
7152
+ fontWeight: "800",
7153
+ fontVariant: ["tabular-nums"],
7154
+ letterSpacing: showDone ? 1 : -1,
7155
+ ...showDone ? { color: colorVarMap[doneColor] } : null
7156
+ },
7157
+ testID: "circular-timer-label",
7158
+ children: showDone ? doneLabel : label
7159
+ }
7160
+ )
7161
+ }
7162
+ ),
7163
+ controls
7164
+ ]
7165
+ }
7166
+ );
7167
+ }
7076
7168
  var sizeConfig2 = {
7077
7169
  sm: { value: "text-lg font-bold", label: "text-xs", unit: "text-xs" },
7078
7170
  md: { value: "text-2xl font-bold", label: "text-xs", unit: "text-sm" },
@@ -7718,137 +7810,6 @@ function getLuminance(color) {
7718
7810
  function getContrastText(backgroundColor) {
7719
7811
  return getLuminance(backgroundColor) === "light" ? "#000000" : "#FFFFFF";
7720
7812
  }
7721
- var t2 = getSemanticColors("dark");
7722
- var TEMPO_PACING = {
7723
- behindThresholdPct: 0.15,
7724
- minPhaseDurationMs: 500
7725
- };
7726
- function getTempoPacingState(elapsedMs, targetMs) {
7727
- if (!targetMs || targetMs < TEMPO_PACING.minPhaseDurationMs) return "none";
7728
- const ratio = elapsedMs / targetMs;
7729
- if (ratio > 1 + TEMPO_PACING.behindThresholdPct) return "behind";
7730
- return "on-pace";
7731
- }
7732
- function getTempoFillPct(elapsedMs, targetMs) {
7733
- if (!targetMs) return 100;
7734
- return Math.min(100, elapsedMs / targetMs * 100);
7735
- }
7736
- var PHASE_ORDER = ["concentric", "hold", "eccentric"];
7737
- var PHASE_CONFIG = {
7738
- concentric: { label: "Con", color: t2["status-success"], flex: 2 },
7739
- hold: { label: "Hold", color: t2["brand-primary"], flex: 1 },
7740
- eccentric: { label: "Ecc", color: t2["status-warning"], flex: 3 }
7741
- };
7742
- function TempoBar({
7743
- activePhase,
7744
- phaseElapsedMs,
7745
- completed,
7746
- target,
7747
- className,
7748
- ...props
7749
- }) {
7750
- return /* @__PURE__ */ jsxs(View, { className, testID: "tempo-bar", ...props, children: [
7751
- /* @__PURE__ */ jsx(View, { style: { flexDirection: "row", marginBottom: 4 }, children: PHASE_ORDER.map((phase) => {
7752
- const config = PHASE_CONFIG[phase];
7753
- return /* @__PURE__ */ jsx(View, { style: { flex: config.flex, alignItems: "center" }, children: /* @__PURE__ */ jsx(Text, { style: { fontSize: 10, color: t2["text-disabled"] }, children: config.label }) }, phase);
7754
- }) }),
7755
- /* @__PURE__ */ jsx(View, { style: { flexDirection: "row", gap: 2, height: 20 }, children: PHASE_ORDER.map((phase) => {
7756
- const config = PHASE_CONFIG[phase];
7757
- const isActive = activePhase === phase;
7758
- const completedMs = completed?.[phase];
7759
- const targetSec = target?.[phase];
7760
- const targetMs = targetSec != null ? targetSec * 1e3 : null;
7761
- return /* @__PURE__ */ jsx(
7762
- View,
7763
- {
7764
- style: {
7765
- flex: config.flex,
7766
- backgroundColor: alpha("#ffffff", 0.06),
7767
- borderRadius: 4,
7768
- overflow: "hidden"
7769
- },
7770
- children: isActive ? /* @__PURE__ */ jsx(
7771
- ActiveSegment,
7772
- {
7773
- config,
7774
- phaseElapsedMs,
7775
- targetMs
7776
- }
7777
- ) : completedMs != null ? /* @__PURE__ */ jsx(CompletedSegment, { config, completedMs, targetMs }) : null
7778
- },
7779
- phase
7780
- );
7781
- }) })
7782
- ] });
7783
- }
7784
- function ActiveSegment({
7785
- config,
7786
- phaseElapsedMs,
7787
- targetMs
7788
- }) {
7789
- const pacing = getTempoPacingState(phaseElapsedMs, targetMs);
7790
- const barColor = pacing === "behind" ? t2["status-error"] : config.color;
7791
- const fillPct = getTempoFillPct(phaseElapsedMs, targetMs);
7792
- const label = targetMs ? `${formatDuration2(phaseElapsedMs)} / ${formatDuration2(targetMs)}` : formatDuration2(phaseElapsedMs);
7793
- return /* @__PURE__ */ jsxs(View, { style: { height: "100%", position: "relative" }, testID: `tempo-segment-active-${config.label}`, children: [
7794
- /* @__PURE__ */ jsx(
7795
- View,
7796
- {
7797
- style: {
7798
- position: "absolute",
7799
- left: 0,
7800
- top: 0,
7801
- bottom: 0,
7802
- width: `${fillPct}%`,
7803
- backgroundColor: alpha(barColor, 0.3),
7804
- borderRadius: 4
7805
- }
7806
- }
7807
- ),
7808
- /* @__PURE__ */ jsx(
7809
- View,
7810
- {
7811
- style: {
7812
- height: "100%",
7813
- flexDirection: "row",
7814
- alignItems: "center",
7815
- justifyContent: "center"
7816
- },
7817
- children: /* @__PURE__ */ jsx(Text, { style: { fontSize: 12, fontWeight: "700", color: barColor }, children: label })
7818
- }
7819
- )
7820
- ] });
7821
- }
7822
- function CompletedSegment({
7823
- config,
7824
- completedMs,
7825
- targetMs
7826
- }) {
7827
- const pacing = getTempoPacingState(completedMs, targetMs);
7828
- const hitTarget = pacing !== "behind";
7829
- const indicator = targetMs && targetMs >= TEMPO_PACING.minPhaseDurationMs ? hitTarget ? " \u2713" : " \u2717" : "";
7830
- return /* @__PURE__ */ jsx(
7831
- View,
7832
- {
7833
- style: {
7834
- height: "100%",
7835
- flexDirection: "row",
7836
- alignItems: "center",
7837
- justifyContent: "center",
7838
- backgroundColor: alpha(config.color, 0.15)
7839
- },
7840
- testID: `tempo-segment-completed-${config.label}`,
7841
- children: /* @__PURE__ */ jsxs(Text, { style: { fontSize: 12, fontWeight: "500", color: alpha(config.color, 0.7) }, children: [
7842
- formatDuration2(completedMs),
7843
- indicator
7844
- ] })
7845
- }
7846
- );
7847
- }
7848
- function formatDuration2(ms) {
7849
- const s = ms / 1e3;
7850
- return s < 10 ? s.toFixed(1) : Math.round(s).toString();
7851
- }
7852
7813
  var METRIC_FONT = "Inter, sans-serif";
7853
7814
  function MetricCell({ children, color, fontSize = 11 }) {
7854
7815
  return /* @__PURE__ */ jsx(
@@ -7867,14 +7828,32 @@ function MetricCell({ children, color, fontSize = 11 }) {
7867
7828
  }
7868
7829
 
7869
7830
  // src/components/custom/Workout/TempoDisplay.tsx
7870
- var t3 = getSemanticColors("dark");
7831
+ var t2 = getSemanticColors("dark");
7871
7832
  var INTER = "Inter, sans-serif";
7872
7833
  var TEXT_TERTIARY = "#6B7280";
7873
- var STATUS_ERROR = t3["status-error"];
7834
+ var STATUS_ERROR = t2["status-error"];
7835
+ var STATUS_SUCCESS = t2["status-success"];
7836
+ var STATUS_WARNING = t2["status-warning"];
7837
+ var ON_TARGET_MS = 100;
7838
+ function getTempoFillPct(elapsedMs, targetMs) {
7839
+ if (!targetMs) return 100;
7840
+ return Math.min(100, elapsedMs / targetMs * 100);
7841
+ }
7842
+ function activeNumberTone(elapsedMs, targetMs) {
7843
+ if (targetMs == null) return t2["text-primary"];
7844
+ const remainingMs = targetMs - elapsedMs;
7845
+ if (remainingMs > ON_TARGET_MS) return STATUS_WARNING;
7846
+ if (remainingMs >= -ON_TARGET_MS) return STATUS_SUCCESS;
7847
+ return STATUS_ERROR;
7848
+ }
7849
+ function liveReadoutText(elapsedMs, targetMs, readout) {
7850
+ const seconds = readout === "countup" ? elapsedMs / 1e3 : (targetMs - elapsedMs) / 1e3;
7851
+ return seconds.toFixed(1);
7852
+ }
7874
7853
  var phaseColors = {
7875
- eccentric: t3["status-warning"],
7854
+ eccentric: primitiveRamps.magenta[400],
7876
7855
  pauseBottom: TEXT_TERTIARY,
7877
- concentric: t3["status-success"],
7856
+ concentric: primitiveRamps.cyan[300],
7878
7857
  pauseTop: TEXT_TERTIARY,
7879
7858
  dash: TEXT_TERTIARY
7880
7859
  };
@@ -7894,63 +7873,98 @@ var LIVE_PHASES = [
7894
7873
  { key: "concentric", color: phaseColors.concentric },
7895
7874
  { key: "pauseTop", color: phaseColors.pauseTop }
7896
7875
  ];
7876
+ function CellFill({ color, pct: pct2 }) {
7877
+ return /* @__PURE__ */ jsx(
7878
+ View,
7879
+ {
7880
+ style: {
7881
+ position: "absolute",
7882
+ left: 2,
7883
+ right: 2,
7884
+ bottom: 0,
7885
+ height: `${pct2}%`,
7886
+ backgroundColor: alpha(color, 0.28),
7887
+ borderRadius: 3
7888
+ }
7889
+ }
7890
+ );
7891
+ }
7892
+ var MONO = "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace";
7893
+ var READOUT_MAX_CHARS = 5;
7897
7894
  function LiveTempoCell({
7898
7895
  value,
7899
7896
  color,
7900
- isActive,
7897
+ status,
7901
7898
  phaseElapsedMs,
7902
- fontSize
7899
+ completedMs,
7900
+ fontSize,
7901
+ readout
7903
7902
  }) {
7904
- if (!isActive) {
7905
- return /* @__PURE__ */ jsx(TempoValue, { value, color: alpha(color, 0.35), fontSize });
7906
- }
7907
7903
  const targetMs = value > 0 ? value * 1e3 : null;
7908
- const barColor = getTempoPacingState(phaseElapsedMs, targetMs) === "behind" ? STATUS_ERROR : color;
7904
+ const cellW = Math.ceil(fontSize * 0.62 * READOUT_MAX_CHARS);
7905
+ const wrap = {
7906
+ position: "relative",
7907
+ width: cellW,
7908
+ minHeight: Math.round(fontSize * 1.25),
7909
+ alignItems: "center",
7910
+ justifyContent: "center"
7911
+ };
7912
+ const num = (c, text) => /* @__PURE__ */ jsx(Text, { style: { fontFamily: MONO, fontSize, color: c, fontWeight: "700" }, children: text });
7913
+ if (status === "upcoming") {
7914
+ const targetText = targetMs != null ? (targetMs / 1e3).toFixed(1) : String(value);
7915
+ return /* @__PURE__ */ jsx(View, { style: wrap, children: num(color, targetText) });
7916
+ }
7917
+ if (status === "done") {
7918
+ const finalMs = completedMs ?? targetMs ?? 0;
7919
+ const finalText = targetMs != null ? liveReadoutText(finalMs, targetMs, readout) : String(value);
7920
+ const finalTone = targetMs != null ? activeNumberTone(finalMs, targetMs) : color;
7921
+ return /* @__PURE__ */ jsxs(View, { style: wrap, testID: "tempo-live-done", children: [
7922
+ /* @__PURE__ */ jsx(CellFill, { color, pct: 100 }),
7923
+ num(finalTone, finalText)
7924
+ ] });
7925
+ }
7926
+ const numberTone = activeNumberTone(phaseElapsedMs, targetMs);
7909
7927
  const fillPct = getTempoFillPct(phaseElapsedMs, targetMs);
7910
- return /* @__PURE__ */ jsxs(View, { style: { position: "relative" }, testID: "tempo-live-active", children: [
7911
- /* @__PURE__ */ jsx(
7912
- View,
7913
- {
7914
- style: {
7915
- position: "absolute",
7916
- left: 0,
7917
- top: 0,
7918
- bottom: 0,
7919
- width: `${fillPct}%`,
7920
- backgroundColor: alpha(barColor, 0.3),
7921
- borderRadius: 2
7922
- }
7923
- }
7924
- ),
7925
- /* @__PURE__ */ jsx(TempoValue, { value, color: barColor, fontSize })
7928
+ const activeText = targetMs != null ? liveReadoutText(phaseElapsedMs, targetMs, readout) : String(value);
7929
+ return /* @__PURE__ */ jsxs(View, { style: wrap, testID: "tempo-live-active", children: [
7930
+ /* @__PURE__ */ jsx(CellFill, { color, pct: fillPct }),
7931
+ num(numberTone, activeText)
7926
7932
  ] });
7927
7933
  }
7928
7934
  function LiveTempoRow({
7929
7935
  values,
7930
7936
  live,
7931
- fontSize
7932
- }) {
7933
- return /* @__PURE__ */ jsx(Fragment, { children: LIVE_PHASES.map((phase, i) => /* @__PURE__ */ jsxs(View, { style: { flexDirection: "row" }, children: [
7934
- i > 0 && /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
7935
- /* @__PURE__ */ jsx(
7936
- LiveTempoCell,
7937
- {
7938
- value: values[i],
7939
- color: phase.color,
7940
- isActive: live.activePhase === phase.key,
7941
- phaseElapsedMs: live.phaseElapsedMs,
7942
- fontSize
7943
- }
7944
- )
7945
- ] }, phase.key)) });
7937
+ fontSize,
7938
+ readout
7939
+ }) {
7940
+ const activeIndex = live.activePhase ? LIVE_PHASES.findIndex((p) => p.key === live.activePhase) : -1;
7941
+ return /* @__PURE__ */ jsx(Fragment, { children: LIVE_PHASES.map((phase, i) => {
7942
+ const status = activeIndex < 0 || i > activeIndex ? "upcoming" : i < activeIndex ? "done" : "active";
7943
+ return /* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "center" }, children: [
7944
+ i > 0 && /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
7945
+ /* @__PURE__ */ jsx(
7946
+ LiveTempoCell,
7947
+ {
7948
+ value: values[i],
7949
+ color: phase.color,
7950
+ status,
7951
+ phaseElapsedMs: live.phaseElapsedMs,
7952
+ completedMs: live.completed?.[phase.key],
7953
+ fontSize,
7954
+ readout
7955
+ }
7956
+ )
7957
+ ] }, phase.key);
7958
+ }) });
7946
7959
  }
7947
7960
  function TempoDisplay({
7948
7961
  tempo,
7949
7962
  size = "md",
7950
- colored = true,
7963
+ fontSize: fontSizeProp,
7951
7964
  showLabel = true,
7952
7965
  showInfo = true,
7953
7966
  live,
7967
+ liveReadout = "countdown",
7954
7968
  onPress,
7955
7969
  className,
7956
7970
  ...props
@@ -7958,8 +7972,11 @@ function TempoDisplay({
7958
7972
  const [showTooltip, setShowTooltip] = useState(false);
7959
7973
  const [eccentric, pauseBottom, concentric, pauseTop] = roundTempo(tempo);
7960
7974
  const isSm = size === "sm";
7961
- const fontSize = isSm ? 9 : 11;
7962
- const monoColor = TEXT_TERTIARY;
7975
+ const fontSize = fontSizeProp ?? (isSm ? 9 : 11);
7976
+ const chromePadX = Math.round(fontSize * 0.6);
7977
+ const chromePadY = Math.round(fontSize * 0.3);
7978
+ const chromeRadius = Math.round(fontSize * 0.4);
7979
+ const labelFont = Math.max(9, Math.round(fontSize * 0.5));
7963
7980
  const handlePress = useCallback(() => {
7964
7981
  if (onPress) {
7965
7982
  onPress();
@@ -7976,9 +7993,9 @@ function TempoDisplay({
7976
7993
  flexDirection: "row",
7977
7994
  alignItems: "center",
7978
7995
  backgroundColor: "#1C1C1C",
7979
- paddingHorizontal: isSm ? 6 : 8,
7980
- paddingVertical: 3,
7981
- borderRadius: 4
7996
+ paddingHorizontal: chromePadX,
7997
+ paddingVertical: chromePadY,
7998
+ borderRadius: chromeRadius
7982
7999
  },
7983
8000
  ...props,
7984
8001
  children: [
@@ -7987,12 +8004,13 @@ function TempoDisplay({
7987
8004
  {
7988
8005
  style: {
7989
8006
  fontFamily: INTER,
7990
- fontSize: 9,
8007
+ fontSize: labelFont,
7991
8008
  fontWeight: "500",
7992
- color: TEXT_TERTIARY,
8009
+ // Live-muted green while a rep is running, tertiary at rest.
8010
+ color: live ? t2["status-live-muted"] : TEXT_TERTIARY,
7993
8011
  letterSpacing: 0.5,
7994
8012
  textTransform: "uppercase",
7995
- marginRight: 6
8013
+ marginRight: Math.round(fontSize * 0.5)
7996
8014
  },
7997
8015
  children: "TEMPO"
7998
8016
  }
@@ -8002,36 +8020,20 @@ function TempoDisplay({
8002
8020
  {
8003
8021
  values: [eccentric, pauseBottom, concentric, pauseTop],
8004
8022
  live,
8005
- fontSize
8006
- }
8007
- ) : colored ? /* @__PURE__ */ jsxs(Fragment, { children: [
8008
- /* @__PURE__ */ jsx(TempoValue, { value: eccentric, color: phaseColors.eccentric, fontSize }),
8009
- /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8010
- /* @__PURE__ */ jsx(TempoValue, { value: pauseBottom, color: phaseColors.pauseBottom, fontSize }),
8011
- /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8012
- /* @__PURE__ */ jsx(TempoValue, { value: concentric, color: phaseColors.concentric, fontSize }),
8013
- /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8014
- /* @__PURE__ */ jsx(TempoValue, { value: pauseTop, color: phaseColors.pauseTop, fontSize })
8015
- ] }) : /* @__PURE__ */ jsxs(
8016
- Text,
8017
- {
8018
- style: {
8019
- fontFamily: INTER,
8020
- fontSize,
8021
- color: monoColor,
8022
- fontWeight: "600",
8023
- letterSpacing: 1
8024
- },
8025
- children: [
8026
- eccentric,
8027
- "-",
8028
- pauseBottom,
8029
- "-",
8030
- concentric,
8031
- "-",
8032
- pauseTop
8033
- ]
8023
+ fontSize,
8024
+ readout: liveReadout
8034
8025
  }
8026
+ ) : (
8027
+ // Static prescription: the phase-coloured lockup (the same colours the live row rests at).
8028
+ /* @__PURE__ */ jsxs(Fragment, { children: [
8029
+ /* @__PURE__ */ jsx(TempoValue, { value: eccentric, color: phaseColors.eccentric, fontSize }),
8030
+ /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8031
+ /* @__PURE__ */ jsx(TempoValue, { value: pauseBottom, color: phaseColors.pauseBottom, fontSize }),
8032
+ /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8033
+ /* @__PURE__ */ jsx(TempoValue, { value: concentric, color: phaseColors.concentric, fontSize }),
8034
+ /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8035
+ /* @__PURE__ */ jsx(TempoValue, { value: pauseTop, color: phaseColors.pauseTop, fontSize })
8036
+ ] })
8035
8037
  ) })
8036
8038
  ]
8037
8039
  }
@@ -8116,13 +8118,13 @@ function TempoDisplay({
8116
8118
  }
8117
8119
  );
8118
8120
  }
8119
- var t4 = getSemanticColors("dark");
8121
+ var t3 = getSemanticColors("dark");
8120
8122
  function getDotColor(deviation) {
8121
8123
  const abs = Math.abs(deviation);
8122
- if (deviation < -0.3) return t4["status-success"];
8124
+ if (deviation < -0.3) return t3["status-success"];
8123
8125
  if (abs <= 0.3) return "#6B7280";
8124
- if (abs <= 0.7) return t4["status-warning"];
8125
- return t4["status-error"];
8126
+ if (abs <= 0.7) return t3["status-warning"];
8127
+ return t3["status-error"];
8126
8128
  }
8127
8129
  function getDeviationDescription(deviation) {
8128
8130
  if (deviation < -0.3) return "lighter than planned";
@@ -8190,19 +8192,19 @@ function DeviationBar({
8190
8192
  }
8191
8193
  );
8192
8194
  }
8193
- var t5 = getSemanticColors("dark");
8195
+ var t4 = getSemanticColors("dark");
8194
8196
  var TRACK_BG = "#333333";
8195
8197
  var LABEL_COLOR = "#6B7280";
8196
8198
  var TARGET_LINE_COLOR = "rgba(33, 150, 243, 0.5)";
8197
8199
  var AT_TARGET_GLOW = "0 0 5px 1px rgba(33, 150, 243, 0.35), 0 0 10px 3px rgba(33, 150, 243, 0.15)";
8198
8200
  var ZONE = {
8199
- building: t5["status-success"],
8201
+ building: t4["status-success"],
8200
8202
  // below target ramp-up
8201
- approaching: t5["status-warning"],
8203
+ approaching: t4["status-warning"],
8202
8204
  // nearing target
8203
- target: t5["brand-primary"],
8205
+ target: t4["brand-primary"],
8204
8206
  // exactly at target
8205
- over1: t5["status-error"],
8207
+ over1: t4["status-error"],
8206
8208
  // mild over-target
8207
8209
  over2: WORKOUT_TOKENS.intensity.over2,
8208
8210
  // moderate over-target
@@ -8371,7 +8373,7 @@ var MESO_ACCENT_GRADIENT_LIGHT = primitiveRamps.orange[300];
8371
8373
  var WORKOUT_PILL_DELOAD = primitiveRamps.magenta[600];
8372
8374
 
8373
8375
  // src/components/custom/Workout/WorkoutPill.tsx
8374
- var t6 = getSemanticColors("dark");
8376
+ var t5 = getSemanticColors("dark");
8375
8377
  var statusBgColors = {
8376
8378
  completed: "rgba(46,213,115,0.15)",
8377
8379
  current: "rgba(255,121,0,0.15)",
@@ -8389,9 +8391,9 @@ var statusBorderStyles = {
8389
8391
  deload: { borderWidth: 1, borderColor: "rgba(186,41,150,0.3)" }
8390
8392
  };
8391
8393
  var textColors = {
8392
- completed: t6["status-success"],
8393
- current: t6["brand-primary"],
8394
- next: t6["brand-primary"],
8394
+ completed: t5["status-success"],
8395
+ current: t5["brand-primary"],
8396
+ next: t5["brand-primary"],
8395
8397
  upcoming: "#6B7280",
8396
8398
  missed: "rgba(209,67,67,0.7)",
8397
8399
  deload: WORKOUT_PILL_DELOAD
@@ -8931,8 +8933,18 @@ var EXPANDED_ENCODED_PCT = 45;
8931
8933
  var EXPANDED_HEIGHT = 60;
8932
8934
  var FIXED_MAX_VELOCITY = 1.15;
8933
8935
  var BARE_STUB_HEIGHT = 3;
8936
+ var HERO_HEIGHT = 220;
8937
+ var HERO_LABEL_HEADROOM = 20;
8938
+ var HERO_BAR_GAP = 8;
8939
+ var HERO_BAR_MAX_WIDTH = 120;
8940
+ var HERO_LABEL_MIN_BAR_WIDTH = 30;
8941
+ var HERO_BAR_RADIUS = 5;
8942
+ var HERO_MIN_BAR_HEIGHT = 4;
8943
+ var HERO_PENDING_COLOR = primitiveColors.charcoal[100];
8944
+ var HERO_REFERENCE_COLOR = primitiveColors.charcoal[0];
8945
+ var PEAK_HEADROOM = 1.06;
8934
8946
  function scaleDenominator(scale, maxVelocity) {
8935
- return scale === "fixed" ? FIXED_MAX_VELOCITY : maxVelocity * 1.15;
8947
+ return scale === "fixed" ? FIXED_MAX_VELOCITY : maxVelocity * PEAK_HEADROOM;
8936
8948
  }
8937
8949
  function bareSlotHeight(slot, height, denom) {
8938
8950
  if (slot.kind !== "rep" || denom <= 0) return BARE_STUB_HEIGHT;
@@ -8958,6 +8970,195 @@ function usePrefersReducedMotion() {
8958
8970
  var ANIMATION_DURATION = 400;
8959
8971
  var ANIMATION_EASING = Easing.bezier(0.22, 1, 0.36, 1);
8960
8972
  var POP_EASING = Easing.bezier(0.34, 1.56, 0.64, 1);
8973
+ function useLiveRepPop(active, liveRepIndex, liveVelocity, isNewPeak) {
8974
+ const prefersReducedMotion = usePrefersReducedMotion();
8975
+ const [liveScale] = useState(() => new Animated.Value(1));
8976
+ useEffect(() => {
8977
+ if (!active || liveRepIndex == null || liveVelocity == null) return;
8978
+ if (prefersReducedMotion) {
8979
+ liveScale.setValue(1);
8980
+ return;
8981
+ }
8982
+ if (isNewPeak) {
8983
+ liveScale.setValue(1);
8984
+ Animated.sequence([
8985
+ Animated.timing(liveScale, {
8986
+ toValue: 1.25,
8987
+ duration: 150,
8988
+ easing: Easing.out(Easing.quad),
8989
+ useNativeDriver: true
8990
+ }),
8991
+ Animated.spring(liveScale, {
8992
+ toValue: 1,
8993
+ friction: 3,
8994
+ tension: 140,
8995
+ useNativeDriver: true
8996
+ })
8997
+ ]).start();
8998
+ } else {
8999
+ liveScale.setValue(0.8);
9000
+ Animated.timing(liveScale, {
9001
+ toValue: 1,
9002
+ duration: 300,
9003
+ easing: POP_EASING,
9004
+ useNativeDriver: true
9005
+ }).start();
9006
+ }
9007
+ }, [active, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
9008
+ return liveScale;
9009
+ }
9010
+ function HeroVelocityChart({
9011
+ doneVelocities,
9012
+ barColorFor,
9013
+ scaleDenom,
9014
+ referenceVelocity,
9015
+ liveRepIndex,
9016
+ isNewPeak,
9017
+ targetReps,
9018
+ height,
9019
+ className,
9020
+ viewProps
9021
+ }) {
9022
+ const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
9023
+ const liveScale = useLiveRepPop(true, liveRepIndex, liveVelocity, isNewPeak);
9024
+ const [plotW, setPlotW] = useState(0);
9025
+ const onPlotLayout = (e) => setPlotW(e.nativeEvent.layout.width);
9026
+ const plotHeight = Math.max(0, height - HERO_LABEL_HEADROOM);
9027
+ const barHeight = (velocity) => scaleDenom > 0 ? Math.max(HERO_MIN_BAR_HEIGHT, Math.min(1, velocity / scaleDenom) * plotHeight) : HERO_MIN_BAR_HEIGHT;
9028
+ const pendingCount = Math.max(0, (targetReps ?? 0) - doneVelocities.length);
9029
+ const referencePx = referenceVelocity > 0 && scaleDenom > 0 ? Math.min(plotHeight, referenceVelocity / scaleDenom * plotHeight) : 0;
9030
+ const totalSlots = doneVelocities.length + pendingCount;
9031
+ const barWidth = plotW > 0 && totalSlots > 0 ? Math.min(HERO_BAR_MAX_WIDTH, (plotW - HERO_BAR_GAP * (totalSlots - 1)) / totalSlots) : HERO_BAR_MAX_WIDTH;
9032
+ const labelsCrowded = plotW > 0 && barWidth < HERO_LABEL_MIN_BAR_WIDTH;
9033
+ const heroGap = plotW === 0 ? HERO_BAR_GAP : barWidth < 16 ? 2 : barWidth < 28 ? 4 : HERO_BAR_GAP;
9034
+ const peakIndex = doneVelocities.reduce(
9035
+ (best, v, i) => v > doneVelocities[best] ? i : best,
9036
+ 0
9037
+ );
9038
+ const showBarLabel = (i) => !labelsCrowded || i === peakIndex || i === liveRepIndex;
9039
+ const repCount = doneVelocities.length;
9040
+ const total = Math.max(repCount, targetReps ?? repCount);
9041
+ const label = referenceVelocity > 0 ? `Velocity chart, ${repCount} of ${total} reps, best ${formatVelocity(referenceVelocity)} meters per second` : `Velocity chart, ${repCount} of ${total} reps`;
9042
+ const { style: externalStyle, ...restProps } = viewProps;
9043
+ return /* @__PURE__ */ jsx(
9044
+ View,
9045
+ {
9046
+ className,
9047
+ style: [{ height }, externalStyle],
9048
+ accessibilityRole: "image",
9049
+ accessibilityLabel: label,
9050
+ testID: "velocity-strip-hero",
9051
+ ...restProps,
9052
+ children: /* @__PURE__ */ jsxs(
9053
+ View,
9054
+ {
9055
+ onLayout: onPlotLayout,
9056
+ style: {
9057
+ flex: 1,
9058
+ flexDirection: "row",
9059
+ alignItems: "flex-end",
9060
+ gap: heroGap,
9061
+ position: "relative",
9062
+ borderBottomWidth: 2,
9063
+ borderBottomColor: HERO_PENDING_COLOR
9064
+ },
9065
+ children: [
9066
+ referencePx > 0 && /* @__PURE__ */ jsx(
9067
+ View,
9068
+ {
9069
+ accessibilityElementsHidden: true,
9070
+ style: {
9071
+ position: "absolute",
9072
+ left: 0,
9073
+ right: 0,
9074
+ bottom: referencePx,
9075
+ borderTopWidth: 1,
9076
+ borderStyle: "dashed",
9077
+ borderColor: HERO_REFERENCE_COLOR,
9078
+ pointerEvents: "none"
9079
+ },
9080
+ testID: "velocity-hero-reference"
9081
+ }
9082
+ ),
9083
+ doneVelocities.map((velocity, i) => {
9084
+ const isLive = liveRepIndex === i;
9085
+ return /* @__PURE__ */ jsxs(
9086
+ View,
9087
+ {
9088
+ accessibilityElementsHidden: true,
9089
+ style: {
9090
+ flex: 1,
9091
+ maxWidth: HERO_BAR_MAX_WIDTH,
9092
+ height: "100%",
9093
+ alignItems: "center",
9094
+ justifyContent: "flex-end"
9095
+ },
9096
+ children: [
9097
+ showBarLabel(i) && /* @__PURE__ */ jsx(
9098
+ Text,
9099
+ {
9100
+ className: "text-text-primary",
9101
+ style: { fontSize: 12, fontWeight: "800", marginBottom: 4 },
9102
+ testID: `velocity-label-${i}`,
9103
+ children: formatVelocity(velocity)
9104
+ }
9105
+ ),
9106
+ /* @__PURE__ */ jsx(
9107
+ Animated.View,
9108
+ {
9109
+ style: [
9110
+ {
9111
+ width: "100%",
9112
+ height: barHeight(velocity),
9113
+ borderTopLeftRadius: HERO_BAR_RADIUS,
9114
+ borderTopRightRadius: HERO_BAR_RADIUS,
9115
+ backgroundColor: barColorFor(velocity)
9116
+ },
9117
+ isLive ? { transform: [{ scale: liveScale }] } : null
9118
+ ],
9119
+ testID: `velocity-bar-${i}`
9120
+ }
9121
+ )
9122
+ ]
9123
+ },
9124
+ i
9125
+ );
9126
+ }),
9127
+ Array.from({ length: pendingCount }, (_, i) => /* @__PURE__ */ jsx(
9128
+ View,
9129
+ {
9130
+ accessibilityElementsHidden: true,
9131
+ style: {
9132
+ flex: 1,
9133
+ maxWidth: HERO_BAR_MAX_WIDTH,
9134
+ height: "100%",
9135
+ alignItems: "center",
9136
+ justifyContent: "flex-end"
9137
+ },
9138
+ children: /* @__PURE__ */ jsx(
9139
+ View,
9140
+ {
9141
+ style: {
9142
+ width: "100%",
9143
+ height: HERO_MIN_BAR_HEIGHT * 3,
9144
+ borderWidth: 1,
9145
+ borderStyle: "dashed",
9146
+ borderColor: HERO_PENDING_COLOR,
9147
+ borderTopLeftRadius: HERO_BAR_RADIUS,
9148
+ borderTopRightRadius: HERO_BAR_RADIUS
9149
+ },
9150
+ testID: "velocity-slot-todo"
9151
+ }
9152
+ )
9153
+ },
9154
+ `pending-${i}`
9155
+ ))
9156
+ ]
9157
+ }
9158
+ )
9159
+ }
9160
+ );
9161
+ }
8961
9162
  function VelocityStrip({
8962
9163
  velocities,
8963
9164
  set,
@@ -8968,6 +9169,7 @@ function VelocityStrip({
8968
9169
  onRepPress,
8969
9170
  variant = "expanded",
8970
9171
  height = EXPANDED_HEIGHT,
9172
+ targetReps,
8971
9173
  scale = "peak",
8972
9174
  showNumbers = true,
8973
9175
  showInfo = true,
@@ -8993,45 +9195,17 @@ function VelocityStrip({
8993
9195
  return SET_STRIP_VARIABLE_COLOR;
8994
9196
  };
8995
9197
  const meanZone = hasZones ? classifyBand(meanVelocity, zones)?.label ?? "" : getVelocityZoneName(meanVelocity);
8996
- const prefersReducedMotion = usePrefersReducedMotion();
8997
9198
  const [heightAnim] = useState(() => new Animated.Value(expanded ? height : 3));
8998
9199
  const [labelOpacity] = useState(() => new Animated.Value(expanded ? 1 : 0));
8999
9200
  const [infoOpacity] = useState(() => new Animated.Value(expanded ? 1 : 0));
9000
- const [liveScale] = useState(() => new Animated.Value(1));
9001
9201
  const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
9002
9202
  const isNewPeak = liveVelocity != null && maxVelocity > 0 && liveVelocity === maxVelocity;
9003
- useEffect(() => {
9004
- if (variant !== "expanded" || !framed || liveRepIndex == null || liveVelocity == null) return;
9005
- if (prefersReducedMotion) {
9006
- liveScale.setValue(1);
9007
- return;
9008
- }
9009
- if (isNewPeak) {
9010
- liveScale.setValue(1);
9011
- Animated.sequence([
9012
- Animated.timing(liveScale, {
9013
- toValue: 1.25,
9014
- duration: 150,
9015
- easing: Easing.out(Easing.quad),
9016
- useNativeDriver: true
9017
- }),
9018
- Animated.spring(liveScale, {
9019
- toValue: 1,
9020
- friction: 3,
9021
- tension: 140,
9022
- useNativeDriver: true
9023
- })
9024
- ]).start();
9025
- } else {
9026
- liveScale.setValue(0.8);
9027
- Animated.timing(liveScale, {
9028
- toValue: 1,
9029
- duration: 300,
9030
- easing: POP_EASING,
9031
- useNativeDriver: true
9032
- }).start();
9033
- }
9034
- }, [variant, framed, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
9203
+ const liveScale = useLiveRepPop(
9204
+ variant === "expanded" && framed,
9205
+ liveRepIndex,
9206
+ liveVelocity,
9207
+ isNewPeak
9208
+ );
9035
9209
  useEffect(() => {
9036
9210
  if (variant !== "expanded" || !framed) return;
9037
9211
  Animated.parallel([
@@ -9058,6 +9232,24 @@ function VelocityStrip({
9058
9232
  if (set == null && velocities == null) return null;
9059
9233
  const repCount = doneVelocities.length;
9060
9234
  const miniLabel = set ? setAccessibilityLabel(set, repCount) : `Velocity strip, ${repCount} reps`;
9235
+ if (variant === "hero") {
9236
+ const heroHeight = height === EXPANDED_HEIGHT ? HERO_HEIGHT : height;
9237
+ return /* @__PURE__ */ jsx(
9238
+ HeroVelocityChart,
9239
+ {
9240
+ doneVelocities,
9241
+ barColorFor,
9242
+ scaleDenom,
9243
+ referenceVelocity: maxVelocity,
9244
+ liveRepIndex,
9245
+ isNewPeak,
9246
+ targetReps,
9247
+ height: heroHeight,
9248
+ className,
9249
+ viewProps: props
9250
+ }
9251
+ );
9252
+ }
9061
9253
  if (variant === "mini") {
9062
9254
  const { style: externalStyle, ...restProps } = props;
9063
9255
  return /* @__PURE__ */ jsx(
@@ -9348,13 +9540,13 @@ function VelocityStrip({
9348
9540
  }
9349
9541
  return stripContent;
9350
9542
  }
9351
- var t7 = getSemanticColors("dark");
9543
+ var t6 = getSemanticColors("dark");
9352
9544
  var dotColorMap = {
9353
9545
  untrained: "#2C2C2C",
9354
- behind: t7["brand-secondary"],
9355
- ontrack: t7["status-success"],
9356
- target: t7["brand-primary"],
9357
- over: t7["status-error"]
9546
+ behind: t6["brand-secondary"],
9547
+ ontrack: t6["status-success"],
9548
+ target: t6["brand-primary"],
9549
+ over: t6["status-error"]
9358
9550
  };
9359
9551
  function MuscleGroupChip({
9360
9552
  name,
@@ -9822,6 +10014,99 @@ function InputBar({
9822
10014
  );
9823
10015
  }
9824
10016
  var BRAND_PRIMARY4 = getSemanticColors("dark")["brand-primary"];
10017
+ var RING_DEFAULT_SIZE = 180;
10018
+ function RestActions({ onAddTime, onSkip }) {
10019
+ return /* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", gap: 8 }, children: [
10020
+ /* @__PURE__ */ jsx(
10021
+ Pressable,
10022
+ {
10023
+ onPress: onAddTime,
10024
+ style: {
10025
+ backgroundColor: "rgba(255,255,255,0.06)",
10026
+ paddingVertical: 8,
10027
+ paddingHorizontal: 20,
10028
+ borderRadius: 8
10029
+ },
10030
+ accessibilityRole: "button",
10031
+ accessibilityLabel: "Add 30 seconds",
10032
+ testID: "rest-timer-add-time",
10033
+ children: /* @__PURE__ */ jsx(
10034
+ Text,
10035
+ {
10036
+ className: "text-text-secondary",
10037
+ style: { fontSize: 11, fontFamily: "Inter, sans-serif", fontWeight: "600" },
10038
+ children: "+30s"
10039
+ }
10040
+ )
10041
+ }
10042
+ ),
10043
+ /* @__PURE__ */ jsx(
10044
+ Pressable,
10045
+ {
10046
+ onPress: onSkip,
10047
+ style: {
10048
+ backgroundColor: "rgba(255,121,0,0.12)",
10049
+ paddingVertical: 8,
10050
+ paddingHorizontal: 20,
10051
+ borderRadius: 8
10052
+ },
10053
+ accessibilityRole: "button",
10054
+ accessibilityLabel: "Skip rest",
10055
+ testID: "rest-timer-skip",
10056
+ children: /* @__PURE__ */ jsx(
10057
+ Text,
10058
+ {
10059
+ style: {
10060
+ fontSize: 11,
10061
+ fontFamily: "Inter, sans-serif",
10062
+ fontWeight: "600",
10063
+ color: BRAND_PRIMARY4
10064
+ },
10065
+ children: "Skip"
10066
+ }
10067
+ )
10068
+ }
10069
+ )
10070
+ ] });
10071
+ }
10072
+ function RestTimerRing({
10073
+ totalSeconds,
10074
+ elapsedMs,
10075
+ remainingSec,
10076
+ done,
10077
+ size,
10078
+ nextSetInfo,
10079
+ displayOnly,
10080
+ onSkip,
10081
+ onAddTime
10082
+ }) {
10083
+ const a11yLabel = done ? "Rest complete, next set ready" : `Rest timer, ${Math.max(0, remainingSec)} seconds remaining`;
10084
+ return /* @__PURE__ */ jsxs(View, { style: { alignItems: "center", gap: 16 }, testID: "rest-timer", children: [
10085
+ /* @__PURE__ */ jsx(
10086
+ CircularTimer,
10087
+ {
10088
+ durationMs: totalSeconds * 1e3,
10089
+ elapsedMs,
10090
+ mode: "down",
10091
+ size,
10092
+ doneLabel: "GO",
10093
+ doneColor: "success",
10094
+ accessibilityLabel: a11yLabel,
10095
+ controls: !displayOnly ? /* @__PURE__ */ jsx(RestActions, { onAddTime, onSkip }) : void 0,
10096
+ testID: "rest-timer-ring"
10097
+ }
10098
+ ),
10099
+ nextSetInfo != null && /* @__PURE__ */ jsx(
10100
+ Text,
10101
+ {
10102
+ className: "text-text-tertiary",
10103
+ style: { fontSize: 13, fontFamily: "Inter, sans-serif" },
10104
+ testID: "rest-timer-next-set",
10105
+ children: nextSetInfo
10106
+ }
10107
+ )
10108
+ ] });
10109
+ }
9825
10110
  function RestTimer({
9826
10111
  totalSeconds,
9827
10112
  elapsedMs,
@@ -9829,12 +10114,15 @@ function RestTimer({
9829
10114
  onAddTime,
9830
10115
  nextSetInfo,
9831
10116
  visible,
9832
- displayOnly = false
10117
+ displayOnly = false,
10118
+ variant = "bar",
10119
+ size = RING_DEFAULT_SIZE
9833
10120
  }) {
9834
10121
  const {
9835
10122
  remainingMs,
9836
10123
  progress,
9837
- label: timeDisplay
10124
+ label: timeDisplay,
10125
+ done
9838
10126
  } = useTimer({
9839
10127
  mode: "down",
9840
10128
  durationMs: totalSeconds * 1e3,
@@ -9843,6 +10131,22 @@ function RestTimer({
9843
10131
  const remainingSec = Math.ceil(remainingMs / 1e3);
9844
10132
  const progressPct = progress * 100;
9845
10133
  if (!visible) return null;
10134
+ if (variant === "ring") {
10135
+ return /* @__PURE__ */ jsx(
10136
+ RestTimerRing,
10137
+ {
10138
+ totalSeconds,
10139
+ elapsedMs,
10140
+ remainingSec,
10141
+ done,
10142
+ size,
10143
+ nextSetInfo,
10144
+ displayOnly,
10145
+ onSkip,
10146
+ onAddTime
10147
+ }
10148
+ );
10149
+ }
9846
10150
  return /* @__PURE__ */ jsxs(
9847
10151
  View,
9848
10152
  {
@@ -9940,67 +10244,12 @@ function RestTimer({
9940
10244
  )
9941
10245
  }
9942
10246
  ),
9943
- !displayOnly && /* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", gap: 8 }, children: [
9944
- /* @__PURE__ */ jsx(
9945
- Pressable,
9946
- {
9947
- onPress: onAddTime,
9948
- style: {
9949
- backgroundColor: "rgba(255,255,255,0.06)",
9950
- paddingVertical: 8,
9951
- paddingHorizontal: 20,
9952
- borderRadius: 8
9953
- },
9954
- accessibilityRole: "button",
9955
- accessibilityLabel: "Add 30 seconds",
9956
- testID: "rest-timer-add-time",
9957
- children: /* @__PURE__ */ jsx(
9958
- Text,
9959
- {
9960
- className: "text-text-secondary",
9961
- style: {
9962
- fontSize: 11,
9963
- fontFamily: "Inter, sans-serif",
9964
- fontWeight: "600"
9965
- },
9966
- children: "+30s"
9967
- }
9968
- )
9969
- }
9970
- ),
9971
- /* @__PURE__ */ jsx(
9972
- Pressable,
9973
- {
9974
- onPress: onSkip,
9975
- style: {
9976
- backgroundColor: "rgba(255,121,0,0.12)",
9977
- paddingVertical: 8,
9978
- paddingHorizontal: 20,
9979
- borderRadius: 8
9980
- },
9981
- accessibilityRole: "button",
9982
- accessibilityLabel: "Skip rest",
9983
- testID: "rest-timer-skip",
9984
- children: /* @__PURE__ */ jsx(
9985
- Text,
9986
- {
9987
- style: {
9988
- fontSize: 11,
9989
- fontFamily: "Inter, sans-serif",
9990
- fontWeight: "600",
9991
- color: BRAND_PRIMARY4
9992
- },
9993
- children: "Skip"
9994
- }
9995
- )
9996
- }
9997
- )
9998
- ] })
10247
+ !displayOnly && /* @__PURE__ */ jsx(RestActions, { onAddTime, onSkip })
9999
10248
  ]
10000
10249
  }
10001
10250
  );
10002
10251
  }
10003
- var t8 = getSemanticColors("dark");
10252
+ var t7 = getSemanticColors("dark");
10004
10253
  var TIMES = "\xD7";
10005
10254
  var AT = "@";
10006
10255
  function SetsRepsLoad({
@@ -10008,11 +10257,12 @@ function SetsRepsLoad({
10008
10257
  reps,
10009
10258
  load,
10010
10259
  unit = "lb",
10260
+ fontSize,
10011
10261
  className,
10012
10262
  ...props
10013
10263
  }) {
10014
- const value = t8["text-primary"];
10015
- const sep = t8["text-tertiary"];
10264
+ const value = t7["text-primary"];
10265
+ const sep = t7["text-tertiary"];
10016
10266
  return /* @__PURE__ */ jsxs(
10017
10267
  View,
10018
10268
  {
@@ -10022,12 +10272,12 @@ function SetsRepsLoad({
10022
10272
  testID: "sets-reps-load",
10023
10273
  ...props,
10024
10274
  children: [
10025
- /* @__PURE__ */ jsx(MetricCell, { color: value, children: sets }),
10026
- /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${TIMES} ` }),
10027
- /* @__PURE__ */ jsx(MetricCell, { color: value, children: reps }),
10028
- /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${AT} ` }),
10029
- /* @__PURE__ */ jsx(MetricCell, { color: value, children: load }),
10030
- /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${unit}` })
10275
+ /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, children: sets }),
10276
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, children: ` ${TIMES} ` }),
10277
+ /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, children: reps }),
10278
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, children: ` ${AT} ` }),
10279
+ /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, children: load }),
10280
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, children: ` ${unit}` })
10031
10281
  ]
10032
10282
  }
10033
10283
  );
@@ -10125,14 +10375,14 @@ function ScheduleTiles({ when, now, gap = 1, ...props }) {
10125
10375
  )
10126
10376
  ] });
10127
10377
  }
10128
- var t9 = getSemanticColors("dark");
10378
+ var t8 = getSemanticColors("dark");
10129
10379
  var INDICATOR_CONFIG = {
10130
- imbalance: { Icon: ScaleIcon, color: t9["status-error"], label: "Left/right imbalance" },
10131
- overshoot: { Icon: AlertTriangleIcon, color: t9["status-error"], label: "Load overshoot" },
10132
- "velocity-loss": { Icon: TrendingDownIcon, color: t9["status-warning"], label: "Velocity loss" },
10133
- "missed-reps": { Icon: CircleSlashIcon, color: t9["status-warning"], label: "Missed reps" },
10134
- pr: { Icon: AwardIcon, color: t9["status-success"], label: "Personal record" },
10135
- info: { Icon: InfoIcon, color: t9["status-info"], label: "More info" }
10380
+ imbalance: { Icon: ScaleIcon, color: t8["status-error"], label: "Left/right imbalance" },
10381
+ overshoot: { Icon: AlertTriangleIcon, color: t8["status-error"], label: "Load overshoot" },
10382
+ "velocity-loss": { Icon: TrendingDownIcon, color: t8["status-warning"], label: "Velocity loss" },
10383
+ "missed-reps": { Icon: CircleSlashIcon, color: t8["status-warning"], label: "Missed reps" },
10384
+ pr: { Icon: AwardIcon, color: t8["status-success"], label: "Personal record" },
10385
+ info: { Icon: InfoIcon, color: t8["status-info"], label: "More info" }
10136
10386
  };
10137
10387
  var INDICATOR_PRECEDENCE = [
10138
10388
  "imbalance",
@@ -10607,7 +10857,7 @@ function ExerciseCard({
10607
10857
  if (upcoming) return /* @__PURE__ */ jsx(UpcomingCard, { ...rest, onToggle });
10608
10858
  return isExpanded ? /* @__PURE__ */ jsx(ExpandedCard, { ...rest, onToggle }) : /* @__PURE__ */ jsx(CollapsedCard, { ...rest, onToggle });
10609
10859
  }
10610
- var t10 = getSemanticColors("dark");
10860
+ var t9 = getSemanticColors("dark");
10611
10861
  var RAISED = primitiveColors.charcoal[400];
10612
10862
  var BAR_HEIGHT = 9;
10613
10863
  var BAR_GAP = 3;
@@ -10629,7 +10879,7 @@ function SessionHeader({
10629
10879
  const hasPace = !upcoming && budgetMs != null && elapsedMs != null;
10630
10880
  const target = hasPace ? elapsedMs / budgetMs : void 0;
10631
10881
  const setsLabel = upcoming ? `${totalSets} sets` : `${Math.floor(setsDone)}/${totalSets} sets`;
10632
- const setsLabelColor = upcoming ? t10["text-secondary"] : paceToneColor(paceTone(totalSets > 0 ? setsDone / totalSets : 0, target));
10882
+ const setsLabelColor = upcoming ? t9["text-secondary"] : paceToneColor(paceTone(totalSets > 0 ? setsDone / totalSets : 0, target));
10633
10883
  return /* @__PURE__ */ jsxs(
10634
10884
  View,
10635
10885
  {
@@ -10657,7 +10907,7 @@ function SessionHeader({
10657
10907
  lineHeight: 18,
10658
10908
  fontWeight: "700",
10659
10909
  fontFamily: '"Space Grotesk", sans-serif',
10660
- color: t10["text-primary"],
10910
+ color: t9["text-primary"],
10661
10911
  marginBottom: 10
10662
10912
  },
10663
10913
  testID: "session-rail-title",
@@ -11052,10 +11302,10 @@ function WeekRow({
11052
11302
  }
11053
11303
  );
11054
11304
  }
11055
- var t11 = getSemanticColors("dark");
11305
+ var t10 = getSemanticColors("dark");
11056
11306
  var COLORS = {
11057
- statusSuccess: t11["status-success"],
11058
- brandPrimary: t11["brand-primary"],
11307
+ statusSuccess: t10["status-success"],
11308
+ brandPrimary: t10["brand-primary"],
11059
11309
  borderDefault: "#1F1F1F",
11060
11310
  brandPrimarySubtle: "rgba(255,121,0,0.06)"
11061
11311
  };
@@ -11539,12 +11789,12 @@ function PrHistoryModal({
11539
11789
  }
11540
11790
  );
11541
11791
  }
11542
- var t12 = getSemanticColors("dark");
11543
- var BRAND_PRIMARY9 = t12["brand-primary"];
11792
+ var t11 = getSemanticColors("dark");
11793
+ var BRAND_PRIMARY9 = t11["brand-primary"];
11544
11794
  var BRAND_PRIMARY_SUBTLE = "rgba(255,121,0,0.12)";
11545
- var STATUS_SUCCESS = t12["status-success"];
11546
- var STATUS_WARNING = t12["status-warning"];
11547
- var STATUS_ERROR2 = t12["status-error"];
11795
+ var STATUS_SUCCESS2 = t11["status-success"];
11796
+ var STATUS_WARNING2 = t11["status-warning"];
11797
+ var STATUS_ERROR2 = t11["status-error"];
11548
11798
  var EMOJI_SETS = {
11549
11799
  sleep: ["\u{1F634}", "\u{1F610}", "\u{1F642}", "\u{1F60A}", "\u{1F929}"],
11550
11800
  soreness: ["\u{1F635}", "\u{1F623}", "\u{1F610}", "\u{1F642}", "\u{1F4AA}"],
@@ -11554,8 +11804,8 @@ var EMOJI_SETS = {
11554
11804
  var DEFAULT_EMOJI_SET = ["\u{1F61E}", "\u{1F615}", "\u{1F610}", "\u{1F642}", "\u{1F604}"];
11555
11805
  function scoreColor(score) {
11556
11806
  if (score < 40) return STATUS_ERROR2;
11557
- if (score < 70) return STATUS_WARNING;
11558
- return STATUS_SUCCESS;
11807
+ if (score < 70) return STATUS_WARNING2;
11808
+ return STATUS_SUCCESS2;
11559
11809
  }
11560
11810
  var WARMUP_BADGE = {
11561
11811
  good: { color: "success", label: "Good" },
@@ -11734,13 +11984,13 @@ function ReadinessCheck({
11734
11984
  )
11735
11985
  ] }) });
11736
11986
  }
11737
- var t13 = getSemanticColors("dark");
11738
- var BRAND_PRIMARY10 = t13["brand-primary"];
11987
+ var t12 = getSemanticColors("dark");
11988
+ var BRAND_PRIMARY10 = t12["brand-primary"];
11739
11989
  var BRAND_PRIMARY_DARK2 = MESO_ACCENT_GRADIENT_DARK;
11740
11990
  var BRAND_PRIMARY_LIGHT2 = MESO_ACCENT_GRADIENT_LIGHT;
11741
- var SUCCESS = t13["status-success"];
11742
- var WARNING = t13["status-warning"];
11743
- var ERROR = t13["status-error"];
11991
+ var SUCCESS = t12["status-success"];
11992
+ var WARNING = t12["status-warning"];
11993
+ var ERROR = t12["status-error"];
11744
11994
  var ACCENT_STOPS2 = [BRAND_PRIMARY_DARK2, BRAND_PRIMARY10, BRAND_PRIMARY_LIGHT2];
11745
11995
  var CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor("surface-elevated")} 0%, ${resolveColor("surface-raised")} 100%)`;
11746
11996
  var GAUGE_GRADIENT = "linear-gradient(90deg, rgba(46,213,115,0.25) 0%, rgba(249,180,21,0.25) 50%, rgba(209,67,67,0.25) 100%)";
@@ -12100,9 +12350,9 @@ function MesoStatusCard({
12100
12350
  var sem = getSemanticColors("dark");
12101
12351
  var BRAND_PRIMARY11 = sem["brand-primary"];
12102
12352
  var PROJECTION_LINE_COLOR = resolveColor("text-tertiary");
12103
- var STATUS_SUCCESS2 = sem["status-success"];
12353
+ var STATUS_SUCCESS3 = sem["status-success"];
12104
12354
  var STATUS_ERROR3 = sem["status-error"];
12105
- var STATUS_WARNING2 = sem["status-warning"];
12355
+ var STATUS_WARNING3 = sem["status-warning"];
12106
12356
  var GRID_LINE = "rgba(255,255,255,0.06)";
12107
12357
  var SUCCESS_PILL_BG = "rgba(46,213,115,0.10)";
12108
12358
  var SUCCESS_PILL_BORDER = "rgba(46,213,115,0.20)";
@@ -12111,8 +12361,8 @@ var ERROR_PILL_BORDER = "rgba(209,67,67,0.20)";
12111
12361
  var PLOT_LEFT = 26;
12112
12362
  var TOOLTIP_WIDTH = 124;
12113
12363
  function timeOf(dateStr) {
12114
- const t18 = Date.parse(dateStr);
12115
- return Number.isNaN(t18) ? 0 : t18;
12364
+ const t17 = Date.parse(dateStr);
12365
+ return Number.isNaN(t17) ? 0 : t17;
12116
12366
  }
12117
12367
  function formatValue(value) {
12118
12368
  return `${Math.round(value)}`;
@@ -12408,7 +12658,7 @@ function StrengthTrendChart({
12408
12658
  left: c.x - 7,
12409
12659
  top: c.y - 20,
12410
12660
  fontSize: 12,
12411
- color: STATUS_WARNING2
12661
+ color: STATUS_WARNING3
12412
12662
  },
12413
12663
  children: "\u2605"
12414
12664
  },
@@ -12501,7 +12751,7 @@ function StrengthTrendChart({
12501
12751
  marginTop: 1,
12502
12752
  fontSize: 10,
12503
12753
  fontFamily: "Inter, sans-serif",
12504
- color: selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm >= 0 ? STATUS_SUCCESS2 : STATUS_ERROR3
12754
+ color: selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm >= 0 ? STATUS_SUCCESS3 : STATUS_ERROR3
12505
12755
  },
12506
12756
  children: `${selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm >= 0 ? "+" : "-"}${Math.abs(
12507
12757
  selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm
@@ -12557,7 +12807,7 @@ function StrengthTrendChart({
12557
12807
  fontSize: 11,
12558
12808
  fontFamily: "Inter, sans-serif",
12559
12809
  fontWeight: "600",
12560
- color: trend.positive ? STATUS_SUCCESS2 : STATUS_ERROR3
12810
+ color: trend.positive ? STATUS_SUCCESS3 : STATUS_ERROR3
12561
12811
  },
12562
12812
  children: trendText
12563
12813
  }
@@ -12591,7 +12841,7 @@ function StrengthTrendChart({
12591
12841
  /* @__PURE__ */ jsx(Text, { className: "text-text-secondary", style: { fontSize: 10, fontFamily: "Inter, sans-serif" }, children: "Projected" })
12592
12842
  ] }),
12593
12843
  /* @__PURE__ */ jsxs(View, { className: "flex-row items-center", style: { gap: 5 }, children: [
12594
- /* @__PURE__ */ jsx(Text, { style: { fontSize: 12, color: STATUS_WARNING2 }, children: "\u2605" }),
12844
+ /* @__PURE__ */ jsx(Text, { style: { fontSize: 12, color: STATUS_WARNING3 }, children: "\u2605" }),
12595
12845
  /* @__PURE__ */ jsx(Text, { className: "text-text-secondary", style: { fontSize: 10, fontFamily: "Inter, sans-serif" }, children: "PR" })
12596
12846
  ] })
12597
12847
  ]
@@ -12601,10 +12851,10 @@ function StrengthTrendChart({
12601
12851
  }
12602
12852
  );
12603
12853
  }
12604
- var t14 = getSemanticColors("dark");
12605
- var STATUS_SUCCESS3 = t14["status-success"];
12606
- var STATUS_WARNING3 = t14["status-warning"];
12607
- var STATUS_INFO = t14["status-info"];
12854
+ var t13 = getSemanticColors("dark");
12855
+ var STATUS_SUCCESS4 = t13["status-success"];
12856
+ var STATUS_WARNING4 = t13["status-warning"];
12857
+ var STATUS_INFO = t13["status-info"];
12608
12858
  var DOT_BORDER = "#F3F4F6";
12609
12859
  var BAND_FILL = "rgba(46,213,115,0.1)";
12610
12860
  var BAND_EDGE = "rgba(46,213,115,0.45)";
@@ -12616,8 +12866,8 @@ var PADDING_BOTTOM = 16;
12616
12866
  var COLUMN_STEP = 4;
12617
12867
  var DOT_SIZE = 8;
12618
12868
  var DOT_COLORS = {
12619
- within: STATUS_SUCCESS3,
12620
- above: STATUS_WARNING3,
12869
+ within: STATUS_SUCCESS4,
12870
+ above: STATUS_WARNING4,
12621
12871
  below: STATUS_INFO
12622
12872
  };
12623
12873
  var STATUS_PHRASES = {
@@ -12647,8 +12897,8 @@ function interpEdge(pixels, x, key) {
12647
12897
  const a = pixels[i];
12648
12898
  const b = pixels[i + 1];
12649
12899
  if (x >= a.x && x <= b.x) {
12650
- const t18 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
12651
- return a[key] + t18 * (b[key] - a[key]);
12900
+ const t17 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
12901
+ return a[key] + t17 * (b[key] - a[key]);
12652
12902
  }
12653
12903
  }
12654
12904
  return pixels[pixels.length - 1][key];
@@ -12842,8 +13092,8 @@ function CapacityBandChart({
12842
13092
  projection && /* @__PURE__ */ jsxs(View, { testID: "capacity-band-chart-projection", children: [
12843
13093
  renderColumns(buildColumns2(trainingPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
12844
13094
  renderColumns(buildColumns2(restPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
12845
- renderEdges(buildEdges(trainingPixels, "yHigh"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
12846
- renderEdges(buildEdges(trainingPixels, "yLow"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
13095
+ renderEdges(buildEdges(trainingPixels, "yHigh"), STATUS_SUCCESS4, true, "capacity-band-chart-projection-training"),
13096
+ renderEdges(buildEdges(trainingPixels, "yLow"), STATUS_SUCCESS4, true, "capacity-band-chart-projection-training"),
12847
13097
  renderEdges(buildEdges(restPixels, "yHigh"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
12848
13098
  renderEdges(buildEdges(restPixels, "yLow"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
12849
13099
  /* @__PURE__ */ jsx(
@@ -12855,7 +13105,7 @@ function CapacityBandChart({
12855
13105
  top: trainingPixels[trainingPixels.length - 1]?.yHigh - 12,
12856
13106
  fontSize: 9,
12857
13107
  fontFamily: "Inter, sans-serif",
12858
- color: STATUS_SUCCESS3
13108
+ color: STATUS_SUCCESS4
12859
13109
  },
12860
13110
  testID: "capacity-band-chart-projection-training-label",
12861
13111
  children: "Training"
@@ -12978,16 +13228,43 @@ function CapacityBandChart({
12978
13228
  }
12979
13229
  );
12980
13230
  }
12981
- var t15 = getSemanticColors("dark");
13231
+ var t14 = getSemanticColors("dark");
12982
13232
  function glowShadow(color) {
12983
13233
  return `0 0 5px 1px ${alpha(color, 0.3)}, 0 0 10px 3px ${alpha(color, 0.12)}`;
12984
13234
  }
12985
13235
  var DEFAULT_TRACK_COLOR = primitiveColors.charcoal[200];
12986
13236
  var DEFAULT_MARKER_COLOR2 = primitiveColors.white;
12987
- var TICK_COLOR = t15["text-tertiary"];
12988
- var DEFAULT_TRACK_HEIGHT = 14;
12989
- var DEFAULT_NEEDLE_OVERHANG = 6;
12990
- var NEEDLE_WIDTH = 4;
13237
+ var TICK_COLOR = t14["text-tertiary"];
13238
+ var ZONE_SIZES = {
13239
+ default: {
13240
+ trackHeight: 14,
13241
+ needleOverhang: 6,
13242
+ needleWidth: 4,
13243
+ tickLineNormal: 1.5,
13244
+ tickLineEmphasized: 2,
13245
+ tickLineHeightPad: 4,
13246
+ tickLineTopPad: 2,
13247
+ labelRowHeight: 16,
13248
+ labelMarginTop: 5,
13249
+ tickFont: 9,
13250
+ tickLetterSpacing: 0.5,
13251
+ tickCellWidth: 28
13252
+ },
13253
+ wall: {
13254
+ trackHeight: 24,
13255
+ needleOverhang: 9,
13256
+ needleWidth: 7,
13257
+ tickLineNormal: 2.5,
13258
+ tickLineEmphasized: 3.5,
13259
+ tickLineHeightPad: 6,
13260
+ tickLineTopPad: 3,
13261
+ labelRowHeight: 24,
13262
+ labelMarginTop: 8,
13263
+ tickFont: 14,
13264
+ tickLetterSpacing: 0.5,
13265
+ tickCellWidth: 48
13266
+ }
13267
+ };
12991
13268
  function fraction(value, min, max) {
12992
13269
  if (max <= min) return 0;
12993
13270
  return Math.max(0, Math.min(1, (value - min) / (max - min)));
@@ -13002,14 +13279,24 @@ function ZoneTrack({
13002
13279
  marker = null,
13003
13280
  ticks,
13004
13281
  band = null,
13005
- trackHeight = DEFAULT_TRACK_HEIGHT,
13006
- needleOverhang = DEFAULT_NEEDLE_OVERHANG,
13282
+ size = "default",
13283
+ trackHeight: trackHeightProp,
13284
+ needleOverhang: needleOverhangProp,
13007
13285
  trackColor = DEFAULT_TRACK_COLOR,
13008
13286
  className,
13009
13287
  style,
13010
13288
  accessibilityLabel: accessibilityLabel2,
13011
13289
  ...props
13012
13290
  }) {
13291
+ const s = ZONE_SIZES[size];
13292
+ const trackHeight = trackHeightProp ?? s.trackHeight;
13293
+ const needleOverhang = needleOverhangProp ?? s.needleOverhang;
13294
+ const [trackW, setTrackW] = useState(0);
13295
+ const onTrackLayout = (e) => setTrackW(e.nativeEvent.layout.width);
13296
+ const tickCount = ticks?.length ?? 0;
13297
+ const tickSpacing = trackW > 0 && tickCount > 0 ? trackW / tickCount : Infinity;
13298
+ const tickStep = tickSpacing < s.tickCellWidth ? Math.ceil(s.tickCellWidth / tickSpacing) : 1;
13299
+ const showTickLabel = (i, emphasized) => tickStep <= 1 || i === 0 || i === tickCount - 1 || i % tickStep === 0 || emphasized;
13013
13300
  const isNeedle = marker?.type === "needle";
13014
13301
  const markerHeight = isNeedle ? trackHeight + needleOverhang * 2 : trackHeight;
13015
13302
  const markerFrac = marker != null ? fraction(marker.value, min, max) : 0;
@@ -13028,6 +13315,7 @@ function ZoneTrack({
13028
13315
  accessibilityValue: marker != null ? { min, max, now: marker.value } : { min, max, now: min },
13029
13316
  accessibilityLabel: accessibilityLabel2 ?? `Zone track: ${marker?.value ?? min}`,
13030
13317
  testID: "zone-track",
13318
+ onLayout: onTrackLayout,
13031
13319
  ...props,
13032
13320
  children: [
13033
13321
  /* @__PURE__ */ jsxs(View, { style: { position: "relative", height: markerHeight }, children: [
@@ -13119,18 +13407,18 @@ function ZoneTrack({
13119
13407
  position: "absolute",
13120
13408
  top: 0,
13121
13409
  left: pct(markerFrac),
13122
- width: NEEDLE_WIDTH,
13410
+ width: s.needleWidth,
13123
13411
  height: markerHeight,
13124
- borderRadius: NEEDLE_WIDTH / 2,
13412
+ borderRadius: s.needleWidth / 2,
13125
13413
  backgroundColor: marker.color ?? DEFAULT_MARKER_COLOR2,
13126
- transform: [{ translateX: -NEEDLE_WIDTH / 2 }]
13414
+ transform: [{ translateX: -s.needleWidth / 2 }]
13127
13415
  }
13128
13416
  }
13129
13417
  ),
13130
13418
  ticks?.map((tick, i) => {
13131
13419
  const emphasized = tick.emphasized === true;
13132
- const lineColor = tick.color ?? (emphasized ? t15["brand-primary"] : TICK_COLOR);
13133
- const lineWidth = emphasized ? 2 : 1.5;
13420
+ const lineColor = tick.color ?? (emphasized ? t14["brand-primary"] : TICK_COLOR);
13421
+ const lineWidth = emphasized ? s.tickLineEmphasized : s.tickLineNormal;
13134
13422
  return /* @__PURE__ */ jsx(
13135
13423
  View,
13136
13424
  {
@@ -13138,10 +13426,10 @@ function ZoneTrack({
13138
13426
  accessibilityElementsHidden: true,
13139
13427
  style: {
13140
13428
  position: "absolute",
13141
- top: (markerHeight - trackHeight) / 2 - 2,
13429
+ top: (markerHeight - trackHeight) / 2 - s.tickLineTopPad,
13142
13430
  left: pct(fraction(tick.value, min, max)),
13143
13431
  width: lineWidth,
13144
- height: trackHeight + 4,
13432
+ height: trackHeight + s.tickLineHeightPad,
13145
13433
  borderRadius: lineWidth / 2,
13146
13434
  backgroundColor: lineColor,
13147
13435
  transform: [{ translateX: -lineWidth / 2 }],
@@ -13152,40 +13440,47 @@ function ZoneTrack({
13152
13440
  );
13153
13441
  })
13154
13442
  ] }),
13155
- ticks != null && ticks.length > 0 && /* @__PURE__ */ jsx(View, { style: { position: "relative", height: 16, marginTop: 5 }, children: ticks.map((tick, i) => {
13156
- if (tick.label == null) return null;
13157
- const emphasized = tick.emphasized === true;
13158
- const labelColor = tick.color ?? (emphasized ? t15["brand-primary"] : TICK_COLOR);
13159
- const labelNode = /* @__PURE__ */ jsx(
13160
- Text,
13161
- {
13162
- testID: "zone-track-tick-label",
13163
- style: {
13164
- fontSize: 9,
13165
- letterSpacing: 0.5,
13166
- color: labelColor,
13167
- fontFamily: "monospace",
13168
- fontWeight: emphasized ? "700" : "400"
13169
- },
13170
- children: tick.label
13171
- }
13172
- );
13173
- return /* @__PURE__ */ jsx(
13174
- View,
13175
- {
13176
- testID: "zone-track-tick",
13177
- style: {
13178
- position: "absolute",
13179
- left: pct(fraction(tick.value, min, max)),
13180
- transform: [{ translateX: -14 }],
13181
- width: 28,
13182
- alignItems: "center"
13183
- },
13184
- children: tick.tooltip != null ? /* @__PURE__ */ jsx(Tooltip, { label: tick.tooltip, placement: "top", children: labelNode }) : labelNode
13185
- },
13186
- `label-${i}`
13187
- );
13188
- }) })
13443
+ ticks != null && ticks.length > 0 && /* @__PURE__ */ jsx(
13444
+ View,
13445
+ {
13446
+ style: { position: "relative", height: s.labelRowHeight, marginTop: s.labelMarginTop },
13447
+ children: ticks.map((tick, i) => {
13448
+ if (tick.label == null) return null;
13449
+ const emphasized = tick.emphasized === true;
13450
+ if (!showTickLabel(i, emphasized)) return null;
13451
+ const labelColor = tick.color ?? (emphasized ? t14["brand-primary"] : TICK_COLOR);
13452
+ const labelNode = /* @__PURE__ */ jsx(
13453
+ Text,
13454
+ {
13455
+ testID: "zone-track-tick-label",
13456
+ style: {
13457
+ fontSize: s.tickFont,
13458
+ letterSpacing: s.tickLetterSpacing,
13459
+ color: labelColor,
13460
+ fontFamily: "monospace",
13461
+ fontWeight: emphasized ? "700" : "400"
13462
+ },
13463
+ children: tick.label
13464
+ }
13465
+ );
13466
+ return /* @__PURE__ */ jsx(
13467
+ View,
13468
+ {
13469
+ testID: "zone-track-tick",
13470
+ style: {
13471
+ position: "absolute",
13472
+ left: pct(fraction(tick.value, min, max)),
13473
+ transform: [{ translateX: -s.tickCellWidth / 2 }],
13474
+ width: s.tickCellWidth,
13475
+ alignItems: "center"
13476
+ },
13477
+ children: tick.tooltip != null ? /* @__PURE__ */ jsx(Tooltip, { label: tick.tooltip, placement: "top", children: labelNode }) : labelNode
13478
+ },
13479
+ `label-${i}`
13480
+ );
13481
+ })
13482
+ }
13483
+ )
13189
13484
  ]
13190
13485
  }
13191
13486
  );
@@ -13203,7 +13498,8 @@ function FatigueMeter({
13203
13498
  thresholds = DEFAULT_THRESHOLDS,
13204
13499
  zoneColors = DEFAULT_ZONE_COLORS,
13205
13500
  labels = DEFAULT_LABELS,
13206
- trackHeight = 14,
13501
+ size = "default",
13502
+ trackHeight,
13207
13503
  needleColor,
13208
13504
  className,
13209
13505
  ...props
@@ -13224,6 +13520,7 @@ function FatigueMeter({
13224
13520
  zones,
13225
13521
  min: 0,
13226
13522
  max,
13523
+ size,
13227
13524
  trackHeight,
13228
13525
  ticks,
13229
13526
  marker: { type: "needle", value, color: needleColor },
@@ -13235,7 +13532,7 @@ function FatigueMeter({
13235
13532
  }
13236
13533
  var HEAT = WORKOUT_TOKENS.heatmap;
13237
13534
  var NEUTRAL_TRACK = primitiveColors.charcoal[200];
13238
- var MONO = "monospace";
13535
+ var MONO2 = "monospace";
13239
13536
  var HEAT_BY_ZONE = {
13240
13537
  under: HEAT.under,
13241
13538
  maintenance: HEAT.maintenance,
@@ -13294,7 +13591,7 @@ function VolumeLandmarkBar({
13294
13591
  Text,
13295
13592
  {
13296
13593
  testID: "volume-landmark-pct",
13297
- style: { fontSize: 14, fontWeight: "700", fontFamily: MONO, color: fillColor },
13594
+ style: { fontSize: 14, fontWeight: "700", fontFamily: MONO2, color: fillColor },
13298
13595
  children: [
13299
13596
  pct2,
13300
13597
  "%"
@@ -13337,7 +13634,7 @@ function VolumeLandmarkBar({
13337
13634
  }
13338
13635
  );
13339
13636
  }
13340
- var t16 = getSemanticColors("dark");
13637
+ var t15 = getSemanticColors("dark");
13341
13638
  var statusToken = {
13342
13639
  productive: "status-success",
13343
13640
  threshold: "status-warning",
@@ -13354,7 +13651,7 @@ var defaultLabel = {
13354
13651
  stop: "Stop"
13355
13652
  };
13356
13653
  function statusPillColor(status) {
13357
- return t16[statusToken[status]];
13654
+ return t15[statusToken[status]];
13358
13655
  }
13359
13656
  function StatusPill2({ status, label, className, style, ...props }) {
13360
13657
  const color = statusPillColor(status);
@@ -13387,7 +13684,7 @@ function StatusPill2({ status, label, className, style, ...props }) {
13387
13684
  }
13388
13685
  );
13389
13686
  }
13390
- var t17 = getSemanticColors("dark");
13687
+ var t16 = getSemanticColors("dark");
13391
13688
  var categoryToken = {
13392
13689
  productive: null,
13393
13690
  threshold: "status-warning",
@@ -13400,7 +13697,7 @@ var floodOpacity = {
13400
13697
  };
13401
13698
  function liveAuraColor(category) {
13402
13699
  const token = categoryToken[category];
13403
- return token ? t17[token] : null;
13700
+ return token ? t16[token] : null;
13404
13701
  }
13405
13702
  function LiveAuraFrame({
13406
13703
  category,
@@ -13429,9 +13726,9 @@ function LiveAuraFrame({
13429
13726
  position: "relative",
13430
13727
  overflow: "hidden",
13431
13728
  borderRadius: 10,
13432
- backgroundColor: t17["background-base"],
13729
+ backgroundColor: t16["background-base"],
13433
13730
  borderWidth: 1,
13434
- borderColor: t17["border-default"]
13731
+ borderColor: t16["border-default"]
13435
13732
  },
13436
13733
  style
13437
13734
  ],
@@ -13658,28 +13955,28 @@ function Scatter({
13658
13955
  testID: "scatter-canvas",
13659
13956
  style: { position: "absolute", top: 0, left: 0, width, height, overflow: "hidden" },
13660
13957
  children: [
13661
- ticksOf(yd, TICK_COUNT).map((t18, i) => {
13662
- const y = toY(t18);
13958
+ ticksOf(yd, TICK_COUNT).map((t17, i) => {
13959
+ const y = toY(t17);
13663
13960
  return /* @__PURE__ */ jsx(
13664
13961
  View,
13665
13962
  {
13666
13963
  accessibilityElementsHidden: true,
13667
13964
  testID: "scatter-gridline-y",
13668
13965
  style: { position: "absolute", left: PLOT_LEFT2, width: innerW, top: y, height: 1, backgroundColor: GRID_LINE2 },
13669
- children: /* @__PURE__ */ jsx(Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", left: -PLOT_LEFT2, top: -6, width: PLOT_LEFT2 - 6, textAlign: "right" }, children: formatTick(t18) })
13966
+ children: /* @__PURE__ */ jsx(Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", left: -PLOT_LEFT2, top: -6, width: PLOT_LEFT2 - 6, textAlign: "right" }, children: formatTick(t17) })
13670
13967
  },
13671
13968
  `y-${i}`
13672
13969
  );
13673
13970
  }),
13674
- ticksOf(xd, TICK_COUNT).map((t18, i) => {
13675
- const x = toX(t18);
13971
+ ticksOf(xd, TICK_COUNT).map((t17, i) => {
13972
+ const x = toX(t17);
13676
13973
  return /* @__PURE__ */ jsx(
13677
13974
  View,
13678
13975
  {
13679
13976
  accessibilityElementsHidden: true,
13680
13977
  testID: "scatter-gridline-x",
13681
13978
  style: { position: "absolute", top: PLOT_TOP, height: innerH, left: x, width: 1, backgroundColor: GRID_LINE2 },
13682
- children: /* @__PURE__ */ jsx(Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", top: innerH + 4, left: -16, width: 32, textAlign: "center" }, children: formatTick(t18) })
13979
+ children: /* @__PURE__ */ jsx(Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", top: innerH + 4, left: -16, width: 32, textAlign: "center" }, children: formatTick(t17) })
13683
13980
  },
13684
13981
  `x-${i}`
13685
13982
  );
@@ -13740,14 +14037,14 @@ function Scatter({
13740
14037
  ] });
13741
14038
  }
13742
14039
  var sem2 = getSemanticColors("dark");
13743
- var STATUS_SUCCESS4 = sem2["status-success"];
13744
- var STATUS_WARNING4 = sem2["status-warning"];
14040
+ var STATUS_SUCCESS5 = sem2["status-success"];
14041
+ var STATUS_WARNING5 = sem2["status-warning"];
13745
14042
  var STATUS_ERROR4 = sem2["status-error"];
13746
14043
  var TRACK = "rgba(255,255,255,0.08)";
13747
14044
  var DEFAULT_THRESHOLDS2 = [
13748
14045
  { value: 0, color: STATUS_ERROR4 },
13749
- { value: 60, color: STATUS_WARNING4 },
13750
- { value: 80, color: STATUS_SUCCESS4 }
14046
+ { value: 60, color: STATUS_WARNING5 },
14047
+ { value: 80, color: STATUS_SUCCESS5 }
13751
14048
  ];
13752
14049
  var ARC_START = 135;
13753
14050
  var ARC_SWEEP = 270;
@@ -13762,7 +14059,7 @@ function formatValue2(v) {
13762
14059
  function bandColor2(fraction2, bands, min, max) {
13763
14060
  const value = min + fraction2 * (max - min);
13764
14061
  const sorted = [...bands].sort((a, b) => a.value - b.value);
13765
- let color = sorted[0]?.color ?? STATUS_SUCCESS4;
14062
+ let color = sorted[0]?.color ?? STATUS_SUCCESS5;
13766
14063
  for (const band of sorted) {
13767
14064
  if (value >= band.value) color = band.color;
13768
14065
  }
@@ -13814,23 +14111,23 @@ function Gauge2({
13814
14111
  testID: "gauge",
13815
14112
  ...props,
13816
14113
  children: [
13817
- ticks.map((t18) => /* @__PURE__ */ jsx(
14114
+ ticks.map((t17) => /* @__PURE__ */ jsx(
13818
14115
  View,
13819
14116
  {
13820
14117
  accessibilityElementsHidden: true,
13821
14118
  testID: "gauge-segment",
13822
14119
  style: {
13823
14120
  position: "absolute",
13824
- left: t18.left - tickThickness / 2,
13825
- top: t18.top - tickLength / 2,
14121
+ left: t17.left - tickThickness / 2,
14122
+ top: t17.top - tickLength / 2,
13826
14123
  width: tickThickness,
13827
14124
  height: tickLength,
13828
14125
  borderRadius: tickThickness / 2,
13829
- backgroundColor: t18.color,
13830
- transform: [{ rotate: t18.rotate }]
14126
+ backgroundColor: t17.color,
14127
+ transform: [{ rotate: t17.rotate }]
13831
14128
  }
13832
14129
  },
13833
- t18.key
14130
+ t17.key
13834
14131
  )),
13835
14132
  /* @__PURE__ */ jsxs(View, { style: { position: "absolute", top: 0, left: 0, width: size, height: size }, className: "items-center justify-center", children: [
13836
14133
  /* @__PURE__ */ jsxs(View, { className: "flex-row items-baseline gap-0.5", children: [
@@ -14250,6 +14547,6 @@ function composeValidators(...validators) {
14250
14547
  };
14251
14548
  }
14252
14549
 
14253
- export { Accordion, AccordionButton, AccordionItem, AccordionPanel, ActivityIcon, Alert, AlertDescription, AlertTitle, AlertTriangleIcon, Autocomplete, Avatar, AvatarBadge, AvatarGroup, AwardIcon, Badge, BadgeText, BaseBadge, BluetoothIcon, BrandLockup, BreadcrumbItem, Breadcrumbs, Button, ButtonIcon, ButtonSpinner, ButtonText, CATEGORICAL_CVD_SAFE_MAX, CapacityBandChart, Caption, Card, CardContent, CardDescription, CardFooter, CardHeader, CardSkeleton, CardTitle, Checkbox, CheckboxGroup, Chip, CircleSlashIcon, CircularProgress, Collapse, CollapseButton, CollapseContent, DashboardShell, DataRow, DateTime, DeviationBar, DeviceIndicator, DeviceMenu, DeviceRow, Divider, Drawer, DrawerBody, DrawerFooter, DrawerHeader, DumbbellIcon, EmptyState, ExerciseCard, ExerciseCardHeading, ExerciseHeading, ExerciseIndicator, FatigueMeter, FormActions, FormField, FormRow, FormSection, Gauge2 as Gauge, HStack, Heading, HelpTip, HistoryIcon, INDICATOR_PRECEDENCE, IconBox, Indicator, InfoIcon, Input, InputBar, InputGroup, IntensityBar, Label, LabelWithHelp, LayersIcon, Link, ListItem, ListItemContent, ListItemDivider, ListItemIcon, ListItemTrailing, LiveAuraFrame, METRIC_FONT, Menu, MenuDivider, MenuGroup, MenuItem, MenuList, MenuTrigger, MesoCard, MesoProgressBar, MesoStatusCard, Metric, MetricCell, MetricGroup, MetricTiles, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalTitle, MuscleGroupChip, NavItem, Overline, Paragraph, PasswordInput, PersonStandingIcon, Pill, PlaceholderStrip, Popover, PopoverCloseButton, PopoverContent, PopoverTrigger, PrBadge, PrHistoryModal, Progress, ProgressSteps, Radio, RadioGroup, ReadinessCheck, RestTimer, SET_STRIP_VARIABLE_COLOR, SET_STRIP_ZONES, ScaleIcon, Scatter, ScheduleTiles, Section, SectionContent, SectionHeader, SegmentedBar, SegmentedProgressBar, Select, SessionHeader, SessionRail, SessionStatePill, SetBar, SetRow, SetStrip, SetTableHeader, SetsRepsLoad, SideNav, Sidebar, SidebarContent, SidebarDivider, SidebarFooter, SidebarHeader, SidebarItem, SidebarSection, Skeleton, SkeletonCard, SkeletonCircle, SkeletonListItem, SkeletonText, Sparkline, Spinner, Stack, StarIcon, StatusDot, StatusPill2 as StatusPill, StepContent, StepIndicator, StepLabel, Stepper, Step as StepperStep, StrengthTrendChart, SupersetWrapper, Surface, SvgIcon, Switch, TEMPO_PACING, Tab, TabList, TabPanel, TabPanels, Table, TableBody, TableCell, TableHeader, TableHeaderCell, TablePagination, TableRow, Tabs, TempoBar, TempoDisplay, Tile, TimerReadout, Toast, ToastProvider, ToolbarButton, ToolbarButtonGroup, Tooltip, TopBar, Treemap, TrendingDownIcon, Typography, VStack, VelocityStrip, VoltrasMark, VolumeLandmarkBar, WeekRow, WeightBadge, WorkoutCard, WorkoutPill, ZoneTrack, alpha, applyThemePreset, audiobookPreset, bestTextColor, buttonSizes, calculateMeanVelocity, calculateVelocityLoss, categoricalPalette, cn, componentElevationRanges, composeValidators, createFieldId, createFlatShadow, createPressedHoverShadow, createPressedShadow, createRaisedHoverShadow, createRaisedShadow, darkThemeCSSVars, darken, defaultNavItems, defaultPreset, discreteRainbow, divergingScale, elevationSystem, formatDateTime, formatDuration, formatPrescription, formatSignedPct, formatVelocity, getBaseSurfaceColor, getCategoricalColor, getContrastText, getDiscreteRainbowColor, getElevationConfig, getElevationShadow, getElevationSurface, getFieldAriaProps, getFieldValidationProps, getGlowShadow, getHoverColors, getLuminance, getResultColor, getSemanticColors, getStatusColor, getTempoFillPct, getTempoPacingState, getThemeCSSVars, getValidatedElevation, getVelocityZoneColor, getVelocityZoneName, gluestackConfig, hasMaxLength, hasMinLength, hexToRgb, hsvToRgb, isCssPropertyManifest, isDark, isEmpty, isValidEmail, lightThemeCSSVars, lighten, linearGradient, liveAuraColor, neumorphicShadows, paceTone, paceToneColor, primitiveBorderRadius, primitiveBreakpoints, primitiveColors, primitiveRamps, primitiveShadows, primitiveSpacing, primitiveTypography, primitiveZIndex, resolveColor, resolveIndicator, rgbToHex, rgbToHsv, roundRpe, roundTempo, roundWeight, rpeColor, semanticColorsDark, semanticColorsLight, semanticTypography, sequentialEffort, shadowColors, statusPillColor, surfaceGradient, useTable, useTimer, useToast, useToolbarButton, validateCssPropertyManifest, validationRules, velocityZoneColor };
14550
+ export { Accordion, AccordionButton, AccordionItem, AccordionPanel, ActivityIcon, Alert, AlertDescription, AlertTitle, AlertTriangleIcon, Autocomplete, Avatar, AvatarBadge, AvatarGroup, AwardIcon, Badge, BadgeText, BaseBadge, BluetoothIcon, BrandLockup, BreadcrumbItem, Breadcrumbs, Button, ButtonIcon, ButtonSpinner, ButtonText, CATEGORICAL_CVD_SAFE_MAX, CapacityBandChart, Caption, Card, CardContent, CardDescription, CardFooter, CardHeader, CardSkeleton, CardTitle, Checkbox, CheckboxGroup, Chip, CircleSlashIcon, CircularProgress, CircularTimer, Collapse, CollapseButton, CollapseContent, DashboardShell, DataRow, DateTime, DeviationBar, DeviceIndicator, DeviceMenu, DeviceRow, Divider, Drawer, DrawerBody, DrawerFooter, DrawerHeader, DumbbellIcon, EmptyState, ExerciseCard, ExerciseCardHeading, ExerciseHeading, ExerciseIndicator, FatigueMeter, FormActions, FormField, FormRow, FormSection, Gauge2 as Gauge, HStack, Heading, HelpTip, HistoryIcon, INDICATOR_PRECEDENCE, IconBox, Indicator, InfoIcon, Input, InputBar, InputGroup, IntensityBar, Label, LabelWithHelp, LayersIcon, Link, ListItem, ListItemContent, ListItemDivider, ListItemIcon, ListItemTrailing, LiveAuraFrame, METRIC_FONT, Menu, MenuDivider, MenuGroup, MenuItem, MenuList, MenuTrigger, MesoCard, MesoProgressBar, MesoStatusCard, Metric, MetricCell, MetricGroup, MetricTiles, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalTitle, MuscleGroupChip, NavItem, Overline, Paragraph, PasswordInput, PersonStandingIcon, Pill, PlaceholderStrip, Popover, PopoverCloseButton, PopoverContent, PopoverTrigger, PrBadge, PrHistoryModal, Progress, ProgressSteps, Radio, RadioGroup, ReadinessCheck, RestTimer, SET_STRIP_VARIABLE_COLOR, SET_STRIP_ZONES, ScaleIcon, Scatter, ScheduleTiles, Section, SectionContent, SectionHeader, SegmentedBar, SegmentedProgressBar, Select, SessionHeader, SessionRail, SessionStatePill, SetBar, SetRow, SetStrip, SetTableHeader, SetsRepsLoad, SideNav, Sidebar, SidebarContent, SidebarDivider, SidebarFooter, SidebarHeader, SidebarItem, SidebarSection, Skeleton, SkeletonCard, SkeletonCircle, SkeletonListItem, SkeletonText, Sparkline, Spinner, Stack, StarIcon, StatusDot, StatusPill2 as StatusPill, StepContent, StepIndicator, StepLabel, Stepper, Step as StepperStep, StrengthTrendChart, SupersetWrapper, Surface, SvgIcon, Switch, Tab, TabList, TabPanel, TabPanels, Table, TableBody, TableCell, TableHeader, TableHeaderCell, TablePagination, TableRow, Tabs, TempoDisplay, Tile, TimerReadout, Toast, ToastProvider, ToolbarButton, ToolbarButtonGroup, Tooltip, TopBar, Treemap, TrendingDownIcon, Typography, VStack, VelocityStrip, VoltrasMark, VolumeLandmarkBar, WeekRow, WeightBadge, WorkoutCard, WorkoutPill, ZoneTrack, alpha, applyThemePreset, audiobookPreset, bestTextColor, buttonSizes, calculateMeanVelocity, calculateVelocityLoss, categoricalPalette, cn, componentElevationRanges, composeValidators, createFieldId, createFlatShadow, createPressedHoverShadow, createPressedShadow, createRaisedHoverShadow, createRaisedShadow, darkThemeCSSVars, darken, defaultNavItems, defaultPreset, discreteRainbow, divergingScale, elevationSystem, formatDateTime, formatDuration, formatPrescription, formatSignedPct, formatVelocity, getBaseSurfaceColor, getCategoricalColor, getContrastText, getDiscreteRainbowColor, getElevationConfig, getElevationShadow, getElevationSurface, getFieldAriaProps, getFieldValidationProps, getGlowShadow, getHoverColors, getLuminance, getResultColor, getSemanticColors, getStatusColor, getThemeCSSVars, getValidatedElevation, getVelocityZoneColor, getVelocityZoneName, gluestackConfig, hasMaxLength, hasMinLength, hexToRgb, hsvToRgb, isCssPropertyManifest, isDark, isEmpty, isValidEmail, lightThemeCSSVars, lighten, linearGradient, liveAuraColor, neumorphicShadows, paceTone, paceToneColor, primitiveBorderRadius, primitiveBreakpoints, primitiveColors, primitiveRamps, primitiveShadows, primitiveSpacing, primitiveTypography, primitiveZIndex, resolveColor, resolveIndicator, rgbToHex, rgbToHsv, roundRpe, roundTempo, roundWeight, rpeColor, semanticColorsDark, semanticColorsLight, semanticTypography, sequentialEffort, shadowColors, statusPillColor, surfaceGradient, useTable, useTimer, useToast, useToolbarButton, validateCssPropertyManifest, validationRules, velocityZoneColor };
14254
14551
  //# sourceMappingURL=index.mjs.map
14255
14552
  //# sourceMappingURL=index.mjs.map