@titan-design/react-ui 0.8.0 → 0.9.1

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 (30) hide show
  1. package/dist/index.d.mts +19 -59
  2. package/dist/index.d.ts +19 -59
  3. package/dist/index.js +248 -371
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +249 -368
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/{pages-DCFBqp79.d.mts → pages-D7hOD4Vj.d.mts} +1 -1
  8. package/dist/{pages-_fI3-x7Z.d.ts → pages-DyEx-lBf.d.ts} +1 -1
  9. package/dist/pages.d.mts +1 -1
  10. package/dist/pages.d.ts +1 -1
  11. package/dist/pages.js +199 -161
  12. package/dist/pages.js.map +1 -1
  13. package/dist/pages.mjs +199 -161
  14. package/dist/pages.mjs.map +1 -1
  15. package/package.json +2 -1
  16. package/src/components/custom/Workout/ExerciseIndicator.tsx +1 -1
  17. package/src/components/custom/Workout/README.md +33 -13
  18. package/src/components/custom/Workout/SetsRepsLoad.test.tsx +6 -0
  19. package/src/components/custom/Workout/SetsRepsLoad.tsx +15 -6
  20. package/src/components/custom/Workout/TempoDisplay.stories.tsx +240 -65
  21. package/src/components/custom/Workout/TempoDisplay.test.tsx +61 -16
  22. package/src/components/custom/Workout/TempoDisplay.tsx +173 -60
  23. package/src/components/custom/Workout/VelocityStrip.test.tsx +3 -3
  24. package/src/components/custom/Workout/VelocityStrip.tsx +45 -11
  25. package/src/components/custom/Workout/ZoneTrack.tsx +15 -1
  26. package/src/components/custom/Workout/index.ts +1 -9
  27. package/src/test/no-device-internals.test.ts +51 -0
  28. package/src/components/custom/Workout/TempoBar.stories.tsx +0 -164
  29. package/src/components/custom/Workout/TempoBar.test.tsx +0 -218
  30. package/src/components/custom/Workout/TempoBar.tsx +0 -300
package/dist/index.js CHANGED
@@ -5262,7 +5262,7 @@ function ToastProvider({
5262
5262
  return id;
5263
5263
  }, [defaultDuration, maxToasts]);
5264
5264
  const removeToast = React24.useCallback((id) => {
5265
- setToasts((prev) => prev.filter((t18) => t18.id !== id));
5265
+ setToasts((prev) => prev.filter((t17) => t17.id !== id));
5266
5266
  }, []);
5267
5267
  const removeAllToasts = React24.useCallback(() => {
5268
5268
  setToasts([]);
@@ -7816,189 +7816,6 @@ function getLuminance(color) {
7816
7816
  function getContrastText(backgroundColor) {
7817
7817
  return getLuminance(backgroundColor) === "light" ? "#000000" : "#FFFFFF";
7818
7818
  }
7819
- var t2 = getSemanticColors("dark");
7820
- var TEMPO_PACING = {
7821
- behindThresholdPct: 0.15,
7822
- minPhaseDurationMs: 500
7823
- };
7824
- function formatTempoDelta(remainingMs) {
7825
- const s = remainingMs / 1e3;
7826
- const rounded = Math.abs(s) < 0.05 ? 0 : s;
7827
- return rounded.toFixed(1);
7828
- }
7829
- function getTempoPacingState(elapsedMs, targetMs) {
7830
- if (!targetMs || targetMs < TEMPO_PACING.minPhaseDurationMs) return "none";
7831
- const ratio = elapsedMs / targetMs;
7832
- if (ratio > 1 + TEMPO_PACING.behindThresholdPct) return "behind";
7833
- return "on-pace";
7834
- }
7835
- function getTempoFillPct(elapsedMs, targetMs) {
7836
- if (!targetMs) return 100;
7837
- return Math.min(100, elapsedMs / targetMs * 100);
7838
- }
7839
- var PHASE_ORDER = ["concentric", "hold", "eccentric"];
7840
- var PHASE_CONFIG = {
7841
- concentric: { label: "Con", wallLabel: "Concentric", color: t2["status-success"], flex: 2 },
7842
- hold: { label: "Hold", wallLabel: "Hold", color: t2["brand-primary"], flex: 1 },
7843
- eccentric: { label: "Ecc", wallLabel: "Eccentric", color: t2["status-warning"], flex: 3 }
7844
- };
7845
- var TEMPO_SIZES = {
7846
- default: { labelMargin: 4, labelFont: 10, barGap: 2, barHeight: 20, radius: 4, segFont: 12 },
7847
- wall: { labelMargin: 8, labelFont: 15, barGap: 4, barHeight: 40, radius: 6, segFont: 22 }
7848
- };
7849
- function TempoBar({
7850
- activePhase,
7851
- phaseElapsedMs,
7852
- completed,
7853
- target,
7854
- size = "default",
7855
- activeDisplay = "time",
7856
- className,
7857
- ...props
7858
- }) {
7859
- const s = TEMPO_SIZES[size];
7860
- const [display, setDisplay] = React24.useState(activeDisplay);
7861
- const toggleDisplay = () => setDisplay((d) => d === "time" ? "delta" : "time");
7862
- return /* @__PURE__ */ jsxs(reactNative.View, { className, testID: "tempo-bar", ...props, children: [
7863
- /* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", marginBottom: s.labelMargin }, children: PHASE_ORDER.map((phase) => {
7864
- const config = PHASE_CONFIG[phase];
7865
- return /* @__PURE__ */ jsx(reactNative.View, { style: { flex: config.flex, alignItems: "center" }, children: /* @__PURE__ */ jsx(reactNative.Text, { numberOfLines: 1, style: { fontSize: s.labelFont, color: t2["text-disabled"] }, children: size === "wall" ? config.wallLabel : config.label }) }, phase);
7866
- }) }),
7867
- /* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", gap: s.barGap, height: s.barHeight }, children: PHASE_ORDER.map((phase) => {
7868
- const config = PHASE_CONFIG[phase];
7869
- const isActive = activePhase === phase;
7870
- const completedMs = completed?.[phase];
7871
- const targetSec = target?.[phase];
7872
- const targetMs = targetSec != null ? targetSec * 1e3 : null;
7873
- return /* @__PURE__ */ jsx(
7874
- reactNative.View,
7875
- {
7876
- style: {
7877
- flex: config.flex,
7878
- backgroundColor: alpha("#ffffff", 0.06),
7879
- borderRadius: s.radius,
7880
- overflow: "hidden"
7881
- },
7882
- children: isActive ? /* @__PURE__ */ jsx(
7883
- ActiveSegment,
7884
- {
7885
- config,
7886
- phaseElapsedMs,
7887
- targetMs,
7888
- radius: s.radius,
7889
- font: s.segFont,
7890
- display,
7891
- onToggleDisplay: toggleDisplay
7892
- }
7893
- ) : completedMs != null ? /* @__PURE__ */ jsx(
7894
- CompletedSegment,
7895
- {
7896
- config,
7897
- completedMs,
7898
- targetMs,
7899
- font: s.segFont
7900
- }
7901
- ) : null
7902
- },
7903
- phase
7904
- );
7905
- }) })
7906
- ] });
7907
- }
7908
- function ActiveSegment({
7909
- config,
7910
- phaseElapsedMs,
7911
- targetMs,
7912
- radius,
7913
- font,
7914
- display,
7915
- onToggleDisplay
7916
- }) {
7917
- const pacing = getTempoPacingState(phaseElapsedMs, targetMs);
7918
- const barColor = pacing === "behind" ? t2["status-error"] : config.color;
7919
- const fillPct = getTempoFillPct(phaseElapsedMs, targetMs);
7920
- const label = display === "delta" && targetMs != null ? formatTempoDelta(targetMs - phaseElapsedMs) : targetMs != null ? `${formatDuration2(phaseElapsedMs)} / ${formatDuration2(targetMs)}` : formatDuration2(phaseElapsedMs);
7921
- return /* @__PURE__ */ jsxs(
7922
- reactNative.Pressable,
7923
- {
7924
- onPress: onToggleDisplay,
7925
- style: { height: "100%", position: "relative" },
7926
- accessibilityRole: "button",
7927
- accessibilityLabel: `Tempo readout: ${display === "delta" ? "delta" : "time"} \u2014 tap to switch`,
7928
- testID: `tempo-segment-active-${config.label}`,
7929
- children: [
7930
- /* @__PURE__ */ jsx(
7931
- reactNative.View,
7932
- {
7933
- style: {
7934
- position: "absolute",
7935
- left: 0,
7936
- top: 0,
7937
- bottom: 0,
7938
- width: `${fillPct}%`,
7939
- backgroundColor: alpha(barColor, 0.3),
7940
- borderRadius: radius
7941
- }
7942
- }
7943
- ),
7944
- /* @__PURE__ */ jsx(
7945
- reactNative.View,
7946
- {
7947
- style: {
7948
- height: "100%",
7949
- flexDirection: "row",
7950
- alignItems: "center",
7951
- justifyContent: "center"
7952
- },
7953
- children: /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: font, fontWeight: "700", color: barColor }, children: label })
7954
- }
7955
- )
7956
- ]
7957
- }
7958
- );
7959
- }
7960
- function CompletedSegment({
7961
- config,
7962
- completedMs,
7963
- targetMs,
7964
- font
7965
- }) {
7966
- const pacing = getTempoPacingState(completedMs, targetMs);
7967
- const hitTarget = pacing !== "behind";
7968
- const indicator = targetMs && targetMs >= TEMPO_PACING.minPhaseDurationMs ? hitTarget ? " \u2713" : " \u2717" : "";
7969
- const cueColor = hitTarget ? config.color : t2["status-error"];
7970
- return /* @__PURE__ */ jsx(
7971
- reactNative.View,
7972
- {
7973
- style: {
7974
- height: "100%",
7975
- flexDirection: "row",
7976
- alignItems: "center",
7977
- justifyContent: "center",
7978
- backgroundColor: alpha(cueColor, 0.15)
7979
- },
7980
- testID: `tempo-segment-completed-${config.label}`,
7981
- children: /* @__PURE__ */ jsxs(
7982
- reactNative.Text,
7983
- {
7984
- style: {
7985
- fontSize: font,
7986
- fontWeight: "500",
7987
- color: hitTarget ? alpha(config.color, 0.7) : cueColor
7988
- },
7989
- children: [
7990
- formatDuration2(completedMs),
7991
- indicator
7992
- ]
7993
- }
7994
- )
7995
- }
7996
- );
7997
- }
7998
- function formatDuration2(ms) {
7999
- const s = ms / 1e3;
8000
- return s < 10 ? s.toFixed(1) : Math.round(s).toString();
8001
- }
8002
7819
  var METRIC_FONT = "Inter, sans-serif";
