@titan-design/react-ui 0.8.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 (34) 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 +1 -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/lab/north-star/LivePage.tsx +101 -0
  28. package/src/lab/north-star/LiveView.tsx +409 -0
  29. package/src/lab/north-star/LiveWallDashboard.stories.tsx +129 -0
  30. package/src/lab/north-star/RestView.tsx +140 -0
  31. package/src/lab/north-star/fixtures.ts +224 -0
  32. package/src/components/custom/Workout/TempoBar.stories.tsx +0 -164
  33. package/src/components/custom/Workout/TempoBar.test.tsx +0 -218
  34. package/src/components/custom/Workout/TempoBar.tsx +0 -300
package/dist/pages.js CHANGED
@@ -78,6 +78,7 @@ var primitiveRamps = {
78
78
  500: "#22A444",
79
79
  600: "#298732"},
80
80
  cyan: {
81
+ 300: "#22D3EE",
81
82
  // pin
82
83
  400: "#01B5D1",
83
84
  500: "#2697B7",
@@ -92,6 +93,7 @@ var primitiveRamps = {
92
93
  // pin
93
94
  600: "#1072CB"},
94
95
  magenta: {
96
+ 400: "#ED69C3",
95
97
  600: "#BA2996"}
96
98
  };
97
99
  var sequentialEffort = [
@@ -611,13 +613,15 @@ var BARE_STUB_HEIGHT = 3;
611
613
  var HERO_HEIGHT = 220;
612
614
  var HERO_LABEL_HEADROOM = 20;
613
615
  var HERO_BAR_GAP = 8;
614
- var HERO_BAR_MAX_WIDTH = 52;
616
+ var HERO_BAR_MAX_WIDTH = 120;
617
+ var HERO_LABEL_MIN_BAR_WIDTH = 30;
615
618
  var HERO_BAR_RADIUS = 5;
616
619
  var HERO_MIN_BAR_HEIGHT = 4;
617
620
  var HERO_PENDING_COLOR = primitiveColors.charcoal[100];
618
621
  var HERO_REFERENCE_COLOR = primitiveColors.charcoal[0];
622
+ var PEAK_HEADROOM = 1.06;
619
623
  function scaleDenominator(scale, maxVelocity) {
620
- return scale === "fixed" ? FIXED_MAX_VELOCITY : maxVelocity * 1.15;
624
+ return scale === "fixed" ? FIXED_MAX_VELOCITY : maxVelocity * PEAK_HEADROOM;
621
625
  }
622
626
  function bareSlotHeight(slot, height, denom) {
623
627
  if (slot.kind !== "rep" || denom <= 0) return BARE_STUB_HEIGHT;
@@ -694,10 +698,21 @@ function HeroVelocityChart({
694
698
  }) {
695
699
  const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
696
700
  const liveScale = useLiveRepPop(true, liveRepIndex, liveVelocity, isNewPeak);
701
+ const [plotW, setPlotW] = react.useState(0);
702
+ const onPlotLayout = (e) => setPlotW(e.nativeEvent.layout.width);
697
703
  const plotHeight = Math.max(0, height - HERO_LABEL_HEADROOM);
698
704
  const barHeight = (velocity) => scaleDenom > 0 ? Math.max(HERO_MIN_BAR_HEIGHT, Math.min(1, velocity / scaleDenom) * plotHeight) : HERO_MIN_BAR_HEIGHT;
699
705
  const pendingCount = Math.max(0, (targetReps ?? 0) - doneVelocities.length);
700
706
  const referencePx = referenceVelocity > 0 && scaleDenom > 0 ? Math.min(plotHeight, referenceVelocity / scaleDenom * plotHeight) : 0;
707
+ const totalSlots = doneVelocities.length + pendingCount;
708
+ const barWidth = plotW > 0 && totalSlots > 0 ? Math.min(HERO_BAR_MAX_WIDTH, (plotW - HERO_BAR_GAP * (totalSlots - 1)) / totalSlots) : HERO_BAR_MAX_WIDTH;
709
+ const labelsCrowded = plotW > 0 && barWidth < HERO_LABEL_MIN_BAR_WIDTH;
710
+ const heroGap = plotW === 0 ? HERO_BAR_GAP : barWidth < 16 ? 2 : barWidth < 28 ? 4 : HERO_BAR_GAP;
711
+ const peakIndex = doneVelocities.reduce(
712
+ (best, v, i) => v > doneVelocities[best] ? i : best,
713
+ 0
714
+ );
715
+ const showBarLabel = (i) => !labelsCrowded || i === peakIndex || i === liveRepIndex;
701
716
  const repCount = doneVelocities.length;
702
717
  const total = Math.max(repCount, targetReps ?? repCount);
703
718
  const label = referenceVelocity > 0 ? `Velocity chart, ${repCount} of ${total} reps, best ${formatVelocity(referenceVelocity)} meters per second` : `Velocity chart, ${repCount} of ${total} reps`;
@@ -714,11 +729,12 @@ function HeroVelocityChart({
714
729
  children: /* @__PURE__ */ jsxs(
715
730
  reactNative.View,
716
731
  {
732
+ onLayout: onPlotLayout,
717
733
  style: {
718
734
  flex: 1,
719
735
  flexDirection: "row",
720
736
  alignItems: "flex-end",
721
- gap: HERO_BAR_GAP,
737
+ gap: heroGap,
722
738
  position: "relative",
723
739
  borderBottomWidth: 2,
724
740
  borderBottomColor: HERO_PENDING_COLOR
@@ -755,7 +771,7 @@ function HeroVelocityChart({
755
771
  justifyContent: "flex-end"
756
772
  },
757
773
  children: [
758
- /* @__PURE__ */ jsx(
774
+ showBarLabel(i) && /* @__PURE__ */ jsx(
759
775
  reactNative.Text,
760
776
  {
761
777
  className: "text-text-primary",
@@ -1954,6 +1970,7 @@ function SetsRepsLoad({
1954
1970
  reps,
1955
1971
  load,
1956
1972
  unit = "lb",
1973
+ fontSize,
1957
1974
  className,
1958
1975
  ...props
1959
1976
  }) {
@@ -1968,12 +1985,12 @@ function SetsRepsLoad({
1968
1985
  testID: "sets-reps-load",
1969
1986
  ...props,
1970
1987
  children: [
1971
- /* @__PURE__ */ jsx(MetricCell, { color: value, children: sets }),
1972
- /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${TIMES} ` }),
1973
- /* @__PURE__ */ jsx(MetricCell, { color: value, children: reps }),
1974
- /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${AT} ` }),
1975
- /* @__PURE__ */ jsx(MetricCell, { color: value, children: load }),
1976
- /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${unit}` })
1988
+ /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, children: sets }),
1989
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, children: ` ${TIMES} ` }),
1990
+ /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, children: reps }),
1991
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, children: ` ${AT} ` }),
1992
+ /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, children: load }),
1993
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, children: ` ${unit}` })
1977
1994
  ]
1978
1995
  }
1979
1996
  );
@@ -2287,36 +2304,34 @@ function alpha(color, opacity) {
2287
2304
  }
2288
2305
  return color;
2289
2306
  }
2307
+
2308
+ // src/components/custom/Workout/TempoDisplay.tsx
2290
2309
  var t2 = getSemanticColors("dark");
2291
- var TEMPO_PACING = {
2292
- behindThresholdPct: 0.15,
2293
- minPhaseDurationMs: 500
2294
- };
2295
- function getTempoPacingState(elapsedMs, targetMs) {
2296
- if (!targetMs || targetMs < TEMPO_PACING.minPhaseDurationMs) return "none";
2297
- const ratio = elapsedMs / targetMs;
2298
- if (ratio > 1 + TEMPO_PACING.behindThresholdPct) return "behind";
2299
- return "on-pace";
2300
- }
2310
+ var INTER = "Inter, sans-serif";
2311
+ var TEXT_TERTIARY = "#6B7280";
2312
+ var STATUS_ERROR = t2["status-error"];
2313
+ var STATUS_SUCCESS = t2["status-success"];
2314
+ var STATUS_WARNING = t2["status-warning"];
2315
+ var ON_TARGET_MS = 100;
2301
2316
  function getTempoFillPct(elapsedMs, targetMs) {
2302
2317
  if (!targetMs) return 100;
2303
2318
  return Math.min(100, elapsedMs / targetMs * 100);
2304
2319
  }
2305
- ({
2306
- concentric: { color: t2["status-success"]},
2307
- hold: { color: t2["brand-primary"]},
2308
- eccentric: { color: t2["status-warning"]}
2309
- });
2310
-
2311
- // src/components/custom/Workout/TempoDisplay.tsx
2312
- var t3 = getSemanticColors("dark");
2313
- var INTER = "Inter, sans-serif";
2314
- var TEXT_TERTIARY = "#6B7280";
2315
- var STATUS_ERROR = t3["status-error"];
2320
+ function activeNumberTone(elapsedMs, targetMs) {
2321
+ if (targetMs == null) return t2["text-primary"];
2322
+ const remainingMs = targetMs - elapsedMs;
2323
+ if (remainingMs > ON_TARGET_MS) return STATUS_WARNING;
2324
+ if (remainingMs >= -ON_TARGET_MS) return STATUS_SUCCESS;
2325
+ return STATUS_ERROR;
2326
+ }
2327
+ function liveReadoutText(elapsedMs, targetMs, readout) {
2328
+ const seconds = readout === "countup" ? elapsedMs / 1e3 : (targetMs - elapsedMs) / 1e3;
2329
+ return seconds.toFixed(1);
2330
+ }
2316
2331
  var phaseColors = {
2317
- eccentric: t3["status-warning"],
2332
+ eccentric: primitiveRamps.magenta[400],
2318
2333
  pauseBottom: TEXT_TERTIARY,
2319
- concentric: t3["status-success"],
2334
+ concentric: primitiveRamps.cyan[300],
2320
2335
  pauseTop: TEXT_TERTIARY,
2321
2336
  dash: TEXT_TERTIARY
2322
2337
  };
@@ -2336,63 +2351,98 @@ var LIVE_PHASES = [
2336
2351
  { key: "concentric", color: phaseColors.concentric },
2337
2352
  { key: "pauseTop", color: phaseColors.pauseTop }
2338
2353
  ];
2354
+ function CellFill({ color, pct }) {
2355
+ return /* @__PURE__ */ jsx(
2356
+ reactNative.View,
2357
+ {
2358
+ style: {
2359
+ position: "absolute",
2360
+ left: 2,
2361
+ right: 2,
2362
+ bottom: 0,
2363
+ height: `${pct}%`,
2364
+ backgroundColor: alpha(color, 0.28),
2365
+ borderRadius: 3
2366
+ }
2367
+ }
2368
+ );
2369
+ }
2370
+ var MONO = "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace";
2371
+ var READOUT_MAX_CHARS = 5;
2339
2372
  function LiveTempoCell({
2340
2373
  value,
2341
2374
  color,
2342
- isActive,
2375
+ status,
2343
2376
  phaseElapsedMs,
2344
- fontSize
2377
+ completedMs,
2378
+ fontSize,
2379
+ readout
2345
2380
  }) {
2346
- if (!isActive) {
2347
- return /* @__PURE__ */ jsx(TempoValue, { value, color: alpha(color, 0.35), fontSize });
2348
- }
2349
2381
  const targetMs = value > 0 ? value * 1e3 : null;
2350
- const barColor = getTempoPacingState(phaseElapsedMs, targetMs) === "behind" ? STATUS_ERROR : color;
2382
+ const cellW = Math.ceil(fontSize * 0.62 * READOUT_MAX_CHARS);
2383
+ const wrap = {
2384
+ position: "relative",
2385
+ width: cellW,
2386
+ minHeight: Math.round(fontSize * 1.25),
2387
+ alignItems: "center",
2388
+ justifyContent: "center"
2389
+ };
2390
+ const num = (c, text) => /* @__PURE__ */ jsx(reactNative.Text, { style: { fontFamily: MONO, fontSize, color: c, fontWeight: "700" }, children: text });
2391
+ if (status === "upcoming") {
2392
+ const targetText = targetMs != null ? (targetMs / 1e3).toFixed(1) : String(value);
2393
+ return /* @__PURE__ */ jsx(reactNative.View, { style: wrap, children: num(color, targetText) });
2394
+ }
2395
+ if (status === "done") {
2396
+ const finalMs = completedMs ?? targetMs ?? 0;
2397
+ const finalText = targetMs != null ? liveReadoutText(finalMs, targetMs, readout) : String(value);
2398
+ const finalTone = targetMs != null ? activeNumberTone(finalMs, targetMs) : color;
2399
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: wrap, testID: "tempo-live-done", children: [
2400
+ /* @__PURE__ */ jsx(CellFill, { color, pct: 100 }),
2401
+ num(finalTone, finalText)
2402
+ ] });
2403
+ }
2404
+ const numberTone = activeNumberTone(phaseElapsedMs, targetMs);
2351
2405
  const fillPct = getTempoFillPct(phaseElapsedMs, targetMs);
2352
- return /* @__PURE__ */ jsxs(reactNative.View, { style: { position: "relative" }, testID: "tempo-live-active", children: [
2353
- /* @__PURE__ */ jsx(
2354
- reactNative.View,
2355
- {
2356
- style: {
2357
- position: "absolute",
2358
- left: 0,
2359
- top: 0,
2360
- bottom: 0,
2361
- width: `${fillPct}%`,
2362
- backgroundColor: alpha(barColor, 0.3),
2363
- borderRadius: 2
2364
- }
2365
- }
2366
- ),
2367
- /* @__PURE__ */ jsx(TempoValue, { value, color: barColor, fontSize })
2406
+ const activeText = targetMs != null ? liveReadoutText(phaseElapsedMs, targetMs, readout) : String(value);
2407
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: wrap, testID: "tempo-live-active", children: [
2408
+ /* @__PURE__ */ jsx(CellFill, { color, pct: fillPct }),
2409
+ num(numberTone, activeText)
2368
2410
  ] });
2369
2411
  }
2370
2412
  function LiveTempoRow({
2371
2413
  values,
2372
2414
  live,
2373
- fontSize
2415
+ fontSize,
2416
+ readout
2374
2417
  }) {
2375
- return /* @__PURE__ */ jsx(jsxRuntime.Fragment, { children: LIVE_PHASES.map((phase, i) => /* @__PURE__ */ jsxs(reactNative.View, { style: { flexDirection: "row" }, children: [
2376
- i > 0 && /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
2377
- /* @__PURE__ */ jsx(
2378
- LiveTempoCell,
2379
- {
2380
- value: values[i],
2381
- color: phase.color,
2382
- isActive: live.activePhase === phase.key,
2383
- phaseElapsedMs: live.phaseElapsedMs,
2384
- fontSize
2385
- }
2386
- )
2387
- ] }, phase.key)) });
2418
+ const activeIndex = live.activePhase ? LIVE_PHASES.findIndex((p) => p.key === live.activePhase) : -1;
2419
+ return /* @__PURE__ */ jsx(jsxRuntime.Fragment, { children: LIVE_PHASES.map((phase, i) => {
2420
+ const status = activeIndex < 0 || i > activeIndex ? "upcoming" : i < activeIndex ? "done" : "active";
2421
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
2422
+ i > 0 && /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
2423
+ /* @__PURE__ */ jsx(
2424
+ LiveTempoCell,
2425
+ {
2426
+ value: values[i],
2427
+ color: phase.color,
2428
+ status,
2429
+ phaseElapsedMs: live.phaseElapsedMs,
2430
+ completedMs: live.completed?.[phase.key],
2431
+ fontSize,
2432
+ readout
2433
+ }
2434
+ )
2435
+ ] }, phase.key);
2436
+ }) });
2388
2437
  }
2389
2438
  function TempoDisplay({
2390
2439
  tempo,
2391
2440
  size = "md",
2392
- colored = true,
2441
+ fontSize: fontSizeProp,
2393
2442
  showLabel = true,
2394
2443
  showInfo = true,
2395
2444
  live,
2445
+ liveReadout = "countdown",
2396
2446
  onPress,
2397
2447
  className,
2398
2448
  ...props
@@ -2400,8 +2450,11 @@ function TempoDisplay({
2400
2450
  const [showTooltip, setShowTooltip] = react.useState(false);
2401
2451
  const [eccentric, pauseBottom, concentric, pauseTop] = roundTempo(tempo);
2402
2452
  const isSm = size === "sm";
2403
- const fontSize = isSm ? 9 : 11;
2404
- const monoColor = TEXT_TERTIARY;
2453
+ const fontSize = fontSizeProp ?? (isSm ? 9 : 11);
2454
+ const chromePadX = Math.round(fontSize * 0.6);
2455
+ const chromePadY = Math.round(fontSize * 0.3);
2456
+ const chromeRadius = Math.round(fontSize * 0.4);
2457
+ const labelFont = Math.max(9, Math.round(fontSize * 0.5));
2405
2458
  const handlePress = react.useCallback(() => {
2406
2459
  if (onPress) {
2407
2460
  onPress();
@@ -2418,9 +2471,9 @@ function TempoDisplay({
2418
2471
  flexDirection: "row",
2419
2472
  alignItems: "center",
2420
2473
  backgroundColor: "#1C1C1C",
2421
- paddingHorizontal: isSm ? 6 : 8,
2422
- paddingVertical: 3,
2423
- borderRadius: 4
2474
+ paddingHorizontal: chromePadX,
2475
+ paddingVertical: chromePadY,
2476
+ borderRadius: chromeRadius
2424
2477
  },
2425
2478
  ...props,
2426
2479
  children: [
@@ -2429,12 +2482,13 @@ function TempoDisplay({
2429
2482
  {
2430
2483
  style: {
2431
2484
  fontFamily: INTER,
2432
- fontSize: 9,
2485
+ fontSize: labelFont,
2433
2486
  fontWeight: "500",
2434
- color: TEXT_TERTIARY,
2487
+ // Live-muted green while a rep is running, tertiary at rest.
2488
+ color: live ? t2["status-live-muted"] : TEXT_TERTIARY,
2435
2489
  letterSpacing: 0.5,
2436
2490
  textTransform: "uppercase",
2437
- marginRight: 6
2491
+ marginRight: Math.round(fontSize * 0.5)
2438
2492
  },
2439
2493
  children: "TEMPO"
2440
2494
  }
@@ -2444,36 +2498,20 @@ function TempoDisplay({
2444
2498
  {
2445
2499
  values: [eccentric, pauseBottom, concentric, pauseTop],
2446
2500
  live,
2447
- fontSize
2448
- }
2449
- ) : colored ? /* @__PURE__ */ jsxs(jsxRuntime.Fragment, { children: [
2450
- /* @__PURE__ */ jsx(TempoValue, { value: eccentric, color: phaseColors.eccentric, fontSize }),
2451
- /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
2452
- /* @__PURE__ */ jsx(TempoValue, { value: pauseBottom, color: phaseColors.pauseBottom, fontSize }),
2453
- /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
2454
- /* @__PURE__ */ jsx(TempoValue, { value: concentric, color: phaseColors.concentric, fontSize }),
2455
- /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
2456
- /* @__PURE__ */ jsx(TempoValue, { value: pauseTop, color: phaseColors.pauseTop, fontSize })
2457
- ] }) : /* @__PURE__ */ jsxs(
2458
- reactNative.Text,
2459
- {
2460
- style: {
2461
- fontFamily: INTER,
2462
- fontSize,
2463
- color: monoColor,
2464
- fontWeight: "600",
2465
- letterSpacing: 1
2466
- },
2467
- children: [
2468
- eccentric,
2469
- "-",
2470
- pauseBottom,
2471
- "-",
2472
- concentric,
2473
- "-",
2474
- pauseTop
2475
- ]
2501
+ fontSize,
2502
+ readout: liveReadout
2476
2503
  }
2504
+ ) : (
2505
+ // Static prescription: the phase-coloured lockup (the same colours the live row rests at).
2506
+ /* @__PURE__ */ jsxs(jsxRuntime.Fragment, { children: [
2507
+ /* @__PURE__ */ jsx(TempoValue, { value: eccentric, color: phaseColors.eccentric, fontSize }),
2508
+ /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
2509
+ /* @__PURE__ */ jsx(TempoValue, { value: pauseBottom, color: phaseColors.pauseBottom, fontSize }),
2510
+ /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
2511
+ /* @__PURE__ */ jsx(TempoValue, { value: concentric, color: phaseColors.concentric, fontSize }),
2512
+ /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
2513
+ /* @__PURE__ */ jsx(TempoValue, { value: pauseTop, color: phaseColors.pauseTop, fontSize })
2514
+ ] })
2477
2515
  ) })
2478
2516
  ]
2479
2517
  }
@@ -2558,14 +2596,14 @@ function TempoDisplay({
2558
2596
  }
2559
2597
  );
2560
2598
  }
2561
- var t4 = getSemanticColors("dark");
2599
+ var t3 = getSemanticColors("dark");
2562
2600
  var INDICATOR_CONFIG = {
2563
- imbalance: { Icon: ScaleIcon, color: t4["status-error"], label: "Left/right imbalance" },
2564
- overshoot: { Icon: AlertTriangleIcon, color: t4["status-error"], label: "Load overshoot" },
2565
- "velocity-loss": { Icon: TrendingDownIcon, color: t4["status-warning"], label: "Velocity loss" },
2566
- "missed-reps": { Icon: CircleSlashIcon, color: t4["status-warning"], label: "Missed reps" },
2567
- pr: { Icon: AwardIcon, color: t4["status-success"], label: "Personal record" },
2568
- info: { Icon: InfoIcon, color: t4["status-info"], label: "More info" }
2601
+ imbalance: { Icon: ScaleIcon, color: t3["status-error"], label: "Left/right imbalance" },
2602
+ overshoot: { Icon: AlertTriangleIcon, color: t3["status-error"], label: "Load overshoot" },
2603
+ "velocity-loss": { Icon: TrendingDownIcon, color: t3["status-warning"], label: "Velocity loss" },
2604
+ "missed-reps": { Icon: CircleSlashIcon, color: t3["status-warning"], label: "Missed reps" },
2605
+ pr: { Icon: AwardIcon, color: t3["status-success"], label: "Personal record" },
2606
+ info: { Icon: InfoIcon, color: t3["status-info"], label: "More info" }
2569
2607
  };
2570
2608
  function ExerciseIndicator({ kind, onPress, className, ...props }) {
2571
2609
  const { Icon, color, label } = INDICATOR_CONFIG[kind];
@@ -4362,11 +4400,11 @@ function CardContent({ children, className }) {
4362
4400
  function CardFooter({ children, className }) {
4363
4401
  return /* @__PURE__ */ jsx(reactNative.View, { className: cn("px-6 py-4 flex-row items-center gap-2", className), children });
4364
4402
  }
4365
- var t5 = getSemanticColors("dark");
4403
+ var t4 = getSemanticColors("dark");
4366
4404
  var solidVariantColors = {
4367
- success: t5["status-success"],
4368
- warning: t5["status-warning"],
4369
- error: t5["status-error"],
4405
+ success: t4["status-success"],
4406
+ warning: t4["status-warning"],
4407
+ error: t4["status-error"],
4370
4408
  neutral: "#6B7280"
4371
4409
  };
4372
4410
  var ringVariantStyles = {
@@ -4406,8 +4444,8 @@ var iconColors = {
4406
4444
  warning: "#6B4000",
4407
4445
  error: "#5C1A1A",
4408
4446
  neutral: "#D1D5DB",
4409
- "on-track": t5["status-success"],
4410
- deviation: t5["status-warning"],
4447
+ "on-track": t4["status-success"],
4448
+ deviation: t4["status-warning"],
4411
4449
  future: "#6B7280"
4412
4450
  };
4413
4451
  var sizeDimensions = {
@@ -4493,13 +4531,13 @@ var MESO_ACCENT_GRADIENT_LIGHT = primitiveRamps.orange[300];
4493
4531
  var WORKOUT_PILL_DELOAD = primitiveRamps.magenta[600];
4494
4532
 
4495
4533
  // src/components/custom/Workout/MesoStatusCard.tsx
4496
- var t6 = getSemanticColors("dark");
4497
- var BRAND_PRIMARY5 = t6["brand-primary"];
4534
+ var t5 = getSemanticColors("dark");
4535
+ var BRAND_PRIMARY5 = t5["brand-primary"];
4498
4536
  var BRAND_PRIMARY_DARK = MESO_ACCENT_GRADIENT_DARK;
4499
4537
  var BRAND_PRIMARY_LIGHT = MESO_ACCENT_GRADIENT_LIGHT;
4500
- var SUCCESS = t6["status-success"];
4501
- var WARNING = t6["status-warning"];
4502
- var ERROR = t6["status-error"];
4538
+ var SUCCESS = t5["status-success"];
4539
+ var WARNING = t5["status-warning"];
4540
+ var ERROR = t5["status-error"];
4503
4541
  var ACCENT_STOPS = [BRAND_PRIMARY_DARK, BRAND_PRIMARY5, BRAND_PRIMARY_LIGHT];
4504
4542
  var CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor("surface-elevated")} 0%, ${resolveColor("surface-raised")} 100%)`;
4505
4543
  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%)";
@@ -4859,9 +4897,9 @@ function MesoStatusCard({
4859
4897
  var sem = getSemanticColors("dark");
4860
4898
  var BRAND_PRIMARY6 = sem["brand-primary"];
4861
4899
  var PROJECTION_LINE_COLOR = resolveColor("text-tertiary");
4862
- var STATUS_SUCCESS = sem["status-success"];
4900
+ var STATUS_SUCCESS2 = sem["status-success"];
4863
4901
  var STATUS_ERROR2 = sem["status-error"];
4864
- var STATUS_WARNING = sem["status-warning"];
4902
+ var STATUS_WARNING2 = sem["status-warning"];
4865
4903
  var GRID_LINE = "rgba(255,255,255,0.06)";
4866
4904
  var SUCCESS_PILL_BG = "rgba(46,213,115,0.10)";
4867
4905
  var SUCCESS_PILL_BORDER = "rgba(46,213,115,0.20)";
@@ -4870,8 +4908,8 @@ var ERROR_PILL_BORDER = "rgba(209,67,67,0.20)";
4870
4908
  var PLOT_LEFT = 26;
4871
4909
  var TOOLTIP_WIDTH = 124;
4872
4910
  function timeOf(dateStr) {
4873
- const t12 = Date.parse(dateStr);
4874
- return Number.isNaN(t12) ? 0 : t12;
4911
+ const t11 = Date.parse(dateStr);
4912
+ return Number.isNaN(t11) ? 0 : t11;
4875
4913
  }
4876
4914
  function formatValue(value) {
4877
4915
  return `${Math.round(value)}`;
@@ -5167,7 +5205,7 @@ function StrengthTrendChart({
5167
5205
  left: c.x - 7,
5168
5206
  top: c.y - 20,
5169
5207
  fontSize: 12,
5170
- color: STATUS_WARNING
5208
+ color: STATUS_WARNING2
5171
5209
  },
5172
5210
  children: "\u2605"
5173
5211
  },
@@ -5260,7 +5298,7 @@ function StrengthTrendChart({
5260
5298
  marginTop: 1,
5261
5299
  fontSize: 10,
5262
5300
  fontFamily: "Inter, sans-serif",
5263
- color: selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm >= 0 ? STATUS_SUCCESS : STATUS_ERROR2
5301
+ color: selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm >= 0 ? STATUS_SUCCESS2 : STATUS_ERROR2
5264
5302
  },
5265
5303
  children: `${selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm >= 0 ? "+" : "-"}${Math.abs(
5266
5304
  selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm
@@ -5316,7 +5354,7 @@ function StrengthTrendChart({
5316
5354
  fontSize: 11,
5317
5355
  fontFamily: "Inter, sans-serif",
5318
5356
  fontWeight: "600",
5319
- color: trend.positive ? STATUS_SUCCESS : STATUS_ERROR2
5357
+ color: trend.positive ? STATUS_SUCCESS2 : STATUS_ERROR2
5320
5358
  },
5321
5359
  children: trendText
5322
5360
  }
@@ -5350,7 +5388,7 @@ function StrengthTrendChart({
5350
5388
  /* @__PURE__ */ jsx(reactNative.Text, { className: "text-text-secondary", style: { fontSize: 10, fontFamily: "Inter, sans-serif" }, children: "Projected" })
5351
5389
  ] }),
5352
5390
  /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", style: { gap: 5 }, children: [
5353
- /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 12, color: STATUS_WARNING }, children: "\u2605" }),
5391
+ /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 12, color: STATUS_WARNING2 }, children: "\u2605" }),
5354
5392
  /* @__PURE__ */ jsx(reactNative.Text, { className: "text-text-secondary", style: { fontSize: 10, fontFamily: "Inter, sans-serif" }, children: "PR" })
5355
5393
  ] })
5356
5394
  ]
@@ -5360,10 +5398,10 @@ function StrengthTrendChart({
5360
5398
  }
5361
5399
  );
5362
5400
  }
5363
- var t7 = getSemanticColors("dark");
5364
- var STATUS_SUCCESS2 = t7["status-success"];
5365
- var STATUS_WARNING2 = t7["status-warning"];
5366
- var STATUS_INFO = t7["status-info"];
5401
+ var t6 = getSemanticColors("dark");
5402
+ var STATUS_SUCCESS3 = t6["status-success"];
5403
+ var STATUS_WARNING3 = t6["status-warning"];
5404
+ var STATUS_INFO = t6["status-info"];
5367
5405
  var DOT_BORDER = "#F3F4F6";
5368
5406
  var BAND_FILL = "rgba(46,213,115,0.1)";
5369
5407
  var BAND_EDGE = "rgba(46,213,115,0.45)";
@@ -5375,8 +5413,8 @@ var PADDING_BOTTOM = 16;
5375
5413
  var COLUMN_STEP = 4;
5376
5414
  var DOT_SIZE = 8;
5377
5415
  var DOT_COLORS = {
5378
- within: STATUS_SUCCESS2,
5379
- above: STATUS_WARNING2,
5416
+ within: STATUS_SUCCESS3,
5417
+ above: STATUS_WARNING3,
5380
5418
  below: STATUS_INFO
5381
5419
  };
5382
5420
  var STATUS_PHRASES = {
@@ -5406,8 +5444,8 @@ function interpEdge(pixels, x, key) {
5406
5444
  const a = pixels[i];
5407
5445
  const b = pixels[i + 1];
5408
5446
  if (x >= a.x && x <= b.x) {
5409
- const t12 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
5410
- return a[key] + t12 * (b[key] - a[key]);
5447
+ const t11 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
5448
+ return a[key] + t11 * (b[key] - a[key]);
5411
5449
  }
5412
5450
  }
5413
5451
  return pixels[pixels.length - 1][key];
@@ -5601,8 +5639,8 @@ function CapacityBandChart({
5601
5639
  projection && /* @__PURE__ */ jsxs(reactNative.View, { testID: "capacity-band-chart-projection", children: [
5602
5640
  renderColumns(buildColumns2(trainingPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
5603
5641
  renderColumns(buildColumns2(restPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
5604
- renderEdges(buildEdges(trainingPixels, "yHigh"), STATUS_SUCCESS2, true, "capacity-band-chart-projection-training"),
5605
- renderEdges(buildEdges(trainingPixels, "yLow"), STATUS_SUCCESS2, true, "capacity-band-chart-projection-training"),
5642
+ renderEdges(buildEdges(trainingPixels, "yHigh"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
5643
+ renderEdges(buildEdges(trainingPixels, "yLow"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
5606
5644
  renderEdges(buildEdges(restPixels, "yHigh"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
5607
5645
  renderEdges(buildEdges(restPixels, "yLow"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
5608
5646
  /* @__PURE__ */ jsx(
@@ -5614,7 +5652,7 @@ function CapacityBandChart({
5614
5652
  top: trainingPixels[trainingPixels.length - 1]?.yHigh - 12,
5615
5653
  fontSize: 9,
5616
5654
  fontFamily: "Inter, sans-serif",
5617
- color: STATUS_SUCCESS2
5655
+ color: STATUS_SUCCESS3
5618
5656
  },
5619
5657
  testID: "capacity-band-chart-projection-training-label",
5620
5658
  children: "Training"
@@ -6446,7 +6484,7 @@ function Badge({
6446
6484
  }
6447
6485
  );
6448
6486
  }
6449
- var t8 = getSemanticColors("dark");
6487
+ var t7 = getSemanticColors("dark");
6450
6488
  var statusBgColors = {
6451
6489
  completed: "rgba(46,213,115,0.15)",
6452
6490
  current: "rgba(255,121,0,0.15)",
@@ -6464,9 +6502,9 @@ var statusBorderStyles = {
6464
6502
  deload: { borderWidth: 1, borderColor: "rgba(186,41,150,0.3)" }
6465
6503
  };
6466
6504
  var textColors = {
6467
- completed: t8["status-success"],
6468
- current: t8["brand-primary"],
6469
- next: t8["brand-primary"],
6505
+ completed: t7["status-success"],
6506
+ current: t7["brand-primary"],
6507
+ next: t7["brand-primary"],
6470
6508
  upcoming: "#6B7280",
6471
6509
  missed: "rgba(209,67,67,0.7)",
6472
6510
  deload: WORKOUT_PILL_DELOAD
@@ -6599,19 +6637,19 @@ function WorkoutPill({
6599
6637
  }
6600
6638
  return pill;
6601
6639
  }
6602
- var t9 = getSemanticColors("dark");
6640
+ var t8 = getSemanticColors("dark");
6603
6641
  var TRACK_BG = "#333333";
6604
6642
  var LABEL_COLOR = "#6B7280";
6605
6643
  var TARGET_LINE_COLOR = "rgba(33, 150, 243, 0.5)";
6606
6644
  var AT_TARGET_GLOW = "0 0 5px 1px rgba(33, 150, 243, 0.35), 0 0 10px 3px rgba(33, 150, 243, 0.15)";
6607
6645
  var ZONE = {
6608
- building: t9["status-success"],
6646
+ building: t8["status-success"],
6609
6647
  // below target ramp-up
6610
- approaching: t9["status-warning"],
6648
+ approaching: t8["status-warning"],
6611
6649
  // nearing target
6612
- target: t9["brand-primary"],
6650
+ target: t8["brand-primary"],
6613
6651
  // exactly at target
6614
- over1: t9["status-error"],
6652
+ over1: t8["status-error"],
6615
6653
  // mild over-target
6616
6654
  over2: WORKOUT_TOKENS.intensity.over2,
6617
6655
  // moderate over-target
@@ -7034,13 +7072,13 @@ function MesoCard({
7034
7072
  }
7035
7073
  ) });
7036
7074
  }
7037
- var t10 = getSemanticColors("dark");
7075
+ var t9 = getSemanticColors("dark");
7038
7076
  var dotColorMap = {
7039
7077
  untrained: "#2C2C2C",
7040
- behind: t10["brand-secondary"],
7041
- ontrack: t10["status-success"],
7042
- target: t10["brand-primary"],
7043
- over: t10["status-error"]
7078
+ behind: t9["brand-secondary"],
7079
+ ontrack: t9["status-success"],
7080
+ target: t9["brand-primary"],
7081
+ over: t9["status-error"]
7044
7082
  };
7045
7083
  function MuscleGroupChip({
7046
7084
  name,
@@ -7114,10 +7152,10 @@ function MuscleGroupChip({
7114
7152
  }
7115
7153
 
7116
7154
  // src/components/custom/Workout/WorkoutCard.tsx
7117
- var t11 = getSemanticColors("dark");
7155
+ var t10 = getSemanticColors("dark");
7118
7156
  var COLORS = {
7119
- statusSuccess: t11["status-success"],
7120
- brandPrimary: t11["brand-primary"],
7157
+ statusSuccess: t10["status-success"],
7158
+ brandPrimary: t10["brand-primary"],
7121
7159
  borderDefault: "#1F1F1F",
7122
7160
  brandPrimarySubtle: "rgba(255,121,0,0.06)"
7123
7161
  };