8003
7820
  function MetricCell({ children, color, fontSize = 11 }) {
8004
7821
  return /* @__PURE__ */ jsx(
@@ -8017,14 +7834,32 @@ function MetricCell({ children, color, fontSize = 11 }) {
8017
7834
  }
8018
7835
 
8019
7836
  // src/components/custom/Workout/TempoDisplay.tsx
8020
- var t3 = getSemanticColors("dark");
7837
+ var t2 = getSemanticColors("dark");
8021
7838
  var INTER = "Inter, sans-serif";
8022
7839
  var TEXT_TERTIARY = "#6B7280";
8023
- var STATUS_ERROR = t3["status-error"];
7840
+ var STATUS_ERROR = t2["status-error"];
7841
+ var STATUS_SUCCESS = t2["status-success"];
7842
+ var STATUS_WARNING = t2["status-warning"];
7843
+ var ON_TARGET_MS = 100;
7844
+ function getTempoFillPct(elapsedMs, targetMs) {
7845
+ if (!targetMs) return 100;
7846
+ return Math.min(100, elapsedMs / targetMs * 100);
7847
+ }
7848
+ function activeNumberTone(elapsedMs, targetMs) {
7849
+ if (targetMs == null) return t2["text-primary"];
7850
+ const remainingMs = targetMs - elapsedMs;
7851
+ if (remainingMs > ON_TARGET_MS) return STATUS_WARNING;
7852
+ if (remainingMs >= -ON_TARGET_MS) return STATUS_SUCCESS;
7853
+ return STATUS_ERROR;
7854
+ }
7855
+ function liveReadoutText(elapsedMs, targetMs, readout) {
7856
+ const seconds = readout === "countup" ? elapsedMs / 1e3 : (targetMs - elapsedMs) / 1e3;
7857
+ return seconds.toFixed(1);
7858
+ }
8024
7859
  var phaseColors = {
8025
- eccentric: t3["status-warning"],
7860
+ eccentric: primitiveRamps.magenta[400],
8026
7861
  pauseBottom: TEXT_TERTIARY,
8027
- concentric: t3["status-success"],
7862
+ concentric: primitiveRamps.cyan[300],
8028
7863
  pauseTop: TEXT_TERTIARY,
8029
7864
  dash: TEXT_TERTIARY
8030
7865
  };
@@ -8044,63 +7879,98 @@ var LIVE_PHASES = [
8044
7879
  { key: "concentric", color: phaseColors.concentric },
8045
7880
  { key: "pauseTop", color: phaseColors.pauseTop }
8046
7881
  ];
7882
+ function CellFill({ color, pct: pct2 }) {
7883
+ return /* @__PURE__ */ jsx(
7884
+ reactNative.View,
7885
+ {
7886
+ style: {
7887
+ position: "absolute",
7888
+ left: 2,
7889
+ right: 2,
7890
+ bottom: 0,
7891
+ height: `${pct2}%`,
7892
+ backgroundColor: alpha(color, 0.28),
7893
+ borderRadius: 3
7894
+ }
7895
+ }
7896
+ );
7897
+ }
7898
+ var MONO = "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace";
7899
+ var READOUT_MAX_CHARS = 5;
8047
7900
  function LiveTempoCell({
8048
7901
  value,
8049
7902
  color,
8050
- isActive,
7903
+ status,
8051
7904
  phaseElapsedMs,
8052
- fontSize
7905
+ completedMs,
7906
+ fontSize,
7907
+ readout
8053
7908
  }) {
8054
- if (!isActive) {
8055
- return /* @__PURE__ */ jsx(TempoValue, { value, color: alpha(color, 0.35), fontSize });
8056
- }
8057
7909
  const targetMs = value > 0 ? value * 1e3 : null;
8058
- const barColor = getTempoPacingState(phaseElapsedMs, targetMs) === "behind" ? STATUS_ERROR : color;
7910
+ const cellW = Math.ceil(fontSize * 0.62 * READOUT_MAX_CHARS);
7911
+ const wrap = {
7912
+ position: "relative",
7913
+ width: cellW,
7914
+ minHeight: Math.round(fontSize * 1.25),
7915
+ alignItems: "center",
7916
+ justifyContent: "center"
7917
+ };
7918
+ const num = (c, text) => /* @__PURE__ */ jsx(reactNative.Text, { style: { fontFamily: MONO, fontSize, color: c, fontWeight: "700" }, children: text });
7919
+ if (status === "upcoming") {
7920
+ const targetText = targetMs != null ? (targetMs / 1e3).toFixed(1) : String(value);
7921
+ return /* @__PURE__ */ jsx(reactNative.View, { style: wrap, children: num(color, targetText) });
7922
+ }
7923
+ if (status === "done") {
7924
+ const finalMs = completedMs ?? targetMs ?? 0;
7925
+ const finalText = targetMs != null ? liveReadoutText(finalMs, targetMs, readout) : String(value);
7926
+ const finalTone = targetMs != null ? activeNumberTone(finalMs, targetMs) : color;
7927
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: wrap, testID: "tempo-live-done", children: [
7928
+ /* @__PURE__ */ jsx(CellFill, { color, pct: 100 }),
7929
+ num(finalTone, finalText)
7930
+ ] });
7931
+ }
7932
+ const numberTone = activeNumberTone(phaseElapsedMs, targetMs);
8059
7933
  const fillPct = getTempoFillPct(phaseElapsedMs, targetMs);
8060
- return /* @__PURE__ */ jsxs(reactNative.View, { style: { position: "relative" }, testID: "tempo-live-active", children: [
8061
- /* @__PURE__ */ jsx(
8062
- reactNative.View,
8063
- {
8064
- style: {
8065
- position: "absolute",
8066
- left: 0,
8067
- top: 0,
8068
- bottom: 0,
8069
- width: `${fillPct}%`,
8070
- backgroundColor: alpha(barColor, 0.3),
8071
- borderRadius: 2
8072
- }
8073
- }
8074
- ),
8075
- /* @__PURE__ */ jsx(TempoValue, { value, color: barColor, fontSize })
7934
+ const activeText = targetMs != null ? liveReadoutText(phaseElapsedMs, targetMs, readout) : String(value);
7935
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: wrap, testID: "tempo-live-active", children: [
7936
+ /* @__PURE__ */ jsx(CellFill, { color, pct: fillPct }),
7937
+ num(numberTone, activeText)
8076
7938
  ] });
8077
7939
  }
8078
7940
  function LiveTempoRow({
8079
7941
  values,
8080
7942
  live,
8081
- fontSize
8082
- }) {
8083
- return /* @__PURE__ */ jsx(jsxRuntime.Fragment, { children: LIVE_PHASES.map((phase, i) => /* @__PURE__ */ jsxs(reactNative.View, { style: { flexDirection: "row" }, children: [
8084
- i > 0 && /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8085
- /* @__PURE__ */ jsx(
8086
- LiveTempoCell,
8087
- {
8088
- value: values[i],
8089
- color: phase.color,
8090
- isActive: live.activePhase === phase.key,
8091
- phaseElapsedMs: live.phaseElapsedMs,
8092
- fontSize
8093
- }
8094
- )
8095
- ] }, phase.key)) });
7943
+ fontSize,
7944
+ readout
7945
+ }) {
7946
+ const activeIndex = live.activePhase ? LIVE_PHASES.findIndex((p) => p.key === live.activePhase) : -1;
7947
+ return /* @__PURE__ */ jsx(jsxRuntime.Fragment, { children: LIVE_PHASES.map((phase, i) => {
7948
+ const status = activeIndex < 0 || i > activeIndex ? "upcoming" : i < activeIndex ? "done" : "active";
7949
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
7950
+ i > 0 && /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
7951
+ /* @__PURE__ */ jsx(
7952
+ LiveTempoCell,
7953
+ {
7954
+ value: values[i],
7955
+ color: phase.color,
7956
+ status,
7957
+ phaseElapsedMs: live.phaseElapsedMs,
7958
+ completedMs: live.completed?.[phase.key],
7959
+ fontSize,
7960
+ readout
7961
+ }
7962
+ )
7963
+ ] }, phase.key);
7964
+ }) });
8096
7965
  }
8097
7966
  function TempoDisplay({
8098
7967
  tempo,
8099
7968
  size = "md",
8100
- colored = true,
7969
+ fontSize: fontSizeProp,
8101
7970
  showLabel = true,
8102
7971
  showInfo = true,
8103
7972
  live,
7973
+ liveReadout = "countdown",
8104
7974
  onPress,
8105
7975
  className,
8106
7976
  ...props
@@ -8108,8 +7978,11 @@ function TempoDisplay({
8108
7978
  const [showTooltip, setShowTooltip] = React24.useState(false);
8109
7979
  const [eccentric, pauseBottom, concentric, pauseTop] = roundTempo(tempo);
8110
7980
  const isSm = size === "sm";
8111
- const fontSize = isSm ? 9 : 11;
8112
- const monoColor = TEXT_TERTIARY;
7981
+ const fontSize = fontSizeProp ?? (isSm ? 9 : 11);
7982
+ const chromePadX = Math.round(fontSize * 0.6);
7983
+ const chromePadY = Math.round(fontSize * 0.3);
7984
+ const chromeRadius = Math.round(fontSize * 0.4);
7985
+ const labelFont = Math.max(9, Math.round(fontSize * 0.5));
8113
7986
  const handlePress = React24.useCallback(() => {
8114
7987
  if (onPress) {
8115
7988
  onPress();
@@ -8126,9 +7999,9 @@ function TempoDisplay({
8126
7999
  flexDirection: "row",
8127
8000
  alignItems: "center",
8128
8001
  backgroundColor: "#1C1C1C",
8129
- paddingHorizontal: isSm ? 6 : 8,
8130
- paddingVertical: 3,
8131
- borderRadius: 4
8002
+ paddingHorizontal: chromePadX,
8003
+ paddingVertical: chromePadY,
8004
+ borderRadius: chromeRadius
8132
8005
  },
8133
8006
  ...props,
8134
8007
  children: [
@@ -8137,12 +8010,13 @@ function TempoDisplay({
8137
8010
  {
8138
8011
  style: {
8139
8012
  fontFamily: INTER,
8140
- fontSize: 9,
8013
+ fontSize: labelFont,
8141
8014
  fontWeight: "500",
8142
- color: TEXT_TERTIARY,
8015
+ // Live-muted green while a rep is running, tertiary at rest.
8016
+ color: live ? t2["status-live-muted"] : TEXT_TERTIARY,
8143
8017
  letterSpacing: 0.5,
8144
8018
  textTransform: "uppercase",
8145
- marginRight: 6
8019
+ marginRight: Math.round(fontSize * 0.5)
8146
8020
  },
8147
8021
  children: "TEMPO"
8148
8022
  }
@@ -8152,36 +8026,20 @@ function TempoDisplay({
8152
8026
  {
8153
8027
  values: [eccentric, pauseBottom, concentric, pauseTop],
8154
8028
  live,
8155
- fontSize
8156
- }
8157
- ) : colored ? /* @__PURE__ */ jsxs(jsxRuntime.Fragment, { children: [
8158
- /* @__PURE__ */ jsx(TempoValue, { value: eccentric, color: phaseColors.eccentric, fontSize }),
8159
- /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8160
- /* @__PURE__ */ jsx(TempoValue, { value: pauseBottom, color: phaseColors.pauseBottom, fontSize }),
8161
- /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8162
- /* @__PURE__ */ jsx(TempoValue, { value: concentric, color: phaseColors.concentric, fontSize }),
8163
- /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8164
- /* @__PURE__ */ jsx(TempoValue, { value: pauseTop, color: phaseColors.pauseTop, fontSize })
8165
- ] }) : /* @__PURE__ */ jsxs(
8166
- reactNative.Text,
8167
- {
8168
- style: {
8169
- fontFamily: INTER,
8170
- fontSize,
8171
- color: monoColor,
8172
- fontWeight: "600",
8173
- letterSpacing: 1
8174
- },
8175
- children: [
8176
- eccentric,
8177
- "-",
8178
- pauseBottom,
8179
- "-",
8180
- concentric,
8181
- "-",
8182
- pauseTop
8183
- ]
8029
+ fontSize,
8030
+ readout: liveReadout
8184
8031
  }
8032
+ ) : (
8033
+ // Static prescription: the phase-coloured lockup (the same colours the live row rests at).
8034
+ /* @__PURE__ */ jsxs(jsxRuntime.Fragment, { children: [
8035
+ /* @__PURE__ */ jsx(TempoValue, { value: eccentric, color: phaseColors.eccentric, fontSize }),
8036
+ /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8037
+ /* @__PURE__ */ jsx(TempoValue, { value: pauseBottom, color: phaseColors.pauseBottom, fontSize }),
8038
+ /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8039
+ /* @__PURE__ */ jsx(TempoValue, { value: concentric, color: phaseColors.concentric, fontSize }),
8040
+ /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8041
+ /* @__PURE__ */ jsx(TempoValue, { value: pauseTop, color: phaseColors.pauseTop, fontSize })
8042
+ ] })
8185
8043
  ) })
8186
8044
  ]
8187
8045
  }
@@ -8266,13 +8124,13 @@ function TempoDisplay({
8266
8124
  }
8267
8125
  );
8268
8126
  }
8269
- var t4 = getSemanticColors("dark");
8127
+ var t3 = getSemanticColors("dark");
8270
8128
  function getDotColor(deviation) {
8271
8129
  const abs = Math.abs(deviation);
8272
- if (deviation < -0.3) return t4["status-success"];
8130
+ if (deviation < -0.3) return t3["status-success"];
8273
8131
  if (abs <= 0.3) return "#6B7280";
8274
- if (abs <= 0.7) return t4["status-warning"];
8275
- return t4["status-error"];
8132
+ if (abs <= 0.7) return t3["status-warning"];
8133
+ return t3["status-error"];
8276
8134
  }
8277
8135
  function getDeviationDescription(deviation) {
8278
8136
  if (deviation < -0.3) return "lighter than planned";
@@ -8340,19 +8198,19 @@ function DeviationBar({
8340
8198
  }
8341
8199
  );
8342
8200
  }
8343
- var t5 = getSemanticColors("dark");
8201
+ var t4 = getSemanticColors("dark");
8344
8202
  var TRACK_BG = "#333333";
8345
8203
  var LABEL_COLOR = "#6B7280";
8346
8204
  var TARGET_LINE_COLOR = "rgba(33, 150, 243, 0.5)";
8347
8205
  var AT_TARGET_GLOW = "0 0 5px 1px rgba(33, 150, 243, 0.35), 0 0 10px 3px rgba(33, 150, 243, 0.15)";
8348
8206
  var ZONE = {
8349
- building: t5["status-success"],
8207
+ building: t4["status-success"],
8350
8208
  // below target ramp-up
8351
- approaching: t5["status-warning"],
8209
+ approaching: t4["status-warning"],
8352
8210
  // nearing target
8353
- target: t5["brand-primary"],
8211
+ target: t4["brand-primary"],
8354
8212
  // exactly at target
8355
- over1: t5["status-error"],
8213
+ over1: t4["status-error"],
8356
8214
  // mild over-target
8357
8215
  over2: WORKOUT_TOKENS.intensity.over2,
8358
8216
  // moderate over-target
@@ -8521,7 +8379,7 @@ var MESO_ACCENT_GRADIENT_LIGHT = primitiveRamps.orange[300];
8521
8379
  var WORKOUT_PILL_DELOAD = primitiveRamps.magenta[600];
8522
8380
 
8523
8381
  // src/components/custom/Workout/WorkoutPill.tsx
8524
- var t6 = getSemanticColors("dark");
8382
+ var t5 = getSemanticColors("dark");
8525
8383
  var statusBgColors = {
8526
8384
  completed: "rgba(46,213,115,0.15)",
8527
8385
  current: "rgba(255,121,0,0.15)",
@@ -8539,9 +8397,9 @@ var statusBorderStyles = {
8539
8397
  deload: { borderWidth: 1, borderColor: "rgba(186,41,150,0.3)" }
8540
8398
  };
8541
8399
  var textColors = {
8542
- completed: t6["status-success"],
8543
- current: t6["brand-primary"],
8544
- next: t6["brand-primary"],
8400
+ completed: t5["status-success"],
8401
+ current: t5["brand-primary"],
8402
+ next: t5["brand-primary"],
8545
8403
  upcoming: "#6B7280",
8546
8404
  missed: "rgba(209,67,67,0.7)",
8547
8405
  deload: WORKOUT_PILL_DELOAD
@@ -9084,13 +8942,15 @@ var BARE_STUB_HEIGHT = 3;
9084
8942
  var HERO_HEIGHT = 220;
9085
8943
  var HERO_LABEL_HEADROOM = 20;
9086
8944
  var HERO_BAR_GAP = 8;
9087
- var HERO_BAR_MAX_WIDTH = 52;
8945
+ var HERO_BAR_MAX_WIDTH = 120;
8946
+ var HERO_LABEL_MIN_BAR_WIDTH = 30;
9088
8947
  var HERO_BAR_RADIUS = 5;
9089
8948
  var HERO_MIN_BAR_HEIGHT = 4;
9090
8949
  var HERO_PENDING_COLOR = primitiveColors.charcoal[100];
9091
8950
  var HERO_REFERENCE_COLOR = primitiveColors.charcoal[0];
8951
+ var PEAK_HEADROOM = 1.06;
9092
8952
  function scaleDenominator(scale, maxVelocity) {
9093
- return scale === "fixed" ? FIXED_MAX_VELOCITY : maxVelocity * 1.15;
8953
+ return scale === "fixed" ? FIXED_MAX_VELOCITY : maxVelocity * PEAK_HEADROOM;
9094
8954
  }
9095
8955
  function bareSlotHeight(slot, height, denom) {
9096
8956
  if (slot.kind !== "rep" || denom <= 0) return BARE_STUB_HEIGHT;
@@ -9167,10 +9027,21 @@ function HeroVelocityChart({
9167
9027
  }) {
9168
9028
  const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
9169
9029
  const liveScale = useLiveRepPop(true, liveRepIndex, liveVelocity, isNewPeak);
9030
+ const [plotW, setPlotW] = React24.useState(0);
9031
+ const onPlotLayout = (e) => setPlotW(e.nativeEvent.layout.width);
9170
9032
  const plotHeight = Math.max(0, height - HERO_LABEL_HEADROOM);
9171
9033
  const barHeight = (velocity) => scaleDenom > 0 ? Math.max(HERO_MIN_BAR_HEIGHT, Math.min(1, velocity / scaleDenom) * plotHeight) : HERO_MIN_BAR_HEIGHT;
9172
9034
  const pendingCount = Math.max(0, (targetReps ?? 0) - doneVelocities.length);
9173
9035
  const referencePx = referenceVelocity > 0 && scaleDenom > 0 ? Math.min(plotHeight, referenceVelocity / scaleDenom * plotHeight) : 0;
9036
+ const totalSlots = doneVelocities.length + pendingCount;
9037
+ const barWidth = plotW > 0 && totalSlots > 0 ? Math.min(HERO_BAR_MAX_WIDTH, (plotW - HERO_BAR_GAP * (totalSlots - 1)) / totalSlots) : HERO_BAR_MAX_WIDTH;
9038
+ const labelsCrowded = plotW > 0 && barWidth < HERO_LABEL_MIN_BAR_WIDTH;
9039
+ const heroGap = plotW === 0 ? HERO_BAR_GAP : barWidth < 16 ? 2 : barWidth < 28 ? 4 : HERO_BAR_GAP;
9040
+ const peakIndex = doneVelocities.reduce(
9041
+ (best, v, i) => v > doneVelocities[best] ? i : best,
9042
+ 0
9043
+ );
9044
+ const showBarLabel = (i) => !labelsCrowded || i === peakIndex || i === liveRepIndex;
9174
9045
  const repCount = doneVelocities.length;
9175
9046
  const total = Math.max(repCount, targetReps ?? repCount);
9176
9047
  const label = referenceVelocity > 0 ? `Velocity chart, ${repCount} of ${total} reps, best ${formatVelocity(referenceVelocity)} meters per second` : `Velocity chart, ${repCount} of ${total} reps`;
@@ -9187,11 +9058,12 @@ function HeroVelocityChart({
9187
9058
  children: /* @__PURE__ */ jsxs(
9188
9059
  reactNative.View,
9189
9060
  {
9061
+ onLayout: onPlotLayout,
9190
9062
  style: {
9191
9063
  flex: 1,
9192
9064
  flexDirection: "row",
9193
9065
  alignItems: "flex-end",
9194
- gap: HERO_BAR_GAP,
9066
+ gap: heroGap,
9195
9067
  position: "relative",
9196
9068
  borderBottomWidth: 2,
9197
9069
  borderBottomColor: HERO_PENDING_COLOR
@@ -9228,7 +9100,7 @@ function HeroVelocityChart({
9228
9100
  justifyContent: "flex-end"
9229
9101
  },
9230
9102
  children: [
9231
- /* @__PURE__ */ jsx(
9103
+ showBarLabel(i) && /* @__PURE__ */ jsx(
9232
9104
  reactNative.Text,
9233
9105
  {
9234
9106
  className: "text-text-primary",
@@ -9674,13 +9546,13 @@ function VelocityStrip({
9674
9546
  }
9675
9547
  return stripContent;
9676
9548
  }
9677
- var t7 = getSemanticColors("dark");
9549
+ var t6 = getSemanticColors("dark");
9678
9550
  var dotColorMap = {
9679
9551
  untrained: "#2C2C2C",
9680
- behind: t7["brand-secondary"],
9681
- ontrack: t7["status-success"],
9682
- target: t7["brand-primary"],
9683
- over: t7["status-error"]
9552
+ behind: t6["brand-secondary"],
9553
+ ontrack: t6["status-success"],
9554
+ target: t6["brand-primary"],
9555
+ over: t6["status-error"]
9684
9556
  };
9685
9557
  function MuscleGroupChip({
9686
9558
  name,
@@ -10383,7 +10255,7 @@ function RestTimer({
10383
10255
  }
10384
10256
  );
10385
10257
  }
10386
- var t8 = getSemanticColors("dark");
10258
+ var t7 = getSemanticColors("dark");
10387
10259
  var TIMES = "\xD7";
10388
10260
  var AT = "@";
10389
10261
  function SetsRepsLoad({
@@ -10391,11 +10263,12 @@ function SetsRepsLoad({
10391
10263
  reps,
10392
10264
  load,
10393
10265
  unit = "lb",
10266
+ fontSize,
10394
10267
  className,
10395
10268
  ...props
10396
10269
  }) {
10397
- const value = t8["text-primary"];
10398
- const sep = t8["text-tertiary"];
10270
+ const value = t7["text-primary"];
10271
+ const sep = t7["text-tertiary"];
10399
10272
  return /* @__PURE__ */ jsxs(
10400
10273
  reactNative.View,
10401
10274
  {
@@ -10405,12 +10278,12 @@ function SetsRepsLoad({
10405
10278
  testID: "sets-reps-load",
10406
10279
  ...props,
10407
10280
  children: [
10408
- /* @__PURE__ */ jsx(MetricCell, { color: value, children: sets }),
10409
- /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${TIMES} ` }),
10410
- /* @__PURE__ */ jsx(MetricCell, { color: value, children: reps }),
10411
- /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${AT} ` }),
10412
- /* @__PURE__ */ jsx(MetricCell, { color: value, children: load }),
10413
- /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${unit}` })
10281
+ /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, children: sets }),
10282
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, children: ` ${TIMES} ` }),
10283
+ /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, children: reps }),
10284
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, children: ` ${AT} ` }),
10285
+ /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, children: load }),
10286
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, children: ` ${unit}` })
10414
10287
  ]
10415
10288
  }
10416
10289
  );
@@ -10508,14 +10381,14 @@ function ScheduleTiles({ when, now, gap = 1, ...props }) {
10508
10381
  )
10509
10382
  ] });
10510
10383
  }
10511
- var t9 = getSemanticColors("dark");
10384
+ var t8 = getSemanticColors("dark");
10512
10385
  var INDICATOR_CONFIG = {
10513
- imbalance: { Icon: ScaleIcon, color: t9["status-error"], label: "Left/right imbalance" },
10514
- overshoot: { Icon: AlertTriangleIcon, color: t9["status-error"], label: "Load overshoot" },
10515
- "velocity-loss": { Icon: TrendingDownIcon, color: t9["status-warning"], label: "Velocity loss" },
10516
- "missed-reps": { Icon: CircleSlashIcon, color: t9["status-warning"], label: "Missed reps" },
10517
- pr: { Icon: AwardIcon, color: t9["status-success"], label: "Personal record" },
10518
- info: { Icon: InfoIcon, color: t9["status-info"], label: "More info" }
10386
+ imbalance: { Icon: ScaleIcon, color: t8["status-error"], label: "Left/right imbalance" },
10387
+ overshoot: { Icon: AlertTriangleIcon, color: t8["status-error"], label: "Load overshoot" },
10388
+ "velocity-loss": { Icon: TrendingDownIcon, color: t8["status-warning"], label: "Velocity loss" },
10389
+ "missed-reps": { Icon: CircleSlashIcon, color: t8["status-warning"], label: "Missed reps" },
10390
+ pr: { Icon: AwardIcon, color: t8["status-success"], label: "Personal record" },
10391
+ info: { Icon: InfoIcon, color: t8["status-info"], label: "More info" }
10519
10392
  };
10520
10393
  var INDICATOR_PRECEDENCE = [
10521
10394
  "imbalance",
@@ -10990,7 +10863,7 @@ function ExerciseCard({
10990
10863
  if (upcoming) return /* @__PURE__ */ jsx(UpcomingCard, { ...rest, onToggle });
10991
10864
  return isExpanded ? /* @__PURE__ */ jsx(ExpandedCard, { ...rest, onToggle }) : /* @__PURE__ */ jsx(CollapsedCard, { ...rest, onToggle });
10992
10865
  }
10993
- var t10 = getSemanticColors("dark");
10866
+ var t9 = getSemanticColors("dark");
10994
10867
  var RAISED = primitiveColors.charcoal[400];
10995
10868
  var BAR_HEIGHT = 9;
10996
10869
  var BAR_GAP = 3;
@@ -11012,7 +10885,7 @@ function SessionHeader({
11012
10885
  const hasPace = !upcoming && budgetMs != null && elapsedMs != null;
11013
10886
  const target = hasPace ? elapsedMs / budgetMs : void 0;
11014
10887
  const setsLabel = upcoming ? `${totalSets} sets` : `${Math.floor(setsDone)}/${totalSets} sets`;
11015
- const setsLabelColor = upcoming ? t10["text-secondary"] : paceToneColor(paceTone(totalSets > 0 ? setsDone / totalSets : 0, target));
10888
+ const setsLabelColor = upcoming ? t9["text-secondary"] : paceToneColor(paceTone(totalSets > 0 ? setsDone / totalSets : 0, target));
11016
10889
  return /* @__PURE__ */ jsxs(
11017
10890
  reactNative.View,
11018
10891
  {
@@ -11040,7 +10913,7 @@ function SessionHeader({
11040
10913
  lineHeight: 18,
11041
10914
  fontWeight: "700",
11042
10915
  fontFamily: '"Space Grotesk", sans-serif',
11043
- color: t10["text-primary"],
10916
+ color: t9["text-primary"],
11044
10917
  marginBottom: 10
11045
10918
  },
11046
10919
  testID: "session-rail-title",
@@ -11435,10 +11308,10 @@ function WeekRow({
11435
11308
  }
11436
11309
  );
11437
11310
  }
11438
- var t11 = getSemanticColors("dark");
11311
+ var t10 = getSemanticColors("dark");
11439
11312
  var COLORS = {
11440
- statusSuccess: t11["status-success"],
11441
- brandPrimary: t11["brand-primary"],
11313
+ statusSuccess: t10["status-success"],
11314
+ brandPrimary: t10["brand-primary"],
11442
11315
  borderDefault: "#1F1F1F",
11443
11316
  brandPrimarySubtle: "rgba(255,121,0,0.06)"
11444
11317
  };
@@ -11922,12 +11795,12 @@ function PrHistoryModal({
11922
11795
  }
11923
11796
  );
11924
11797
  }
11925
- var t12 = getSemanticColors("dark");
11926
- var BRAND_PRIMARY9 = t12["brand-primary"];
11798
+ var t11 = getSemanticColors("dark");
11799
+ var BRAND_PRIMARY9 = t11["brand-primary"];
11927
11800
  var BRAND_PRIMARY_SUBTLE = "rgba(255,121,0,0.12)";
11928
- var STATUS_SUCCESS = t12["status-success"];
11929
- var STATUS_WARNING = t12["status-warning"];
11930
- var STATUS_ERROR2 = t12["status-error"];
11801
+ var STATUS_SUCCESS2 = t11["status-success"];
11802
+ var STATUS_WARNING2 = t11["status-warning"];
11803
+ var STATUS_ERROR2 = t11["status-error"];
11931
11804
  var EMOJI_SETS = {
11932
11805
  sleep: ["\u{1F634}", "\u{1F610}", "\u{1F642}", "\u{1F60A}", "\u{1F929}"],
11933
11806
  soreness: ["\u{1F635}", "\u{1F623}", "\u{1F610}", "\u{1F642}", "\u{1F4AA}"],
@@ -11937,8 +11810,8 @@ var EMOJI_SETS = {
11937
11810
  var DEFAULT_EMOJI_SET = ["\u{1F61E}", "\u{1F615}", "\u{1F610}", "\u{1F642}", "\u{1F604}"];
11938
11811
  function scoreColor(score) {
11939
11812
  if (score < 40) return STATUS_ERROR2;
11940
- if (score < 70) return STATUS_WARNING;
11941
- return STATUS_SUCCESS;
11813
+ if (score < 70) return STATUS_WARNING2;
11814
+ return STATUS_SUCCESS2;
11942
11815
  }
11943
11816
  var WARMUP_BADGE = {
11944
11817
  good: { color: "success", label: "Good" },
@@ -12117,13 +11990,13 @@ function ReadinessCheck({
12117
11990
  )
12118
11991
  ] }) });
12119
11992
  }
12120
- var t13 = getSemanticColors("dark");
12121
- var BRAND_PRIMARY10 = t13["brand-primary"];
11993
+ var t12 = getSemanticColors("dark");
11994
+ var BRAND_PRIMARY10 = t12["brand-primary"];
12122
11995
  var BRAND_PRIMARY_DARK2 = MESO_ACCENT_GRADIENT_DARK;
12123
11996
  var BRAND_PRIMARY_LIGHT2 = MESO_ACCENT_GRADIENT_LIGHT;
12124
- var SUCCESS = t13["status-success"];
12125
- var WARNING = t13["status-warning"];
12126
- var ERROR = t13["status-error"];
11997
+ var SUCCESS = t12["status-success"];
11998
+ var WARNING = t12["status-warning"];
11999
+ var ERROR = t12["status-error"];
12127
12000
  var ACCENT_STOPS2 = [BRAND_PRIMARY_DARK2, BRAND_PRIMARY10, BRAND_PRIMARY_LIGHT2];
12128
12001
  var CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor("surface-elevated")} 0%, ${resolveColor("surface-raised")} 100%)`;
12129
12002
  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%)";
@@ -12483,9 +12356,9 @@ function MesoStatusCard({
12483
12356
  var sem = getSemanticColors("dark");
12484
12357
  var BRAND_PRIMARY11 = sem["brand-primary"];
12485
12358
  var PROJECTION_LINE_COLOR = resolveColor("text-tertiary");
12486
- var STATUS_SUCCESS2 = sem["status-success"];
12359
+ var STATUS_SUCCESS3 = sem["status-success"];
12487
12360
  var STATUS_ERROR3 = sem["status-error"];
12488
- var STATUS_WARNING2 = sem["status-warning"];
12361
+ var STATUS_WARNING3 = sem["status-warning"];
12489
12362
  var GRID_LINE = "rgba(255,255,255,0.06)";
12490
12363
  var SUCCESS_PILL_BG = "rgba(46,213,115,0.10)";
12491
12364
  var SUCCESS_PILL_BORDER = "rgba(46,213,115,0.20)";
@@ -12494,8 +12367,8 @@ var ERROR_PILL_BORDER = "rgba(209,67,67,0.20)";
12494
12367
  var PLOT_LEFT = 26;
12495
12368
  var TOOLTIP_WIDTH = 124;
12496
12369
  function timeOf(dateStr) {
12497
- const t18 = Date.parse(dateStr);
12498
- return Number.isNaN(t18) ? 0 : t18;
12370
+ const t17 = Date.parse(dateStr);
12371
+ return Number.isNaN(t17) ? 0 : t17;
12499
12372
  }
12500
12373
  function formatValue(value) {
12501
12374
  return `${Math.round(value)}`;
@@ -12791,7 +12664,7 @@ function StrengthTrendChart({
12791
12664
  left: c.x - 7,
12792
12665
  top: c.y - 20,
12793
12666
  fontSize: 12,
12794
- color: STATUS_WARNING2
12667
+ color: STATUS_WARNING3
12795
12668
  },
12796
12669
  children: "\u2605"
12797
12670
  },
@@ -12884,7 +12757,7 @@ function StrengthTrendChart({
12884
12757
  marginTop: 1,
12885
12758
  fontSize: 10,
12886
12759
  fontFamily: "Inter, sans-serif",
12887
- color: selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm >= 0 ? STATUS_SUCCESS2 : STATUS_ERROR3
12760
+ color: selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm >= 0 ? STATUS_SUCCESS3 : STATUS_ERROR3
12888
12761
  },
12889
12762
  children: `${selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm >= 0 ? "+" : "-"}${Math.abs(
12890
12763
  selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm
@@ -12940,7 +12813,7 @@ function StrengthTrendChart({
12940
12813
  fontSize: 11,
12941
12814
  fontFamily: "Inter, sans-serif",
12942
12815
  fontWeight: "600",
12943
- color: trend.positive ? STATUS_SUCCESS2 : STATUS_ERROR3
12816
+ color: trend.positive ? STATUS_SUCCESS3 : STATUS_ERROR3
12944
12817
  },
12945
12818
  children: trendText
12946
12819
  }
@@ -12974,7 +12847,7 @@ function StrengthTrendChart({
12974
12847
  /* @__PURE__ */ jsx(reactNative.Text, { className: "text-text-secondary", style: { fontSize: 10, fontFamily: "Inter, sans-serif" }, children: "Projected" })
12975
12848
  ] }),
12976
12849
  /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", style: { gap: 5 }, children: [
12977
- /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 12, color: STATUS_WARNING2 }, children: "\u2605" }),
12850
+ /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 12, color: STATUS_WARNING3 }, children: "\u2605" }),
12978
12851
  /* @__PURE__ */ jsx(reactNative.Text, { className: "text-text-secondary", style: { fontSize: 10, fontFamily: "Inter, sans-serif" }, children: "PR" })
12979
12852
  ] })
12980
12853
  ]
@@ -12984,10 +12857,10 @@ function StrengthTrendChart({
12984
12857
  }
12985
12858
  );
12986
12859
  }
12987
- var t14 = getSemanticColors("dark");
12988
- var STATUS_SUCCESS3 = t14["status-success"];
12989
- var STATUS_WARNING3 = t14["status-warning"];
12990
- var STATUS_INFO = t14["status-info"];
12860
+ var t13 = getSemanticColors("dark");
12861
+ var STATUS_SUCCESS4 = t13["status-success"];
12862
+ var STATUS_WARNING4 = t13["status-warning"];
12863
+ var STATUS_INFO = t13["status-info"];
12991
12864
  var DOT_BORDER = "#F3F4F6";
12992
12865
  var BAND_FILL = "rgba(46,213,115,0.1)";
12993
12866
  var BAND_EDGE = "rgba(46,213,115,0.45)";
@@ -12999,8 +12872,8 @@ var PADDING_BOTTOM = 16;
12999
12872
  var COLUMN_STEP = 4;
13000
12873
  var DOT_SIZE = 8;
13001
12874
  var DOT_COLORS = {
13002
- within: STATUS_SUCCESS3,
13003
- above: STATUS_WARNING3,
12875
+ within: STATUS_SUCCESS4,
12876
+ above: STATUS_WARNING4,
13004
12877
  below: STATUS_INFO
13005
12878
  };
13006
12879
  var STATUS_PHRASES = {
@@ -13030,8 +12903,8 @@ function interpEdge(pixels, x, key) {
13030
12903
  const a = pixels[i];
13031
12904
  const b = pixels[i + 1];
13032
12905
  if (x >= a.x && x <= b.x) {
13033
- const t18 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
13034
- return a[key] + t18 * (b[key] - a[key]);
12906
+ const t17 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
12907
+ return a[key] + t17 * (b[key] - a[key]);
13035
12908
  }
13036
12909
  }
13037
12910
  return pixels[pixels.length - 1][key];
@@ -13225,8 +13098,8 @@ function CapacityBandChart({
13225
13098
  projection && /* @__PURE__ */ jsxs(reactNative.View, { testID: "capacity-band-chart-projection", children: [
13226
13099
  renderColumns(buildColumns2(trainingPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
13227
13100
  renderColumns(buildColumns2(restPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
13228
- renderEdges(buildEdges(trainingPixels, "yHigh"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
13229
- renderEdges(buildEdges(trainingPixels, "yLow"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
13101
+ renderEdges(buildEdges(trainingPixels, "yHigh"), STATUS_SUCCESS4, true, "capacity-band-chart-projection-training"),
13102
+ renderEdges(buildEdges(trainingPixels, "yLow"), STATUS_SUCCESS4, true, "capacity-band-chart-projection-training"),
13230
13103
  renderEdges(buildEdges(restPixels, "yHigh"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
13231
13104
  renderEdges(buildEdges(restPixels, "yLow"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
13232
13105
  /* @__PURE__ */ jsx(
@@ -13238,7 +13111,7 @@ function CapacityBandChart({
13238
13111
  top: trainingPixels[trainingPixels.length - 1]?.yHigh - 12,
13239
13112
  fontSize: 9,
13240
13113
  fontFamily: "Inter, sans-serif",
13241
- color: STATUS_SUCCESS3
13114
+ color: STATUS_SUCCESS4
13242
13115
  },
13243
13116
  testID: "capacity-band-chart-projection-training-label",
13244
13117
  children: "Training"
@@ -13361,13 +13234,13 @@ function CapacityBandChart({
13361
13234
  }
13362
13235
  );
13363
13236
  }
13364
- var t15 = getSemanticColors("dark");
13237
+ var t14 = getSemanticColors("dark");
13365
13238
  function glowShadow(color) {
13366
13239
  return `0 0 5px 1px ${alpha(color, 0.3)}, 0 0 10px 3px ${alpha(color, 0.12)}`;
13367
13240
  }
13368
13241
  var DEFAULT_TRACK_COLOR = primitiveColors.charcoal[200];
13369
13242
  var DEFAULT_MARKER_COLOR2 = primitiveColors.white;
13370
- var TICK_COLOR = t15["text-tertiary"];
13243
+ var TICK_COLOR = t14["text-tertiary"];
13371
13244
  var ZONE_SIZES = {
13372
13245
  default: {
13373
13246
  trackHeight: 14,
@@ -13424,6 +13297,12 @@ function ZoneTrack({
13424
13297
  const s = ZONE_SIZES[size];
13425
13298
  const trackHeight = trackHeightProp ?? s.trackHeight;
13426
13299
  const needleOverhang = needleOverhangProp ?? s.needleOverhang;
13300
+ const [trackW, setTrackW] = React24.useState(0);
13301
+ const onTrackLayout = (e) => setTrackW(e.nativeEvent.layout.width);
13302
+ const tickCount = ticks?.length ?? 0;
13303
+ const tickSpacing = trackW > 0 && tickCount > 0 ? trackW / tickCount : Infinity;
13304
+ const tickStep = tickSpacing < s.tickCellWidth ? Math.ceil(s.tickCellWidth / tickSpacing) : 1;
13305
+ const showTickLabel = (i, emphasized) => tickStep <= 1 || i === 0 || i === tickCount - 1 || i % tickStep === 0 || emphasized;
13427
13306
  const isNeedle = marker?.type === "needle";
13428
13307
  const markerHeight = isNeedle ? trackHeight + needleOverhang * 2 : trackHeight;
13429
13308
  const markerFrac = marker != null ? fraction(marker.value, min, max) : 0;
@@ -13442,6 +13321,7 @@ function ZoneTrack({
13442
13321
  accessibilityValue: marker != null ? { min, max, now: marker.value } : { min, max, now: min },
13443
13322
  accessibilityLabel: accessibilityLabel2 ?? `Zone track: ${marker?.value ?? min}`,
13444
13323
  testID: "zone-track",
13324
+ onLayout: onTrackLayout,
13445
13325
  ...props,
13446
13326
  children: [
13447
13327
  /* @__PURE__ */ jsxs(reactNative.View, { style: { position: "relative", height: markerHeight }, children: [
@@ -13543,7 +13423,7 @@ function ZoneTrack({
13543
13423
  ),
13544
13424
  ticks?.map((tick, i) => {
13545
13425
  const emphasized = tick.emphasized === true;
13546
- const lineColor = tick.color ?? (emphasized ? t15["brand-primary"] : TICK_COLOR);
13426
+ const lineColor = tick.color ?? (emphasized ? t14["brand-primary"] : TICK_COLOR);
13547
13427
  const lineWidth = emphasized ? s.tickLineEmphasized : s.tickLineNormal;
13548
13428
  return /* @__PURE__ */ jsx(
13549
13429
  reactNative.View,
@@ -13573,7 +13453,8 @@ function ZoneTrack({
13573
13453
  children: ticks.map((tick, i) => {
13574
13454
  if (tick.label == null) return null;
13575
13455
  const emphasized = tick.emphasized === true;
13576
- const labelColor = tick.color ?? (emphasized ? t15["brand-primary"] : TICK_COLOR);
13456
+ if (!showTickLabel(i, emphasized)) return null;
13457
+ const labelColor = tick.color ?? (emphasized ? t14["brand-primary"] : TICK_COLOR);
13577
13458
  const labelNode = /* @__PURE__ */ jsx(
13578
13459
  reactNative.Text,
13579
13460
  {
@@ -13657,7 +13538,7 @@ function FatigueMeter({
13657
13538
  }
13658
13539
  var HEAT = WORKOUT_TOKENS.heatmap;
13659
13540
  var NEUTRAL_TRACK = primitiveColors.charcoal[200];
13660
- var MONO = "monospace";
13541
+ var MONO2 = "monospace";
13661
13542
  var HEAT_BY_ZONE = {
13662
13543
  under: HEAT.under,
13663
13544
  maintenance: HEAT.maintenance,
@@ -13716,7 +13597,7 @@ function VolumeLandmarkBar({
13716
13597
  reactNative.Text,
13717
13598
  {
13718
13599
  testID: "volume-landmark-pct",
13719
- style: { fontSize: 14, fontWeight: "700", fontFamily: MONO, color: fillColor },
13600
+ style: { fontSize: 14, fontWeight: "700", fontFamily: MONO2, color: fillColor },
13720
13601
  children: [
13721
13602
  pct2,
13722
13603
  "%"
@@ -13759,7 +13640,7 @@ function VolumeLandmarkBar({
13759
13640
  }
13760
13641
  );
13761
13642
  }
13762
- var t16 = getSemanticColors("dark");
13643
+ var t15 = getSemanticColors("dark");
13763
13644
  var statusToken = {
13764
13645
  productive: "status-success",
13765
13646
  threshold: "status-warning",
@@ -13776,7 +13657,7 @@ var defaultLabel = {
13776
13657
  stop: "Stop"
13777
13658
  };
13778
13659
  function statusPillColor(status) {
13779
- return t16[statusToken[status]];
13660
+ return t15[statusToken[status]];
13780
13661
  }
13781
13662
  function StatusPill2({ status, label, className, style, ...props }) {
13782
13663
  const color = statusPillColor(status);
@@ -13809,7 +13690,7 @@ function StatusPill2({ status, label, className, style, ...props }) {
13809
13690
  }
13810
13691
  );
13811
13692
  }
13812
- var t17 = getSemanticColors("dark");
13693
+ var t16 = getSemanticColors("dark");
13813
13694
  var categoryToken = {
13814
13695
  productive: null,
13815
13696
  threshold: "status-warning",
@@ -13822,7 +13703,7 @@ var floodOpacity = {
13822
13703
  };
13823
13704
  function liveAuraColor(category) {
13824
13705
  const token = categoryToken[category];
13825
- return token ? t17[token] : null;
13706
+ return token ? t16[token] : null;
13826
13707
  }
13827
13708
  function LiveAuraFrame({
13828
13709
  category,
@@ -13851,9 +13732,9 @@ function LiveAuraFrame({
13851
13732
  position: "relative",
13852
13733
  overflow: "hidden",
13853
13734
  borderRadius: 10,
13854
- backgroundColor: t17["background-base"],
13735
+ backgroundColor: t16["background-base"],
13855
13736
  borderWidth: 1,
13856
- borderColor: t17["border-default"]
13737
+ borderColor: t16["border-default"]
13857
13738
  },
13858
13739
  style
13859
13740
  ],
@@ -14080,28 +13961,28 @@ function Scatter({
14080
13961
  testID: "scatter-canvas",
14081
13962
  style: { position: "absolute", top: 0, left: 0, width, height, overflow: "hidden" },
14082
13963
  children: [
14083
- ticksOf(yd, TICK_COUNT).map((t18, i) => {
14084
- const y = toY(t18);
13964
+ ticksOf(yd, TICK_COUNT).map((t17, i) => {
13965
+ const y = toY(t17);
14085
13966
  return /* @__PURE__ */ jsx(
14086
13967
  reactNative.View,
14087
13968
  {
14088
13969
  accessibilityElementsHidden: true,
14089
13970
  testID: "scatter-gridline-y",
14090
13971
  style: { position: "absolute", left: PLOT_LEFT2, width: innerW, top: y, height: 1, backgroundColor: GRID_LINE2 },
14091
- children: /* @__PURE__ */ jsx(reactNative.Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", left: -PLOT_LEFT2, top: -6, width: PLOT_LEFT2 - 6, textAlign: "right" }, children: formatTick(t18) })
13972
+ children: /* @__PURE__ */ jsx(reactNative.Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", left: -PLOT_LEFT2, top: -6, width: PLOT_LEFT2 - 6, textAlign: "right" }, children: formatTick(t17) })
14092
13973
  },
14093
13974
  `y-${i}`
14094
13975
  );
14095
13976
  }),
14096
- ticksOf(xd, TICK_COUNT).map((t18, i) => {
14097
- const x = toX(t18);
13977
+ ticksOf(xd, TICK_COUNT).map((t17, i) => {
13978
+ const x = toX(t17);
14098
13979
  return /* @__PURE__ */ jsx(
14099
13980
  reactNative.View,
14100
13981
  {
14101
13982
  accessibilityElementsHidden: true,
14102
13983
  testID: "scatter-gridline-x",
14103
13984
  style: { position: "absolute", top: PLOT_TOP, height: innerH, left: x, width: 1, backgroundColor: GRID_LINE2 },
14104
- children: /* @__PURE__ */ jsx(reactNative.Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", top: innerH + 4, left: -16, width: 32, textAlign: "center" }, children: formatTick(t18) })
13985
+ children: /* @__PURE__ */ jsx(reactNative.Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", top: innerH + 4, left: -16, width: 32, textAlign: "center" }, children: formatTick(t17) })
14105
13986
  },
14106
13987
  `x-${i}`
14107
13988
  );
@@ -14162,14 +14043,14 @@ function Scatter({
14162
14043
  ] });
14163
14044
  }
14164
14045
  var sem2 = getSemanticColors("dark");
14165
- var STATUS_SUCCESS4 = sem2["status-success"];
14166
- var STATUS_WARNING4 = sem2["status-warning"];
14046
+ var STATUS_SUCCESS5 = sem2["status-success"];
14047
+ var STATUS_WARNING5 = sem2["status-warning"];
14167
14048
  var STATUS_ERROR4 = sem2["status-error"];
14168
14049
  var TRACK = "rgba(255,255,255,0.08)";
14169
14050
  var DEFAULT_THRESHOLDS2 = [
14170
14051
  { value: 0, color: STATUS_ERROR4 },
14171
- { value: 60, color: STATUS_WARNING4 },
14172
- { value: 80, color: STATUS_SUCCESS4 }
14052
+ { value: 60, color: STATUS_WARNING5 },
14053
+ { value: 80, color: STATUS_SUCCESS5 }
14173
14054
  ];
14174
14055
  var ARC_START = 135;
14175
14056
  var ARC_SWEEP = 270;
@@ -14184,7 +14065,7 @@ function formatValue2(v) {
14184
14065
  function bandColor2(fraction2, bands, min, max) {
14185
14066
  const value = min + fraction2 * (max - min);
14186
14067
  const sorted = [...bands].sort((a, b) => a.value - b.value);
14187
- let color = sorted[0]?.color ?? STATUS_SUCCESS4;
14068
+ let color = sorted[0]?.color ?? STATUS_SUCCESS5;
14188
14069
  for (const band of sorted) {
14189
14070
  if (value >= band.value) color = band.color;
14190
14071
  }
@@ -14236,23 +14117,23 @@ function Gauge2({
14236
14117
  testID: "gauge",
14237
14118
  ...props,
14238
14119
  children: [
14239
- ticks.map((t18) => /* @__PURE__ */ jsx(
14120
+ ticks.map((t17) => /* @__PURE__ */ jsx(
14240
14121
  reactNative.View,
14241
14122
  {
14242
14123
  accessibilityElementsHidden: true,
14243
14124
  testID: "gauge-segment",
14244
14125
  style: {
14245
14126
  position: "absolute",
14246
- left: t18.left - tickThickness / 2,
14247
- top: t18.top - tickLength / 2,
14127
+ left: t17.left - tickThickness / 2,
14128
+ top: t17.top - tickLength / 2,
14248
14129
  width: tickThickness,
14249
14130
  height: tickLength,
14250
14131
  borderRadius: tickThickness / 2,
14251
- backgroundColor: t18.color,
14252
- transform: [{ rotate: t18.rotate }]
14132
+ backgroundColor: t17.color,
14133
+ transform: [{ rotate: t17.rotate }]
14253
14134
  }
14254
14135
  },
14255
- t18.key
14136
+ t17.key
14256
14137
  )),
14257
14138
  /* @__PURE__ */ jsxs(reactNative.View, { style: { position: "absolute", top: 0, left: 0, width: size, height: size }, className: "items-center justify-center", children: [
14258
14139
  /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-baseline gap-0.5", children: [
@@ -14851,7 +14732,6 @@ exports.SupersetWrapper = SupersetWrapper;
14851
14732
  exports.Surface = Surface;
14852
14733
  exports.SvgIcon = SvgIcon;
14853
14734
  exports.Switch = Switch;
14854
- exports.TEMPO_PACING = TEMPO_PACING;
14855
14735
  exports.Tab = Tab;
14856
14736
  exports.TabList = TabList;
14857
14737
  exports.TabPanel = TabPanel;
@@ -14864,7 +14744,6 @@ exports.TableHeaderCell = TableHeaderCell;
14864
14744
  exports.TablePagination = TablePagination;
14865
14745
  exports.TableRow = TableRow;
14866
14746
  exports.Tabs = Tabs;
14867
- exports.TempoBar = TempoBar;
14868
14747
  exports.TempoDisplay = TempoDisplay;
14869
14748
  exports.Tile = Tile;
14870
14749
  exports.TimerReadout = TimerReadout;
@@ -14930,8 +14809,6 @@ exports.getLuminance = getLuminance;
14930
14809
  exports.getResultColor = getResultColor;
14931
14810
  exports.getSemanticColors = getSemanticColors;
14932
14811
  exports.getStatusColor = getStatusColor;
14933
- exports.getTempoFillPct = getTempoFillPct;
14934
- exports.getTempoPacingState = getTempoPacingState;
14935
14812
  exports.getThemeCSSVars = getThemeCSSVars;
14936
14813
  exports.getValidatedElevation = getValidatedElevation;
14937
14814
  exports.getVelocityZoneColor = getVelocityZoneColor;