@titan-design/react-ui 0.9.3 → 0.11.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.
- package/dist/bodymap.js +78 -24
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +78 -24
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +429 -11
- package/dist/index.d.ts +429 -11
- package/dist/index.js +2386 -885
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2306 -836
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-DyEx-lBf.d.ts → pages-B1hqnGIC.d.ts} +149 -15
- package/dist/{pages-D7hOD4Vj.d.mts → pages-CvsFqNtx.d.mts} +149 -15
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +1900 -1609
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +1901 -1610
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +129 -34
- package/dist/theme/index.d.ts +129 -34
- package/dist/theme/index.js +95 -19
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +92 -20
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +88 -24
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +88 -24
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/package.json +8 -3
- package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +258 -0
- package/src/components/custom/Fatigue/DualGhostSpark.test.tsx +138 -0
- package/src/components/custom/Fatigue/DualGhostSpark.tsx +208 -0
- package/src/components/custom/Fatigue/FatigueLights.stories.tsx +74 -0
- package/src/components/custom/Fatigue/FatigueLights.test.tsx +33 -0
- package/src/components/custom/Fatigue/FatigueLights.tsx +83 -0
- package/src/components/custom/Fatigue/GhostBand.test.tsx +235 -0
- package/src/components/custom/Fatigue/GhostBand.tsx +246 -0
- package/src/components/custom/Fatigue/GhostBloom.tsx +132 -0
- package/src/components/custom/Fatigue/GhostSpark.stories.tsx +175 -0
- package/src/components/custom/Fatigue/GhostSpark.test.tsx +45 -0
- package/src/components/custom/Fatigue/GhostSpark.tsx +126 -0
- package/src/components/custom/Fatigue/LiveFatigueCard.stories.tsx +126 -0
- package/src/components/custom/Fatigue/LiveFatigueCard.test.tsx +28 -0
- package/src/components/custom/Fatigue/LiveFatigueCard.tsx +86 -0
- package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +95 -0
- package/src/components/custom/Fatigue/LiveFatiguePanel.tsx +151 -0
- package/src/components/custom/Fatigue/README.md +93 -0
- package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +94 -0
- package/src/components/custom/Fatigue/RomProgressionChart.test.tsx +31 -0
- package/src/components/custom/Fatigue/RomProgressionChart.tsx +132 -0
- package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +226 -0
- package/src/components/custom/Fatigue/VelocityHero.stories.tsx +70 -0
- package/src/components/custom/Fatigue/VelocityHero.test.tsx +26 -0
- package/src/components/custom/Fatigue/VelocityHero.tsx +46 -0
- package/src/components/custom/Fatigue/VerdictHero.stories.tsx +59 -0
- package/src/components/custom/Fatigue/VerdictHero.test.tsx +49 -0
- package/src/components/custom/Fatigue/VerdictHero.tsx +79 -0
- package/src/components/custom/Fatigue/fatigue-mock.test.ts +59 -0
- package/src/components/custom/Fatigue/fatigue-mock.ts +268 -0
- package/src/components/custom/Fatigue/fatigue-model.ts +129 -0
- package/src/components/custom/Fatigue/fatigue-tokens.test.ts +70 -0
- package/src/components/custom/Fatigue/fatigue-tokens.ts +180 -0
- package/src/components/custom/Fatigue/index.ts +41 -0
- package/src/components/custom/Fatigue/tempo-pacing.test.ts +176 -0
- package/src/components/custom/Fatigue/tempo-pacing.ts +112 -0
- package/src/components/custom/Workout/BaseBadge.tsx +5 -5
- package/src/components/custom/Workout/DualVelocityStrip.test.tsx +685 -0
- package/src/components/custom/Workout/ExerciseCard.test.tsx +4 -4
- package/src/components/custom/Workout/ExerciseCard.tsx +3 -1
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +7 -1
- package/src/components/custom/Workout/README.md +62 -16
- package/src/components/custom/Workout/RowInventory.stories.tsx +5 -2
- package/src/components/custom/Workout/S3FullRail.stories.tsx +13 -1
- package/src/components/custom/Workout/SegmentedBar.stories.tsx +3 -3
- package/src/components/custom/Workout/SessionHeader.tsx +2 -2
- package/src/components/custom/Workout/SessionRail.stories.tsx +4 -1
- package/src/components/custom/Workout/SessionRail.test.tsx +43 -0
- package/src/components/custom/Workout/SessionRail.tsx +55 -18
- package/src/components/custom/Workout/SetRow.test.tsx +6 -6
- package/src/components/custom/Workout/SetRow.tsx +9 -2
- package/src/components/custom/Workout/SupersetWrapper.stories.tsx +16 -25
- package/src/components/custom/Workout/SupersetWrapper.tsx +1 -1
- package/src/components/custom/Workout/TempoDisplay.tsx +3 -7
- package/src/components/custom/Workout/VelocityStrip.compact.stories.tsx +191 -0
- package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +186 -0
- package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +296 -0
- package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +259 -0
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +217 -250
- package/src/components/custom/Workout/VelocityStrip.test.tsx +196 -79
- package/src/components/custom/Workout/VelocityStrip.tsx +953 -609
- package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +4 -1
- package/src/components/custom/Workout/WorkoutPill.tsx +2 -2
- package/src/components/custom/Workout/index.ts +3 -0
- package/src/components/custom/Workout/velocity-story-kit.tsx +438 -0
- package/src/components/custom/charts/SetBarChart.test.tsx +275 -0
- package/src/components/custom/charts/SetBarChart.tsx +651 -0
- package/src/components/custom/charts/live-rep-growth.ts +91 -0
- package/src/components/custom/index.ts +1 -0
- package/src/components/ui/surface/Surface.stories.tsx +195 -1
- package/src/components/ui/surface/Surface.test.tsx +148 -9
- package/src/components/ui/surface/Surface.tsx +37 -9
- package/src/components/ui/surface/SurfaceContext.ts +48 -8
- package/src/components/ui/surface/index.ts +1 -0
- package/src/components/ui/surface/surface.contract.test.ts +283 -0
- package/src/test/raw-color-patterns.test.ts +71 -0
- package/src/theme/ColorPalettes.stories.tsx +871 -0
- package/src/theme/ColorPrimitives.stories.tsx +300 -0
- package/src/theme/bar-paper.ts +59 -0
- package/src/theme/color-stories.coverage.test.ts +130 -0
- package/src/theme/color-story-kit.tsx +170 -0
- package/src/theme/config.ts +12 -0
- package/src/theme/elevation.ts +90 -67
- package/src/theme/global.css +35 -9
- package/src/theme/tokens/primitives.ts +40 -0
- package/src/theme/tokens/semantic.ts +63 -14
- package/tailwind.config.js +8 -0
- package/src/components/custom/Workout/VelocityStrip.modalities.stories.tsx +0 -254
- package/src/components/custom/Workout/VelocityStrip.responsive.stories.tsx +0 -145
- package/src/theme/ColorSystem.stories.tsx +0 -323
- package/src/theme/Colors.stories.tsx +0 -323
package/dist/index.js
CHANGED
|
@@ -339,6 +339,21 @@ var primitiveColors = {
|
|
|
339
339
|
black: "#000000",
|
|
340
340
|
transparent: "transparent"
|
|
341
341
|
};
|
|
342
|
+
var surfaceRampDark = {
|
|
343
|
+
inset: "#13100D",
|
|
344
|
+
// L*4.5 — sub-shell well / pressed
|
|
345
|
+
background: "#1C1916",
|
|
346
|
+
// L*9 — shell / frame
|
|
347
|
+
base: "#252321",
|
|
348
|
+
// L*13.5 — main content plane
|
|
349
|
+
elevated: "#2C2A28",
|
|
350
|
+
// L*17 — nav / rail
|
|
351
|
+
raised: "#31302F",
|
|
352
|
+
// L*20 — cards
|
|
353
|
+
overlay: "#373635"
|
|
354
|
+
// L*22.5 — hero / popover
|
|
355
|
+
};
|
|
356
|
+
var backgroundFrameDark = "#100D0A";
|
|
342
357
|
var primitiveRamps = {
|
|
343
358
|
red: {
|
|
344
359
|
50: "#FFF4F4",
|
|
@@ -806,10 +821,19 @@ var semanticColorsLight = {
|
|
|
806
821
|
"surface-overlay": primitiveColors.white,
|
|
807
822
|
"surface-input": primitiveColors.neutral[50],
|
|
808
823
|
// Input field background (filled variant)
|
|
824
|
+
// Deepest pressed pit (TD-surface-tokens, S-3) — light-mode counterpart of
|
|
825
|
+
// the dark ramp's `inset` plane; a placeholder pairing, not part of the
|
|
826
|
+
// dark-ramp investigation (surface.contract.test.ts is dark-mode only).
|
|
827
|
+
"surface-inset": primitiveColors.neutral[300],
|
|
809
828
|
// Background colors (background-*)
|
|
810
829
|
"background-base": "#EBEBEB",
|
|
811
830
|
"background-default": primitiveColors.white,
|
|
812
831
|
"background-subtle": primitiveColors.neutral[50],
|
|
832
|
+
// Frame/bezel chrome (TD-surface-tokens, S-3) — top bar + side nav shell,
|
|
833
|
+
// one step below `background-base` (darker still than `surface-inset`
|
|
834
|
+
// above, mirroring the dark-mode ordering). Placeholder pairing for light
|
|
835
|
+
// mode (see dark-mode note on `surface-inset` above).
|
|
836
|
+
"background-frame": primitiveColors.neutral[400],
|
|
813
837
|
// Border colors (border-*)
|
|
814
838
|
"border-default": "#E8E9EB",
|
|
815
839
|
"border-subtle": primitiveColors.neutral[100],
|
|
@@ -825,6 +849,11 @@ var semanticColorsLight = {
|
|
|
825
849
|
// Input field border on focus
|
|
826
850
|
"border-input-error": primitiveColors.red[600],
|
|
827
851
|
// Input field border on error
|
|
852
|
+
// Alpha hairline separators (surface-independent — composite toward black on
|
|
853
|
+
// light surfaces, mirroring the dark-mode white-alpha family). See §4/S-2.
|
|
854
|
+
"hairline-subtle": "rgba(0, 0, 0, 0.06)",
|
|
855
|
+
"hairline-default": "rgba(0, 0, 0, 0.09)",
|
|
856
|
+
"hairline-strong": "rgba(0, 0, 0, 0.14)",
|
|
828
857
|
// Interactive states (interactive-*)
|
|
829
858
|
"interactive-hover": "rgba(55, 65, 81, 0.04)",
|
|
830
859
|
"interactive-focus": "rgba(55, 65, 81, 0.12)",
|
|
@@ -917,25 +946,46 @@ var semanticColorsDark = {
|
|
|
917
946
|
"text-inverse": primitiveColors.neutral[900],
|
|
918
947
|
"text-link": "#828DF8",
|
|
919
948
|
"text-link-hover": primitiveColors.blue[400],
|
|
920
|
-
// Surface colors - dark backgrounds
|
|
921
|
-
|
|
922
|
-
//
|
|
923
|
-
|
|
924
|
-
//
|
|
925
|
-
|
|
926
|
-
//
|
|
927
|
-
|
|
928
|
-
//
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
//
|
|
932
|
-
"
|
|
933
|
-
//
|
|
934
|
-
"
|
|
935
|
-
//
|
|
936
|
-
"
|
|
937
|
-
//
|
|
938
|
-
|
|
949
|
+
// Surface colors - dark backgrounds — warm-tapered DERIVED ramp (TD-surface-tokens,
|
|
950
|
+
// S-1, re-spaced S-3). Shipped verbatim from `deriveSurfaceRamp()` — see
|
|
951
|
+
// `surfaceRampDark` in primitives.ts for the full derivation note. `surface-overlay`
|
|
952
|
+
// and `surface-elevated` are distinct (were both #191919 pre-S-1); `surface-input`
|
|
953
|
+
// tracks one plane above `surface-base`, same relative position as before the remap.
|
|
954
|
+
// `background-base` backs `Surface level="background"` (SurfaceContext.SURFACE_LEVEL_TOKEN),
|
|
955
|
+
// so it takes the ramp's `background` (shell/frame) role, NOT `inset`. The ramp's
|
|
956
|
+
// deepest `inset` plane is now promoted to `surface-inset` below (S-3) — still no
|
|
957
|
+
// `SurfaceLevel` member for it (follow-up, alongside the elevation.ts -1/-2 pressed
|
|
958
|
+
// levels — parallel workstream, not this branch).
|
|
959
|
+
"surface-base": surfaceRampDark.base,
|
|
960
|
+
// main surface (#252321, L*13.5)
|
|
961
|
+
"surface-elevated": surfaceRampDark.elevated,
|
|
962
|
+
// elevated surface (#2C2A28, L*17 — nav/rail)
|
|
963
|
+
"surface-raised": surfaceRampDark.raised,
|
|
964
|
+
// raised surface (#31302F, L*20 — cards)
|
|
965
|
+
"surface-overlay": surfaceRampDark.overlay,
|
|
966
|
+
// overlay surface (#373635, L*22.5 — hero/popover)
|
|
967
|
+
"surface-input": surfaceRampDark.elevated,
|
|
968
|
+
// input surface (#2C2A28 — one plane above base)
|
|
969
|
+
"surface-inset": surfaceRampDark.inset,
|
|
970
|
+
// deepest pressed pit (#13100D, L*4.5 — sub-shell well)
|
|
971
|
+
// Background colors — same ramp, the frame/shell end of it.
|
|
972
|
+
"background-base": surfaceRampDark.background,
|
|
973
|
+
// shell / frame (#1C1916, L*9 — Surface level="background")
|
|
974
|
+
"background-default": surfaceRampDark.base,
|
|
975
|
+
// main background (#252321, L*13.5 — matches surface-base)
|
|
976
|
+
"background-subtle": surfaceRampDark.elevated,
|
|
977
|
+
// subtle background (#2C2A28, L*17 — matches surface-elevated)
|
|
978
|
+
// Frame/bezel chrome (S-3) — the top bar + side nav shell; one step BELOW
|
|
979
|
+
// `background-base`, darker even than `surface-inset` — it exits the content
|
|
980
|
+
// ramp entirely rather than sitting in it. See `backgroundFrameDark` in
|
|
981
|
+
// primitives.ts.
|
|
982
|
+
"background-frame": backgroundFrameDark,
|
|
983
|
+
// frame / bezel (#100D0A, L*3.79)
|
|
984
|
+
// Border colors — `border-subtle` (#1C1C1C) previously collided with
|
|
985
|
+
// `surface-raised` (also #1C1C1C, an invisible border on raised cards). The
|
|
986
|
+
// re-spaced ramp above moved `surface-raised` to #2D2C2B, so this value is now
|
|
987
|
+
// distinct from every surface hex without needing to change it — see
|
|
988
|
+
// `surface.contract.test.ts` R2/R4.
|
|
939
989
|
"border-default": primitiveColors.charcoal[400],
|
|
940
990
|
// default border
|
|
941
991
|
"border-subtle": primitiveColors.charcoal[500],
|
|
@@ -949,6 +999,14 @@ var semanticColorsDark = {
|
|
|
949
999
|
"border-input-hover": primitiveColors.neutral[500],
|
|
950
1000
|
"border-input-focus": "#828DF8",
|
|
951
1001
|
"border-input-error": primitiveColors.red[500],
|
|
1002
|
+
// Alpha hairline separators — the primary separation cue (§4/S-2). Self-
|
|
1003
|
+
// normalizing: composites toward white by ~the same amount on ANY plane, so
|
|
1004
|
+
// one family works at every elevation instead of per-surface border tokens.
|
|
1005
|
+
// Shadows are demoted to floating-overlay use only (not shipped as a fill
|
|
1006
|
+
// separator here — see elevation.ts, follow-up S-4).
|
|
1007
|
+
"hairline-subtle": "rgba(255, 255, 255, 0.06)",
|
|
1008
|
+
"hairline-default": "rgba(255, 255, 255, 0.09)",
|
|
1009
|
+
"hairline-strong": "rgba(255, 255, 255, 0.14)",
|
|
952
1010
|
// Interactive states
|
|
953
1011
|
"interactive-hover": "rgba(255, 255, 255, 0.04)",
|
|
954
1012
|
"interactive-focus": "rgba(255, 255, 255, 0.12)",
|
|
@@ -1478,9 +1536,11 @@ var lightThemeCSSVars = {
|
|
|
1478
1536
|
"--color-surface-base": semanticColorsLight["surface-base"],
|
|
1479
1537
|
"--color-surface-elevated": semanticColorsLight["surface-elevated"],
|
|
1480
1538
|
"--color-surface-raised": semanticColorsLight["surface-raised"],
|
|
1539
|
+
"--color-surface-inset": semanticColorsLight["surface-inset"],
|
|
1481
1540
|
"--color-background-base": semanticColorsLight["background-base"],
|
|
1482
1541
|
"--color-background-default": semanticColorsLight["background-default"],
|
|
1483
1542
|
"--color-background-subtle": semanticColorsLight["background-subtle"],
|
|
1543
|
+
"--color-background-frame": semanticColorsLight["background-frame"],
|
|
1484
1544
|
"--color-border-default": semanticColorsLight["border-default"],
|
|
1485
1545
|
"--color-border-subtle": semanticColorsLight["border-subtle"],
|
|
1486
1546
|
"--color-border-strong": semanticColorsLight["border-strong"],
|
|
@@ -1537,6 +1597,9 @@ var lightThemeCSSVars = {
|
|
|
1537
1597
|
"--color-border-input-hover": semanticColorsLight["border-input-hover"],
|
|
1538
1598
|
"--color-border-input-focus": semanticColorsLight["border-input-focus"],
|
|
1539
1599
|
"--color-border-input-error": semanticColorsLight["border-input-error"],
|
|
1600
|
+
"--color-hairline-subtle": semanticColorsLight["hairline-subtle"],
|
|
1601
|
+
"--color-hairline-default": semanticColorsLight["hairline-default"],
|
|
1602
|
+
"--color-hairline-strong": semanticColorsLight["hairline-strong"],
|
|
1540
1603
|
"--color-interactive-disabled-text": semanticColorsLight["interactive-disabled-text"],
|
|
1541
1604
|
"--color-avatar-background": semanticColorsLight["avatar-background"],
|
|
1542
1605
|
"--color-avatar-text": semanticColorsLight["avatar-text"],
|
|
@@ -1576,9 +1639,11 @@ var darkThemeCSSVars = {
|
|
|
1576
1639
|
"--color-surface-base": semanticColorsDark["surface-base"],
|
|
1577
1640
|
"--color-surface-elevated": semanticColorsDark["surface-elevated"],
|
|
1578
1641
|
"--color-surface-raised": semanticColorsDark["surface-raised"],
|
|
1642
|
+
"--color-surface-inset": semanticColorsDark["surface-inset"],
|
|
1579
1643
|
"--color-background-base": semanticColorsDark["background-base"],
|
|
1580
1644
|
"--color-background-default": semanticColorsDark["background-default"],
|
|
1581
1645
|
"--color-background-subtle": semanticColorsDark["background-subtle"],
|
|
1646
|
+
"--color-background-frame": semanticColorsDark["background-frame"],
|
|
1582
1647
|
"--color-border-default": semanticColorsDark["border-default"],
|
|
1583
1648
|
"--color-border-subtle": semanticColorsDark["border-subtle"],
|
|
1584
1649
|
"--color-border-strong": semanticColorsDark["border-strong"],
|
|
@@ -1635,6 +1700,9 @@ var darkThemeCSSVars = {
|
|
|
1635
1700
|
"--color-border-input-hover": semanticColorsDark["border-input-hover"],
|
|
1636
1701
|
"--color-border-input-focus": semanticColorsDark["border-input-focus"],
|
|
1637
1702
|
"--color-border-input-error": semanticColorsDark["border-input-error"],
|
|
1703
|
+
"--color-hairline-subtle": semanticColorsDark["hairline-subtle"],
|
|
1704
|
+
"--color-hairline-default": semanticColorsDark["hairline-default"],
|
|
1705
|
+
"--color-hairline-strong": semanticColorsDark["hairline-strong"],
|
|
1638
1706
|
"--color-interactive-disabled-text": semanticColorsDark["interactive-disabled-text"],
|
|
1639
1707
|
"--color-avatar-background": semanticColorsDark["avatar-background"],
|
|
1640
1708
|
"--color-avatar-text": semanticColorsDark["avatar-text"],
|
|
@@ -2269,6 +2337,10 @@ function getElevationShadow(baseColor, level, theme, isHovered = false) {
|
|
|
2269
2337
|
elevationShadowCache.set(cacheKey, result);
|
|
2270
2338
|
return result;
|
|
2271
2339
|
}
|
|
2340
|
+
var PRESSED_ELEVATION_LEVEL = -1;
|
|
2341
|
+
function getPressedRecessShadow(fillColor, theme) {
|
|
2342
|
+
return getElevationShadow(fillColor, PRESSED_ELEVATION_LEVEL, theme);
|
|
2343
|
+
}
|
|
2272
2344
|
function getBaseSurfaceColor(theme) {
|
|
2273
2345
|
return theme === "light" ? semanticColorsLight["surface-raised"] : semanticColorsDark["surface-elevated"];
|
|
2274
2346
|
}
|
|
@@ -2571,6 +2643,7 @@ var SURFACE_LEVEL_TOKEN = {
|
|
|
2571
2643
|
raised: "surface-raised",
|
|
2572
2644
|
overlay: "surface-overlay"
|
|
2573
2645
|
};
|
|
2646
|
+
var PRESSED_RAMP = ["inset", "background", "base", "elevated", "raised", "overlay"];
|
|
2574
2647
|
var ON_SURFACE_TOKEN = {
|
|
2575
2648
|
primary: "text-primary",
|
|
2576
2649
|
secondary: "text-secondary",
|
|
@@ -2595,9 +2668,19 @@ function onSurfaceColors(mode) {
|
|
|
2595
2668
|
function useOnSurfaceColor(role = "primary") {
|
|
2596
2669
|
return getSemanticColors(useSurfaceMode())[ON_SURFACE_TOKEN[role]];
|
|
2597
2670
|
}
|
|
2671
|
+
function insetFloor(mode) {
|
|
2672
|
+
const colors = getSemanticColors(mode);
|
|
2673
|
+
return colors["surface-inset"] ?? surfaceRampDark.inset;
|
|
2674
|
+
}
|
|
2598
2675
|
function surfaceBackground(level, mode) {
|
|
2676
|
+
if (level === "inset") return insetFloor(mode);
|
|
2599
2677
|
return getSemanticColors(mode)[SURFACE_LEVEL_TOKEN[level]];
|
|
2600
2678
|
}
|
|
2679
|
+
function pressedLevel(parent) {
|
|
2680
|
+
const index = PRESSED_RAMP.indexOf(parent);
|
|
2681
|
+
const down = Math.max(0, index - 1);
|
|
2682
|
+
return PRESSED_RAMP[down];
|
|
2683
|
+
}
|
|
2601
2684
|
function getTheme() {
|
|
2602
2685
|
if (typeof document === "undefined") return "dark";
|
|
2603
2686
|
return document.documentElement.classList.contains("light") ? "light" : "dark";
|
|
@@ -5296,7 +5379,7 @@ function ToastProvider({
|
|
|
5296
5379
|
return id;
|
|
5297
5380
|
}, [defaultDuration, maxToasts]);
|
|
5298
5381
|
const removeToast = React25.useCallback((id) => {
|
|
5299
|
-
setToasts((prev) => prev.filter((
|
|
5382
|
+
setToasts((prev) => prev.filter((t26) => t26.id !== id));
|
|
5300
5383
|
}, []);
|
|
5301
5384
|
const removeAllToasts = React25.useCallback(() => {
|
|
5302
5385
|
setToasts([]);
|
|
@@ -6049,6 +6132,7 @@ function IconBox({
|
|
|
6049
6132
|
function Surface({
|
|
6050
6133
|
elevation = 0,
|
|
6051
6134
|
level,
|
|
6135
|
+
pressed = false,
|
|
6052
6136
|
glowColor,
|
|
6053
6137
|
glowIntensity,
|
|
6054
6138
|
theme,
|
|
@@ -6062,13 +6146,24 @@ function Surface({
|
|
|
6062
6146
|
const mode = theme ?? inherited.mode;
|
|
6063
6147
|
const isPlane = level != null;
|
|
6064
6148
|
const baseColor = getBaseSurfaceColor(mode);
|
|
6065
|
-
const
|
|
6066
|
-
|
|
6149
|
+
const resolvedLevel = pressed ? pressedLevel(inherited.level) : level ?? inherited.level;
|
|
6150
|
+
let backgroundColor;
|
|
6151
|
+
let shadowStyle;
|
|
6152
|
+
if (pressed) {
|
|
6153
|
+
backgroundColor = surfaceBackground(resolvedLevel, mode);
|
|
6154
|
+
shadowStyle = getPressedRecessShadow(backgroundColor, mode);
|
|
6155
|
+
} else if (isPlane) {
|
|
6156
|
+
backgroundColor = surfaceBackground(level, mode);
|
|
6157
|
+
shadowStyle = {};
|
|
6158
|
+
} else {
|
|
6159
|
+
backgroundColor = getElevationSurface(baseColor, elevation, mode);
|
|
6160
|
+
shadowStyle = getElevationShadow(baseColor, elevation, mode);
|
|
6161
|
+
}
|
|
6067
6162
|
const glowStyle = glowColor ? getGlowShadow(glowColor, glowIntensity) : {};
|
|
6068
|
-
const applyRounded = rounded ?? !isPlane;
|
|
6163
|
+
const applyRounded = rounded ?? (pressed || !isPlane);
|
|
6069
6164
|
const value = React25.useMemo(
|
|
6070
|
-
() => ({ mode, level:
|
|
6071
|
-
[mode,
|
|
6165
|
+
() => ({ mode, level: resolvedLevel }),
|
|
6166
|
+
[mode, resolvedLevel]
|
|
6072
6167
|
);
|
|
6073
6168
|
return /* @__PURE__ */ jsx(SurfaceContext.Provider, { value, children: /* @__PURE__ */ jsx(
|
|
6074
6169
|
reactNative.View,
|
|
@@ -7261,13 +7356,14 @@ function MetricGroup({
|
|
|
7261
7356
|
)
|
|
7262
7357
|
] }, i)) });
|
|
7263
7358
|
}
|
|
7359
|
+
var t = getSemanticColors("dark");
|
|
7264
7360
|
var baseBadgeSizeConfig = {
|
|
7265
7361
|
sm: { fontSize: 9, paddingH: 6, paddingV: 2, iconSize: 10 },
|
|
7266
7362
|
md: { fontSize: 10, paddingH: 8, paddingV: 2, iconSize: 12 },
|
|
7267
7363
|
lg: { fontSize: 12, paddingH: 10, paddingV: 4, iconSize: 14 }
|
|
7268
7364
|
};
|
|
7269
7365
|
var variantColors = {
|
|
7270
|
-
plain: { backgroundColor: "
|
|
7366
|
+
plain: { backgroundColor: t["surface-raised"], borderColor: "#1F1F1F" },
|
|
7271
7367
|
pr: {
|
|
7272
7368
|
backgroundColor: "rgba(255, 121, 0, 0.12)",
|
|
7273
7369
|
borderColor: "rgba(255, 121, 0, 0.3)"
|
|
@@ -7507,11 +7603,11 @@ function PrBadge({
|
|
|
7507
7603
|
}
|
|
7508
7604
|
return badge;
|
|
7509
7605
|
}
|
|
7510
|
-
var
|
|
7606
|
+
var t2 = getSemanticColors("dark");
|
|
7511
7607
|
var solidVariantColors = {
|
|
7512
|
-
success:
|
|
7513
|
-
warning:
|
|
7514
|
-
error:
|
|
7608
|
+
success: t2["status-success"],
|
|
7609
|
+
warning: t2["status-warning"],
|
|
7610
|
+
error: t2["status-error"],
|
|
7515
7611
|
neutral: "#6B7280"
|
|
7516
7612
|
};
|
|
7517
7613
|
var ringVariantStyles = {
|
|
@@ -7551,8 +7647,8 @@ var iconColors2 = {
|
|
|
7551
7647
|
warning: "#6B4000",
|
|
7552
7648
|
error: "#5C1A1A",
|
|
7553
7649
|
neutral: "#D1D5DB",
|
|
7554
|
-
"on-track":
|
|
7555
|
-
deviation:
|
|
7650
|
+
"on-track": t2["status-success"],
|
|
7651
|
+
deviation: t2["status-warning"],
|
|
7556
7652
|
future: "#6B7280"
|
|
7557
7653
|
};
|
|
7558
7654
|
var sizeDimensions = {
|
|
@@ -7873,19 +7969,19 @@ function MetricCell({ children, color, fontSize = 11 }) {
|
|
|
7873
7969
|
}
|
|
7874
7970
|
|
|
7875
7971
|
// src/components/custom/Workout/TempoDisplay.tsx
|
|
7876
|
-
var
|
|
7972
|
+
var t3 = getSemanticColors("dark");
|
|
7877
7973
|
var INTER = "Inter, sans-serif";
|
|
7878
7974
|
var TEXT_TERTIARY = "#6B7280";
|
|
7879
|
-
var STATUS_ERROR =
|
|
7880
|
-
var STATUS_SUCCESS =
|
|
7881
|
-
var STATUS_WARNING =
|
|
7975
|
+
var STATUS_ERROR = t3["status-error"];
|
|
7976
|
+
var STATUS_SUCCESS = t3["status-success"];
|
|
7977
|
+
var STATUS_WARNING = t3["status-warning"];
|
|
7882
7978
|
var ON_TARGET_MS = 100;
|
|
7883
7979
|
function getTempoFillPct(elapsedMs, targetMs) {
|
|
7884
7980
|
if (!targetMs) return 100;
|
|
7885
7981
|
return Math.min(100, elapsedMs / targetMs * 100);
|
|
7886
7982
|
}
|
|
7887
7983
|
function activeNumberTone(elapsedMs, targetMs) {
|
|
7888
|
-
if (targetMs == null) return
|
|
7984
|
+
if (targetMs == null) return t3["text-primary"];
|
|
7889
7985
|
const remainingMs = targetMs - elapsedMs;
|
|
7890
7986
|
if (remainingMs > ON_TARGET_MS) return STATUS_WARNING;
|
|
7891
7987
|
if (remainingMs >= -ON_TARGET_MS) return STATUS_SUCCESS;
|
|
@@ -8037,7 +8133,7 @@ function TempoDisplay({
|
|
|
8037
8133
|
style: {
|
|
8038
8134
|
flexDirection: "row",
|
|
8039
8135
|
alignItems: "center",
|
|
8040
|
-
backgroundColor: "
|
|
8136
|
+
backgroundColor: t3["surface-raised"],
|
|
8041
8137
|
paddingHorizontal: chromePadX,
|
|
8042
8138
|
paddingVertical: chromePadY,
|
|
8043
8139
|
borderRadius: chromeRadius
|
|
@@ -8052,7 +8148,7 @@ function TempoDisplay({
|
|
|
8052
8148
|
fontSize: labelFont,
|
|
8053
8149
|
fontWeight: "500",
|
|
8054
8150
|
// Live-muted green while a rep is running, tertiary at rest.
|
|
8055
|
-
color: live ?
|
|
8151
|
+
color: live ? t3["status-live-muted"] : TEXT_TERTIARY,
|
|
8056
8152
|
letterSpacing: 0.5,
|
|
8057
8153
|
textTransform: "uppercase",
|
|
8058
8154
|
marginRight: Math.round(fontSize * 0.5)
|
|
@@ -8110,7 +8206,7 @@ function TempoDisplay({
|
|
|
8110
8206
|
reactNative.View,
|
|
8111
8207
|
{
|
|
8112
8208
|
style: {
|
|
8113
|
-
backgroundColor: "
|
|
8209
|
+
backgroundColor: t3["surface-overlay"],
|
|
8114
8210
|
borderRadius: 6,
|
|
8115
8211
|
paddingVertical: 8,
|
|
8116
8212
|
paddingHorizontal: 12,
|
|
@@ -8163,13 +8259,13 @@ function TempoDisplay({
|
|
|
8163
8259
|
}
|
|
8164
8260
|
);
|
|
8165
8261
|
}
|
|
8166
|
-
var
|
|
8262
|
+
var t4 = getSemanticColors("dark");
|
|
8167
8263
|
function getDotColor(deviation) {
|
|
8168
8264
|
const abs = Math.abs(deviation);
|
|
8169
|
-
if (deviation < -0.3) return
|
|
8265
|
+
if (deviation < -0.3) return t4["status-success"];
|
|
8170
8266
|
if (abs <= 0.3) return "#6B7280";
|
|
8171
|
-
if (abs <= 0.7) return
|
|
8172
|
-
return
|
|
8267
|
+
if (abs <= 0.7) return t4["status-warning"];
|
|
8268
|
+
return t4["status-error"];
|
|
8173
8269
|
}
|
|
8174
8270
|
function getDeviationDescription(deviation) {
|
|
8175
8271
|
if (deviation < -0.3) return "lighter than planned";
|
|
@@ -8237,19 +8333,19 @@ function DeviationBar({
|
|
|
8237
8333
|
}
|
|
8238
8334
|
);
|
|
8239
8335
|
}
|
|
8240
|
-
var
|
|
8336
|
+
var t5 = getSemanticColors("dark");
|
|
8241
8337
|
var TRACK_BG = "#333333";
|
|
8242
8338
|
var LABEL_COLOR = "#6B7280";
|
|
8243
8339
|
var TARGET_LINE_COLOR = "rgba(33, 150, 243, 0.5)";
|
|
8244
8340
|
var AT_TARGET_GLOW = "0 0 5px 1px rgba(33, 150, 243, 0.35), 0 0 10px 3px rgba(33, 150, 243, 0.15)";
|
|
8245
8341
|
var ZONE = {
|
|
8246
|
-
building:
|
|
8342
|
+
building: t5["status-success"],
|
|
8247
8343
|
// below target ramp-up
|
|
8248
|
-
approaching:
|
|
8344
|
+
approaching: t5["status-warning"],
|
|
8249
8345
|
// nearing target
|
|
8250
|
-
target:
|
|
8346
|
+
target: t5["brand-primary"],
|
|
8251
8347
|
// exactly at target
|
|
8252
|
-
over1:
|
|
8348
|
+
over1: t5["status-error"],
|
|
8253
8349
|
// mild over-target
|
|
8254
8350
|
over2: WORKOUT_TOKENS.intensity.over2,
|
|
8255
8351
|
// moderate over-target
|
|
@@ -8418,12 +8514,12 @@ var MESO_ACCENT_GRADIENT_LIGHT = primitiveRamps.orange[300];
|
|
|
8418
8514
|
var WORKOUT_PILL_DELOAD = primitiveRamps.magenta[600];
|
|
8419
8515
|
|
|
8420
8516
|
// src/components/custom/Workout/WorkoutPill.tsx
|
|
8421
|
-
var
|
|
8517
|
+
var t6 = getSemanticColors("dark");
|
|
8422
8518
|
var statusBgColors = {
|
|
8423
8519
|
completed: "rgba(46,213,115,0.15)",
|
|
8424
8520
|
current: "rgba(255,121,0,0.15)",
|
|
8425
8521
|
next: "transparent",
|
|
8426
|
-
upcoming: "
|
|
8522
|
+
upcoming: t6["surface-raised"],
|
|
8427
8523
|
missed: "rgba(209,67,67,0.1)",
|
|
8428
8524
|
deload: "rgba(186,41,150,0.15)"
|
|
8429
8525
|
};
|
|
@@ -8436,9 +8532,9 @@ var statusBorderStyles = {
|
|
|
8436
8532
|
deload: { borderWidth: 1, borderColor: "rgba(186,41,150,0.3)" }
|
|
8437
8533
|
};
|
|
8438
8534
|
var textColors = {
|
|
8439
|
-
completed:
|
|
8440
|
-
current:
|
|
8441
|
-
next:
|
|
8535
|
+
completed: t6["status-success"],
|
|
8536
|
+
current: t6["brand-primary"],
|
|
8537
|
+
next: t6["brand-primary"],
|
|
8442
8538
|
upcoming: "#6B7280",
|
|
8443
8539
|
missed: "rgba(209,67,67,0.7)",
|
|
8444
8540
|
deload: WORKOUT_PILL_DELOAD
|
|
@@ -8571,257 +8667,412 @@ function WorkoutPill({
|
|
|
8571
8667
|
}
|
|
8572
8668
|
return pill;
|
|
8573
8669
|
}
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
const
|
|
8670
|
+
|
|
8671
|
+
// src/theme/bar-paper.ts
|
|
8672
|
+
function grainOpacityForColor(hex) {
|
|
8673
|
+
const h = hex.replace("#", "");
|
|
8674
|
+
const full = h.length === 3 ? h.split("").map((ch) => ch + ch).join("") : h;
|
|
8675
|
+
const r = parseInt(full.slice(0, 2), 16);
|
|
8676
|
+
const g = parseInt(full.slice(2, 4), 16);
|
|
8677
|
+
const b = parseInt(full.slice(4, 6), 16);
|
|
8678
|
+
if ([r, g, b].some(Number.isNaN)) return 0.14;
|
|
8679
|
+
const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
8680
|
+
return Math.min(0.24, Math.max(0.04, 0.02 + 0.31 * lum));
|
|
8681
|
+
}
|
|
8682
|
+
function barGrain(color) {
|
|
8683
|
+
const op = grainOpacityForColor(color).toFixed(3);
|
|
8684
|
+
return `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)' opacity='${op}'/%3E%3C/svg%3E")`;
|
|
8685
|
+
}
|
|
8686
|
+
function barPaper(color, flip = false) {
|
|
8687
|
+
return {
|
|
8688
|
+
backgroundImage: barGrain(color),
|
|
8689
|
+
boxShadow: flip ? "0 -6px 16px rgba(0,0,0,0.45)" : "inset 0 1.5px 0 rgba(255,255,255,0.22), 0 6px 16px rgba(0,0,0,0.45)"
|
|
8690
|
+
};
|
|
8691
|
+
}
|
|
8692
|
+
var ANIMATION_EASING = reactNative.Easing.bezier(0.22, 1, 0.36, 1);
|
|
8693
|
+
var PEAK_OVERSHOOT = 1.12;
|
|
8694
|
+
function getReducedMotionPreference() {
|
|
8695
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
8696
|
+
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
8697
|
+
}
|
|
8698
|
+
function usePrefersReducedMotion() {
|
|
8699
|
+
const [reduced, setReduced] = React25.useState(getReducedMotionPreference);
|
|
8579
8700
|
React25.useEffect(() => {
|
|
8580
|
-
if (
|
|
8581
|
-
const
|
|
8701
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
8702
|
+
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
8703
|
+
const handler = () => setReduced(mq.matches);
|
|
8704
|
+
handler();
|
|
8705
|
+
mq.addEventListener?.("change", handler);
|
|
8706
|
+
return () => mq.removeEventListener?.("change", handler);
|
|
8707
|
+
}, []);
|
|
8708
|
+
return reduced;
|
|
8709
|
+
}
|
|
8710
|
+
function useLiveRepGrowth(active, liveRepIndex, liveVelocity, isNewPeak) {
|
|
8711
|
+
const prefersReducedMotion = usePrefersReducedMotion();
|
|
8712
|
+
const [liveGrowth] = React25.useState(() => new reactNative.Animated.Value(1));
|
|
8713
|
+
React25.useEffect(() => {
|
|
8714
|
+
if (liveRepIndex == null || liveVelocity == null) return;
|
|
8715
|
+
if (prefersReducedMotion) {
|
|
8716
|
+
liveGrowth.setValue(1);
|
|
8717
|
+
return;
|
|
8718
|
+
}
|
|
8719
|
+
liveGrowth.setValue(0);
|
|
8720
|
+
if (isNewPeak) {
|
|
8582
8721
|
reactNative.Animated.sequence([
|
|
8583
|
-
reactNative.Animated.timing(
|
|
8584
|
-
toValue:
|
|
8585
|
-
duration:
|
|
8586
|
-
easing: reactNative.Easing.
|
|
8722
|
+
reactNative.Animated.timing(liveGrowth, {
|
|
8723
|
+
toValue: PEAK_OVERSHOOT,
|
|
8724
|
+
duration: 220,
|
|
8725
|
+
easing: reactNative.Easing.out(reactNative.Easing.quad),
|
|
8587
8726
|
useNativeDriver: false
|
|
8588
8727
|
}),
|
|
8589
|
-
reactNative.Animated.
|
|
8590
|
-
toValue:
|
|
8591
|
-
|
|
8592
|
-
|
|
8728
|
+
reactNative.Animated.spring(liveGrowth, {
|
|
8729
|
+
toValue: 1,
|
|
8730
|
+
friction: 5,
|
|
8731
|
+
tension: 140,
|
|
8593
8732
|
useNativeDriver: false
|
|
8594
8733
|
})
|
|
8595
|
-
])
|
|
8734
|
+
]).start();
|
|
8735
|
+
} else {
|
|
8736
|
+
reactNative.Animated.timing(liveGrowth, {
|
|
8737
|
+
toValue: 1,
|
|
8738
|
+
duration: 300,
|
|
8739
|
+
easing: ANIMATION_EASING,
|
|
8740
|
+
useNativeDriver: false
|
|
8741
|
+
}).start();
|
|
8742
|
+
}
|
|
8743
|
+
}, [active, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveGrowth]);
|
|
8744
|
+
return liveGrowth;
|
|
8745
|
+
}
|
|
8746
|
+
|
|
8747
|
+
// src/components/custom/charts/SetBarChart.tsx
|
|
8748
|
+
function mixHex(a, b, t26) {
|
|
8749
|
+
const parse = (h) => {
|
|
8750
|
+
const s = h.replace("#", "");
|
|
8751
|
+
return [parseInt(s.slice(0, 2), 16), parseInt(s.slice(2, 4), 16), parseInt(s.slice(4, 6), 16)];
|
|
8752
|
+
};
|
|
8753
|
+
const [ar, ag, ab] = parse(a);
|
|
8754
|
+
const [br, bg, bb] = parse(b);
|
|
8755
|
+
const ch = (x, y) => Math.round(x + (y - x) * t26).toString(16).padStart(2, "0");
|
|
8756
|
+
return `#${ch(ar, br)}${ch(ag, bg)}${ch(ab, bb)}`;
|
|
8757
|
+
}
|
|
8758
|
+
var VARIABLE_FILL = primitiveRamps.cyan[900];
|
|
8759
|
+
var CONTINUE_OUTLINE = primitiveRamps.cyan[800];
|
|
8760
|
+
var FIXED_MAX_VALUE = 1.15;
|
|
8761
|
+
var PEAK_HEADROOM = 1.03;
|
|
8762
|
+
function scaleDenominator(scale, maxValue) {
|
|
8763
|
+
return scale === "fixed" ? FIXED_MAX_VALUE : maxValue * PEAK_HEADROOM;
|
|
8764
|
+
}
|
|
8765
|
+
function valueLabelFontSize(height) {
|
|
8766
|
+
return Math.round(Math.max(8, Math.min(12, height * 0.11)));
|
|
8767
|
+
}
|
|
8768
|
+
var SET_BAR_DEFAULT_HEIGHT = 220;
|
|
8769
|
+
var LABEL_ROW_HEIGHT = 16;
|
|
8770
|
+
var LABEL_GAP = 3;
|
|
8771
|
+
var GAP_RATIO = 0.08;
|
|
8772
|
+
var MIN_BAR_GAP = 2;
|
|
8773
|
+
var CHUNK_NOTCH_RATIO = 0.25;
|
|
8774
|
+
var CHUNK_NOTCH_MIN_PX = 10;
|
|
8775
|
+
var BAR_MAX_WIDTH = 120;
|
|
8776
|
+
var LABEL_MIN_BAR_WIDTH = 30;
|
|
8777
|
+
var DEFAULT_BAR_RADIUS = 5;
|
|
8778
|
+
var MIN_BAR_HEIGHT = 4;
|
|
8779
|
+
var FLAT_BAR_FRACTION = 0.5;
|
|
8780
|
+
var STUB_HEIGHT = 9;
|
|
8781
|
+
var REP_GAP = 2;
|
|
8782
|
+
function computeBarLayout(plotWidth, count, gapRatio = GAP_RATIO) {
|
|
8783
|
+
const rawBarWidth = plotWidth > 0 && count > 0 ? plotWidth / (count + (count - 1) * gapRatio) : BAR_MAX_WIDTH;
|
|
8784
|
+
const barWidth = Math.min(BAR_MAX_WIDTH, rawBarWidth);
|
|
8785
|
+
const gap = plotWidth === 0 ? MIN_BAR_GAP : Math.max(MIN_BAR_GAP, gapRatio * barWidth);
|
|
8786
|
+
return { barWidth, gap };
|
|
8787
|
+
}
|
|
8788
|
+
function isRep(slot) {
|
|
8789
|
+
return slot.kind === "rep";
|
|
8790
|
+
}
|
|
8791
|
+
function repIndexByCell(cells) {
|
|
8792
|
+
let count = 0;
|
|
8793
|
+
return cells.map((slot) => isRep(slot) ? count++ : -1);
|
|
8794
|
+
}
|
|
8795
|
+
function SetBarChart({
|
|
8796
|
+
slots,
|
|
8797
|
+
colorFor,
|
|
8798
|
+
height,
|
|
8799
|
+
scale = "peak",
|
|
8800
|
+
scaleMax,
|
|
8801
|
+
orientation = "up",
|
|
8802
|
+
liveRepIndex,
|
|
8803
|
+
isNewPeak = false,
|
|
8804
|
+
targetReps,
|
|
8805
|
+
gapRatio = GAP_RATIO,
|
|
8806
|
+
barRadius = DEFAULT_BAR_RADIUS,
|
|
8807
|
+
cornerStyle = "top",
|
|
8808
|
+
flat = false,
|
|
8809
|
+
showValueLabels: showValueLabelsProp = false,
|
|
8810
|
+
formatValue: formatValue3 = String,
|
|
8811
|
+
todoVariant = "solid",
|
|
8812
|
+
minColumns,
|
|
8813
|
+
renderReference,
|
|
8814
|
+
renderBarOverlay,
|
|
8815
|
+
expandProgress,
|
|
8816
|
+
hideBaseline = false,
|
|
8817
|
+
testID,
|
|
8818
|
+
testIDPrefix = "setbar",
|
|
8819
|
+
accessibilityLabel: accessibilityLabel2,
|
|
8820
|
+
label,
|
|
8821
|
+
className,
|
|
8822
|
+
viewProps = {}
|
|
8823
|
+
}) {
|
|
8824
|
+
const targetPadded = Math.max(slots.length, targetReps ?? 0, minColumns ?? 0);
|
|
8825
|
+
const cells = targetPadded > slots.length ? [
|
|
8826
|
+
...slots,
|
|
8827
|
+
...Array.from({ length: targetPadded - slots.length }, () => ({ kind: "todo" }))
|
|
8828
|
+
] : slots;
|
|
8829
|
+
const repValues = cells.filter(isRep).map((s) => s.value ?? 0);
|
|
8830
|
+
const best = repValues.length > 0 ? Math.max(...repValues) : 0;
|
|
8831
|
+
const flip = orientation === "down";
|
|
8832
|
+
const flipStyle = flip ? { transform: [{ scaleY: -1 }] } : null;
|
|
8833
|
+
const placeholderColor = useOnSurfaceColor("tertiary");
|
|
8834
|
+
const surface = useSurface();
|
|
8835
|
+
const surfaceBg = surfaceBackground(surface.level, surface.mode);
|
|
8836
|
+
const solidTodoColor = mixHex(surfaceBg, placeholderColor, 0.55);
|
|
8837
|
+
const emptyColor = mixHex(surfaceBg, placeholderColor, 0.28);
|
|
8838
|
+
const barCorners = cornerStyle === "all" ? { borderRadius: barRadius } : { borderTopLeftRadius: barRadius, borderTopRightRadius: barRadius };
|
|
8839
|
+
const scaleDenom = scaleMax != null && scaleMax > 0 ? scaleMax * PEAK_HEADROOM : scaleDenominator(scale, best);
|
|
8840
|
+
const showValueLabels = showValueLabelsProp && !flat;
|
|
8841
|
+
const plotHeight = Math.max(0, height - (showValueLabels ? LABEL_ROW_HEIGHT + LABEL_GAP : 0));
|
|
8842
|
+
const yOf = (value) => scaleDenom > 0 ? value / scaleDenom * plotHeight : 0;
|
|
8843
|
+
const flatBarHeight = Math.max(MIN_BAR_HEIGHT, FLAT_BAR_FRACTION * plotHeight);
|
|
8844
|
+
const compactBarHeight = Math.max(MIN_BAR_HEIGHT, plotHeight);
|
|
8845
|
+
const valueBarHeight = (value) => scaleDenom > 0 ? Math.max(MIN_BAR_HEIGHT, Math.min(1, value / scaleDenom) * plotHeight) : MIN_BAR_HEIGHT;
|
|
8846
|
+
const barHeight = (value) => flat ? compactBarHeight : valueBarHeight(value);
|
|
8847
|
+
const liveValue = liveRepIndex != null && liveRepIndex < repValues.length ? repValues[liveRepIndex] : void 0;
|
|
8848
|
+
const liveGrowth = useLiveRepGrowth(true, liveRepIndex, liveValue, isNewPeak);
|
|
8849
|
+
const [plotW, setPlotW] = React25.useState(0);
|
|
8850
|
+
const onPlotLayout = (e) => setPlotW(e.nativeEvent.layout.width);
|
|
8851
|
+
const totalCells = cells.length;
|
|
8852
|
+
const { barWidth, gap } = computeBarLayout(plotW, totalCells, gapRatio);
|
|
8853
|
+
const labelsCrowded = plotW > 0 && barWidth < LABEL_MIN_BAR_WIDTH;
|
|
8854
|
+
const peakRepIndex = repValues.reduce((b, v, i) => v > repValues[b] ? i : b, 0);
|
|
8855
|
+
const showBarLabel = (repIndex) => !labelsCrowded || repIndex === peakRepIndex || repIndex === liveRepIndex;
|
|
8856
|
+
const geometry = { scaleDenom, plotHeight, yOf, best, flip };
|
|
8857
|
+
const { style: externalStyle, ...restProps } = viewProps;
|
|
8858
|
+
const a11y = accessibilityLabel2 != null ? { accessibilityRole: "image", accessibilityLabel: accessibilityLabel2 } : {};
|
|
8859
|
+
const repIndices = repIndexByCell(cells);
|
|
8860
|
+
return /* @__PURE__ */ jsxs(
|
|
8861
|
+
reactNative.View,
|
|
8862
|
+
{
|
|
8863
|
+
className,
|
|
8864
|
+
style: [{ height, flexDirection: "row" }, externalStyle],
|
|
8865
|
+
testID,
|
|
8866
|
+
...a11y,
|
|
8867
|
+
...restProps,
|
|
8868
|
+
children: [
|
|
8869
|
+
label ? /* @__PURE__ */ jsx(
|
|
8870
|
+
ChartSideRail,
|
|
8871
|
+
{
|
|
8872
|
+
sections: [{ label, testID: `${testIDPrefix}-side-label` }],
|
|
8873
|
+
sectionExtent: height
|
|
8874
|
+
}
|
|
8875
|
+
) : null,
|
|
8876
|
+
/* @__PURE__ */ jsxs(
|
|
8877
|
+
reactNative.View,
|
|
8878
|
+
{
|
|
8879
|
+
onLayout: onPlotLayout,
|
|
8880
|
+
style: {
|
|
8881
|
+
flex: 1,
|
|
8882
|
+
flexDirection: "row",
|
|
8883
|
+
alignItems: "flex-end",
|
|
8884
|
+
gap,
|
|
8885
|
+
position: "relative",
|
|
8886
|
+
borderBottomWidth: hideBaseline ? 0 : 2,
|
|
8887
|
+
borderBottomColor: placeholderColor,
|
|
8888
|
+
...flipStyle
|
|
8889
|
+
},
|
|
8890
|
+
children: [
|
|
8891
|
+
renderReference?.(geometry),
|
|
8892
|
+
cells.map((slot, i) => {
|
|
8893
|
+
const isChunkBoundary = (slot.leadingGap ?? REP_GAP) > REP_GAP;
|
|
8894
|
+
const extraGap = isChunkBoundary ? Math.max(CHUNK_NOTCH_MIN_PX, CHUNK_NOTCH_RATIO * barWidth) : 0;
|
|
8895
|
+
const column = {
|
|
8896
|
+
flex: 1,
|
|
8897
|
+
maxWidth: BAR_MAX_WIDTH,
|
|
8898
|
+
height: "100%",
|
|
8899
|
+
alignItems: "center",
|
|
8900
|
+
justifyContent: "flex-end",
|
|
8901
|
+
marginLeft: extraGap
|
|
8902
|
+
};
|
|
8903
|
+
if (!isRep(slot)) {
|
|
8904
|
+
return /* @__PURE__ */ jsx(reactNative.View, { accessibilityElementsHidden: true, style: column, children: renderStub(
|
|
8905
|
+
slot.kind,
|
|
8906
|
+
barCorners,
|
|
8907
|
+
// Flat (compact) stubs fill the plot like the compact bars; else the fixed short stub.
|
|
8908
|
+
flat ? compactBarHeight : STUB_HEIGHT,
|
|
8909
|
+
placeholderColor,
|
|
8910
|
+
testIDPrefix,
|
|
8911
|
+
todoVariant,
|
|
8912
|
+
solidTodoColor,
|
|
8913
|
+
emptyColor
|
|
8914
|
+
) }, i);
|
|
8915
|
+
}
|
|
8916
|
+
const repIndex = repIndices[i];
|
|
8917
|
+
const value = slot.value ?? 0;
|
|
8918
|
+
const isLive = liveRepIndex === repIndex;
|
|
8919
|
+
const color = colorFor(value);
|
|
8920
|
+
const barHeightStyle = expandProgress ? expandProgress.interpolate({
|
|
8921
|
+
inputRange: [0, 1],
|
|
8922
|
+
outputRange: [flatBarHeight, valueBarHeight(value)]
|
|
8923
|
+
}) : isLive ? liveGrowth.interpolate({ inputRange: [0, 1], outputRange: [0, barHeight(value)] }) : barHeight(value);
|
|
8924
|
+
const ownLabel = showValueLabels && !renderBarOverlay && showBarLabel(repIndex);
|
|
8925
|
+
const labelBottom = (expandProgress ? valueBarHeight(best) : barHeight(best)) + LABEL_GAP;
|
|
8926
|
+
return /* @__PURE__ */ jsxs(reactNative.View, { accessibilityElementsHidden: renderBarOverlay == null, style: column, children: [
|
|
8927
|
+
ownLabel && // All labels pin to one aligned row JUST ABOVE THE PEAK BAR (not the container top),
|
|
8928
|
+
// in the muted on-surface-secondary tone — so the row hugs the bars, no floating void.
|
|
8929
|
+
/* @__PURE__ */ jsx(
|
|
8930
|
+
reactNative.Animated.View,
|
|
8931
|
+
{
|
|
8932
|
+
style: {
|
|
8933
|
+
position: "absolute",
|
|
8934
|
+
bottom: labelBottom,
|
|
8935
|
+
left: 0,
|
|
8936
|
+
right: 0,
|
|
8937
|
+
alignItems: "center",
|
|
8938
|
+
...expandProgress ? { opacity: expandProgress } : null
|
|
8939
|
+
},
|
|
8940
|
+
pointerEvents: "none",
|
|
8941
|
+
children: /* @__PURE__ */ jsx(
|
|
8942
|
+
reactNative.Text,
|
|
8943
|
+
{
|
|
8944
|
+
className: "text-text-secondary",
|
|
8945
|
+
style: [{ fontSize: valueLabelFontSize(height), fontWeight: "700" }, flipStyle],
|
|
8946
|
+
testID: `${testIDPrefix}-label-${repIndex}`,
|
|
8947
|
+
children: formatValue3(value)
|
|
8948
|
+
}
|
|
8949
|
+
)
|
|
8950
|
+
}
|
|
8951
|
+
),
|
|
8952
|
+
/* @__PURE__ */ jsx(
|
|
8953
|
+
reactNative.Animated.View,
|
|
8954
|
+
{
|
|
8955
|
+
style: [
|
|
8956
|
+
{ width: "100%", height: barHeightStyle, backgroundColor: color },
|
|
8957
|
+
barCorners,
|
|
8958
|
+
barPaper(color, flip)
|
|
8959
|
+
],
|
|
8960
|
+
testID: `${testIDPrefix}-bar-${repIndex}`
|
|
8961
|
+
}
|
|
8962
|
+
),
|
|
8963
|
+
renderBarOverlay?.(repIndex, value)
|
|
8964
|
+
] }, i);
|
|
8965
|
+
})
|
|
8966
|
+
]
|
|
8967
|
+
}
|
|
8968
|
+
)
|
|
8969
|
+
]
|
|
8970
|
+
}
|
|
8971
|
+
);
|
|
8972
|
+
}
|
|
8973
|
+
function renderStub(kind, barCorners, stubHeight, placeholderColor, testIDPrefix, todoVariant, solidTodoColor, emptyColor) {
|
|
8974
|
+
const base = {
|
|
8975
|
+
width: "100%",
|
|
8976
|
+
height: stubHeight,
|
|
8977
|
+
...barCorners
|
|
8978
|
+
};
|
|
8979
|
+
if (kind === "variable") {
|
|
8980
|
+
return /* @__PURE__ */ jsx(
|
|
8981
|
+
reactNative.View,
|
|
8982
|
+
{
|
|
8983
|
+
style: { ...base, backgroundColor: VARIABLE_FILL },
|
|
8984
|
+
testID: `${testIDPrefix}-slot-variable`
|
|
8985
|
+
}
|
|
8596
8986
|
);
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8987
|
+
}
|
|
8988
|
+
if (kind === "continue") {
|
|
8989
|
+
return /* @__PURE__ */ jsx(
|
|
8990
|
+
reactNative.View,
|
|
8991
|
+
{
|
|
8992
|
+
style: { ...base, borderWidth: 1, borderColor: CONTINUE_OUTLINE },
|
|
8993
|
+
testID: `${testIDPrefix}-slot-continue`
|
|
8994
|
+
}
|
|
8995
|
+
);
|
|
8996
|
+
}
|
|
8997
|
+
if (kind === "empty") {
|
|
8998
|
+
return /* @__PURE__ */ jsx(
|
|
8999
|
+
reactNative.View,
|
|
9000
|
+
{
|
|
9001
|
+
style: { ...base, backgroundColor: emptyColor },
|
|
9002
|
+
testID: `${testIDPrefix}-slot-empty`
|
|
9003
|
+
}
|
|
9004
|
+
);
|
|
9005
|
+
}
|
|
9006
|
+
const todoStyle = todoVariant === "solid" ? { ...base, backgroundColor: solidTodoColor } : { ...base, borderWidth: 1, borderStyle: "dashed", borderColor: placeholderColor };
|
|
9007
|
+
return /* @__PURE__ */ jsx(reactNative.View, { style: todoStyle, testID: `${testIDPrefix}-slot-todo` });
|
|
8601
9008
|
}
|
|
8602
|
-
function
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
9009
|
+
function sideLabelFontSize(height) {
|
|
9010
|
+
return Math.round(Math.max(7, Math.min(13, height * 0.06)));
|
|
9011
|
+
}
|
|
9012
|
+
var SIDE_LABEL_INITIALS_BELOW = 70;
|
|
9013
|
+
function sideLabelText(label, extent) {
|
|
9014
|
+
if (extent >= SIDE_LABEL_INITIALS_BELOW) return label;
|
|
9015
|
+
return label.trim().split(/\s+/).map((word) => word.charAt(0)).join(" ").toUpperCase();
|
|
9016
|
+
}
|
|
9017
|
+
function ChartSideRail({
|
|
9018
|
+
sections,
|
|
9019
|
+
sectionExtent,
|
|
9020
|
+
variant = "hero"
|
|
8611
9021
|
}) {
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
9022
|
+
if (!sections.some((s) => s.label)) return null;
|
|
9023
|
+
const isRail = variant === "rail";
|
|
9024
|
+
const labelLength = Math.max(0, sectionExtent - (isRail ? 0 : 6));
|
|
9025
|
+
const fontSize = isRail ? 7 : sideLabelFontSize(sectionExtent);
|
|
9026
|
+
const textStyle = {
|
|
9027
|
+
maxWidth: "100%",
|
|
9028
|
+
textAlign: "center",
|
|
9029
|
+
fontSize,
|
|
9030
|
+
fontWeight: "700",
|
|
9031
|
+
letterSpacing: isRail || fontSize < 10 ? 0 : 2,
|
|
9032
|
+
textTransform: "uppercase"
|
|
9033
|
+
};
|
|
9034
|
+
return /* @__PURE__ */ jsx(reactNative.View, { style: { width: isRail ? 18 : 34 }, accessibilityElementsHidden: true, children: sections.map((s) => /* @__PURE__ */ jsx(
|
|
9035
|
+
reactNative.View,
|
|
9036
|
+
{
|
|
9037
|
+
style: { height: sectionExtent, alignItems: "center", justifyContent: "center" },
|
|
9038
|
+
children: s.label ? /* @__PURE__ */ jsx(
|
|
8623
9039
|
reactNative.View,
|
|
8624
9040
|
{
|
|
8625
9041
|
style: {
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
marginLeft: seg.leadingGap,
|
|
8630
|
-
borderRadius: radius,
|
|
8631
|
-
overflow: "hidden"
|
|
9042
|
+
width: labelLength,
|
|
9043
|
+
alignItems: "center",
|
|
9044
|
+
transform: [{ rotate: "-90deg" }]
|
|
8632
9045
|
},
|
|
8633
|
-
children:
|
|
8634
|
-
reactNative.
|
|
9046
|
+
children: /* @__PURE__ */ jsx(
|
|
9047
|
+
reactNative.Text,
|
|
8635
9048
|
{
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
outputRange: seg.pulseColor
|
|
8642
|
-
})
|
|
8643
|
-
} : {
|
|
8644
|
-
opacity: pulse.interpolate({
|
|
8645
|
-
inputRange: [0, 1],
|
|
8646
|
-
outputRange: [PULSE_MIN_OPACITY, 1]
|
|
8647
|
-
})
|
|
8648
|
-
}
|
|
8649
|
-
},
|
|
8650
|
-
accessibilityElementsHidden: true,
|
|
8651
|
-
testID
|
|
9049
|
+
className: "text-text-tertiary",
|
|
9050
|
+
style: textStyle,
|
|
9051
|
+
numberOfLines: 1,
|
|
9052
|
+
testID: s.testID,
|
|
9053
|
+
children: isRail ? s.label : sideLabelText(s.label, sectionExtent)
|
|
8652
9054
|
}
|
|
8653
|
-
)
|
|
8654
|
-
}
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
|
|
8658
|
-
|
|
8659
|
-
reactNative.View,
|
|
8660
|
-
{
|
|
8661
|
-
style: {
|
|
8662
|
-
position: "absolute",
|
|
8663
|
-
left: `${marker.position * 100}%`,
|
|
8664
|
-
top: 0,
|
|
8665
|
-
bottom: 0,
|
|
8666
|
-
width: 2,
|
|
8667
|
-
backgroundColor: marker.color
|
|
8668
|
-
},
|
|
8669
|
-
accessibilityElementsHidden: true,
|
|
8670
|
-
testID: "segmented-bar-marker"
|
|
8671
|
-
}
|
|
8672
|
-
) : null
|
|
8673
|
-
] });
|
|
9055
|
+
)
|
|
9056
|
+
}
|
|
9057
|
+
) : null
|
|
9058
|
+
},
|
|
9059
|
+
s.testID
|
|
9060
|
+
)) });
|
|
8674
9061
|
}
|
|
8675
9062
|
|
|
8676
|
-
// src/components/custom/Workout/
|
|
8677
|
-
var
|
|
8678
|
-
|
|
8679
|
-
|
|
8680
|
-
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
var MYO_UPCOMING_FADE = 0.6;
|
|
8690
|
-
var PULSE_RANGE = {
|
|
8691
|
-
[SET_STRIP_ZONES.slow]: [primitiveRamps.red[500], primitiveRamps.red[700]],
|
|
8692
|
-
[SET_STRIP_ZONES.moderate]: [primitiveRamps.orange[300], primitiveRamps.orange[500]],
|
|
8693
|
-
[SET_STRIP_ZONES.fast]: [primitiveRamps.amber[200], primitiveRamps.amber[400]],
|
|
8694
|
-
[SET_STRIP_ZONES.fastest]: [primitiveRamps.green[200], primitiveRamps.green[400]]
|
|
8695
|
-
};
|
|
8696
|
-
function velocityZoneColor(v) {
|
|
8697
|
-
if (v < 0.5) return SET_STRIP_ZONES.slow;
|
|
8698
|
-
if (v < 0.75) return SET_STRIP_ZONES.moderate;
|
|
8699
|
-
if (v < 1) return SET_STRIP_ZONES.fast;
|
|
8700
|
-
return SET_STRIP_ZONES.fastest;
|
|
8701
|
-
}
|
|
8702
|
-
function chunkedSegments(chunks, gap) {
|
|
8703
|
-
return chunks.flatMap(
|
|
8704
|
-
(vels, ci) => vels.map((v, ri) => ({
|
|
8705
|
-
color: velocityZoneColor(v),
|
|
8706
|
-
...ci > 0 && ri === 0 ? { leadingGap: gap } : {}
|
|
8707
|
-
}))
|
|
8708
|
-
);
|
|
8709
|
-
}
|
|
8710
|
-
function rangeSegments(floor, max, doneVels) {
|
|
8711
|
-
return Array.from({ length: max }, (_, i) => {
|
|
8712
|
-
if (i < doneVels.length) return { color: velocityZoneColor(doneVels[i]) };
|
|
8713
|
-
return { color: i < floor ? TODO_COLOR : SET_STRIP_VARIABLE_COLOR };
|
|
8714
|
-
});
|
|
8715
|
-
}
|
|
8716
|
-
function myoUpcomingSegments(activationLen, clusterCount) {
|
|
8717
|
-
const activation = Array.from({ length: activationLen }, () => ({ color: TODO_COLOR }));
|
|
8718
|
-
const trail = Array.from(
|
|
8719
|
-
{ length: clusterCount },
|
|
8720
|
-
(_, ci) => Array.from({ length: MYO_UPCOMING_CLUSTER_LEN }, (_2, ri) => ({
|
|
8721
|
-
color: SET_STRIP_VARIABLE_COLOR,
|
|
8722
|
-
opacity: MYO_UPCOMING_FADE ** ci,
|
|
8723
|
-
...ri === 0 ? { leadingGap: CLUSTER_GAP } : {}
|
|
8724
|
-
}))
|
|
8725
|
-
).flat();
|
|
8726
|
-
return [...activation, ...trail];
|
|
8727
|
-
}
|
|
8728
|
-
function setSegments(set) {
|
|
8729
|
-
if (set.status === "todo") {
|
|
8730
|
-
return [{ color: TODO_COLOR }];
|
|
8731
|
-
}
|
|
8732
|
-
if (set.status === "done") {
|
|
8733
|
-
return set.velocities.map((v) => ({ color: velocityZoneColor(v) }));
|
|
8734
|
-
}
|
|
8735
|
-
if (set.status === "range") {
|
|
8736
|
-
return rangeSegments(set.floor, set.max, set.doneVels);
|
|
8737
|
-
}
|
|
8738
|
-
if (set.status === "drop") {
|
|
8739
|
-
return chunkedSegments(set.subloads, NOTCH_GAP);
|
|
8740
|
-
}
|
|
8741
|
-
if (set.status === "myo") {
|
|
8742
|
-
return chunkedSegments([set.activation, ...set.clusters], CLUSTER_GAP);
|
|
8743
|
-
}
|
|
8744
|
-
if (set.status === "myo-upcoming") {
|
|
8745
|
-
return myoUpcomingSegments(set.activationLen, set.clusterCount ?? MYO_UPCOMING_CLUSTERS);
|
|
8746
|
-
}
|
|
8747
|
-
const performed = set.velocities.map((v) => {
|
|
8748
|
-
const color = velocityZoneColor(v);
|
|
8749
|
-
return { color, pulse: true, pulseColor: PULSE_RANGE[color] };
|
|
8750
|
-
});
|
|
8751
|
-
const remaining = Math.max(0, set.planned - set.velocities.length);
|
|
8752
|
-
return [...performed, ...Array.from({ length: remaining }, () => ({ color: TODO_COLOR }))];
|
|
8753
|
-
}
|
|
8754
|
-
function setBarSegmentTestID(seg) {
|
|
8755
|
-
if (seg.pulse) return "set-strip-pulse";
|
|
8756
|
-
if (seg.color === SET_STRIP_VARIABLE_COLOR) return "set-strip-variable";
|
|
8757
|
-
return seg.color === TODO_COLOR ? "set-strip-empty" : "set-strip-fill";
|
|
8758
|
-
}
|
|
8759
|
-
function SetBar({ set, height = 8 }) {
|
|
8760
|
-
return /* @__PURE__ */ jsx(
|
|
8761
|
-
SegmentedBar,
|
|
8762
|
-
{
|
|
8763
|
-
segments: setSegments(set),
|
|
8764
|
-
height,
|
|
8765
|
-
gap: 0,
|
|
8766
|
-
radius: 0,
|
|
8767
|
-
segmentTestID: setBarSegmentTestID,
|
|
8768
|
-
style: { flex: 1, minWidth: 0, borderRadius: 2, overflow: "hidden" },
|
|
8769
|
-
testID: "set-strip-set"
|
|
8770
|
-
}
|
|
8771
|
-
);
|
|
8772
|
-
}
|
|
8773
|
-
|
|
8774
|
-
// src/components/custom/Workout/SetStrip.tsx
|
|
8775
|
-
var SET_STRIP_GAP = 5;
|
|
8776
|
-
function setCategory(set) {
|
|
8777
|
-
switch (set.status) {
|
|
8778
|
-
case "done":
|
|
8779
|
-
case "drop":
|
|
8780
|
-
case "myo":
|
|
8781
|
-
return "done";
|
|
8782
|
-
case "active":
|
|
8783
|
-
return "active";
|
|
8784
|
-
case "range":
|
|
8785
|
-
return set.doneVels.length > 0 ? "active" : "upcoming";
|
|
8786
|
-
case "todo":
|
|
8787
|
-
case "myo-upcoming":
|
|
8788
|
-
return "upcoming";
|
|
8789
|
-
}
|
|
8790
|
-
}
|
|
8791
|
-
function describeSets(sets) {
|
|
8792
|
-
const done = sets.filter((s) => setCategory(s) === "done").length;
|
|
8793
|
-
const active = sets.filter((s) => setCategory(s) === "active").length;
|
|
8794
|
-
const upcoming = sets.filter((s) => setCategory(s) === "upcoming").length;
|
|
8795
|
-
return `Set progress: ${done} done, ${active} in progress, ${upcoming} upcoming`;
|
|
8796
|
-
}
|
|
8797
|
-
function SetStrip({ sets, height = 8, className, ...props }) {
|
|
8798
|
-
return /* @__PURE__ */ jsx(
|
|
8799
|
-
reactNative.View,
|
|
8800
|
-
{
|
|
8801
|
-
className,
|
|
8802
|
-
style: { flexDirection: "row", width: "100%", height, gap: SET_STRIP_GAP },
|
|
8803
|
-
accessibilityRole: "image",
|
|
8804
|
-
accessibilityLabel: describeSets(sets),
|
|
8805
|
-
testID: "set-strip",
|
|
8806
|
-
...props,
|
|
8807
|
-
children: sets.map((set, i) => /* @__PURE__ */ jsx(SetBar, { set, height }, i))
|
|
8808
|
-
}
|
|
8809
|
-
);
|
|
8810
|
-
}
|
|
8811
|
-
|
|
8812
|
-
// src/components/custom/Workout/VelocityStrip.tsx
|
|
8813
|
-
var VEL_COLORS = WORKOUT_TOKENS.scale;
|
|
8814
|
-
var bandIdToEffortColor = {
|
|
8815
|
-
speed: sequentialEffort[0],
|
|
8816
|
-
// green-300
|
|
8817
|
-
power: sequentialEffort[2],
|
|
8818
|
-
// amber-300 (gold)
|
|
8819
|
-
strengthSpeed: sequentialEffort[3],
|
|
8820
|
-
// orange-400
|
|
8821
|
-
maximalStrength: sequentialEffort[4],
|
|
8822
|
-
// red-600
|
|
8823
|
-
grinding: sequentialEffort[5]
|
|
8824
|
-
// red-700 — the distinct 5th band
|
|
9063
|
+
// src/components/custom/Workout/VelocityStrip.tsx
|
|
9064
|
+
var VEL_COLORS = WORKOUT_TOKENS.scale;
|
|
9065
|
+
var bandIdToEffortColor = {
|
|
9066
|
+
speed: sequentialEffort[0],
|
|
9067
|
+
// green-300
|
|
9068
|
+
power: sequentialEffort[2],
|
|
9069
|
+
// amber-300 (gold)
|
|
9070
|
+
strengthSpeed: sequentialEffort[3],
|
|
9071
|
+
// orange-400
|
|
9072
|
+
maximalStrength: sequentialEffort[4],
|
|
9073
|
+
// red-600
|
|
9074
|
+
grinding: sequentialEffort[5]
|
|
9075
|
+
// red-700 — the distinct 5th band
|
|
8825
9076
|
};
|
|
8826
9077
|
function getVelocityZoneColor(velocity) {
|
|
8827
9078
|
if (velocity >= 1) return "vel-green";
|
|
@@ -8853,6 +9104,18 @@ function calculateMeanVelocity(velocities) {
|
|
|
8853
9104
|
const sum = velocities.reduce((acc, v) => acc + v, 0);
|
|
8854
9105
|
return sum / velocities.length;
|
|
8855
9106
|
}
|
|
9107
|
+
var VL_LOSS_THRESHOLDS = [10, 20, 30];
|
|
9108
|
+
function getVelocityLossColor(lossPct) {
|
|
9109
|
+
const [t1, t26, t32] = VL_LOSS_THRESHOLDS;
|
|
9110
|
+
if (lossPct < t1) return VEL_COLORS.green;
|
|
9111
|
+
if (lossPct < t26) return VEL_COLORS.yellow;
|
|
9112
|
+
if (lossPct < t32) return VEL_COLORS.orange;
|
|
9113
|
+
return VEL_COLORS.red;
|
|
9114
|
+
}
|
|
9115
|
+
function velocityLossForRep(velocity, best) {
|
|
9116
|
+
if (best <= 0) return 0;
|
|
9117
|
+
return Math.max(0, Math.round((best - velocity) / best * 100));
|
|
9118
|
+
}
|
|
8856
9119
|
function classifyBand(velocity, bands) {
|
|
8857
9120
|
for (const band of bands) {
|
|
8858
9121
|
if (band.max === null || velocity < band.max) return band;
|
|
@@ -8862,15 +9125,17 @@ function classifyBand(velocity, bands) {
|
|
|
8862
9125
|
function bandColor(band) {
|
|
8863
9126
|
return bandIdToEffortColor[band.id] ?? VEL_COLORS.green;
|
|
8864
9127
|
}
|
|
9128
|
+
function makeBarColorFor(zones) {
|
|
9129
|
+
const hasZones = zones != null && zones.length > 0;
|
|
9130
|
+
return (v) => hasZones ? bandColor(classifyBand(v, zones)) : zoneHexMap[getVelocityZoneColor(v)];
|
|
9131
|
+
}
|
|
8865
9132
|
function getLossStyle(loss) {
|
|
8866
9133
|
if (loss > 25) return { color: VEL_COLORS.red };
|
|
8867
9134
|
if (loss > 20) return { color: VEL_COLORS.orange };
|
|
8868
9135
|
return null;
|
|
8869
9136
|
}
|
|
8870
|
-
var
|
|
9137
|
+
var REP_GAP2 = 2;
|
|
8871
9138
|
var WIDE_GAP = 8;
|
|
8872
|
-
var TODO_COLOR2 = primitiveColors.charcoal[300];
|
|
8873
|
-
var CONTINUE_OUTLINE = primitiveRamps.cyan[800];
|
|
8874
9139
|
function deriveDoneVelocities(set) {
|
|
8875
9140
|
switch (set.type) {
|
|
8876
9141
|
case "drop":
|
|
@@ -8886,7 +9151,7 @@ function chunkedRepSlots(chunks) {
|
|
|
8886
9151
|
chunks.forEach((chunk) => {
|
|
8887
9152
|
chunk.forEach((velocity, ri) => {
|
|
8888
9153
|
const first = slots.length === 0;
|
|
8889
|
-
slots.push({ kind: "rep", velocity, leadingGap: first ? 0 : ri === 0 ? WIDE_GAP :
|
|
9154
|
+
slots.push({ kind: "rep", velocity, leadingGap: first ? 0 : ri === 0 ? WIDE_GAP : REP_GAP2 });
|
|
8890
9155
|
});
|
|
8891
9156
|
});
|
|
8892
9157
|
return slots;
|
|
@@ -8900,7 +9165,7 @@ function buildSlots(set) {
|
|
|
8900
9165
|
return {
|
|
8901
9166
|
kind: done ? "rep" : "todo",
|
|
8902
9167
|
velocity: done ? set.velocities[i] : void 0,
|
|
8903
|
-
leadingGap: i === 0 ? 0 :
|
|
9168
|
+
leadingGap: i === 0 ? 0 : REP_GAP2
|
|
8904
9169
|
};
|
|
8905
9170
|
});
|
|
8906
9171
|
}
|
|
@@ -8911,7 +9176,7 @@ function buildSlots(set) {
|
|
|
8911
9176
|
return {
|
|
8912
9177
|
kind,
|
|
8913
9178
|
velocity: kind === "rep" ? set.velocities[i] : void 0,
|
|
8914
|
-
leadingGap: i === 0 ? 0 :
|
|
9179
|
+
leadingGap: i === 0 ? 0 : REP_GAP2
|
|
8915
9180
|
};
|
|
8916
9181
|
});
|
|
8917
9182
|
}
|
|
@@ -8919,16 +9184,16 @@ function buildSlots(set) {
|
|
|
8919
9184
|
const reps = set.velocities.map((velocity, i) => ({
|
|
8920
9185
|
kind: "rep",
|
|
8921
9186
|
velocity,
|
|
8922
|
-
leadingGap: i === 0 ? 0 :
|
|
9187
|
+
leadingGap: i === 0 ? 0 : REP_GAP2
|
|
8923
9188
|
}));
|
|
8924
|
-
reps.push({ kind: "continue", leadingGap: reps.length === 0 ? 0 :
|
|
9189
|
+
reps.push({ kind: "continue", leadingGap: reps.length === 0 ? 0 : REP_GAP2 });
|
|
8925
9190
|
return reps;
|
|
8926
9191
|
}
|
|
8927
9192
|
case "drop":
|
|
8928
9193
|
return chunkedRepSlots(set.subloads);
|
|
8929
9194
|
case "myo": {
|
|
8930
9195
|
const slots = chunkedRepSlots([set.activation, ...set.clusters]);
|
|
8931
|
-
if (set.open) slots.push({ kind: "continue", leadingGap: slots.length === 0 ? 0 :
|
|
9196
|
+
if (set.open) slots.push({ kind: "continue", leadingGap: slots.length === 0 ? 0 : REP_GAP2 });
|
|
8932
9197
|
return slots;
|
|
8933
9198
|
}
|
|
8934
9199
|
case "cluster": {
|
|
@@ -8939,7 +9204,7 @@ function buildSlots(set) {
|
|
|
8939
9204
|
return {
|
|
8940
9205
|
kind: done ? "rep" : "todo",
|
|
8941
9206
|
velocity: done ? set.velocities[i] : void 0,
|
|
8942
|
-
leadingGap: i === 0 ? 0 : boundary ? WIDE_GAP :
|
|
9207
|
+
leadingGap: i === 0 ? 0 : boundary ? WIDE_GAP : REP_GAP2
|
|
8943
9208
|
};
|
|
8944
9209
|
});
|
|
8945
9210
|
}
|
|
@@ -8961,253 +9226,406 @@ function setAccessibilityLabel(set, repCount) {
|
|
|
8961
9226
|
return `Velocity strip, cluster set, groups of ${set.groupSize}, ${repCount} reps`;
|
|
8962
9227
|
}
|
|
8963
9228
|
}
|
|
8964
|
-
function slotAccessibilityLabel(slot, index) {
|
|
8965
|
-
switch (slot.kind) {
|
|
8966
|
-
case "rep":
|
|
8967
|
-
return `Rep ${index + 1}: ${formatVelocity(slot.velocity ?? 0)} meters per second`;
|
|
8968
|
-
case "todo":
|
|
8969
|
-
return `Rep ${index + 1}: planned`;
|
|
8970
|
-
case "variable":
|
|
8971
|
-
return `Rep ${index + 1}: variable`;
|
|
8972
|
-
case "continue":
|
|
8973
|
-
return "Keep going";
|
|
8974
|
-
}
|
|
8975
|
-
}
|
|
8976
|
-
var EXPANDED_TODO_STUB_PCT = 16;
|
|
8977
|
-
var EXPANDED_ENCODED_PCT = 45;
|
|
8978
9229
|
var EXPANDED_HEIGHT = 60;
|
|
8979
|
-
var
|
|
8980
|
-
var BARE_STUB_HEIGHT = 3;
|
|
8981
|
-
var HERO_HEIGHT = 220;
|
|
8982
|
-
var HERO_LABEL_HEADROOM = 20;
|
|
8983
|
-
var HERO_BAR_GAP = 8;
|
|
8984
|
-
var HERO_BAR_MAX_WIDTH = 120;
|
|
8985
|
-
var HERO_LABEL_MIN_BAR_WIDTH = 30;
|
|
8986
|
-
var HERO_BAR_RADIUS = 5;
|
|
8987
|
-
var HERO_MIN_BAR_HEIGHT = 4;
|
|
8988
|
-
var HERO_PENDING_COLOR = primitiveColors.charcoal[100];
|
|
9230
|
+
var COMPACT_HEIGHT = 8;
|
|
8989
9231
|
var HERO_REFERENCE_COLOR = primitiveColors.charcoal[0];
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
|
|
8998
|
-
|
|
8999
|
-
|
|
9000
|
-
|
|
9001
|
-
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
return reduced;
|
|
9232
|
+
function DashedReferenceLine({
|
|
9233
|
+
anchor,
|
|
9234
|
+
offset,
|
|
9235
|
+
testID
|
|
9236
|
+
}) {
|
|
9237
|
+
const edge = anchor === "top" ? { top: offset } : { bottom: offset };
|
|
9238
|
+
return /* @__PURE__ */ jsx(
|
|
9239
|
+
reactNative.View,
|
|
9240
|
+
{
|
|
9241
|
+
accessibilityElementsHidden: true,
|
|
9242
|
+
style: {
|
|
9243
|
+
position: "absolute",
|
|
9244
|
+
left: 0,
|
|
9245
|
+
right: 0,
|
|
9246
|
+
borderTopWidth: 1,
|
|
9247
|
+
borderStyle: "dashed",
|
|
9248
|
+
borderColor: HERO_REFERENCE_COLOR,
|
|
9249
|
+
pointerEvents: "none",
|
|
9250
|
+
...edge
|
|
9251
|
+
},
|
|
9252
|
+
testID
|
|
9253
|
+
}
|
|
9254
|
+
);
|
|
9014
9255
|
}
|
|
9015
|
-
var
|
|
9016
|
-
var
|
|
9017
|
-
var
|
|
9018
|
-
function
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
9024
|
-
|
|
9025
|
-
|
|
9256
|
+
var BAND_LABEL_FONT = "monospace";
|
|
9257
|
+
var VL_LABEL_MIN_PLOT = 65;
|
|
9258
|
+
var VL_SEMANTIC = getSemanticColors("dark");
|
|
9259
|
+
function VelocityLossBands({
|
|
9260
|
+
best,
|
|
9261
|
+
scaleDenom,
|
|
9262
|
+
plotHeight,
|
|
9263
|
+
flip = false
|
|
9264
|
+
}) {
|
|
9265
|
+
if (best <= 0 || scaleDenom <= 0 || plotHeight <= 0) return null;
|
|
9266
|
+
const yOf = (v) => v / scaleDenom * plotHeight;
|
|
9267
|
+
const vl20 = best * 0.8;
|
|
9268
|
+
const vl30 = best * 0.7;
|
|
9269
|
+
const vlFont = Math.round(Math.max(7, Math.min(9, plotHeight * 0.05)));
|
|
9270
|
+
const showLabels = plotHeight >= VL_LABEL_MIN_PLOT;
|
|
9271
|
+
const band = (loV, hiV, color) => /* @__PURE__ */ jsx(
|
|
9272
|
+
reactNative.View,
|
|
9273
|
+
{
|
|
9274
|
+
style: {
|
|
9275
|
+
position: "absolute",
|
|
9276
|
+
left: 0,
|
|
9277
|
+
right: 0,
|
|
9278
|
+
bottom: yOf(loV),
|
|
9279
|
+
height: Math.max(0, yOf(hiV) - yOf(loV)),
|
|
9280
|
+
backgroundColor: color
|
|
9281
|
+
}
|
|
9026
9282
|
}
|
|
9027
|
-
|
|
9028
|
-
|
|
9029
|
-
|
|
9030
|
-
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9037
|
-
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
|
|
9046
|
-
|
|
9047
|
-
|
|
9048
|
-
|
|
9049
|
-
|
|
9050
|
-
|
|
9283
|
+
);
|
|
9284
|
+
const threshold = (v, color, label) => /* @__PURE__ */ jsxs(
|
|
9285
|
+
reactNative.View,
|
|
9286
|
+
{
|
|
9287
|
+
style: {
|
|
9288
|
+
position: "absolute",
|
|
9289
|
+
left: 0,
|
|
9290
|
+
right: 0,
|
|
9291
|
+
bottom: yOf(v),
|
|
9292
|
+
// Collapse to zero height so the dashed border lands EXACTLY on `yOf(v)` — the
|
|
9293
|
+
// band edge — instead of floating up half a text-row (the label then centres on
|
|
9294
|
+
// the line, breaking it). Matches DashedReferenceLine's bare-border anchoring.
|
|
9295
|
+
height: 0,
|
|
9296
|
+
flexDirection: "row",
|
|
9297
|
+
alignItems: "center"
|
|
9298
|
+
},
|
|
9299
|
+
children: [
|
|
9300
|
+
/* @__PURE__ */ jsx(reactNative.View, { style: { flex: 9, borderTopWidth: 1, borderStyle: "dashed", borderColor: color } }),
|
|
9301
|
+
showLabels ? /* @__PURE__ */ jsxs(jsxRuntime.Fragment, { children: [
|
|
9302
|
+
/* @__PURE__ */ jsx(
|
|
9303
|
+
reactNative.Text,
|
|
9304
|
+
{
|
|
9305
|
+
style: {
|
|
9306
|
+
marginHorizontal: 6,
|
|
9307
|
+
fontSize: vlFont,
|
|
9308
|
+
fontWeight: "800",
|
|
9309
|
+
fontFamily: BAND_LABEL_FONT,
|
|
9310
|
+
color,
|
|
9311
|
+
...flip ? { transform: [{ scaleY: -1 }] } : null
|
|
9312
|
+
},
|
|
9313
|
+
children: label
|
|
9314
|
+
}
|
|
9315
|
+
),
|
|
9316
|
+
/* @__PURE__ */ jsx(reactNative.View, { style: { flex: 1, borderTopWidth: 1, borderStyle: "dashed", borderColor: color } })
|
|
9317
|
+
] }) : null
|
|
9318
|
+
]
|
|
9319
|
+
}
|
|
9320
|
+
);
|
|
9321
|
+
return /* @__PURE__ */ jsxs(
|
|
9322
|
+
reactNative.View,
|
|
9323
|
+
{
|
|
9324
|
+
accessibilityElementsHidden: true,
|
|
9325
|
+
pointerEvents: "none",
|
|
9326
|
+
style: { position: "absolute", left: 0, right: 0, bottom: 0, top: 0 },
|
|
9327
|
+
testID: "velocity-loss-bands",
|
|
9328
|
+
children: [
|
|
9329
|
+
band(0, vl30, alpha(VL_SEMANTIC["status-error"], 0.09)),
|
|
9330
|
+
band(vl30, vl20, alpha(VL_SEMANTIC["status-warning"], 0.08)),
|
|
9331
|
+
threshold(vl20, alpha(VL_SEMANTIC["status-warning"], 0.75), "VL 20%"),
|
|
9332
|
+
threshold(vl30, alpha(VL_SEMANTIC["status-error"], 0.75), "VL 30%")
|
|
9333
|
+
]
|
|
9051
9334
|
}
|
|
9052
|
-
|
|
9053
|
-
return liveScale;
|
|
9335
|
+
);
|
|
9054
9336
|
}
|
|
9055
|
-
function
|
|
9056
|
-
|
|
9057
|
-
|
|
9058
|
-
|
|
9059
|
-
|
|
9060
|
-
|
|
9061
|
-
|
|
9337
|
+
function velocityReferenceOverlay(g, showLossBands) {
|
|
9338
|
+
const referencePx = g.best > 0 && g.scaleDenom > 0 ? Math.min(g.plotHeight, g.yOf(g.best)) : 0;
|
|
9339
|
+
return /* @__PURE__ */ jsxs(jsxRuntime.Fragment, { children: [
|
|
9340
|
+
showLossBands && /* @__PURE__ */ jsx(
|
|
9341
|
+
VelocityLossBands,
|
|
9342
|
+
{
|
|
9343
|
+
best: g.best,
|
|
9344
|
+
scaleDenom: g.scaleDenom,
|
|
9345
|
+
plotHeight: g.plotHeight,
|
|
9346
|
+
flip: g.flip
|
|
9347
|
+
}
|
|
9348
|
+
),
|
|
9349
|
+
referencePx > 0 && /* @__PURE__ */ jsx(
|
|
9350
|
+
DashedReferenceLine,
|
|
9351
|
+
{
|
|
9352
|
+
anchor: "bottom",
|
|
9353
|
+
offset: referencePx,
|
|
9354
|
+
testID: "velocity-hero-reference"
|
|
9355
|
+
}
|
|
9356
|
+
)
|
|
9357
|
+
] });
|
|
9358
|
+
}
|
|
9359
|
+
var ANIMATION_DURATION = 400;
|
|
9360
|
+
var DUAL_HERO_HEIGHT = SET_BAR_DEFAULT_HEIGHT;
|
|
9361
|
+
var DUAL_RAIL_HEIGHT = 96;
|
|
9362
|
+
var DUAL_COMPACT_HEIGHT = 11.5;
|
|
9363
|
+
var DUAL_WING_GAP = 2;
|
|
9364
|
+
var COMPACT_FOLD_GAP = 1.5;
|
|
9365
|
+
var COMPACT_FOLD_HALF = 5;
|
|
9366
|
+
function streamDone(stream) {
|
|
9367
|
+
return stream.set ? deriveDoneVelocities(stream.set) : stream.velocities ?? [];
|
|
9368
|
+
}
|
|
9369
|
+
function streamNaturalSlots(stream, targetReps) {
|
|
9370
|
+
if (stream.set) {
|
|
9371
|
+
return buildSlots(stream.set).map((s) => ({
|
|
9372
|
+
kind: s.kind,
|
|
9373
|
+
value: s.velocity,
|
|
9374
|
+
leadingGap: s.leadingGap
|
|
9375
|
+
}));
|
|
9376
|
+
}
|
|
9377
|
+
const reps = (stream.velocities ?? []).map((v, i) => ({
|
|
9378
|
+
kind: "rep",
|
|
9379
|
+
value: v,
|
|
9380
|
+
leadingGap: i === 0 ? 0 : REP_GAP2
|
|
9381
|
+
}));
|
|
9382
|
+
const pad = Math.max(0, (targetReps ?? 0) - reps.length);
|
|
9383
|
+
return [...reps, ...Array.from({ length: pad }, () => ({ kind: "todo" }))];
|
|
9384
|
+
}
|
|
9385
|
+
function mergeColumnKind(l, r) {
|
|
9386
|
+
const kinds = [l?.kind, r?.kind];
|
|
9387
|
+
if (kinds.includes("variable")) return "variable";
|
|
9388
|
+
if (kinds.includes("continue")) return "continue";
|
|
9389
|
+
if (kinds.includes("rep")) return "rep";
|
|
9390
|
+
return "todo";
|
|
9391
|
+
}
|
|
9392
|
+
function sideColumnCell(slot, kind, leadingGap) {
|
|
9393
|
+
if (kind !== "rep") return { kind, leadingGap };
|
|
9394
|
+
return slot?.kind === "rep" ? { kind: "rep", value: slot.value, leadingGap } : { kind: "empty", leadingGap };
|
|
9395
|
+
}
|
|
9396
|
+
function alignDualSlots(left, right) {
|
|
9397
|
+
const n = Math.max(left.length, right.length);
|
|
9398
|
+
const L = [];
|
|
9399
|
+
const R = [];
|
|
9400
|
+
for (let i = 0; i < n; i++) {
|
|
9401
|
+
const lt = left[i];
|
|
9402
|
+
const rt = right[i];
|
|
9403
|
+
const kind = mergeColumnKind(lt, rt);
|
|
9404
|
+
const gap = Math.max(lt?.leadingGap ?? REP_GAP2, rt?.leadingGap ?? REP_GAP2);
|
|
9405
|
+
L.push(sideColumnCell(lt, kind, gap));
|
|
9406
|
+
R.push(sideColumnCell(rt, kind, gap));
|
|
9407
|
+
}
|
|
9408
|
+
return { left: L, right: R };
|
|
9409
|
+
}
|
|
9410
|
+
function DualVelocityHero({
|
|
9411
|
+
leftDone,
|
|
9412
|
+
rightDone,
|
|
9413
|
+
leftStream,
|
|
9414
|
+
rightStream,
|
|
9415
|
+
leftLabel,
|
|
9416
|
+
rightLabel,
|
|
9417
|
+
zones,
|
|
9418
|
+
barColor: barColor2,
|
|
9419
|
+
scale,
|
|
9062
9420
|
targetReps,
|
|
9421
|
+
liveRepIndex,
|
|
9063
9422
|
height,
|
|
9064
9423
|
className,
|
|
9424
|
+
label,
|
|
9065
9425
|
viewProps
|
|
9066
9426
|
}) {
|
|
9067
|
-
const
|
|
9068
|
-
const
|
|
9069
|
-
const
|
|
9070
|
-
|
|
9071
|
-
|
|
9072
|
-
|
|
9073
|
-
const pendingCount = Math.max(0, (targetReps ?? 0) - doneVelocities.length);
|
|
9074
|
-
const referencePx = referenceVelocity > 0 && scaleDenom > 0 ? Math.min(plotHeight, referenceVelocity / scaleDenom * plotHeight) : 0;
|
|
9075
|
-
const totalSlots = doneVelocities.length + pendingCount;
|
|
9076
|
-
const barWidth = plotW > 0 && totalSlots > 0 ? Math.min(HERO_BAR_MAX_WIDTH, (plotW - HERO_BAR_GAP * (totalSlots - 1)) / totalSlots) : HERO_BAR_MAX_WIDTH;
|
|
9077
|
-
const labelsCrowded = plotW > 0 && barWidth < HERO_LABEL_MIN_BAR_WIDTH;
|
|
9078
|
-
const heroGap = plotW === 0 ? HERO_BAR_GAP : barWidth < 16 ? 2 : barWidth < 28 ? 4 : HERO_BAR_GAP;
|
|
9079
|
-
const peakIndex = doneVelocities.reduce(
|
|
9080
|
-
(best, v, i) => v > doneVelocities[best] ? i : best,
|
|
9081
|
-
0
|
|
9082
|
-
);
|
|
9083
|
-
const showBarLabel = (i) => !labelsCrowded || i === peakIndex || i === liveRepIndex;
|
|
9084
|
-
const repCount = doneVelocities.length;
|
|
9085
|
-
const total = Math.max(repCount, targetReps ?? repCount);
|
|
9086
|
-
const label = referenceVelocity > 0 ? `Velocity chart, ${repCount} of ${total} reps, best ${formatVelocity(referenceVelocity)} meters per second` : `Velocity chart, ${repCount} of ${total} reps`;
|
|
9427
|
+
const plotHalf = (height - DUAL_WING_GAP) / 2;
|
|
9428
|
+
const sharedMax = Math.max(...leftDone, ...rightDone, 0);
|
|
9429
|
+
const aligned = alignDualSlots(
|
|
9430
|
+
leftStream ? streamNaturalSlots(leftStream, targetReps) : [],
|
|
9431
|
+
rightStream ? streamNaturalSlots(rightStream, targetReps) : []
|
|
9432
|
+
);
|
|
9087
9433
|
const { style: externalStyle, ...restProps } = viewProps;
|
|
9088
|
-
|
|
9434
|
+
const wing = (orientation, columns, done) => /* @__PURE__ */ jsx(
|
|
9435
|
+
VelocityStrip,
|
|
9436
|
+
{
|
|
9437
|
+
variant: "hero",
|
|
9438
|
+
orientation,
|
|
9439
|
+
velocities: done,
|
|
9440
|
+
columnSlots: columns,
|
|
9441
|
+
scale,
|
|
9442
|
+
scaleMax: scale === "fixed" ? void 0 : sharedMax,
|
|
9443
|
+
barColor: barColor2,
|
|
9444
|
+
zones,
|
|
9445
|
+
liveRepIndex,
|
|
9446
|
+
height: plotHalf,
|
|
9447
|
+
hideBaseline: true
|
|
9448
|
+
}
|
|
9449
|
+
);
|
|
9450
|
+
return /* @__PURE__ */ jsxs(
|
|
9089
9451
|
reactNative.View,
|
|
9090
9452
|
{
|
|
9091
9453
|
className,
|
|
9092
|
-
style: [{ height }, externalStyle],
|
|
9454
|
+
style: [{ height, flexDirection: "row" }, externalStyle],
|
|
9093
9455
|
accessibilityRole: "image",
|
|
9094
9456
|
accessibilityLabel: label,
|
|
9095
|
-
testID: "velocity-strip
|
|
9457
|
+
testID: "dual-velocity-strip",
|
|
9096
9458
|
...restProps,
|
|
9097
|
-
children:
|
|
9098
|
-
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9106
|
-
|
|
9107
|
-
|
|
9108
|
-
|
|
9109
|
-
|
|
9110
|
-
children:
|
|
9111
|
-
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
|
|
9126
|
-
|
|
9127
|
-
|
|
9128
|
-
|
|
9129
|
-
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9148
|
-
|
|
9149
|
-
|
|
9150
|
-
|
|
9151
|
-
|
|
9152
|
-
|
|
9153
|
-
|
|
9154
|
-
|
|
9155
|
-
|
|
9156
|
-
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
|
|
9160
|
-
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9164
|
-
|
|
9165
|
-
|
|
9166
|
-
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9459
|
+
children: [
|
|
9460
|
+
/* @__PURE__ */ jsx(
|
|
9461
|
+
ChartSideRail,
|
|
9462
|
+
{
|
|
9463
|
+
sectionExtent: plotHalf,
|
|
9464
|
+
variant: "hero",
|
|
9465
|
+
sections: [
|
|
9466
|
+
{ label: leftLabel, testID: "dual-velocity-side-label-L" },
|
|
9467
|
+
{ label: rightLabel, testID: "dual-velocity-side-label-R" }
|
|
9468
|
+
]
|
|
9469
|
+
}
|
|
9470
|
+
),
|
|
9471
|
+
/* @__PURE__ */ jsxs(reactNative.View, { style: { flex: 1, gap: DUAL_WING_GAP }, children: [
|
|
9472
|
+
/* @__PURE__ */ jsx(reactNative.View, { accessibilityElementsHidden: true, testID: "dual-velocity-wing-up", children: wing("up", aligned.left, leftDone) }),
|
|
9473
|
+
/* @__PURE__ */ jsx(reactNative.View, { accessibilityElementsHidden: true, testID: "dual-velocity-wing-down", children: wing("down", aligned.right, rightDone) })
|
|
9474
|
+
] })
|
|
9475
|
+
]
|
|
9476
|
+
}
|
|
9477
|
+
);
|
|
9478
|
+
}
|
|
9479
|
+
function DualVelocityRail({
|
|
9480
|
+
leftDone,
|
|
9481
|
+
rightDone,
|
|
9482
|
+
leftStream,
|
|
9483
|
+
rightStream,
|
|
9484
|
+
zones,
|
|
9485
|
+
barColor: barColor2,
|
|
9486
|
+
scale,
|
|
9487
|
+
targetReps,
|
|
9488
|
+
liveRepIndex,
|
|
9489
|
+
height,
|
|
9490
|
+
className,
|
|
9491
|
+
label,
|
|
9492
|
+
viewProps
|
|
9493
|
+
}) {
|
|
9494
|
+
const plotHalf = (height - DUAL_WING_GAP) / 2;
|
|
9495
|
+
const sharedMax = Math.max(...leftDone, ...rightDone, 0);
|
|
9496
|
+
const aligned = alignDualSlots(
|
|
9497
|
+
leftStream ? streamNaturalSlots(leftStream, targetReps) : [],
|
|
9498
|
+
rightStream ? streamNaturalSlots(rightStream, targetReps) : []
|
|
9499
|
+
);
|
|
9500
|
+
const { style: externalStyle, ...restProps } = viewProps;
|
|
9501
|
+
const wing = (orientation, columns, done) => /* @__PURE__ */ jsx(
|
|
9502
|
+
VelocityStrip,
|
|
9503
|
+
{
|
|
9504
|
+
variant: "expanded",
|
|
9505
|
+
showNumbers: false,
|
|
9506
|
+
showInfo: false,
|
|
9507
|
+
orientation,
|
|
9508
|
+
velocities: done,
|
|
9509
|
+
columnSlots: columns,
|
|
9510
|
+
scale,
|
|
9511
|
+
scaleMax: scale === "fixed" ? void 0 : sharedMax,
|
|
9512
|
+
barColor: barColor2,
|
|
9513
|
+
zones,
|
|
9514
|
+
liveRepIndex,
|
|
9515
|
+
height: plotHalf
|
|
9516
|
+
}
|
|
9517
|
+
);
|
|
9518
|
+
return /* @__PURE__ */ jsxs(
|
|
9519
|
+
reactNative.View,
|
|
9520
|
+
{
|
|
9521
|
+
className,
|
|
9522
|
+
style: [{ height, flexDirection: "column", gap: DUAL_WING_GAP }, externalStyle],
|
|
9523
|
+
accessibilityRole: "image",
|
|
9524
|
+
accessibilityLabel: label,
|
|
9525
|
+
testID: "dual-velocity-strip",
|
|
9526
|
+
...restProps,
|
|
9527
|
+
children: [
|
|
9528
|
+
/* @__PURE__ */ jsx(reactNative.View, { accessibilityElementsHidden: true, testID: "dual-velocity-wing-up", children: wing("up", aligned.left, leftDone) }),
|
|
9529
|
+
/* @__PURE__ */ jsx(reactNative.View, { accessibilityElementsHidden: true, testID: "dual-velocity-wing-down", children: wing("down", aligned.right, rightDone) })
|
|
9530
|
+
]
|
|
9531
|
+
}
|
|
9532
|
+
);
|
|
9533
|
+
}
|
|
9534
|
+
function DualVelocityCompactStrip({
|
|
9535
|
+
leftDone,
|
|
9536
|
+
rightDone,
|
|
9537
|
+
leftStream,
|
|
9538
|
+
rightStream,
|
|
9539
|
+
zones,
|
|
9540
|
+
barColor: barColor2,
|
|
9541
|
+
targetReps,
|
|
9542
|
+
height,
|
|
9543
|
+
className,
|
|
9544
|
+
label,
|
|
9545
|
+
viewProps
|
|
9546
|
+
}) {
|
|
9547
|
+
const half = height === DUAL_COMPACT_HEIGHT ? COMPACT_FOLD_HALF : (height - COMPACT_FOLD_GAP) / 2;
|
|
9548
|
+
const aligned = alignDualSlots(
|
|
9549
|
+
leftStream ? streamNaturalSlots(leftStream, targetReps) : [],
|
|
9550
|
+
rightStream ? streamNaturalSlots(rightStream, targetReps) : []
|
|
9551
|
+
);
|
|
9552
|
+
const { style: externalStyle, ...restProps } = viewProps;
|
|
9553
|
+
const wing = (orientation, columns, done) => /* @__PURE__ */ jsx(
|
|
9554
|
+
VelocityStrip,
|
|
9555
|
+
{
|
|
9556
|
+
variant: "compact",
|
|
9557
|
+
orientation,
|
|
9558
|
+
velocities: done,
|
|
9559
|
+
columnSlots: columns,
|
|
9560
|
+
barColor: barColor2,
|
|
9561
|
+
zones,
|
|
9562
|
+
height: half
|
|
9563
|
+
}
|
|
9564
|
+
);
|
|
9565
|
+
return /* @__PURE__ */ jsxs(
|
|
9566
|
+
reactNative.View,
|
|
9567
|
+
{
|
|
9568
|
+
className,
|
|
9569
|
+
style: [{ height, flexDirection: "column", gap: COMPACT_FOLD_GAP }, externalStyle],
|
|
9570
|
+
accessibilityRole: "image",
|
|
9571
|
+
accessibilityLabel: label,
|
|
9572
|
+
testID: "dual-velocity-strip",
|
|
9573
|
+
...restProps,
|
|
9574
|
+
children: [
|
|
9575
|
+
/* @__PURE__ */ jsx(reactNative.View, { accessibilityElementsHidden: true, testID: "dual-velocity-wing-up", children: wing("up", aligned.left, leftDone) }),
|
|
9576
|
+
/* @__PURE__ */ jsx(reactNative.View, { accessibilityElementsHidden: true, testID: "dual-velocity-wing-down", children: wing("down", aligned.right, rightDone) })
|
|
9577
|
+
]
|
|
9204
9578
|
}
|
|
9205
9579
|
);
|
|
9206
9580
|
}
|
|
9581
|
+
function DualVelocityStrip({
|
|
9582
|
+
left,
|
|
9583
|
+
right,
|
|
9584
|
+
zones,
|
|
9585
|
+
barColor: barColor2 = "loss",
|
|
9586
|
+
targetReps,
|
|
9587
|
+
liveRepIndex,
|
|
9588
|
+
variant = "hero",
|
|
9589
|
+
scale = "peak",
|
|
9590
|
+
height,
|
|
9591
|
+
className,
|
|
9592
|
+
...props
|
|
9593
|
+
}) {
|
|
9594
|
+
const resolvedHeight = height ?? (variant === "hero" ? DUAL_HERO_HEIGHT : variant === "compact" ? DUAL_COMPACT_HEIGHT : DUAL_RAIL_HEIGHT);
|
|
9595
|
+
const leftDone = streamDone(left);
|
|
9596
|
+
const rightDone = streamDone(right);
|
|
9597
|
+
const label = `Dual velocity chart, left ${leftDone.length} of ${Math.max(leftDone.length, targetReps ?? leftDone.length)} reps, right ${rightDone.length} of ${Math.max(rightDone.length, targetReps ?? rightDone.length)} reps`;
|
|
9598
|
+
const shared = {
|
|
9599
|
+
leftDone,
|
|
9600
|
+
rightDone,
|
|
9601
|
+
leftStream: left,
|
|
9602
|
+
rightStream: right,
|
|
9603
|
+
leftLabel: left.label,
|
|
9604
|
+
rightLabel: right.label,
|
|
9605
|
+
zones,
|
|
9606
|
+
barColor: barColor2,
|
|
9607
|
+
scale,
|
|
9608
|
+
targetReps,
|
|
9609
|
+
height: resolvedHeight,
|
|
9610
|
+
className,
|
|
9611
|
+
label,
|
|
9612
|
+
viewProps: props
|
|
9613
|
+
};
|
|
9614
|
+
if (variant === "rail") return /* @__PURE__ */ jsx(DualVelocityRail, { ...shared, liveRepIndex });
|
|
9615
|
+
if (variant === "compact") return /* @__PURE__ */ jsx(DualVelocityCompactStrip, { ...shared });
|
|
9616
|
+
return /* @__PURE__ */ jsx(DualVelocityHero, { ...shared, liveRepIndex });
|
|
9617
|
+
}
|
|
9207
9618
|
function VelocityStrip({
|
|
9208
9619
|
velocities,
|
|
9209
9620
|
set,
|
|
9210
9621
|
zones,
|
|
9622
|
+
barColor: barColor2 = "loss",
|
|
9623
|
+
showLossBands,
|
|
9624
|
+
orientation = "up",
|
|
9625
|
+
scaleMax,
|
|
9626
|
+
hideBaseline,
|
|
9627
|
+
columnSlots,
|
|
9628
|
+
label,
|
|
9211
9629
|
liveRepIndex,
|
|
9212
9630
|
expanded = true,
|
|
9213
9631
|
onToggle,
|
|
@@ -9221,51 +9639,29 @@ function VelocityStrip({
|
|
|
9221
9639
|
className,
|
|
9222
9640
|
...props
|
|
9223
9641
|
}) {
|
|
9642
|
+
const lossBandsOn = showLossBands ?? barColor2 === "loss";
|
|
9224
9643
|
const doneVelocities = set ? deriveDoneVelocities(set) : velocities ?? [];
|
|
9225
|
-
const slots = set ? buildSlots(set) : doneVelocities.map((v, i) => ({
|
|
9226
|
-
kind: "rep",
|
|
9227
|
-
velocity: v,
|
|
9228
|
-
leadingGap: i === 0 ? 0 : REP_GAP
|
|
9229
|
-
}));
|
|
9230
9644
|
const maxVelocity = Math.max(...doneVelocities, 0);
|
|
9231
9645
|
const meanVelocity = calculateMeanVelocity(doneVelocities);
|
|
9232
9646
|
const loss = calculateVelocityLoss(doneVelocities);
|
|
9233
9647
|
const framed = showNumbers || showInfo;
|
|
9234
|
-
const scaleDenom = scaleDenominator(scale, maxVelocity);
|
|
9235
9648
|
const hasZones = zones != null && zones.length > 0;
|
|
9236
|
-
const
|
|
9237
|
-
const
|
|
9238
|
-
if (slot.kind === "rep") return barColorFor(slot.velocity ?? 0);
|
|
9239
|
-
if (slot.kind === "todo") return TODO_COLOR2;
|
|
9240
|
-
return SET_STRIP_VARIABLE_COLOR;
|
|
9241
|
-
};
|
|
9649
|
+
const zoneColorFor = makeBarColorFor(zones);
|
|
9650
|
+
const barColorFor = (v) => barColor2 === "loss" ? getVelocityLossColor(velocityLossForRep(v, maxVelocity)) : zoneColorFor(v);
|
|
9242
9651
|
const meanZone = hasZones ? classifyBand(meanVelocity, zones)?.label ?? "" : getVelocityZoneName(meanVelocity);
|
|
9243
|
-
const [
|
|
9244
|
-
const [labelOpacity] = React25.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
|
|
9652
|
+
const [expandProgress] = React25.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
|
|
9245
9653
|
const [infoOpacity] = React25.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
|
|
9246
9654
|
const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
|
|
9247
9655
|
const isNewPeak = liveVelocity != null && maxVelocity > 0 && liveVelocity === maxVelocity;
|
|
9248
|
-
const liveScale = useLiveRepPop(
|
|
9249
|
-
variant === "expanded" && framed,
|
|
9250
|
-
liveRepIndex,
|
|
9251
|
-
liveVelocity,
|
|
9252
|
-
isNewPeak
|
|
9253
|
-
);
|
|
9254
9656
|
React25.useEffect(() => {
|
|
9255
9657
|
if (variant !== "expanded" || !framed) return;
|
|
9256
9658
|
reactNative.Animated.parallel([
|
|
9257
|
-
reactNative.Animated.timing(
|
|
9258
|
-
toValue: expanded ?
|
|
9659
|
+
reactNative.Animated.timing(expandProgress, {
|
|
9660
|
+
toValue: expanded ? 1 : 0,
|
|
9259
9661
|
duration: ANIMATION_DURATION,
|
|
9260
9662
|
easing: ANIMATION_EASING,
|
|
9261
9663
|
useNativeDriver: false
|
|
9262
9664
|
}),
|
|
9263
|
-
reactNative.Animated.timing(labelOpacity, {
|
|
9264
|
-
toValue: expanded ? 1 : 0,
|
|
9265
|
-
duration: 300,
|
|
9266
|
-
delay: expanded ? 150 : 0,
|
|
9267
|
-
useNativeDriver: false
|
|
9268
|
-
}),
|
|
9269
9665
|
reactNative.Animated.timing(infoOpacity, {
|
|
9270
9666
|
toValue: expanded ? 1 : 0,
|
|
9271
9667
|
duration: 300,
|
|
@@ -9273,250 +9669,176 @@ function VelocityStrip({
|
|
|
9273
9669
|
useNativeDriver: false
|
|
9274
9670
|
})
|
|
9275
9671
|
]).start();
|
|
9276
|
-
}, [expanded, variant, framed,
|
|
9672
|
+
}, [expanded, variant, framed, expandProgress, infoOpacity]);
|
|
9277
9673
|
if (set == null && velocities == null) return null;
|
|
9278
9674
|
const repCount = doneVelocities.length;
|
|
9279
9675
|
const miniLabel = set ? setAccessibilityLabel(set, repCount) : `Velocity strip, ${repCount} reps`;
|
|
9280
9676
|
if (variant === "hero") {
|
|
9281
|
-
const heroHeight = height === EXPANDED_HEIGHT ?
|
|
9677
|
+
const heroHeight = height === EXPANDED_HEIGHT && columnSlots == null ? SET_BAR_DEFAULT_HEIGHT : height;
|
|
9678
|
+
const heroSlots = columnSlots ?? (set ? buildSlots(set).map((s) => ({
|
|
9679
|
+
kind: s.kind,
|
|
9680
|
+
value: s.velocity,
|
|
9681
|
+
leadingGap: s.leadingGap
|
|
9682
|
+
})) : doneVelocities.map((v) => ({ kind: "rep", value: v })));
|
|
9683
|
+
const total = Math.max(repCount, targetReps ?? repCount);
|
|
9684
|
+
const heroLabel = maxVelocity > 0 ? `Velocity chart, ${repCount} of ${total} reps, best ${formatVelocity(maxVelocity)} meters per second` : `Velocity chart, ${repCount} of ${total} reps`;
|
|
9282
9685
|
return /* @__PURE__ */ jsx(
|
|
9283
|
-
|
|
9686
|
+
SetBarChart,
|
|
9284
9687
|
{
|
|
9285
|
-
|
|
9286
|
-
barColorFor,
|
|
9287
|
-
|
|
9288
|
-
|
|
9688
|
+
slots: heroSlots,
|
|
9689
|
+
colorFor: barColorFor,
|
|
9690
|
+
height: heroHeight,
|
|
9691
|
+
scale,
|
|
9692
|
+
scaleMax,
|
|
9693
|
+
orientation,
|
|
9289
9694
|
liveRepIndex,
|
|
9290
9695
|
isNewPeak,
|
|
9291
|
-
targetReps,
|
|
9292
|
-
|
|
9696
|
+
targetReps: set || columnSlots ? void 0 : targetReps,
|
|
9697
|
+
label,
|
|
9698
|
+
showValueLabels: true,
|
|
9699
|
+
formatValue: formatVelocity,
|
|
9700
|
+
renderReference: (g) => velocityReferenceOverlay(g, lossBandsOn),
|
|
9701
|
+
hideBaseline: true,
|
|
9702
|
+
testID: "velocity-strip-hero",
|
|
9703
|
+
testIDPrefix: "velocity",
|
|
9704
|
+
accessibilityLabel: heroLabel,
|
|
9293
9705
|
className,
|
|
9294
9706
|
viewProps: props
|
|
9295
9707
|
}
|
|
9296
9708
|
);
|
|
9297
9709
|
}
|
|
9298
|
-
if (variant === "
|
|
9299
|
-
const
|
|
9710
|
+
if (variant === "compact") {
|
|
9711
|
+
const compactSlots = columnSlots ?? (set ? buildSlots(set).map((s) => ({
|
|
9712
|
+
kind: s.kind,
|
|
9713
|
+
value: s.velocity,
|
|
9714
|
+
leadingGap: s.leadingGap
|
|
9715
|
+
})) : doneVelocities.map((v) => ({ kind: "rep", value: v })));
|
|
9716
|
+
const compactHeight = height === EXPANDED_HEIGHT ? COMPACT_HEIGHT : height;
|
|
9300
9717
|
return /* @__PURE__ */ jsx(
|
|
9301
|
-
|
|
9718
|
+
SetBarChart,
|
|
9302
9719
|
{
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9720
|
+
slots: compactSlots,
|
|
9721
|
+
colorFor: barColorFor,
|
|
9722
|
+
height: compactHeight,
|
|
9723
|
+
scale,
|
|
9724
|
+
scaleMax,
|
|
9725
|
+
orientation,
|
|
9726
|
+
flat: true,
|
|
9727
|
+
barRadius: 2,
|
|
9728
|
+
cornerStyle: "all",
|
|
9729
|
+
targetReps: set || columnSlots ? void 0 : targetReps,
|
|
9730
|
+
label,
|
|
9731
|
+
hideBaseline: true,
|
|
9732
|
+
testID: "velocity-strip-compact",
|
|
9733
|
+
testIDPrefix: "velocity",
|
|
9309
9734
|
accessibilityLabel: miniLabel,
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
children: slots.map((slot, i) => /* @__PURE__ */ jsx(
|
|
9313
|
-
reactNative.View,
|
|
9314
|
-
{
|
|
9315
|
-
style: {
|
|
9316
|
-
flex: 1,
|
|
9317
|
-
backgroundColor: slotColor(slot),
|
|
9318
|
-
borderRadius: 1,
|
|
9319
|
-
minWidth: 4,
|
|
9320
|
-
height: "100%",
|
|
9321
|
-
// The container's uniform 2px gap covers rep spacing; a wide slot adds
|
|
9322
|
-
// only the EXTRA (WIDE_GAP − REP_GAP) so the notch totals WIDE_GAP.
|
|
9323
|
-
marginLeft: Math.max(0, slot.leadingGap - REP_GAP),
|
|
9324
|
-
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
9325
|
-
},
|
|
9326
|
-
accessibilityElementsHidden: true,
|
|
9327
|
-
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`
|
|
9328
|
-
},
|
|
9329
|
-
i
|
|
9330
|
-
))
|
|
9735
|
+
className,
|
|
9736
|
+
viewProps: props
|
|
9331
9737
|
}
|
|
9332
9738
|
);
|
|
9333
9739
|
}
|
|
9334
9740
|
if (!framed) {
|
|
9335
|
-
const
|
|
9741
|
+
const spotlightSlots = columnSlots ?? (set ? buildSlots(set).map((s) => ({
|
|
9742
|
+
kind: s.kind,
|
|
9743
|
+
value: s.velocity,
|
|
9744
|
+
leadingGap: s.leadingGap
|
|
9745
|
+
})) : doneVelocities.map((v) => ({ kind: "rep", value: v })));
|
|
9336
9746
|
return /* @__PURE__ */ jsx(
|
|
9337
|
-
|
|
9747
|
+
SetBarChart,
|
|
9338
9748
|
{
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
|
|
9344
|
-
|
|
9749
|
+
slots: spotlightSlots,
|
|
9750
|
+
colorFor: barColorFor,
|
|
9751
|
+
height,
|
|
9752
|
+
scale,
|
|
9753
|
+
scaleMax,
|
|
9754
|
+
orientation,
|
|
9755
|
+
liveRepIndex,
|
|
9756
|
+
isNewPeak,
|
|
9757
|
+
barRadius: 2,
|
|
9758
|
+
cornerStyle: "top",
|
|
9759
|
+
targetReps: set ? void 0 : targetReps,
|
|
9760
|
+
label,
|
|
9761
|
+
hideBaseline: true,
|
|
9762
|
+
testID: "velocity-strip-spotlight",
|
|
9763
|
+
testIDPrefix: "velocity",
|
|
9345
9764
|
accessibilityLabel: miniLabel,
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
children: slots.map((slot, i) => /* @__PURE__ */ jsx(
|
|
9349
|
-
reactNative.View,
|
|
9350
|
-
{
|
|
9351
|
-
style: {
|
|
9352
|
-
flex: 1,
|
|
9353
|
-
minWidth: 4,
|
|
9354
|
-
height: bareSlotHeight(slot, height, scaleDenom),
|
|
9355
|
-
// Top-rounded to match the framed chart's bars (the "rounded tops from
|
|
9356
|
-
// the numbers one"); square bottoms since bars sit on the baseline.
|
|
9357
|
-
borderTopLeftRadius: 2,
|
|
9358
|
-
borderTopRightRadius: 2,
|
|
9359
|
-
backgroundColor: slotColor(slot),
|
|
9360
|
-
marginLeft: Math.max(0, slot.leadingGap - REP_GAP),
|
|
9361
|
-
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
9362
|
-
},
|
|
9363
|
-
accessibilityElementsHidden: true,
|
|
9364
|
-
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`
|
|
9365
|
-
},
|
|
9366
|
-
i
|
|
9367
|
-
))
|
|
9765
|
+
className,
|
|
9766
|
+
viewProps: props
|
|
9368
9767
|
}
|
|
9369
9768
|
);
|
|
9370
9769
|
}
|
|
9371
9770
|
const stripLabel = set ? `${setAccessibilityLabel(set, repCount)}, tap to ${expanded ? "collapse" : "expand"}` : `Velocity chart for set, ${repCount} reps, tap to ${expanded ? "collapse" : "expand"}`;
|
|
9372
9771
|
const hasInteractiveContainer = onToggle != null;
|
|
9373
9772
|
const hasInteractiveReps = onRepPress != null && expanded;
|
|
9374
|
-
const
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9773
|
+
const framedSlots = set ? buildSlots(set).map((sl) => ({
|
|
9774
|
+
kind: sl.kind,
|
|
9775
|
+
value: sl.velocity,
|
|
9776
|
+
leadingGap: sl.leadingGap
|
|
9777
|
+
})) : doneVelocities.map((v) => ({ kind: "rep", value: v }));
|
|
9778
|
+
const needsBarOverlay = showNumbers || onRepPress != null;
|
|
9779
|
+
const renderFramedBarOverlay = needsBarOverlay ? (repIndex, value) => /* @__PURE__ */ jsxs(jsxRuntime.Fragment, { children: [
|
|
9780
|
+
showNumbers && /* @__PURE__ */ jsx(
|
|
9781
|
+
reactNative.Animated.View,
|
|
9379
9782
|
{
|
|
9380
9783
|
style: {
|
|
9381
|
-
|
|
9382
|
-
|
|
9383
|
-
|
|
9384
|
-
|
|
9784
|
+
opacity: expandProgress,
|
|
9785
|
+
position: "absolute",
|
|
9786
|
+
top: -13,
|
|
9787
|
+
left: 0,
|
|
9788
|
+
right: 0,
|
|
9789
|
+
alignItems: "center"
|
|
9385
9790
|
},
|
|
9791
|
+
accessibilityElementsHidden: true,
|
|
9792
|
+
pointerEvents: "none",
|
|
9386
9793
|
children: /* @__PURE__ */ jsx(
|
|
9387
|
-
reactNative.
|
|
9794
|
+
reactNative.Text,
|
|
9388
9795
|
{
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
borderTopRightRadius: 2,
|
|
9394
|
-
backgroundColor: slotColor(slot),
|
|
9395
|
-
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
9396
|
-
},
|
|
9397
|
-
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`,
|
|
9398
|
-
accessibilityRole: "image",
|
|
9399
|
-
accessibilityLabel: slotAccessibilityLabel(slot, i)
|
|
9796
|
+
className: "text-text-secondary",
|
|
9797
|
+
style: { fontSize: 8, fontWeight: "600" },
|
|
9798
|
+
testID: `velocity-label-${repIndex}`,
|
|
9799
|
+
children: formatVelocity(value)
|
|
9400
9800
|
}
|
|
9401
9801
|
)
|
|
9402
|
-
}
|
|
9403
|
-
|
|
9404
|
-
|
|
9405
|
-
|
|
9802
|
+
}
|
|
9803
|
+
),
|
|
9804
|
+
onRepPress && expanded && /* @__PURE__ */ jsx(
|
|
9805
|
+
reactNative.Pressable,
|
|
9806
|
+
{
|
|
9807
|
+
style: { position: "absolute", top: 0, bottom: 0, left: 0, right: 0 },
|
|
9808
|
+
onPress: () => onRepPress(repIndex, value),
|
|
9809
|
+
accessibilityRole: "button",
|
|
9810
|
+
accessibilityLabel: `Rep ${repIndex + 1}: ${formatVelocity(value)} meters per second, tap for details`,
|
|
9811
|
+
testID: `velocity-bar-pressable-${repIndex}`
|
|
9812
|
+
}
|
|
9813
|
+
)
|
|
9814
|
+
] }) : void 0;
|
|
9406
9815
|
const stripContent = /* @__PURE__ */ jsxs(
|
|
9407
9816
|
reactNative.Animated.View,
|
|
9408
9817
|
{
|
|
9409
|
-
className: [className,
|
|
9410
|
-
style:
|
|
9411
|
-
{
|
|
9412
|
-
height: heightAnim,
|
|
9413
|
-
width: "100%",
|
|
9414
|
-
gap: 2,
|
|
9415
|
-
borderRadius: expanded ? 6 : 2,
|
|
9416
|
-
paddingTop: expanded ? 16 : 0,
|
|
9417
|
-
paddingBottom: expanded ? 24 : 0,
|
|
9418
|
-
// No horizontal inset — the framed chart's bars span full width, matching
|
|
9419
|
-
// the bare spotlight and mini. Numbers/info sit in the vertical padding.
|
|
9420
|
-
paddingHorizontal: 0,
|
|
9421
|
-
paddingVertical: expanded ? void 0 : 8,
|
|
9422
|
-
overflow: expanded ? "visible" : "hidden"
|
|
9423
|
-
}
|
|
9424
|
-
],
|
|
9818
|
+
className: [className, "bg-surface-raised"].filter(Boolean).join(" "),
|
|
9819
|
+
style: { width: "100%", borderRadius: 6, paddingTop: 16, paddingBottom: showInfo ? 8 : 4 },
|
|
9425
9820
|
accessibilityRole: hasInteractiveContainer || hasInteractiveReps ? "none" : "button",
|
|
9426
9821
|
accessibilityLabel: hasInteractiveContainer || hasInteractiveReps ? void 0 : stripLabel,
|
|
9427
9822
|
testID: "velocity-strip",
|
|
9428
9823
|
...props,
|
|
9429
9824
|
children: [
|
|
9430
|
-
/* @__PURE__ */ jsx(
|
|
9431
|
-
|
|
9432
|
-
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
borderBottomLeftRadius: 0,
|
|
9446
|
-
borderBottomRightRadius: 0,
|
|
9447
|
-
backgroundColor: barBackground
|
|
9448
|
-
};
|
|
9449
|
-
const barInner = isLive ? /* @__PURE__ */ jsx(
|
|
9450
|
-
reactNative.Animated.View,
|
|
9451
|
-
{
|
|
9452
|
-
style: [barStyle, { transform: [{ scale: liveScale }] }],
|
|
9453
|
-
testID: `velocity-bar-${i}`,
|
|
9454
|
-
accessibilityRole: "image",
|
|
9455
|
-
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second${liveLabelSuffix}`
|
|
9456
|
-
}
|
|
9457
|
-
) : /* @__PURE__ */ jsx(
|
|
9458
|
-
reactNative.View,
|
|
9459
|
-
{
|
|
9460
|
-
style: barStyle,
|
|
9461
|
-
testID: `velocity-bar-${i}`,
|
|
9462
|
-
accessibilityRole: "image",
|
|
9463
|
-
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second`
|
|
9464
|
-
}
|
|
9465
|
-
);
|
|
9466
|
-
const bar = /* @__PURE__ */ jsxs(
|
|
9467
|
-
reactNative.View,
|
|
9468
|
-
{
|
|
9469
|
-
style: {
|
|
9470
|
-
flex: 1,
|
|
9471
|
-
height: "100%",
|
|
9472
|
-
justifyContent: "flex-end",
|
|
9473
|
-
position: "relative"
|
|
9474
|
-
},
|
|
9475
|
-
children: [
|
|
9476
|
-
expanded && showNumbers && /* @__PURE__ */ jsx(
|
|
9477
|
-
reactNative.Animated.View,
|
|
9478
|
-
{
|
|
9479
|
-
style: {
|
|
9480
|
-
opacity: labelOpacity,
|
|
9481
|
-
alignItems: "center",
|
|
9482
|
-
position: "absolute",
|
|
9483
|
-
top: -13,
|
|
9484
|
-
left: 0,
|
|
9485
|
-
right: 0
|
|
9486
|
-
},
|
|
9487
|
-
accessibilityElementsHidden: true,
|
|
9488
|
-
children: /* @__PURE__ */ jsx(
|
|
9489
|
-
reactNative.Text,
|
|
9490
|
-
{
|
|
9491
|
-
className: "text-text-secondary",
|
|
9492
|
-
style: { fontSize: 8, fontWeight: "600" },
|
|
9493
|
-
testID: `velocity-label-${i}`,
|
|
9494
|
-
children: formatVelocity(v)
|
|
9495
|
-
}
|
|
9496
|
-
)
|
|
9497
|
-
}
|
|
9498
|
-
),
|
|
9499
|
-
barInner
|
|
9500
|
-
]
|
|
9501
|
-
},
|
|
9502
|
-
i
|
|
9503
|
-
);
|
|
9504
|
-
if (onRepPress && expanded) {
|
|
9505
|
-
return /* @__PURE__ */ jsx(
|
|
9506
|
-
reactNative.Pressable,
|
|
9507
|
-
{
|
|
9508
|
-
style: { flex: 1, height: "100%" },
|
|
9509
|
-
onPress: () => onRepPress(i, v),
|
|
9510
|
-
accessibilityRole: "button",
|
|
9511
|
-
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second, tap for details`,
|
|
9512
|
-
testID: `velocity-bar-pressable-${i}`,
|
|
9513
|
-
children: bar
|
|
9514
|
-
},
|
|
9515
|
-
i
|
|
9516
|
-
);
|
|
9825
|
+
/* @__PURE__ */ jsx(
|
|
9826
|
+
SetBarChart,
|
|
9827
|
+
{
|
|
9828
|
+
slots: framedSlots,
|
|
9829
|
+
colorFor: barColorFor,
|
|
9830
|
+
height,
|
|
9831
|
+
scale,
|
|
9832
|
+
scaleMax,
|
|
9833
|
+
expandProgress,
|
|
9834
|
+
renderBarOverlay: renderFramedBarOverlay,
|
|
9835
|
+
targetReps: set ? void 0 : targetReps,
|
|
9836
|
+
barRadius: 2,
|
|
9837
|
+
cornerStyle: "top",
|
|
9838
|
+
hideBaseline: true,
|
|
9839
|
+
testIDPrefix: "velocity"
|
|
9517
9840
|
}
|
|
9518
|
-
|
|
9519
|
-
}) }),
|
|
9841
|
+
),
|
|
9520
9842
|
expanded && showInfo && /* @__PURE__ */ jsxs(
|
|
9521
9843
|
reactNative.Animated.View,
|
|
9522
9844
|
{
|
|
@@ -9524,10 +9846,8 @@ function VelocityStrip({
|
|
|
9524
9846
|
flexDirection: "row",
|
|
9525
9847
|
justifyContent: "space-between",
|
|
9526
9848
|
opacity: infoOpacity,
|
|
9527
|
-
|
|
9528
|
-
|
|
9529
|
-
left: 6,
|
|
9530
|
-
right: 6
|
|
9849
|
+
marginTop: 6,
|
|
9850
|
+
paddingHorizontal: 6
|
|
9531
9851
|
},
|
|
9532
9852
|
testID: "velocity-info-row",
|
|
9533
9853
|
children: [
|
|
@@ -9535,10 +9855,7 @@ function VelocityStrip({
|
|
|
9535
9855
|
reactNative.Text,
|
|
9536
9856
|
{
|
|
9537
9857
|
className: "text-text-secondary",
|
|
9538
|
-
style: {
|
|
9539
|
-
fontSize: 10,
|
|
9540
|
-
fontFamily: "Inter, sans-serif"
|
|
9541
|
-
},
|
|
9858
|
+
style: { fontSize: 10, fontFamily: "Inter, sans-serif" },
|
|
9542
9859
|
children: [
|
|
9543
9860
|
meanZone,
|
|
9544
9861
|
" ",
|
|
@@ -9553,11 +9870,7 @@ function VelocityStrip({
|
|
|
9553
9870
|
reactNative.Text,
|
|
9554
9871
|
{
|
|
9555
9872
|
className: "text-text-secondary",
|
|
9556
|
-
style: {
|
|
9557
|
-
fontSize: 10,
|
|
9558
|
-
fontFamily: "Inter, sans-serif",
|
|
9559
|
-
...getLossStyle(loss)
|
|
9560
|
-
},
|
|
9873
|
+
style: { fontSize: 10, fontFamily: "Inter, sans-serif", ...getLossStyle(loss) },
|
|
9561
9874
|
children: [
|
|
9562
9875
|
"Loss: ",
|
|
9563
9876
|
loss,
|
|
@@ -9585,13 +9898,13 @@ function VelocityStrip({
|
|
|
9585
9898
|
}
|
|
9586
9899
|
return stripContent;
|
|
9587
9900
|
}
|
|
9588
|
-
var
|
|
9901
|
+
var t7 = getSemanticColors("dark");
|
|
9589
9902
|
var dotColorMap = {
|
|
9590
9903
|
untrained: "#2C2C2C",
|
|
9591
|
-
behind:
|
|
9592
|
-
ontrack:
|
|
9593
|
-
target:
|
|
9594
|
-
over:
|
|
9904
|
+
behind: t7["brand-secondary"],
|
|
9905
|
+
ontrack: t7["status-success"],
|
|
9906
|
+
target: t7["brand-primary"],
|
|
9907
|
+
over: t7["status-error"]
|
|
9595
9908
|
};
|
|
9596
9909
|
function MuscleGroupChip({
|
|
9597
9910
|
name,
|
|
@@ -9865,6 +10178,7 @@ function RowStrip({ set }) {
|
|
|
9865
10178
|
showInfo: false,
|
|
9866
10179
|
height: 24,
|
|
9867
10180
|
scale: "fixed",
|
|
10181
|
+
liveRepIndex: set.velocities.length - 1,
|
|
9868
10182
|
set: { type: "straight", velocities: set.velocities, planned: set.target.reps },
|
|
9869
10183
|
zones
|
|
9870
10184
|
}
|
|
@@ -9874,7 +10188,9 @@ function RowStrip({ set }) {
|
|
|
9874
10188
|
return /* @__PURE__ */ jsx(
|
|
9875
10189
|
VelocityStrip,
|
|
9876
10190
|
{
|
|
9877
|
-
variant: "
|
|
10191
|
+
variant: "compact",
|
|
10192
|
+
height: 8,
|
|
10193
|
+
hideBaseline: true,
|
|
9878
10194
|
set: { type: "straight", velocities, planned: plannedReps(set) },
|
|
9879
10195
|
zones
|
|
9880
10196
|
}
|
|
@@ -10294,7 +10610,7 @@ function RestTimer({
|
|
|
10294
10610
|
}
|
|
10295
10611
|
);
|
|
10296
10612
|
}
|
|
10297
|
-
var
|
|
10613
|
+
var t8 = getSemanticColors("dark");
|
|
10298
10614
|
var TIMES = "\xD7";
|
|
10299
10615
|
var AT = "@";
|
|
10300
10616
|
function SetsRepsLoad({
|
|
@@ -10306,8 +10622,8 @@ function SetsRepsLoad({
|
|
|
10306
10622
|
className,
|
|
10307
10623
|
...props
|
|
10308
10624
|
}) {
|
|
10309
|
-
const value =
|
|
10310
|
-
const sep =
|
|
10625
|
+
const value = t8["text-primary"];
|
|
10626
|
+
const sep = t8["text-tertiary"];
|
|
10311
10627
|
return /* @__PURE__ */ jsxs(
|
|
10312
10628
|
reactNative.View,
|
|
10313
10629
|
{
|
|
@@ -10327,6 +10643,107 @@ function SetsRepsLoad({
|
|
|
10327
10643
|
}
|
|
10328
10644
|
);
|
|
10329
10645
|
}
|
|
10646
|
+
var PULSE_HALF_MS = 950;
|
|
10647
|
+
var PULSE_MIN_OPACITY = 0.45;
|
|
10648
|
+
var SEGMENTED_BAR_GAP = 5;
|
|
10649
|
+
function usePulse2(active) {
|
|
10650
|
+
const [value] = React25.useState(() => new reactNative.Animated.Value(0));
|
|
10651
|
+
React25.useEffect(() => {
|
|
10652
|
+
if (!active) return;
|
|
10653
|
+
const loop = reactNative.Animated.loop(
|
|
10654
|
+
reactNative.Animated.sequence([
|
|
10655
|
+
reactNative.Animated.timing(value, {
|
|
10656
|
+
toValue: 1,
|
|
10657
|
+
duration: PULSE_HALF_MS,
|
|
10658
|
+
easing: reactNative.Easing.inOut(reactNative.Easing.ease),
|
|
10659
|
+
useNativeDriver: false
|
|
10660
|
+
}),
|
|
10661
|
+
reactNative.Animated.timing(value, {
|
|
10662
|
+
toValue: 0,
|
|
10663
|
+
duration: PULSE_HALF_MS,
|
|
10664
|
+
easing: reactNative.Easing.inOut(reactNative.Easing.ease),
|
|
10665
|
+
useNativeDriver: false
|
|
10666
|
+
})
|
|
10667
|
+
])
|
|
10668
|
+
);
|
|
10669
|
+
loop.start();
|
|
10670
|
+
return () => loop.stop();
|
|
10671
|
+
}, [active, value]);
|
|
10672
|
+
return value;
|
|
10673
|
+
}
|
|
10674
|
+
function SegmentedBar({
|
|
10675
|
+
segments,
|
|
10676
|
+
height = 8,
|
|
10677
|
+
gap = SEGMENTED_BAR_GAP,
|
|
10678
|
+
radius = 2,
|
|
10679
|
+
marker = null,
|
|
10680
|
+
segmentTestID,
|
|
10681
|
+
style,
|
|
10682
|
+
...props
|
|
10683
|
+
}) {
|
|
10684
|
+
const pulse = usePulse2(segments.some((s) => s.pulse));
|
|
10685
|
+
return /* @__PURE__ */ jsxs(reactNative.View, { style: [{ flexDirection: "row", height, gap, position: "relative" }, style], ...props, children: [
|
|
10686
|
+
segments.map((seg, i) => {
|
|
10687
|
+
const fillStyle = {
|
|
10688
|
+
width: `${(seg.fill ?? 1) * 100}%`,
|
|
10689
|
+
height: "100%",
|
|
10690
|
+
backgroundColor: seg.color,
|
|
10691
|
+
opacity: seg.opacity
|
|
10692
|
+
};
|
|
10693
|
+
const testID = segmentTestID?.(seg, i) ?? "segmented-bar-segment";
|
|
10694
|
+
return /* @__PURE__ */ jsx(
|
|
10695
|
+
reactNative.View,
|
|
10696
|
+
{
|
|
10697
|
+
style: {
|
|
10698
|
+
flex: seg.weight ?? 1,
|
|
10699
|
+
minWidth: 0,
|
|
10700
|
+
height: "100%",
|
|
10701
|
+
marginLeft: seg.leadingGap,
|
|
10702
|
+
borderRadius: radius,
|
|
10703
|
+
overflow: "hidden"
|
|
10704
|
+
},
|
|
10705
|
+
children: seg.pulse ? /* @__PURE__ */ jsx(
|
|
10706
|
+
reactNative.Animated.View,
|
|
10707
|
+
{
|
|
10708
|
+
style: {
|
|
10709
|
+
...fillStyle,
|
|
10710
|
+
...seg.pulseColor ? {
|
|
10711
|
+
backgroundColor: pulse.interpolate({
|
|
10712
|
+
inputRange: [0, 1],
|
|
10713
|
+
outputRange: seg.pulseColor
|
|
10714
|
+
})
|
|
10715
|
+
} : {
|
|
10716
|
+
opacity: pulse.interpolate({
|
|
10717
|
+
inputRange: [0, 1],
|
|
10718
|
+
outputRange: [PULSE_MIN_OPACITY, 1]
|
|
10719
|
+
})
|
|
10720
|
+
}
|
|
10721
|
+
},
|
|
10722
|
+
accessibilityElementsHidden: true,
|
|
10723
|
+
testID
|
|
10724
|
+
}
|
|
10725
|
+
) : /* @__PURE__ */ jsx(reactNative.View, { style: fillStyle, accessibilityElementsHidden: true, testID })
|
|
10726
|
+
},
|
|
10727
|
+
i
|
|
10728
|
+
);
|
|
10729
|
+
}),
|
|
10730
|
+
marker ? /* @__PURE__ */ jsx(
|
|
10731
|
+
reactNative.View,
|
|
10732
|
+
{
|
|
10733
|
+
style: {
|
|
10734
|
+
position: "absolute",
|
|
10735
|
+
left: `${marker.position * 100}%`,
|
|
10736
|
+
top: 0,
|
|
10737
|
+
bottom: 0,
|
|
10738
|
+
width: 2,
|
|
10739
|
+
backgroundColor: marker.color
|
|
10740
|
+
},
|
|
10741
|
+
accessibilityElementsHidden: true,
|
|
10742
|
+
testID: "segmented-bar-marker"
|
|
10743
|
+
}
|
|
10744
|
+
) : null
|
|
10745
|
+
] });
|
|
10746
|
+
}
|
|
10330
10747
|
|
|
10331
10748
|
// src/components/custom/Workout/paceTone.ts
|
|
10332
10749
|
function paceTone(progress, target) {
|
|
@@ -10420,75 +10837,209 @@ function ScheduleTiles({ when, now, gap = 1, ...props }) {
|
|
|
10420
10837
|
)
|
|
10421
10838
|
] });
|
|
10422
10839
|
}
|
|
10423
|
-
|
|
10424
|
-
|
|
10425
|
-
|
|
10426
|
-
|
|
10427
|
-
|
|
10428
|
-
|
|
10429
|
-
|
|
10430
|
-
info: { Icon: InfoIcon, color: t8["status-info"], label: "More info" }
|
|
10840
|
+
|
|
10841
|
+
// src/components/custom/Workout/SetBar.tsx
|
|
10842
|
+
var SET_STRIP_ZONES = {
|
|
10843
|
+
slow: primitiveRamps.red[600],
|
|
10844
|
+
moderate: primitiveRamps.orange[400],
|
|
10845
|
+
fast: primitiveRamps.amber[300],
|
|
10846
|
+
fastest: primitiveRamps.green[300]
|
|
10431
10847
|
};
|
|
10432
|
-
var
|
|
10433
|
-
|
|
10434
|
-
|
|
10435
|
-
|
|
10436
|
-
|
|
10437
|
-
|
|
10438
|
-
|
|
10439
|
-
|
|
10440
|
-
|
|
10441
|
-
|
|
10848
|
+
var TODO_COLOR = primitiveColors.charcoal[300];
|
|
10849
|
+
var SET_STRIP_VARIABLE_COLOR = primitiveRamps.cyan[900];
|
|
10850
|
+
var NOTCH_GAP = 2;
|
|
10851
|
+
var CLUSTER_GAP = 3;
|
|
10852
|
+
var MYO_UPCOMING_CLUSTERS = 3;
|
|
10853
|
+
var MYO_UPCOMING_CLUSTER_LEN = 5;
|
|
10854
|
+
var MYO_UPCOMING_FADE = 0.6;
|
|
10855
|
+
var PULSE_RANGE = {
|
|
10856
|
+
[SET_STRIP_ZONES.slow]: [primitiveRamps.red[500], primitiveRamps.red[700]],
|
|
10857
|
+
[SET_STRIP_ZONES.moderate]: [primitiveRamps.orange[300], primitiveRamps.orange[500]],
|
|
10858
|
+
[SET_STRIP_ZONES.fast]: [primitiveRamps.amber[200], primitiveRamps.amber[400]],
|
|
10859
|
+
[SET_STRIP_ZONES.fastest]: [primitiveRamps.green[200], primitiveRamps.green[400]]
|
|
10860
|
+
};
|
|
10861
|
+
function velocityZoneColor(v) {
|
|
10862
|
+
if (v < 0.5) return SET_STRIP_ZONES.slow;
|
|
10863
|
+
if (v < 0.75) return SET_STRIP_ZONES.moderate;
|
|
10864
|
+
if (v < 1) return SET_STRIP_ZONES.fast;
|
|
10865
|
+
return SET_STRIP_ZONES.fastest;
|
|
10442
10866
|
}
|
|
10443
|
-
function
|
|
10444
|
-
|
|
10445
|
-
|
|
10446
|
-
|
|
10447
|
-
|
|
10448
|
-
|
|
10449
|
-
style: {
|
|
10450
|
-
width: 16,
|
|
10451
|
-
height: 16,
|
|
10452
|
-
borderRadius: 8,
|
|
10453
|
-
borderWidth: 1,
|
|
10454
|
-
borderColor: color,
|
|
10455
|
-
alignItems: "center",
|
|
10456
|
-
justifyContent: "center"
|
|
10457
|
-
},
|
|
10458
|
-
accessibilityRole: "image",
|
|
10459
|
-
accessibilityLabel: label,
|
|
10460
|
-
testID: "exercise-indicator",
|
|
10461
|
-
...props,
|
|
10462
|
-
children: /* @__PURE__ */ jsx(Icon, { size: 11, color })
|
|
10463
|
-
}
|
|
10867
|
+
function chunkedSegments(chunks, gap) {
|
|
10868
|
+
return chunks.flatMap(
|
|
10869
|
+
(vels, ci) => vels.map((v, ri) => ({
|
|
10870
|
+
color: velocityZoneColor(v),
|
|
10871
|
+
...ci > 0 && ri === 0 ? { leadingGap: gap } : {}
|
|
10872
|
+
}))
|
|
10464
10873
|
);
|
|
10465
|
-
|
|
10466
|
-
|
|
10874
|
+
}
|
|
10875
|
+
function rangeSegments(floor, max, doneVels) {
|
|
10876
|
+
return Array.from({ length: max }, (_, i) => {
|
|
10877
|
+
if (i < doneVels.length) return { color: velocityZoneColor(doneVels[i]) };
|
|
10878
|
+
return { color: i < floor ? TODO_COLOR : SET_STRIP_VARIABLE_COLOR };
|
|
10879
|
+
});
|
|
10880
|
+
}
|
|
10881
|
+
function myoUpcomingSegments(activationLen, clusterCount) {
|
|
10882
|
+
const activation = Array.from({ length: activationLen }, () => ({ color: TODO_COLOR }));
|
|
10883
|
+
const trail = Array.from(
|
|
10884
|
+
{ length: clusterCount },
|
|
10885
|
+
(_, ci) => Array.from({ length: MYO_UPCOMING_CLUSTER_LEN }, (_2, ri) => ({
|
|
10886
|
+
color: SET_STRIP_VARIABLE_COLOR,
|
|
10887
|
+
opacity: MYO_UPCOMING_FADE ** ci,
|
|
10888
|
+
...ri === 0 ? { leadingGap: CLUSTER_GAP } : {}
|
|
10889
|
+
}))
|
|
10890
|
+
).flat();
|
|
10891
|
+
return [...activation, ...trail];
|
|
10892
|
+
}
|
|
10893
|
+
function setSegments(set) {
|
|
10894
|
+
if (set.status === "todo") {
|
|
10895
|
+
return [{ color: TODO_COLOR }];
|
|
10467
10896
|
}
|
|
10468
|
-
|
|
10897
|
+
if (set.status === "done") {
|
|
10898
|
+
return set.velocities.map((v) => ({ color: velocityZoneColor(v) }));
|
|
10899
|
+
}
|
|
10900
|
+
if (set.status === "range") {
|
|
10901
|
+
return rangeSegments(set.floor, set.max, set.doneVels);
|
|
10902
|
+
}
|
|
10903
|
+
if (set.status === "drop") {
|
|
10904
|
+
return chunkedSegments(set.subloads, NOTCH_GAP);
|
|
10905
|
+
}
|
|
10906
|
+
if (set.status === "myo") {
|
|
10907
|
+
return chunkedSegments([set.activation, ...set.clusters], CLUSTER_GAP);
|
|
10908
|
+
}
|
|
10909
|
+
if (set.status === "myo-upcoming") {
|
|
10910
|
+
return myoUpcomingSegments(set.activationLen, set.clusterCount ?? MYO_UPCOMING_CLUSTERS);
|
|
10911
|
+
}
|
|
10912
|
+
const performed = set.velocities.map((v) => {
|
|
10913
|
+
const color = velocityZoneColor(v);
|
|
10914
|
+
return { color, pulse: true, pulseColor: PULSE_RANGE[color] };
|
|
10915
|
+
});
|
|
10916
|
+
const remaining = Math.max(0, set.planned - set.velocities.length);
|
|
10917
|
+
return [...performed, ...Array.from({ length: remaining }, () => ({ color: TODO_COLOR }))];
|
|
10469
10918
|
}
|
|
10470
|
-
function
|
|
10471
|
-
|
|
10472
|
-
|
|
10919
|
+
function setBarSegmentTestID(seg) {
|
|
10920
|
+
if (seg.pulse) return "set-strip-pulse";
|
|
10921
|
+
if (seg.color === SET_STRIP_VARIABLE_COLOR) return "set-strip-variable";
|
|
10922
|
+
return seg.color === TODO_COLOR ? "set-strip-empty" : "set-strip-fill";
|
|
10473
10923
|
}
|
|
10474
|
-
function
|
|
10475
|
-
|
|
10476
|
-
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
|
|
10480
|
-
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
|
|
10487
|
-
|
|
10488
|
-
|
|
10489
|
-
|
|
10490
|
-
|
|
10491
|
-
|
|
10924
|
+
function SetBar({ set, height = 8 }) {
|
|
10925
|
+
return /* @__PURE__ */ jsx(
|
|
10926
|
+
SegmentedBar,
|
|
10927
|
+
{
|
|
10928
|
+
segments: setSegments(set),
|
|
10929
|
+
height,
|
|
10930
|
+
gap: 0,
|
|
10931
|
+
radius: 0,
|
|
10932
|
+
segmentTestID: setBarSegmentTestID,
|
|
10933
|
+
style: { flex: 1, minWidth: 0, borderRadius: 2, overflow: "hidden" },
|
|
10934
|
+
testID: "set-strip-set"
|
|
10935
|
+
}
|
|
10936
|
+
);
|
|
10937
|
+
}
|
|
10938
|
+
var SET_STRIP_GAP = 5;
|
|
10939
|
+
function setCategory(set) {
|
|
10940
|
+
switch (set.status) {
|
|
10941
|
+
case "done":
|
|
10942
|
+
case "drop":
|
|
10943
|
+
case "myo":
|
|
10944
|
+
return "done";
|
|
10945
|
+
case "active":
|
|
10946
|
+
return "active";
|
|
10947
|
+
case "range":
|
|
10948
|
+
return set.doneVels.length > 0 ? "active" : "upcoming";
|
|
10949
|
+
case "todo":
|
|
10950
|
+
case "myo-upcoming":
|
|
10951
|
+
return "upcoming";
|
|
10952
|
+
}
|
|
10953
|
+
}
|
|
10954
|
+
function describeSets(sets) {
|
|
10955
|
+
const done = sets.filter((s) => setCategory(s) === "done").length;
|
|
10956
|
+
const active = sets.filter((s) => setCategory(s) === "active").length;
|
|
10957
|
+
const upcoming = sets.filter((s) => setCategory(s) === "upcoming").length;
|
|
10958
|
+
return `Set progress: ${done} done, ${active} in progress, ${upcoming} upcoming`;
|
|
10959
|
+
}
|
|
10960
|
+
function SetStrip({ sets, height = 8, className, ...props }) {
|
|
10961
|
+
return /* @__PURE__ */ jsx(
|
|
10962
|
+
reactNative.View,
|
|
10963
|
+
{
|
|
10964
|
+
className,
|
|
10965
|
+
style: { flexDirection: "row", width: "100%", height, gap: SET_STRIP_GAP },
|
|
10966
|
+
accessibilityRole: "image",
|
|
10967
|
+
accessibilityLabel: describeSets(sets),
|
|
10968
|
+
testID: "set-strip",
|
|
10969
|
+
...props,
|
|
10970
|
+
children: sets.map((set, i) => /* @__PURE__ */ jsx(SetBar, { set, height }, i))
|
|
10971
|
+
}
|
|
10972
|
+
);
|
|
10973
|
+
}
|
|
10974
|
+
var t9 = getSemanticColors("dark");
|
|
10975
|
+
var INDICATOR_CONFIG = {
|
|
10976
|
+
imbalance: { Icon: ScaleIcon, color: t9["status-error"], label: "Left/right imbalance" },
|
|
10977
|
+
overshoot: { Icon: AlertTriangleIcon, color: t9["status-error"], label: "Load overshoot" },
|
|
10978
|
+
"velocity-loss": { Icon: TrendingDownIcon, color: t9["status-warning"], label: "Velocity loss" },
|
|
10979
|
+
"missed-reps": { Icon: CircleSlashIcon, color: t9["status-warning"], label: "Missed reps" },
|
|
10980
|
+
pr: { Icon: AwardIcon, color: t9["status-success"], label: "Personal record" },
|
|
10981
|
+
info: { Icon: InfoIcon, color: t9["status-info"], label: "More info" }
|
|
10982
|
+
};
|
|
10983
|
+
var INDICATOR_PRECEDENCE = [
|
|
10984
|
+
"imbalance",
|
|
10985
|
+
"overshoot",
|
|
10986
|
+
"velocity-loss",
|
|
10987
|
+
"missed-reps",
|
|
10988
|
+
"pr",
|
|
10989
|
+
"info"
|
|
10990
|
+
];
|
|
10991
|
+
function resolveIndicator(candidates) {
|
|
10992
|
+
return INDICATOR_PRECEDENCE.find((kind) => candidates.includes(kind));
|
|
10993
|
+
}
|
|
10994
|
+
function ExerciseIndicator({ kind, onPress, className, ...props }) {
|
|
10995
|
+
const { Icon, color, label } = INDICATOR_CONFIG[kind];
|
|
10996
|
+
const chip = /* @__PURE__ */ jsx(
|
|
10997
|
+
reactNative.View,
|
|
10998
|
+
{
|
|
10999
|
+
className,
|
|
11000
|
+
style: {
|
|
11001
|
+
width: 16,
|
|
11002
|
+
height: 16,
|
|
11003
|
+
borderRadius: 8,
|
|
11004
|
+
borderWidth: 1,
|
|
11005
|
+
borderColor: color,
|
|
11006
|
+
alignItems: "center",
|
|
11007
|
+
justifyContent: "center"
|
|
11008
|
+
},
|
|
11009
|
+
accessibilityRole: "image",
|
|
11010
|
+
accessibilityLabel: label,
|
|
11011
|
+
testID: "exercise-indicator",
|
|
11012
|
+
...props,
|
|
11013
|
+
children: /* @__PURE__ */ jsx(Icon, { size: 11, color })
|
|
11014
|
+
}
|
|
11015
|
+
);
|
|
11016
|
+
if (onPress) {
|
|
11017
|
+
return /* @__PURE__ */ jsx(reactNative.Pressable, { onPress, accessibilityRole: "button", accessibilityLabel: label, children: chip });
|
|
11018
|
+
}
|
|
11019
|
+
return chip;
|
|
11020
|
+
}
|
|
11021
|
+
function headingLabel(name, sets, reps, load, unit) {
|
|
11022
|
+
const loadLabel = typeof load === "number" ? roundWeight(load) : load;
|
|
11023
|
+
return `${name}, ${sets}\xD7${reps} @ ${loadLabel} ${unit}`;
|
|
11024
|
+
}
|
|
11025
|
+
function ExerciseHeading({
|
|
11026
|
+
name,
|
|
11027
|
+
sets,
|
|
11028
|
+
reps,
|
|
11029
|
+
load,
|
|
11030
|
+
unit = "lbs",
|
|
11031
|
+
tempo,
|
|
11032
|
+
indicator,
|
|
11033
|
+
dimmed,
|
|
11034
|
+
onPress
|
|
11035
|
+
}) {
|
|
11036
|
+
return /* @__PURE__ */ jsxs(reactNative.View, { style: { opacity: dimmed ? 0.55 : 1 }, testID: "exercise-heading", children: [
|
|
11037
|
+
/* @__PURE__ */ jsxs(
|
|
11038
|
+
reactNative.Pressable,
|
|
11039
|
+
{
|
|
11040
|
+
onPress,
|
|
11041
|
+
accessibilityRole: "button",
|
|
11042
|
+
accessibilityLabel: headingLabel(name, sets, reps, load, unit),
|
|
10492
11043
|
className: "flex-row items-center",
|
|
10493
11044
|
style: { gap: 8 },
|
|
10494
11045
|
testID: "exercise-card-header",
|
|
@@ -10722,7 +11273,9 @@ function CollapsedCard({
|
|
|
10722
11273
|
{
|
|
10723
11274
|
velocities,
|
|
10724
11275
|
zones: velocityZones,
|
|
10725
|
-
variant: "
|
|
11276
|
+
variant: "compact",
|
|
11277
|
+
height: 8,
|
|
11278
|
+
hideBaseline: true,
|
|
10726
11279
|
testID: `exercise-card-velocity-strip-${i}`
|
|
10727
11280
|
},
|
|
10728
11281
|
i
|
|
@@ -11021,6 +11574,7 @@ function SessionRail({
|
|
|
11021
11574
|
next,
|
|
11022
11575
|
stripHeight = 8,
|
|
11023
11576
|
width = DEFAULT_WIDTH,
|
|
11577
|
+
expandedIndex,
|
|
11024
11578
|
onExercisePress,
|
|
11025
11579
|
className,
|
|
11026
11580
|
style,
|
|
@@ -11061,7 +11615,24 @@ function SessionRail({
|
|
|
11061
11615
|
borderBottomWidth: i < exercises.length - 1 ? 1 : 0,
|
|
11062
11616
|
borderBottomColor: DIVIDER
|
|
11063
11617
|
},
|
|
11064
|
-
children: /* @__PURE__ */ jsx(
|
|
11618
|
+
children: i === expandedIndex && ex.sets ? /* @__PURE__ */ jsx(
|
|
11619
|
+
ExerciseCard,
|
|
11620
|
+
{
|
|
11621
|
+
name: ex.name,
|
|
11622
|
+
expanded: true,
|
|
11623
|
+
summary: {
|
|
11624
|
+
sets: ex.summary.sets,
|
|
11625
|
+
reps: ex.summary.reps,
|
|
11626
|
+
// ExerciseCard's summary takes a numeric load; a string load is
|
|
11627
|
+
// an unset/discovery weight, which has no expanded form yet.
|
|
11628
|
+
weight: typeof ex.summary.weight === "number" ? ex.summary.weight : 0,
|
|
11629
|
+
unit: ex.summary.unit
|
|
11630
|
+
},
|
|
11631
|
+
tempo: ex.tempo,
|
|
11632
|
+
sets: ex.sets,
|
|
11633
|
+
onExpandedChange: () => onExercisePress?.(ex, i)
|
|
11634
|
+
}
|
|
11635
|
+
) : /* @__PURE__ */ jsx(
|
|
11065
11636
|
ExerciseCardHeading,
|
|
11066
11637
|
{
|
|
11067
11638
|
name: ex.name,
|
|
@@ -11089,7 +11660,7 @@ function SessionRail({
|
|
|
11089
11660
|
var DEFAULTS = {
|
|
11090
11661
|
label: "SS",
|
|
11091
11662
|
color: getSemanticColors("dark")["brand-primary"],
|
|
11092
|
-
bgBase: "
|
|
11663
|
+
bgBase: getSemanticColors("dark")["background-base"]
|
|
11093
11664
|
};
|
|
11094
11665
|
function SupersetWrapper({
|
|
11095
11666
|
label = DEFAULTS.label,
|
|
@@ -11348,10 +11919,10 @@ function WeekRow({
|
|
|
11348
11919
|
}
|
|
11349
11920
|
);
|
|
11350
11921
|
}
|
|
11351
|
-
var
|
|
11922
|
+
var t10 = getSemanticColors("dark");
|
|
11352
11923
|
var COLORS = {
|
|
11353
|
-
statusSuccess:
|
|
11354
|
-
brandPrimary:
|
|
11924
|
+
statusSuccess: t10["status-success"],
|
|
11925
|
+
brandPrimary: t10["brand-primary"],
|
|
11355
11926
|
borderDefault: "#1F1F1F",
|
|
11356
11927
|
brandPrimarySubtle: "rgba(255,121,0,0.06)"
|
|
11357
11928
|
};
|
|
@@ -11835,12 +12406,12 @@ function PrHistoryModal({
|
|
|
11835
12406
|
}
|
|
11836
12407
|
);
|
|
11837
12408
|
}
|
|
11838
|
-
var
|
|
11839
|
-
var BRAND_PRIMARY9 =
|
|
12409
|
+
var t11 = getSemanticColors("dark");
|
|
12410
|
+
var BRAND_PRIMARY9 = t11["brand-primary"];
|
|
11840
12411
|
var BRAND_PRIMARY_SUBTLE = "rgba(255,121,0,0.12)";
|
|
11841
|
-
var STATUS_SUCCESS2 =
|
|
11842
|
-
var STATUS_WARNING2 =
|
|
11843
|
-
var STATUS_ERROR2 =
|
|
12412
|
+
var STATUS_SUCCESS2 = t11["status-success"];
|
|
12413
|
+
var STATUS_WARNING2 = t11["status-warning"];
|
|
12414
|
+
var STATUS_ERROR2 = t11["status-error"];
|
|
11844
12415
|
var EMOJI_SETS = {
|
|
11845
12416
|
sleep: ["\u{1F634}", "\u{1F610}", "\u{1F642}", "\u{1F60A}", "\u{1F929}"],
|
|
11846
12417
|
soreness: ["\u{1F635}", "\u{1F623}", "\u{1F610}", "\u{1F642}", "\u{1F4AA}"],
|
|
@@ -12030,13 +12601,13 @@ function ReadinessCheck({
|
|
|
12030
12601
|
)
|
|
12031
12602
|
] }) });
|
|
12032
12603
|
}
|
|
12033
|
-
var
|
|
12034
|
-
var BRAND_PRIMARY10 =
|
|
12604
|
+
var t12 = getSemanticColors("dark");
|
|
12605
|
+
var BRAND_PRIMARY10 = t12["brand-primary"];
|
|
12035
12606
|
var BRAND_PRIMARY_DARK2 = MESO_ACCENT_GRADIENT_DARK;
|
|
12036
12607
|
var BRAND_PRIMARY_LIGHT2 = MESO_ACCENT_GRADIENT_LIGHT;
|
|
12037
|
-
var SUCCESS =
|
|
12038
|
-
var WARNING =
|
|
12039
|
-
var ERROR =
|
|
12608
|
+
var SUCCESS = t12["status-success"];
|
|
12609
|
+
var WARNING = t12["status-warning"];
|
|
12610
|
+
var ERROR = t12["status-error"];
|
|
12040
12611
|
var ACCENT_STOPS2 = [BRAND_PRIMARY_DARK2, BRAND_PRIMARY10, BRAND_PRIMARY_LIGHT2];
|
|
12041
12612
|
var CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor("surface-elevated")} 0%, ${resolveColor("surface-raised")} 100%)`;
|
|
12042
12613
|
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%)";
|
|
@@ -12407,8 +12978,8 @@ var ERROR_PILL_BORDER = "rgba(209,67,67,0.20)";
|
|
|
12407
12978
|
var PLOT_LEFT = 26;
|
|
12408
12979
|
var TOOLTIP_WIDTH = 124;
|
|
12409
12980
|
function timeOf(dateStr) {
|
|
12410
|
-
const
|
|
12411
|
-
return Number.isNaN(
|
|
12981
|
+
const t26 = Date.parse(dateStr);
|
|
12982
|
+
return Number.isNaN(t26) ? 0 : t26;
|
|
12412
12983
|
}
|
|
12413
12984
|
function formatValue(value) {
|
|
12414
12985
|
return `${Math.round(value)}`;
|
|
@@ -12897,10 +13468,10 @@ function StrengthTrendChart({
|
|
|
12897
13468
|
}
|
|
12898
13469
|
);
|
|
12899
13470
|
}
|
|
12900
|
-
var
|
|
12901
|
-
var STATUS_SUCCESS4 =
|
|
12902
|
-
var STATUS_WARNING4 =
|
|
12903
|
-
var STATUS_INFO =
|
|
13471
|
+
var t13 = getSemanticColors("dark");
|
|
13472
|
+
var STATUS_SUCCESS4 = t13["status-success"];
|
|
13473
|
+
var STATUS_WARNING4 = t13["status-warning"];
|
|
13474
|
+
var STATUS_INFO = t13["status-info"];
|
|
12904
13475
|
var DOT_BORDER = "#F3F4F6";
|
|
12905
13476
|
var BAND_FILL = "rgba(46,213,115,0.1)";
|
|
12906
13477
|
var BAND_EDGE = "rgba(46,213,115,0.45)";
|
|
@@ -12943,8 +13514,8 @@ function interpEdge(pixels, x, key) {
|
|
|
12943
13514
|
const a = pixels[i];
|
|
12944
13515
|
const b = pixels[i + 1];
|
|
12945
13516
|
if (x >= a.x && x <= b.x) {
|
|
12946
|
-
const
|
|
12947
|
-
return a[key] +
|
|
13517
|
+
const t26 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
|
|
13518
|
+
return a[key] + t26 * (b[key] - a[key]);
|
|
12948
13519
|
}
|
|
12949
13520
|
}
|
|
12950
13521
|
return pixels[pixels.length - 1][key];
|
|
@@ -13274,13 +13845,13 @@ function CapacityBandChart({
|
|
|
13274
13845
|
}
|
|
13275
13846
|
);
|
|
13276
13847
|
}
|
|
13277
|
-
var
|
|
13848
|
+
var t14 = getSemanticColors("dark");
|
|
13278
13849
|
function glowShadow(color) {
|
|
13279
13850
|
return `0 0 5px 1px ${alpha(color, 0.3)}, 0 0 10px 3px ${alpha(color, 0.12)}`;
|
|
13280
13851
|
}
|
|
13281
13852
|
var DEFAULT_TRACK_COLOR = primitiveColors.charcoal[200];
|
|
13282
13853
|
var DEFAULT_MARKER_COLOR2 = primitiveColors.white;
|
|
13283
|
-
var TICK_COLOR =
|
|
13854
|
+
var TICK_COLOR = t14["text-tertiary"];
|
|
13284
13855
|
var ZONE_SIZES = {
|
|
13285
13856
|
default: {
|
|
13286
13857
|
trackHeight: 14,
|
|
@@ -13463,7 +14034,7 @@ function ZoneTrack({
|
|
|
13463
14034
|
),
|
|
13464
14035
|
ticks?.map((tick, i) => {
|
|
13465
14036
|
const emphasized = tick.emphasized === true;
|
|
13466
|
-
const lineColor = tick.color ?? (emphasized ?
|
|
14037
|
+
const lineColor = tick.color ?? (emphasized ? t14["brand-primary"] : TICK_COLOR);
|
|
13467
14038
|
const lineWidth = emphasized ? s.tickLineEmphasized : s.tickLineNormal;
|
|
13468
14039
|
return /* @__PURE__ */ jsx(
|
|
13469
14040
|
reactNative.View,
|
|
@@ -13494,7 +14065,7 @@ function ZoneTrack({
|
|
|
13494
14065
|
if (tick.label == null) return null;
|
|
13495
14066
|
const emphasized = tick.emphasized === true;
|
|
13496
14067
|
if (!showTickLabel(i, emphasized)) return null;
|
|
13497
|
-
const labelColor = tick.color ?? (emphasized ?
|
|
14068
|
+
const labelColor = tick.color ?? (emphasized ? t14["brand-primary"] : TICK_COLOR);
|
|
13498
14069
|
const labelNode = /* @__PURE__ */ jsx(
|
|
13499
14070
|
reactNative.Text,
|
|
13500
14071
|
{
|
|
@@ -13550,14 +14121,14 @@ function FatigueMeter({
|
|
|
13550
14121
|
className,
|
|
13551
14122
|
...props
|
|
13552
14123
|
}) {
|
|
13553
|
-
const [t1,
|
|
14124
|
+
const [t1, t26, t32] = thresholds;
|
|
13554
14125
|
const zones = [
|
|
13555
14126
|
{ upTo: t1, color: zoneColors[0] },
|
|
13556
|
-
{ upTo:
|
|
14127
|
+
{ upTo: t26, color: zoneColors[1] },
|
|
13557
14128
|
{ upTo: t32, color: zoneColors[2] },
|
|
13558
14129
|
{ upTo: max, color: zoneColors[3] }
|
|
13559
14130
|
];
|
|
13560
|
-
const tickValues = [0, t1,
|
|
14131
|
+
const tickValues = [0, t1, t26, t32, max];
|
|
13561
14132
|
const ticks = tickValues.map((v, i) => ({ value: v, label: labels[i] }));
|
|
13562
14133
|
return /* @__PURE__ */ jsx(
|
|
13563
14134
|
ZoneTrack,
|
|
@@ -13680,7 +14251,7 @@ function VolumeLandmarkBar({
|
|
|
13680
14251
|
}
|
|
13681
14252
|
);
|
|
13682
14253
|
}
|
|
13683
|
-
var
|
|
14254
|
+
var t15 = getSemanticColors("dark");
|
|
13684
14255
|
var statusToken = {
|
|
13685
14256
|
productive: "status-success",
|
|
13686
14257
|
threshold: "status-warning",
|
|
@@ -13697,7 +14268,7 @@ var defaultLabel = {
|
|
|
13697
14268
|
stop: "Stop"
|
|
13698
14269
|
};
|
|
13699
14270
|
function statusPillColor(status) {
|
|
13700
|
-
return
|
|
14271
|
+
return t15[statusToken[status]];
|
|
13701
14272
|
}
|
|
13702
14273
|
function StatusPill2({ status, label, className, style, ...props }) {
|
|
13703
14274
|
const color = statusPillColor(status);
|
|
@@ -13730,7 +14301,7 @@ function StatusPill2({ status, label, className, style, ...props }) {
|
|
|
13730
14301
|
}
|
|
13731
14302
|
);
|
|
13732
14303
|
}
|
|
13733
|
-
var
|
|
14304
|
+
var t16 = getSemanticColors("dark");
|
|
13734
14305
|
var categoryToken = {
|
|
13735
14306
|
productive: null,
|
|
13736
14307
|
threshold: "status-warning",
|
|
@@ -13743,7 +14314,7 @@ var floodOpacity = {
|
|
|
13743
14314
|
};
|
|
13744
14315
|
function liveAuraColor(category) {
|
|
13745
14316
|
const token = categoryToken[category];
|
|
13746
|
-
return token ?
|
|
14317
|
+
return token ? t16[token] : null;
|
|
13747
14318
|
}
|
|
13748
14319
|
function LiveAuraFrame({
|
|
13749
14320
|
category,
|
|
@@ -13772,9 +14343,9 @@ function LiveAuraFrame({
|
|
|
13772
14343
|
position: "relative",
|
|
13773
14344
|
overflow: "hidden",
|
|
13774
14345
|
borderRadius: 10,
|
|
13775
|
-
backgroundColor:
|
|
14346
|
+
backgroundColor: t16["background-base"],
|
|
13776
14347
|
borderWidth: 1,
|
|
13777
|
-
borderColor:
|
|
14348
|
+
borderColor: t16["border-default"]
|
|
13778
14349
|
},
|
|
13779
14350
|
style
|
|
13780
14351
|
],
|
|
@@ -13795,6 +14366,905 @@ function LiveAuraFrame({
|
|
|
13795
14366
|
}
|
|
13796
14367
|
);
|
|
13797
14368
|
}
|
|
14369
|
+
function VelocityHero({
|
|
14370
|
+
velocities,
|
|
14371
|
+
targetReps,
|
|
14372
|
+
liveRepIndex,
|
|
14373
|
+
width,
|
|
14374
|
+
height = 300
|
|
14375
|
+
}) {
|
|
14376
|
+
return /* @__PURE__ */ jsx(reactNative.View, { testID: "velocity-hero", style: width != null ? { width, height } : { flex: 1, height }, children: /* @__PURE__ */ jsx(
|
|
14377
|
+
VelocityStrip,
|
|
14378
|
+
{
|
|
14379
|
+
variant: "hero",
|
|
14380
|
+
velocities,
|
|
14381
|
+
liveRepIndex: liveRepIndex ?? velocities.length - 1,
|
|
14382
|
+
targetReps,
|
|
14383
|
+
height,
|
|
14384
|
+
scale: "peak"
|
|
14385
|
+
}
|
|
14386
|
+
) });
|
|
14387
|
+
}
|
|
14388
|
+
|
|
14389
|
+
// src/components/custom/Fatigue/fatigue-tokens.ts
|
|
14390
|
+
var t17 = getSemanticColors("dark");
|
|
14391
|
+
var FONT_HEAD = '"Space Grotesk", sans-serif';
|
|
14392
|
+
var FONT_UI = '"Nunito Sans", sans-serif';
|
|
14393
|
+
var FONT_MONO = "monospace";
|
|
14394
|
+
var TONE_COLOR = {
|
|
14395
|
+
ok: t17["status-success"],
|
|
14396
|
+
warn: t17["status-warning"],
|
|
14397
|
+
alarm: t17["status-error"]
|
|
14398
|
+
};
|
|
14399
|
+
var STATE_LABEL = {
|
|
14400
|
+
good: "Good",
|
|
14401
|
+
slowing: "Slowing",
|
|
14402
|
+
grinding: "Grinding",
|
|
14403
|
+
"form-breakdown": "Form breaking down"
|
|
14404
|
+
};
|
|
14405
|
+
var PHASE_AXIS_COLOR = {
|
|
14406
|
+
eccentric: primitiveRamps.magenta[800],
|
|
14407
|
+
concentric: primitiveRamps.cyan[800],
|
|
14408
|
+
hold: primitiveColors.charcoal[100],
|
|
14409
|
+
idle: primitiveColors.charcoal[200]
|
|
14410
|
+
};
|
|
14411
|
+
var PHASE_AXIS_BASE_COLOR = {
|
|
14412
|
+
eccentric: primitiveRamps.magenta[950],
|
|
14413
|
+
concentric: primitiveRamps.cyan[950],
|
|
14414
|
+
hold: primitiveColors.charcoal[300],
|
|
14415
|
+
idle: primitiveColors.charcoal[200]
|
|
14416
|
+
};
|
|
14417
|
+
var PACING_TONE = {
|
|
14418
|
+
ahead: primitiveRamps.amber[300],
|
|
14419
|
+
onPace: primitiveRamps.green[300],
|
|
14420
|
+
over: primitiveRamps.red[300]
|
|
14421
|
+
};
|
|
14422
|
+
var SILVER = primitiveColors.neutral[300];
|
|
14423
|
+
var RED_LIGHT = primitiveRamps.red[400];
|
|
14424
|
+
var RED_MID = primitiveRamps.red[600];
|
|
14425
|
+
var RED_DEEP = primitiveRamps.red[800];
|
|
14426
|
+
var GRIND_THRESHOLD = 0.35;
|
|
14427
|
+
var DRIFT_GREY = primitiveColors.neutral[600];
|
|
14428
|
+
function clamp012(v) {
|
|
14429
|
+
return Math.min(1, Math.max(0, v));
|
|
14430
|
+
}
|
|
14431
|
+
function hexToRgb2(hex) {
|
|
14432
|
+
const h = hex.replace("#", "");
|
|
14433
|
+
const f = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
|
|
14434
|
+
return [parseInt(f.slice(0, 2), 16), parseInt(f.slice(2, 4), 16), parseInt(f.slice(4, 6), 16)];
|
|
14435
|
+
}
|
|
14436
|
+
function mixHex2(a, b, ratio) {
|
|
14437
|
+
const [ar, ag, ab] = hexToRgb2(a);
|
|
14438
|
+
const [br, bg, bb] = hexToRgb2(b);
|
|
14439
|
+
const m = (x, y) => Math.round(x + (y - x) * clamp012(ratio));
|
|
14440
|
+
return `#${[m(ar, br), m(ag, bg), m(ab, bb)].map((v) => v.toString(16).padStart(2, "0")).join("")}`;
|
|
14441
|
+
}
|
|
14442
|
+
function ghostLineColor(tempoDeviation, grindSignature) {
|
|
14443
|
+
if (grindSignature >= GRIND_THRESHOLD) {
|
|
14444
|
+
const severity = clamp012((grindSignature - GRIND_THRESHOLD) / (1 - GRIND_THRESHOLD));
|
|
14445
|
+
return severity < 0.5 ? mixHex2(RED_LIGHT, RED_MID, severity * 2) : mixHex2(RED_MID, RED_DEEP, (severity - 0.5) * 2);
|
|
14446
|
+
}
|
|
14447
|
+
return mixHex2(SILVER, DRIFT_GREY, (tempoDeviation ?? 0) * 0.55);
|
|
14448
|
+
}
|
|
14449
|
+
function auraForVerdict(state) {
|
|
14450
|
+
if (state == null || state === "good") return "productive";
|
|
14451
|
+
if (state === "form-breakdown") return "stop";
|
|
14452
|
+
return "threshold";
|
|
14453
|
+
}
|
|
14454
|
+
|
|
14455
|
+
// src/components/custom/Fatigue/VerdictHero.tsx
|
|
14456
|
+
var t18 = getSemanticColors("dark");
|
|
14457
|
+
function formatRpe(rpe) {
|
|
14458
|
+
if (rpe == null) return "\u2014";
|
|
14459
|
+
return (Math.round(rpe * 2) / 2).toFixed(1);
|
|
14460
|
+
}
|
|
14461
|
+
function VerdictHero({ rpe, verdict }) {
|
|
14462
|
+
const tone = verdict ? TONE_COLOR[verdict.tone] : t18["text-tertiary"];
|
|
14463
|
+
const word = verdict ? STATE_LABEL[verdict.state] : "Warming up";
|
|
14464
|
+
return /* @__PURE__ */ jsxs(reactNative.View, { style: { gap: 6 }, testID: "verdict-hero", children: [
|
|
14465
|
+
/* @__PURE__ */ jsx(
|
|
14466
|
+
reactNative.Text,
|
|
14467
|
+
{
|
|
14468
|
+
style: {
|
|
14469
|
+
fontSize: 9,
|
|
14470
|
+
letterSpacing: 1.5,
|
|
14471
|
+
fontFamily: FONT_MONO,
|
|
14472
|
+
color: t18["text-tertiary"]
|
|
14473
|
+
},
|
|
14474
|
+
children: "FATIGUE"
|
|
14475
|
+
}
|
|
14476
|
+
),
|
|
14477
|
+
/* @__PURE__ */ jsxs(reactNative.View, { style: { gap: 2 }, children: [
|
|
14478
|
+
/* @__PURE__ */ jsxs(reactNative.View, { style: { flexDirection: "row", alignItems: "flex-end", gap: 7 }, children: [
|
|
14479
|
+
/* @__PURE__ */ jsx(
|
|
14480
|
+
reactNative.Text,
|
|
14481
|
+
{
|
|
14482
|
+
style: {
|
|
14483
|
+
fontSize: 62,
|
|
14484
|
+
fontWeight: "900",
|
|
14485
|
+
fontFamily: FONT_HEAD,
|
|
14486
|
+
color: tone,
|
|
14487
|
+
lineHeight: 60
|
|
14488
|
+
},
|
|
14489
|
+
accessibilityLabel: `RPE ${formatRpe(rpe)}`,
|
|
14490
|
+
children: formatRpe(rpe)
|
|
14491
|
+
}
|
|
14492
|
+
),
|
|
14493
|
+
/* @__PURE__ */ jsx(
|
|
14494
|
+
reactNative.Text,
|
|
14495
|
+
{
|
|
14496
|
+
style: {
|
|
14497
|
+
fontSize: 18,
|
|
14498
|
+
fontWeight: "800",
|
|
14499
|
+
color: tone,
|
|
14500
|
+
marginBottom: 9,
|
|
14501
|
+
fontFamily: FONT_HEAD
|
|
14502
|
+
},
|
|
14503
|
+
children: "RPE"
|
|
14504
|
+
}
|
|
14505
|
+
)
|
|
14506
|
+
] }),
|
|
14507
|
+
/* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 17, fontWeight: "800", fontFamily: FONT_HEAD, color: tone }, children: word })
|
|
14508
|
+
] })
|
|
14509
|
+
] });
|
|
14510
|
+
}
|
|
14511
|
+
var t19 = getSemanticColors("dark");
|
|
14512
|
+
var TONE_VARIANT = {
|
|
14513
|
+
ok: "success",
|
|
14514
|
+
warn: "warning",
|
|
14515
|
+
alarm: "error"
|
|
14516
|
+
};
|
|
14517
|
+
var TONE_WORD = { ok: "ok", warn: "watch", alarm: "alarm" };
|
|
14518
|
+
function Light({
|
|
14519
|
+
label,
|
|
14520
|
+
tone,
|
|
14521
|
+
detail
|
|
14522
|
+
}) {
|
|
14523
|
+
const variant = tone ? TONE_VARIANT[tone] : "neutral";
|
|
14524
|
+
const word = tone ? TONE_WORD[tone] : "warming up";
|
|
14525
|
+
return /* @__PURE__ */ jsx(Tooltip, { label: `${detail} \xB7 ${word}`, placement: "bottom", children: /* @__PURE__ */ jsxs(
|
|
14526
|
+
reactNative.View,
|
|
14527
|
+
{
|
|
14528
|
+
accessibilityLabel: `${detail}, ${word}`,
|
|
14529
|
+
style: { flexDirection: "row", alignItems: "center", gap: 5 },
|
|
14530
|
+
children: [
|
|
14531
|
+
/* @__PURE__ */ jsx(StatusDot, { variant, size: "sm", glow: true }),
|
|
14532
|
+
/* @__PURE__ */ jsx(
|
|
14533
|
+
reactNative.Text,
|
|
14534
|
+
{
|
|
14535
|
+
style: {
|
|
14536
|
+
fontSize: 9,
|
|
14537
|
+
letterSpacing: 0.6,
|
|
14538
|
+
fontFamily: FONT_MONO,
|
|
14539
|
+
color: t19["text-secondary"]
|
|
14540
|
+
},
|
|
14541
|
+
children: label
|
|
14542
|
+
}
|
|
14543
|
+
)
|
|
14544
|
+
]
|
|
14545
|
+
}
|
|
14546
|
+
) });
|
|
14547
|
+
}
|
|
14548
|
+
function FatigueLights({ dimensions, spread = false }) {
|
|
14549
|
+
return /* @__PURE__ */ jsxs(
|
|
14550
|
+
reactNative.View,
|
|
14551
|
+
{
|
|
14552
|
+
testID: "fatigue-lights",
|
|
14553
|
+
style: {
|
|
14554
|
+
flexDirection: "row",
|
|
14555
|
+
gap: spread ? 0 : 16,
|
|
14556
|
+
alignItems: "center",
|
|
14557
|
+
justifyContent: spread ? "space-between" : "flex-start"
|
|
14558
|
+
},
|
|
14559
|
+
children: [
|
|
14560
|
+
/* @__PURE__ */ jsx(Light, { label: "VEL", tone: dimensions?.velocityLoss ?? null, detail: "Velocity loss" }),
|
|
14561
|
+
/* @__PURE__ */ jsx(Light, { label: "ROM", tone: dimensions?.rom ?? null, detail: "ROM depth" }),
|
|
14562
|
+
/* @__PURE__ */ jsx(Light, { label: "TEMPO", tone: dimensions?.tempo ?? null, detail: "Tempo" })
|
|
14563
|
+
]
|
|
14564
|
+
}
|
|
14565
|
+
);
|
|
14566
|
+
}
|
|
14567
|
+
function barColor(romM, working, short) {
|
|
14568
|
+
if (short != null && romM < short) return RED_DEEP;
|
|
14569
|
+
if (working != null && romM < working) return RED_LIGHT;
|
|
14570
|
+
return SILVER;
|
|
14571
|
+
}
|
|
14572
|
+
function romReferenceOverlay(g, workingStandardM, shortThresholdM) {
|
|
14573
|
+
return /* @__PURE__ */ jsxs(jsxRuntime.Fragment, { children: [
|
|
14574
|
+
shortThresholdM != null && /* @__PURE__ */ jsx(
|
|
14575
|
+
reactNative.View,
|
|
14576
|
+
{
|
|
14577
|
+
pointerEvents: "none",
|
|
14578
|
+
style: {
|
|
14579
|
+
position: "absolute",
|
|
14580
|
+
left: 0,
|
|
14581
|
+
right: 0,
|
|
14582
|
+
bottom: 0,
|
|
14583
|
+
height: g.yOf(shortThresholdM),
|
|
14584
|
+
backgroundColor: alpha(RED_DEEP, 0.06)
|
|
14585
|
+
}
|
|
14586
|
+
}
|
|
14587
|
+
),
|
|
14588
|
+
workingStandardM != null && /* @__PURE__ */ jsx(
|
|
14589
|
+
reactNative.View,
|
|
14590
|
+
{
|
|
14591
|
+
pointerEvents: "none",
|
|
14592
|
+
style: {
|
|
14593
|
+
position: "absolute",
|
|
14594
|
+
left: 0,
|
|
14595
|
+
right: 0,
|
|
14596
|
+
bottom: g.yOf(workingStandardM),
|
|
14597
|
+
borderTopWidth: 1,
|
|
14598
|
+
borderStyle: "dashed",
|
|
14599
|
+
borderColor: alpha(SILVER, 0.4)
|
|
14600
|
+
}
|
|
14601
|
+
}
|
|
14602
|
+
),
|
|
14603
|
+
shortThresholdM != null && /* @__PURE__ */ jsx(
|
|
14604
|
+
reactNative.View,
|
|
14605
|
+
{
|
|
14606
|
+
pointerEvents: "none",
|
|
14607
|
+
style: {
|
|
14608
|
+
position: "absolute",
|
|
14609
|
+
left: 0,
|
|
14610
|
+
right: 0,
|
|
14611
|
+
bottom: g.yOf(shortThresholdM),
|
|
14612
|
+
borderTopWidth: 1,
|
|
14613
|
+
borderStyle: "dashed",
|
|
14614
|
+
borderColor: alpha(RED_DEEP, 0.4)
|
|
14615
|
+
}
|
|
14616
|
+
}
|
|
14617
|
+
)
|
|
14618
|
+
] });
|
|
14619
|
+
}
|
|
14620
|
+
function RomProgressionChart({
|
|
14621
|
+
points,
|
|
14622
|
+
workingStandardM,
|
|
14623
|
+
shortThresholdM,
|
|
14624
|
+
barHeight = 44,
|
|
14625
|
+
maxReps = 12,
|
|
14626
|
+
plannedReps: plannedReps2
|
|
14627
|
+
}) {
|
|
14628
|
+
const shown = points.slice(-maxReps);
|
|
14629
|
+
const slots = shown.map((p) => ({ kind: "rep", value: p.romM }));
|
|
14630
|
+
const scaleMax = Math.max(...shown.map((p) => p.romM), workingStandardM ?? 0, shortThresholdM ?? 0) || void 0;
|
|
14631
|
+
return /* @__PURE__ */ jsx(reactNative.View, { testID: "rom-progression", children: /* @__PURE__ */ jsx(
|
|
14632
|
+
SetBarChart,
|
|
14633
|
+
{
|
|
14634
|
+
slots,
|
|
14635
|
+
colorFor: (romM) => barColor(romM, workingStandardM, shortThresholdM),
|
|
14636
|
+
height: barHeight,
|
|
14637
|
+
scaleMax,
|
|
14638
|
+
barRadius: 2,
|
|
14639
|
+
hideBaseline: true,
|
|
14640
|
+
targetReps: plannedReps2,
|
|
14641
|
+
renderReference: (g) => romReferenceOverlay(g, workingStandardM, shortThresholdM),
|
|
14642
|
+
testIDPrefix: "rom"
|
|
14643
|
+
}
|
|
14644
|
+
) });
|
|
14645
|
+
}
|
|
14646
|
+
|
|
14647
|
+
// src/components/custom/Fatigue/tempo-pacing.ts
|
|
14648
|
+
var t20 = getSemanticColors("dark");
|
|
14649
|
+
var ON_TARGET_MS2 = 100;
|
|
14650
|
+
function phaseFillFraction(elapsedMs, targetMs) {
|
|
14651
|
+
if (targetMs == null || targetMs <= 0) return 1;
|
|
14652
|
+
return Math.min(1, Math.max(0, elapsedMs / targetMs));
|
|
14653
|
+
}
|
|
14654
|
+
function pacingTone(elapsedMs, targetMs) {
|
|
14655
|
+
if (targetMs == null || targetMs <= 0) return t20["text-primary"];
|
|
14656
|
+
const remainingMs = targetMs - elapsedMs;
|
|
14657
|
+
if (remainingMs > ON_TARGET_MS2) return PACING_TONE.ahead;
|
|
14658
|
+
if (remainingMs >= -ON_TARGET_MS2) return PACING_TONE.onPace;
|
|
14659
|
+
return PACING_TONE.over;
|
|
14660
|
+
}
|
|
14661
|
+
function prescribedSegments(tempo) {
|
|
14662
|
+
if (tempo == null) return [];
|
|
14663
|
+
const [ecc, pauseBottom, con, pauseTop] = tempo;
|
|
14664
|
+
const order = [
|
|
14665
|
+
["eccentric", ecc],
|
|
14666
|
+
["hold", pauseBottom],
|
|
14667
|
+
["concentric", con],
|
|
14668
|
+
["hold", pauseTop]
|
|
14669
|
+
];
|
|
14670
|
+
const out = [];
|
|
14671
|
+
let t26 = 0;
|
|
14672
|
+
for (const [phase, seconds] of order) {
|
|
14673
|
+
const ms = seconds * 1e3;
|
|
14674
|
+
if (ms > 0) out.push({ phase, startMs: t26, endMs: t26 + ms });
|
|
14675
|
+
t26 += ms;
|
|
14676
|
+
}
|
|
14677
|
+
return out;
|
|
14678
|
+
}
|
|
14679
|
+
function phaseTargetsMs(phases, tempo) {
|
|
14680
|
+
if (tempo == null) return phases.map(() => null);
|
|
14681
|
+
const [eccS, pauseBottomS, conS, pauseTopS] = tempo;
|
|
14682
|
+
let seenConcentric = false;
|
|
14683
|
+
return phases.map((phase) => {
|
|
14684
|
+
switch (phase) {
|
|
14685
|
+
case "eccentric":
|
|
14686
|
+
return eccS * 1e3;
|
|
14687
|
+
case "concentric":
|
|
14688
|
+
seenConcentric = true;
|
|
14689
|
+
return conS * 1e3;
|
|
14690
|
+
case "hold":
|
|
14691
|
+
return (seenConcentric ? pauseTopS : pauseBottomS) * 1e3;
|
|
14692
|
+
default:
|
|
14693
|
+
return null;
|
|
14694
|
+
}
|
|
14695
|
+
});
|
|
14696
|
+
}
|
|
14697
|
+
|
|
14698
|
+
// src/components/custom/Fatigue/GhostBand.tsx
|
|
14699
|
+
var t21 = getSemanticColors("dark");
|
|
14700
|
+
var BAND_H = 16;
|
|
14701
|
+
var BAND_GAP = 4;
|
|
14702
|
+
var PHASE_LABEL = {
|
|
14703
|
+
eccentric: "ECC",
|
|
14704
|
+
concentric: "CON"
|
|
14705
|
+
};
|
|
14706
|
+
var LABEL_CH_PX = 7;
|
|
14707
|
+
var LABEL_PAD_PX = 6;
|
|
14708
|
+
var WELL_SHADE = primitiveColors.black;
|
|
14709
|
+
var WELL_FLOOR_LIGHT = primitiveColors.white;
|
|
14710
|
+
function labelFits(label, segW) {
|
|
14711
|
+
return segW >= label.length * LABEL_CH_PX + LABEL_PAD_PX;
|
|
14712
|
+
}
|
|
14713
|
+
function GhostBand({
|
|
14714
|
+
segments,
|
|
14715
|
+
x,
|
|
14716
|
+
top,
|
|
14717
|
+
height = BAND_H,
|
|
14718
|
+
showLabels = false,
|
|
14719
|
+
labelColor = t21["text-primary"],
|
|
14720
|
+
targetTempoSeconds = null,
|
|
14721
|
+
prescribed = false
|
|
14722
|
+
}) {
|
|
14723
|
+
const rawId = React25.useId();
|
|
14724
|
+
const safeId = rawId.replace(/[^a-zA-Z0-9]/g, "");
|
|
14725
|
+
const clipId = `ghost-band-${safeId}`;
|
|
14726
|
+
const wellId = `ghost-band-well-${safeId}`;
|
|
14727
|
+
const drawn = segments.filter((seg) => x(seg.endMs) - x(seg.startMs) > 0);
|
|
14728
|
+
if (drawn.length === 0) return null;
|
|
14729
|
+
const bandLeft = x(drawn[0].startMs);
|
|
14730
|
+
const bandRight = x(drawn[drawn.length - 1].endMs);
|
|
14731
|
+
const bandW = bandRight - bandLeft;
|
|
14732
|
+
if (bandW <= 0) return null;
|
|
14733
|
+
const pacing = targetTempoSeconds != null && !prescribed;
|
|
14734
|
+
const targetsMs = phaseTargetsMs(
|
|
14735
|
+
drawn.map((seg) => seg.phase),
|
|
14736
|
+
targetTempoSeconds
|
|
14737
|
+
);
|
|
14738
|
+
const runs = drawn.map((seg, i) => {
|
|
14739
|
+
const left = x(seg.startMs);
|
|
14740
|
+
const right = i === drawn.length - 1 ? bandRight : x(drawn[i + 1].startMs);
|
|
14741
|
+
const width = Math.max(0, right - left);
|
|
14742
|
+
const targetMs = targetsMs[i];
|
|
14743
|
+
const elapsedMs = seg.endMs - seg.startMs;
|
|
14744
|
+
return {
|
|
14745
|
+
phase: seg.phase,
|
|
14746
|
+
left,
|
|
14747
|
+
width,
|
|
14748
|
+
// Prescribed → nothing performed, so nothing filled. No target at all → the run reads
|
|
14749
|
+
// complete rather than perpetually empty.
|
|
14750
|
+
fillWidth: prescribed ? 0 : pacing ? width * phaseFillFraction(elapsedMs, targetMs) : width,
|
|
14751
|
+
labelTone: pacing ? pacingTone(elapsedMs, targetMs) : labelColor
|
|
14752
|
+
};
|
|
14753
|
+
});
|
|
14754
|
+
return /* @__PURE__ */ jsxs("g", { children: [
|
|
14755
|
+
/* @__PURE__ */ jsxs("defs", { children: [
|
|
14756
|
+
/* @__PURE__ */ jsx("clipPath", { id: clipId, children: /* @__PURE__ */ jsx("rect", { x: bandLeft, y: top, width: bandW, height, rx: 2 }) }),
|
|
14757
|
+
/* @__PURE__ */ jsxs("linearGradient", { id: wellId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
14758
|
+
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: WELL_SHADE, stopOpacity: 0.6 }),
|
|
14759
|
+
/* @__PURE__ */ jsx("stop", { offset: "42%", stopColor: WELL_SHADE, stopOpacity: 0.12 }),
|
|
14760
|
+
/* @__PURE__ */ jsx("stop", { offset: "88%", stopColor: WELL_SHADE, stopOpacity: 0.02 }),
|
|
14761
|
+
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: WELL_FLOOR_LIGHT, stopOpacity: 0.07 })
|
|
14762
|
+
] })
|
|
14763
|
+
] }),
|
|
14764
|
+
/* @__PURE__ */ jsxs("g", { clipPath: `url(#${clipId})`, children: [
|
|
14765
|
+
/* @__PURE__ */ jsx(
|
|
14766
|
+
"rect",
|
|
14767
|
+
{
|
|
14768
|
+
x: bandLeft,
|
|
14769
|
+
y: top,
|
|
14770
|
+
width: bandW,
|
|
14771
|
+
height,
|
|
14772
|
+
fill: PHASE_AXIS_COLOR.idle,
|
|
14773
|
+
"data-testid": "ghost-band-floor"
|
|
14774
|
+
}
|
|
14775
|
+
),
|
|
14776
|
+
runs.map((run, i) => /* @__PURE__ */ jsxs("g", { children: [
|
|
14777
|
+
/* @__PURE__ */ jsx(
|
|
14778
|
+
"rect",
|
|
14779
|
+
{
|
|
14780
|
+
x: run.left,
|
|
14781
|
+
y: top,
|
|
14782
|
+
width: run.width,
|
|
14783
|
+
height,
|
|
14784
|
+
fill: PHASE_AXIS_BASE_COLOR[run.phase],
|
|
14785
|
+
"data-testid": "ghost-band-base"
|
|
14786
|
+
}
|
|
14787
|
+
),
|
|
14788
|
+
/* @__PURE__ */ jsx(
|
|
14789
|
+
"rect",
|
|
14790
|
+
{
|
|
14791
|
+
x: run.left,
|
|
14792
|
+
y: top,
|
|
14793
|
+
width: run.width,
|
|
14794
|
+
height,
|
|
14795
|
+
fill: `url(#${wellId})`,
|
|
14796
|
+
"data-testid": "ghost-band-well"
|
|
14797
|
+
}
|
|
14798
|
+
),
|
|
14799
|
+
/* @__PURE__ */ jsx(
|
|
14800
|
+
"rect",
|
|
14801
|
+
{
|
|
14802
|
+
x: run.left,
|
|
14803
|
+
y: top,
|
|
14804
|
+
width: run.fillWidth,
|
|
14805
|
+
height,
|
|
14806
|
+
fill: PHASE_AXIS_COLOR[run.phase],
|
|
14807
|
+
"data-testid": "ghost-band-fill"
|
|
14808
|
+
}
|
|
14809
|
+
)
|
|
14810
|
+
] }, i)),
|
|
14811
|
+
showLabels && runs.map((run, i) => {
|
|
14812
|
+
const label = PHASE_LABEL[run.phase];
|
|
14813
|
+
if (!label || !labelFits(label, run.width)) return null;
|
|
14814
|
+
return /* @__PURE__ */ jsx(
|
|
14815
|
+
"text",
|
|
14816
|
+
{
|
|
14817
|
+
x: run.left + run.width / 2,
|
|
14818
|
+
y: top + height / 2,
|
|
14819
|
+
textAnchor: "middle",
|
|
14820
|
+
dominantBaseline: "central",
|
|
14821
|
+
fill: run.labelTone,
|
|
14822
|
+
fontSize: 8,
|
|
14823
|
+
fontWeight: 800,
|
|
14824
|
+
letterSpacing: 1,
|
|
14825
|
+
fontFamily: FONT_UI,
|
|
14826
|
+
children: label
|
|
14827
|
+
},
|
|
14828
|
+
i
|
|
14829
|
+
);
|
|
14830
|
+
})
|
|
14831
|
+
] })
|
|
14832
|
+
] });
|
|
14833
|
+
}
|
|
14834
|
+
var t22 = getSemanticColors("dark");
|
|
14835
|
+
var PARCH = t22["text-primary"];
|
|
14836
|
+
function smoothPath(pts) {
|
|
14837
|
+
if (pts.length < 2) return pts.length ? `M ${pts[0][0]} ${pts[0][1]}` : "";
|
|
14838
|
+
let d = `M ${pts[0][0].toFixed(1)} ${pts[0][1].toFixed(1)}`;
|
|
14839
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
14840
|
+
const p0 = pts[i - 1] ?? pts[i];
|
|
14841
|
+
const p1 = pts[i];
|
|
14842
|
+
const p2 = pts[i + 1];
|
|
14843
|
+
const p3 = pts[i + 2] ?? p2;
|
|
14844
|
+
const c1x = p1[0] + (p2[0] - p0[0]) / 6;
|
|
14845
|
+
const c1y = p1[1] + (p2[1] - p0[1]) / 6;
|
|
14846
|
+
const c2x = p2[0] - (p3[0] - p1[0]) / 6;
|
|
14847
|
+
const c2y = p2[1] - (p3[1] - p1[1]) / 6;
|
|
14848
|
+
d += ` C ${c1x.toFixed(1)} ${c1y.toFixed(1)}, ${c2x.toFixed(1)} ${c2y.toFixed(1)}, ${p2[0].toFixed(1)} ${p2[1].toFixed(1)}`;
|
|
14849
|
+
}
|
|
14850
|
+
return d;
|
|
14851
|
+
}
|
|
14852
|
+
var BLUR_STD_DEV = 2.6;
|
|
14853
|
+
function GhostBloom({
|
|
14854
|
+
current,
|
|
14855
|
+
ghosts = [],
|
|
14856
|
+
tint,
|
|
14857
|
+
baseline,
|
|
14858
|
+
orientation = "up",
|
|
14859
|
+
ghostStroke = (i) => alpha(PARCH, 0.1 + i * 0.015),
|
|
14860
|
+
lineWidth = 3.5
|
|
14861
|
+
}) {
|
|
14862
|
+
const rawId = React25.useId();
|
|
14863
|
+
const blurId = `bloom-blur-${rawId.replace(/[^a-zA-Z0-9]/g, "")}`;
|
|
14864
|
+
const toY = (mag) => orientation === "down" ? baseline + mag : baseline - mag;
|
|
14865
|
+
const toAbs = (pts) => pts.map(([px, mag]) => [px, toY(mag)]);
|
|
14866
|
+
const curD = smoothPath(toAbs(current));
|
|
14867
|
+
const ghostDs = ghosts.map((g) => smoothPath(toAbs(g)));
|
|
14868
|
+
return /* @__PURE__ */ jsxs(jsxRuntime.Fragment, { children: [
|
|
14869
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("filter", { id: blurId, x: "-50%", y: "-50%", width: "200%", height: "200%", children: /* @__PURE__ */ jsx("feGaussianBlur", { stdDeviation: BLUR_STD_DEV }) }) }),
|
|
14870
|
+
ghostDs.map((d, i) => /* @__PURE__ */ jsx(
|
|
14871
|
+
"path",
|
|
14872
|
+
{
|
|
14873
|
+
d,
|
|
14874
|
+
fill: "none",
|
|
14875
|
+
stroke: ghostStroke(i),
|
|
14876
|
+
strokeWidth: 1.5,
|
|
14877
|
+
strokeLinejoin: "round"
|
|
14878
|
+
},
|
|
14879
|
+
i
|
|
14880
|
+
)),
|
|
14881
|
+
/* @__PURE__ */ jsx(
|
|
14882
|
+
"path",
|
|
14883
|
+
{
|
|
14884
|
+
d: curD,
|
|
14885
|
+
fill: "none",
|
|
14886
|
+
stroke: alpha(primitiveColors.black, 0.3),
|
|
14887
|
+
strokeWidth: lineWidth + 3,
|
|
14888
|
+
strokeLinejoin: "round",
|
|
14889
|
+
strokeLinecap: "round",
|
|
14890
|
+
filter: `url(#${blurId})`
|
|
14891
|
+
}
|
|
14892
|
+
),
|
|
14893
|
+
/* @__PURE__ */ jsx(
|
|
14894
|
+
"path",
|
|
14895
|
+
{
|
|
14896
|
+
d: curD,
|
|
14897
|
+
fill: "none",
|
|
14898
|
+
stroke: tint,
|
|
14899
|
+
strokeWidth: lineWidth,
|
|
14900
|
+
strokeLinejoin: "round",
|
|
14901
|
+
strokeLinecap: "round"
|
|
14902
|
+
}
|
|
14903
|
+
)
|
|
14904
|
+
] });
|
|
14905
|
+
}
|
|
14906
|
+
|
|
14907
|
+
// src/components/custom/Fatigue/GhostSpark.tsx
|
|
14908
|
+
function GhostSpark({
|
|
14909
|
+
curves,
|
|
14910
|
+
width,
|
|
14911
|
+
height = 172,
|
|
14912
|
+
targetTempoSeconds = null
|
|
14913
|
+
}) {
|
|
14914
|
+
const w = width;
|
|
14915
|
+
const h = height;
|
|
14916
|
+
const padL = 12;
|
|
14917
|
+
const padR = 8;
|
|
14918
|
+
const padTop = 10;
|
|
14919
|
+
const padBot = 6;
|
|
14920
|
+
if (curves.length === 0) {
|
|
14921
|
+
const prescribed = prescribedSegments(targetTempoSeconds);
|
|
14922
|
+
if (prescribed.length === 0) {
|
|
14923
|
+
return /* @__PURE__ */ jsx(reactNative.View, { testID: "ghost-spark", style: { width: w, height: h } });
|
|
14924
|
+
}
|
|
14925
|
+
const totalMs = prescribed[prescribed.length - 1].endMs;
|
|
14926
|
+
const bandTopEmpty = h - padBot - BAND_H;
|
|
14927
|
+
const xEmpty = (ms) => padL + ms / (totalMs * 1.04) * (w - padL - padR);
|
|
14928
|
+
return /* @__PURE__ */ jsx(reactNative.View, { testID: "ghost-spark", style: { paddingHorizontal: 4 }, children: /* @__PURE__ */ jsx("svg", { width: w, height: h, children: /* @__PURE__ */ jsx(
|
|
14929
|
+
GhostBand,
|
|
14930
|
+
{
|
|
14931
|
+
segments: prescribed,
|
|
14932
|
+
x: xEmpty,
|
|
14933
|
+
top: bandTopEmpty,
|
|
14934
|
+
showLabels: true,
|
|
14935
|
+
targetTempoSeconds,
|
|
14936
|
+
prescribed: true
|
|
14937
|
+
}
|
|
14938
|
+
) }) });
|
|
14939
|
+
}
|
|
14940
|
+
const cur = curves[curves.length - 1];
|
|
14941
|
+
const allSamples = curves.flatMap((c) => c.samples);
|
|
14942
|
+
const vmax = Math.max(0.01, ...allSamples.map((s) => s.velocityMps)) * 1.06;
|
|
14943
|
+
const axisMaxT = Math.max(1, ...curves.map((c) => c.samples[c.samples.length - 1]?.tMs ?? 0)) * 1.04;
|
|
14944
|
+
const bandBottom = h - padBot;
|
|
14945
|
+
const bandTop = bandBottom - BAND_H;
|
|
14946
|
+
const baseline = bandTop - BAND_GAP;
|
|
14947
|
+
const plotH = Math.max(1, baseline - padTop);
|
|
14948
|
+
const x = (ms) => padL + ms / axisMaxT * (w - padL - padR);
|
|
14949
|
+
const mag = (v) => clamp012(v / vmax) * plotH;
|
|
14950
|
+
const lineTint = ghostLineColor(cur.tempoDeviation, cur.grindSignature);
|
|
14951
|
+
const curPts = cur.samples.map((s) => [x(s.tMs), mag(s.velocityMps)]);
|
|
14952
|
+
const ghostPts = curves.slice(0, -1).map((c) => c.samples.map((s) => [x(s.tMs), mag(s.velocityMps)]));
|
|
14953
|
+
return /* @__PURE__ */ jsx(reactNative.View, { testID: "ghost-spark", style: { paddingHorizontal: 4 }, children: /* @__PURE__ */ jsxs("svg", { width: w, height: h, children: [
|
|
14954
|
+
/* @__PURE__ */ jsx(
|
|
14955
|
+
GhostBloom,
|
|
14956
|
+
{
|
|
14957
|
+
current: curPts,
|
|
14958
|
+
ghosts: ghostPts,
|
|
14959
|
+
tint: lineTint,
|
|
14960
|
+
baseline,
|
|
14961
|
+
orientation: "up"
|
|
14962
|
+
}
|
|
14963
|
+
),
|
|
14964
|
+
/* @__PURE__ */ jsx(
|
|
14965
|
+
GhostBand,
|
|
14966
|
+
{
|
|
14967
|
+
segments: cur.phaseSegments,
|
|
14968
|
+
x,
|
|
14969
|
+
top: bandTop,
|
|
14970
|
+
showLabels: true,
|
|
14971
|
+
targetTempoSeconds
|
|
14972
|
+
}
|
|
14973
|
+
)
|
|
14974
|
+
] }) });
|
|
14975
|
+
}
|
|
14976
|
+
|
|
14977
|
+
// src/components/custom/Fatigue/LiveFatigueCard.tsx
|
|
14978
|
+
var t23 = getSemanticColors("dark");
|
|
14979
|
+
var PAD = 18;
|
|
14980
|
+
var GHOST_GUTTER = 4;
|
|
14981
|
+
function LiveFatigueCard({ model, width = 318, height }) {
|
|
14982
|
+
const chartW = width - PAD * 2 - GHOST_GUTTER * 2;
|
|
14983
|
+
const chartH = height != null ? Math.round(Math.min(240, Math.max(168, height * 0.4))) : 172;
|
|
14984
|
+
return /* @__PURE__ */ jsxs(
|
|
14985
|
+
Surface,
|
|
14986
|
+
{
|
|
14987
|
+
level: "base",
|
|
14988
|
+
testID: "live-fatigue-card",
|
|
14989
|
+
style: {
|
|
14990
|
+
width,
|
|
14991
|
+
height,
|
|
14992
|
+
borderRadius: 14,
|
|
14993
|
+
padding: PAD,
|
|
14994
|
+
borderWidth: 1,
|
|
14995
|
+
borderColor: t23["hairline-default"],
|
|
14996
|
+
...barPaper(t23["surface-base"])
|
|
14997
|
+
},
|
|
14998
|
+
children: [
|
|
14999
|
+
/* @__PURE__ */ jsxs(reactNative.View, { style: { gap: 12 }, children: [
|
|
15000
|
+
/* @__PURE__ */ jsx(VerdictHero, { rpe: model.rpe, verdict: model.verdict }),
|
|
15001
|
+
/* @__PURE__ */ jsx(FatigueLights, { dimensions: model.verdict?.dimensions ?? null })
|
|
15002
|
+
] }),
|
|
15003
|
+
/* @__PURE__ */ jsx(reactNative.View, { style: { flex: 1, minHeight: 16 } }),
|
|
15004
|
+
/* @__PURE__ */ jsx(
|
|
15005
|
+
RomProgressionChart,
|
|
15006
|
+
{
|
|
15007
|
+
points: model.romProgression,
|
|
15008
|
+
workingStandardM: model.romWorkingStandardM,
|
|
15009
|
+
shortThresholdM: model.romShortThresholdM,
|
|
15010
|
+
plannedReps: model.plannedReps
|
|
15011
|
+
}
|
|
15012
|
+
),
|
|
15013
|
+
/* @__PURE__ */ jsx(reactNative.View, { style: { flex: 1, minHeight: 16 } }),
|
|
15014
|
+
/* @__PURE__ */ jsx(
|
|
15015
|
+
GhostSpark,
|
|
15016
|
+
{
|
|
15017
|
+
curves: model.velocityCurves,
|
|
15018
|
+
width: chartW,
|
|
15019
|
+
height: chartH,
|
|
15020
|
+
targetTempoSeconds: model.targetTempoSeconds
|
|
15021
|
+
}
|
|
15022
|
+
)
|
|
15023
|
+
]
|
|
15024
|
+
}
|
|
15025
|
+
);
|
|
15026
|
+
}
|
|
15027
|
+
|
|
15028
|
+
// src/components/custom/Fatigue/LiveFatiguePanel.tsx
|
|
15029
|
+
var t24 = getSemanticColors("dark");
|
|
15030
|
+
function ExerciseHeaderLite({ header }) {
|
|
15031
|
+
return /* @__PURE__ */ jsxs(
|
|
15032
|
+
reactNative.View,
|
|
15033
|
+
{
|
|
15034
|
+
style: {
|
|
15035
|
+
flexDirection: "row",
|
|
15036
|
+
alignItems: "flex-end",
|
|
15037
|
+
justifyContent: "space-between",
|
|
15038
|
+
paddingHorizontal: 24,
|
|
15039
|
+
paddingTop: 18,
|
|
15040
|
+
paddingBottom: 14,
|
|
15041
|
+
borderBottomWidth: 1,
|
|
15042
|
+
borderColor: alpha(t24["text-primary"], 0.08)
|
|
15043
|
+
},
|
|
15044
|
+
children: [
|
|
15045
|
+
/* @__PURE__ */ jsxs(reactNative.View, { style: { gap: 2 }, children: [
|
|
15046
|
+
/* @__PURE__ */ jsx(
|
|
15047
|
+
reactNative.Text,
|
|
15048
|
+
{
|
|
15049
|
+
style: {
|
|
15050
|
+
fontSize: 28,
|
|
15051
|
+
fontWeight: "700",
|
|
15052
|
+
fontFamily: FONT_HEAD,
|
|
15053
|
+
color: t24["text-primary"]
|
|
15054
|
+
},
|
|
15055
|
+
children: header.title
|
|
15056
|
+
}
|
|
15057
|
+
),
|
|
15058
|
+
header.subtitle && /* @__PURE__ */ jsx(
|
|
15059
|
+
reactNative.Text,
|
|
15060
|
+
{
|
|
15061
|
+
style: {
|
|
15062
|
+
fontSize: 13,
|
|
15063
|
+
fontWeight: "600",
|
|
15064
|
+
fontFamily: FONT_UI,
|
|
15065
|
+
color: t24["text-secondary"]
|
|
15066
|
+
},
|
|
15067
|
+
children: header.subtitle
|
|
15068
|
+
}
|
|
15069
|
+
)
|
|
15070
|
+
] }),
|
|
15071
|
+
header.meta && /* @__PURE__ */ jsx(
|
|
15072
|
+
reactNative.Text,
|
|
15073
|
+
{
|
|
15074
|
+
style: {
|
|
15075
|
+
fontSize: 13,
|
|
15076
|
+
fontWeight: "800",
|
|
15077
|
+
fontFamily: FONT_MONO,
|
|
15078
|
+
color: t24["text-tertiary"]
|
|
15079
|
+
},
|
|
15080
|
+
children: header.meta
|
|
15081
|
+
}
|
|
15082
|
+
)
|
|
15083
|
+
]
|
|
15084
|
+
}
|
|
15085
|
+
);
|
|
15086
|
+
}
|
|
15087
|
+
function LiveFatiguePanel({
|
|
15088
|
+
model,
|
|
15089
|
+
velocity,
|
|
15090
|
+
header,
|
|
15091
|
+
aura,
|
|
15092
|
+
bodyHeight = 508,
|
|
15093
|
+
cardWidth = 318
|
|
15094
|
+
}) {
|
|
15095
|
+
const category = aura ?? auraForVerdict(model.verdict?.state ?? null);
|
|
15096
|
+
const heroH = bodyHeight - 26;
|
|
15097
|
+
return /* @__PURE__ */ jsx(
|
|
15098
|
+
LiveAuraFrame,
|
|
15099
|
+
{
|
|
15100
|
+
category,
|
|
15101
|
+
style: { borderRadius: 0, borderWidth: 0 },
|
|
15102
|
+
testID: "live-fatigue-panel",
|
|
15103
|
+
children: /* @__PURE__ */ jsxs(reactNative.View, { style: { flex: 1 }, children: [
|
|
15104
|
+
header && /* @__PURE__ */ jsx(ExerciseHeaderLite, { header }),
|
|
15105
|
+
/* @__PURE__ */ jsxs(reactNative.View, { style: { padding: 24, flexDirection: "row", gap: 18, alignItems: "stretch" }, children: [
|
|
15106
|
+
/* @__PURE__ */ jsxs(reactNative.View, { style: { flex: 1, gap: 8 }, children: [
|
|
15107
|
+
/* @__PURE__ */ jsx(
|
|
15108
|
+
reactNative.Text,
|
|
15109
|
+
{
|
|
15110
|
+
style: {
|
|
15111
|
+
fontSize: 9,
|
|
15112
|
+
letterSpacing: 1.2,
|
|
15113
|
+
fontFamily: FONT_MONO,
|
|
15114
|
+
color: t24["text-tertiary"]
|
|
15115
|
+
},
|
|
15116
|
+
children: "VELOCITY \xB7 this set"
|
|
15117
|
+
}
|
|
15118
|
+
),
|
|
15119
|
+
/* @__PURE__ */ jsx(
|
|
15120
|
+
VelocityHero,
|
|
15121
|
+
{
|
|
15122
|
+
velocities: velocity.velocities,
|
|
15123
|
+
targetReps: velocity.targetReps,
|
|
15124
|
+
liveRepIndex: velocity.liveRepIndex,
|
|
15125
|
+
height: heroH
|
|
15126
|
+
}
|
|
15127
|
+
)
|
|
15128
|
+
] }),
|
|
15129
|
+
/* @__PURE__ */ jsx(LiveFatigueCard, { model, width: cardWidth, height: bodyHeight })
|
|
15130
|
+
] })
|
|
15131
|
+
] })
|
|
15132
|
+
}
|
|
15133
|
+
);
|
|
15134
|
+
}
|
|
15135
|
+
var t25 = getSemanticColors("dark");
|
|
15136
|
+
function phaseAt(segments, ms) {
|
|
15137
|
+
const hit = segments.find((s) => ms >= s.startMs && ms < s.endMs);
|
|
15138
|
+
return hit ? hit.phase : null;
|
|
15139
|
+
}
|
|
15140
|
+
function mergePhaseSegments(a, b) {
|
|
15141
|
+
if (a.length === 0) return b;
|
|
15142
|
+
if (b.length === 0) return a;
|
|
15143
|
+
const bounds = Array.from(new Set([...a, ...b].flatMap((s) => [s.startMs, s.endMs]))).sort(
|
|
15144
|
+
(p, q) => p - q
|
|
15145
|
+
);
|
|
15146
|
+
const out = [];
|
|
15147
|
+
for (let i = 0; i < bounds.length - 1; i++) {
|
|
15148
|
+
const startMs = bounds[i];
|
|
15149
|
+
const endMs = bounds[i + 1];
|
|
15150
|
+
if (endMs <= startMs) continue;
|
|
15151
|
+
const mid = (startMs + endMs) / 2;
|
|
15152
|
+
const pa = phaseAt(a, mid);
|
|
15153
|
+
const pb = phaseAt(b, mid);
|
|
15154
|
+
const phase = pa == null ? pb ?? "idle" : pb == null ? pa : pa === pb ? pa : "idle";
|
|
15155
|
+
const last = out[out.length - 1];
|
|
15156
|
+
if (last && last.phase === phase && last.endMs === startMs) last.endMs = endMs;
|
|
15157
|
+
else out.push({ phase, startMs, endMs });
|
|
15158
|
+
}
|
|
15159
|
+
return out;
|
|
15160
|
+
}
|
|
15161
|
+
function lastTMs(curves) {
|
|
15162
|
+
return Math.max(0, ...curves.map((c) => c.samples[c.samples.length - 1]?.tMs ?? 0));
|
|
15163
|
+
}
|
|
15164
|
+
function DualGhostSpark({
|
|
15165
|
+
left,
|
|
15166
|
+
right,
|
|
15167
|
+
width,
|
|
15168
|
+
height = 232,
|
|
15169
|
+
leftLabel = "LEFT",
|
|
15170
|
+
rightLabel = "RIGHT",
|
|
15171
|
+
showDeviceLabels = true,
|
|
15172
|
+
targetTempoSeconds = null
|
|
15173
|
+
}) {
|
|
15174
|
+
const w = width;
|
|
15175
|
+
const h = height;
|
|
15176
|
+
const padL = 14;
|
|
15177
|
+
const padR = 14;
|
|
15178
|
+
const padTop = 22;
|
|
15179
|
+
const padBot = 22;
|
|
15180
|
+
if (left.length === 0 && right.length === 0) {
|
|
15181
|
+
return /* @__PURE__ */ jsx(reactNative.View, { testID: "dual-ghost-spark", style: { width: w, height: h } });
|
|
15182
|
+
}
|
|
15183
|
+
const mid = padTop + (h - padTop - padBot) / 2;
|
|
15184
|
+
const bandTop = mid - BAND_H / 2;
|
|
15185
|
+
const baseUp = bandTop - BAND_GAP;
|
|
15186
|
+
const baseDown = mid + BAND_H / 2 + BAND_GAP;
|
|
15187
|
+
const wingH = Math.max(1, Math.min(baseUp - padTop, h - padBot - baseDown));
|
|
15188
|
+
const allSamples = [...left, ...right].flatMap((c) => c.samples);
|
|
15189
|
+
const vmax = Math.max(0.01, ...allSamples.map((s) => s.velocityMps)) * 1.06;
|
|
15190
|
+
const axisMaxT = Math.max(1, lastTMs(left), lastTMs(right)) * 1.04;
|
|
15191
|
+
const x = (ms) => padL + ms / axisMaxT * (w - padL - padR);
|
|
15192
|
+
const mag = (v) => clamp012(v / vmax) * wingH;
|
|
15193
|
+
const toPts = (c) => c.samples.map((s) => [x(s.tMs), mag(s.velocityMps)]);
|
|
15194
|
+
const wing = (curves) => {
|
|
15195
|
+
const cur = curves[curves.length - 1];
|
|
15196
|
+
return {
|
|
15197
|
+
cur,
|
|
15198
|
+
current: cur ? toPts(cur) : [],
|
|
15199
|
+
ghosts: curves.slice(0, -1).map(toPts),
|
|
15200
|
+
tint: cur ? ghostLineColor(cur.tempoDeviation, cur.grindSignature) : t25["text-tertiary"]
|
|
15201
|
+
};
|
|
15202
|
+
};
|
|
15203
|
+
const up = wing(left);
|
|
15204
|
+
const down = wing(right);
|
|
15205
|
+
const segments = mergePhaseSegments(up.cur?.phaseSegments ?? [], down.cur?.phaseSegments ?? []);
|
|
15206
|
+
return /* @__PURE__ */ jsx(reactNative.View, { testID: "dual-ghost-spark", style: { paddingHorizontal: 4 }, children: /* @__PURE__ */ jsxs("svg", { width: w, height: h, children: [
|
|
15207
|
+
up.current.length > 0 && /* @__PURE__ */ jsx("g", { "data-testid": "dual-ghost-bloom-left", children: /* @__PURE__ */ jsx(
|
|
15208
|
+
GhostBloom,
|
|
15209
|
+
{
|
|
15210
|
+
current: up.current,
|
|
15211
|
+
ghosts: up.ghosts,
|
|
15212
|
+
tint: up.tint,
|
|
15213
|
+
baseline: baseUp,
|
|
15214
|
+
orientation: "up"
|
|
15215
|
+
}
|
|
15216
|
+
) }),
|
|
15217
|
+
down.current.length > 0 && /* @__PURE__ */ jsx("g", { "data-testid": "dual-ghost-bloom-right", children: /* @__PURE__ */ jsx(
|
|
15218
|
+
GhostBloom,
|
|
15219
|
+
{
|
|
15220
|
+
current: down.current,
|
|
15221
|
+
ghosts: down.ghosts,
|
|
15222
|
+
tint: down.tint,
|
|
15223
|
+
baseline: baseDown,
|
|
15224
|
+
orientation: "down"
|
|
15225
|
+
}
|
|
15226
|
+
) }),
|
|
15227
|
+
/* @__PURE__ */ jsx(
|
|
15228
|
+
GhostBand,
|
|
15229
|
+
{
|
|
15230
|
+
segments,
|
|
15231
|
+
x,
|
|
15232
|
+
top: bandTop,
|
|
15233
|
+
showLabels: true,
|
|
15234
|
+
labelColor: alpha(t25["text-primary"], 0.92),
|
|
15235
|
+
targetTempoSeconds
|
|
15236
|
+
}
|
|
15237
|
+
),
|
|
15238
|
+
showDeviceLabels && /* @__PURE__ */ jsxs(jsxRuntime.Fragment, { children: [
|
|
15239
|
+
/* @__PURE__ */ jsx(
|
|
15240
|
+
"text",
|
|
15241
|
+
{
|
|
15242
|
+
x: padL,
|
|
15243
|
+
y: padTop - 9,
|
|
15244
|
+
fontSize: 9,
|
|
15245
|
+
fontWeight: 800,
|
|
15246
|
+
letterSpacing: 1,
|
|
15247
|
+
fontFamily: FONT_UI,
|
|
15248
|
+
fill: t25["text-tertiary"],
|
|
15249
|
+
children: leftLabel
|
|
15250
|
+
}
|
|
15251
|
+
),
|
|
15252
|
+
/* @__PURE__ */ jsx(
|
|
15253
|
+
"text",
|
|
15254
|
+
{
|
|
15255
|
+
x: padL,
|
|
15256
|
+
y: h - padBot + 15,
|
|
15257
|
+
fontSize: 9,
|
|
15258
|
+
fontWeight: 800,
|
|
15259
|
+
letterSpacing: 1,
|
|
15260
|
+
fontFamily: FONT_UI,
|
|
15261
|
+
fill: t25["text-tertiary"],
|
|
15262
|
+
children: rightLabel
|
|
15263
|
+
}
|
|
15264
|
+
)
|
|
15265
|
+
] })
|
|
15266
|
+
] }) });
|
|
15267
|
+
}
|
|
13798
15268
|
var PALETTE = DATAVIZ_CATEGORICAL_PALETTE;
|
|
13799
15269
|
function scaleValue(v, scale) {
|
|
13800
15270
|
if (v <= 0) return 0;
|
|
@@ -13883,7 +15353,7 @@ function Treemap({
|
|
|
13883
15353
|
const rects = squarify(weighted, width, height);
|
|
13884
15354
|
return /* @__PURE__ */ jsx(reactNative.View, { className: cn("relative", className), style: { width, height }, ...props, children: rects.map((r) => {
|
|
13885
15355
|
const selected = r.datum.id === selectedId;
|
|
13886
|
-
const
|
|
15356
|
+
const labelFits2 = r.w > 54 && r.h > 22;
|
|
13887
15357
|
const pressable = r.datum.id !== "__more__";
|
|
13888
15358
|
return /* @__PURE__ */ jsx(
|
|
13889
15359
|
reactNative.Pressable,
|
|
@@ -13905,7 +15375,7 @@ function Treemap({
|
|
|
13905
15375
|
padding: 4,
|
|
13906
15376
|
overflow: "hidden"
|
|
13907
15377
|
},
|
|
13908
|
-
children:
|
|
15378
|
+
children: labelFits2 && /* @__PURE__ */ jsx(reactNative.Text, { numberOfLines: 2, className: "text-[10px] font-semibold text-[#0b0b0b]", children: r.datum.label ?? r.datum.id })
|
|
13909
15379
|
},
|
|
13910
15380
|
r.datum.id
|
|
13911
15381
|
);
|
|
@@ -14001,28 +15471,28 @@ function Scatter({
|
|
|
14001
15471
|
testID: "scatter-canvas",
|
|
14002
15472
|
style: { position: "absolute", top: 0, left: 0, width, height, overflow: "hidden" },
|
|
14003
15473
|
children: [
|
|
14004
|
-
ticksOf(yd, TICK_COUNT).map((
|
|
14005
|
-
const y = toY(
|
|
15474
|
+
ticksOf(yd, TICK_COUNT).map((t26, i) => {
|
|
15475
|
+
const y = toY(t26);
|
|
14006
15476
|
return /* @__PURE__ */ jsx(
|
|
14007
15477
|
reactNative.View,
|
|
14008
15478
|
{
|
|
14009
15479
|
accessibilityElementsHidden: true,
|
|
14010
15480
|
testID: "scatter-gridline-y",
|
|
14011
15481
|
style: { position: "absolute", left: PLOT_LEFT2, width: innerW, top: y, height: 1, backgroundColor: GRID_LINE2 },
|
|
14012
|
-
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(
|
|
15482
|
+
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(t26) })
|
|
14013
15483
|
},
|
|
14014
15484
|
`y-${i}`
|
|
14015
15485
|
);
|
|
14016
15486
|
}),
|
|
14017
|
-
ticksOf(xd, TICK_COUNT).map((
|
|
14018
|
-
const x = toX(
|
|
15487
|
+
ticksOf(xd, TICK_COUNT).map((t26, i) => {
|
|
15488
|
+
const x = toX(t26);
|
|
14019
15489
|
return /* @__PURE__ */ jsx(
|
|
14020
15490
|
reactNative.View,
|
|
14021
15491
|
{
|
|
14022
15492
|
accessibilityElementsHidden: true,
|
|
14023
15493
|
testID: "scatter-gridline-x",
|
|
14024
15494
|
style: { position: "absolute", top: PLOT_TOP, height: innerH, left: x, width: 1, backgroundColor: GRID_LINE2 },
|
|
14025
|
-
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(
|
|
15495
|
+
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(t26) })
|
|
14026
15496
|
},
|
|
14027
15497
|
`x-${i}`
|
|
14028
15498
|
);
|
|
@@ -14095,7 +15565,7 @@ var DEFAULT_THRESHOLDS2 = [
|
|
|
14095
15565
|
var ARC_START = 135;
|
|
14096
15566
|
var ARC_SWEEP = 270;
|
|
14097
15567
|
var SEGMENTS = 40;
|
|
14098
|
-
function
|
|
15568
|
+
function clamp013(n) {
|
|
14099
15569
|
if (Number.isNaN(n)) return 0;
|
|
14100
15570
|
return Math.max(0, Math.min(1, n));
|
|
14101
15571
|
}
|
|
@@ -14140,7 +15610,7 @@ function Gauge2({
|
|
|
14140
15610
|
...props
|
|
14141
15611
|
}) {
|
|
14142
15612
|
const span = max - min || 1;
|
|
14143
|
-
const fill =
|
|
15613
|
+
const fill = clamp013((value - min) / span);
|
|
14144
15614
|
const activeColor = (f) => color ?? bandColor2(f, thresholds, min, max);
|
|
14145
15615
|
const ticks = buildTicks(fill, size, activeColor);
|
|
14146
15616
|
const displayColor = color ?? bandColor2(fill, thresholds, min, max);
|
|
@@ -14157,23 +15627,23 @@ function Gauge2({
|
|
|
14157
15627
|
testID: "gauge",
|
|
14158
15628
|
...props,
|
|
14159
15629
|
children: [
|
|
14160
|
-
ticks.map((
|
|
15630
|
+
ticks.map((t26) => /* @__PURE__ */ jsx(
|
|
14161
15631
|
reactNative.View,
|
|
14162
15632
|
{
|
|
14163
15633
|
accessibilityElementsHidden: true,
|
|
14164
15634
|
testID: "gauge-segment",
|
|
14165
15635
|
style: {
|
|
14166
15636
|
position: "absolute",
|
|
14167
|
-
left:
|
|
14168
|
-
top:
|
|
15637
|
+
left: t26.left - tickThickness / 2,
|
|
15638
|
+
top: t26.top - tickLength / 2,
|
|
14169
15639
|
width: tickThickness,
|
|
14170
15640
|
height: tickLength,
|
|
14171
15641
|
borderRadius: tickThickness / 2,
|
|
14172
|
-
backgroundColor:
|
|
14173
|
-
transform: [{ rotate:
|
|
15642
|
+
backgroundColor: t26.color,
|
|
15643
|
+
transform: [{ rotate: t26.rotate }]
|
|
14174
15644
|
}
|
|
14175
15645
|
},
|
|
14176
|
-
|
|
15646
|
+
t26.key
|
|
14177
15647
|
)),
|
|
14178
15648
|
/* @__PURE__ */ jsxs(reactNative.View, { style: { position: "absolute", top: 0, left: 0, width: size, height: size }, className: "items-center justify-center", children: [
|
|
14179
15649
|
/* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-baseline gap-0.5", children: [
|
|
@@ -14607,6 +16077,8 @@ exports.Avatar = Avatar;
|
|
|
14607
16077
|
exports.AvatarBadge = AvatarBadge;
|
|
14608
16078
|
exports.AvatarGroup = AvatarGroup;
|
|
14609
16079
|
exports.AwardIcon = AwardIcon;
|
|
16080
|
+
exports.BAND_GAP = BAND_GAP;
|
|
16081
|
+
exports.BAND_H = BAND_H;
|
|
14610
16082
|
exports.Badge = Badge;
|
|
14611
16083
|
exports.BadgeText = BadgeText;
|
|
14612
16084
|
exports.BaseBadge = BaseBadge;
|
|
@@ -14637,6 +16109,7 @@ exports.CircularTimer = CircularTimer;
|
|
|
14637
16109
|
exports.Collapse = Collapse;
|
|
14638
16110
|
exports.CollapseButton = CollapseButton;
|
|
14639
16111
|
exports.CollapseContent = CollapseContent;
|
|
16112
|
+
exports.DRIFT_GREY = DRIFT_GREY;
|
|
14640
16113
|
exports.DashboardShell = DashboardShell;
|
|
14641
16114
|
exports.DataRow = DataRow;
|
|
14642
16115
|
exports.DateTime = DateTime;
|
|
@@ -14649,18 +16122,25 @@ exports.Drawer = Drawer;
|
|
|
14649
16122
|
exports.DrawerBody = DrawerBody;
|
|
14650
16123
|
exports.DrawerFooter = DrawerFooter;
|
|
14651
16124
|
exports.DrawerHeader = DrawerHeader;
|
|
16125
|
+
exports.DualGhostSpark = DualGhostSpark;
|
|
16126
|
+
exports.DualVelocityStrip = DualVelocityStrip;
|
|
14652
16127
|
exports.DumbbellIcon = DumbbellIcon;
|
|
14653
16128
|
exports.EmptyState = EmptyState;
|
|
14654
16129
|
exports.ExerciseCard = ExerciseCard;
|
|
14655
16130
|
exports.ExerciseCardHeading = ExerciseCardHeading;
|
|
14656
16131
|
exports.ExerciseHeading = ExerciseHeading;
|
|
14657
16132
|
exports.ExerciseIndicator = ExerciseIndicator;
|
|
16133
|
+
exports.FatigueLights = FatigueLights;
|
|
14658
16134
|
exports.FatigueMeter = FatigueMeter;
|
|
14659
16135
|
exports.FormActions = FormActions;
|
|
14660
16136
|
exports.FormField = FormField;
|
|
14661
16137
|
exports.FormRow = FormRow;
|
|
14662
16138
|
exports.FormSection = FormSection;
|
|
16139
|
+
exports.GRIND_THRESHOLD = GRIND_THRESHOLD;
|
|
14663
16140
|
exports.Gauge = Gauge2;
|
|
16141
|
+
exports.GhostBand = GhostBand;
|
|
16142
|
+
exports.GhostBloom = GhostBloom;
|
|
16143
|
+
exports.GhostSpark = GhostSpark;
|
|
14664
16144
|
exports.HStack = HStack;
|
|
14665
16145
|
exports.Heading = Heading;
|
|
14666
16146
|
exports.HelpTip = HelpTip;
|
|
@@ -14683,6 +16163,8 @@ exports.ListItemDivider = ListItemDivider;
|
|
|
14683
16163
|
exports.ListItemIcon = ListItemIcon;
|
|
14684
16164
|
exports.ListItemTrailing = ListItemTrailing;
|
|
14685
16165
|
exports.LiveAuraFrame = LiveAuraFrame;
|
|
16166
|
+
exports.LiveFatigueCard = LiveFatigueCard;
|
|
16167
|
+
exports.LiveFatiguePanel = LiveFatiguePanel;
|
|
14686
16168
|
exports.METRIC_FONT = METRIC_FONT;
|
|
14687
16169
|
exports.Menu = Menu;
|
|
14688
16170
|
exports.MenuDivider = MenuDivider;
|
|
@@ -14707,6 +16189,8 @@ exports.ModalTitle = ModalTitle;
|
|
|
14707
16189
|
exports.MuscleGroupChip = MuscleGroupChip;
|
|
14708
16190
|
exports.NavItem = NavItem;
|
|
14709
16191
|
exports.Overline = Overline;
|
|
16192
|
+
exports.PHASE_AXIS_COLOR = PHASE_AXIS_COLOR;
|
|
16193
|
+
exports.PRESSED_ELEVATION_LEVEL = PRESSED_ELEVATION_LEVEL;
|
|
14710
16194
|
exports.Paragraph = Paragraph;
|
|
14711
16195
|
exports.PasswordInput = PasswordInput;
|
|
14712
16196
|
exports.PersonStandingIcon = PersonStandingIcon;
|
|
@@ -14720,12 +16204,18 @@ exports.PrBadge = PrBadge;
|
|
|
14720
16204
|
exports.PrHistoryModal = PrHistoryModal;
|
|
14721
16205
|
exports.Progress = Progress;
|
|
14722
16206
|
exports.ProgressSteps = ProgressSteps;
|
|
16207
|
+
exports.RED_DEEP = RED_DEEP;
|
|
16208
|
+
exports.RED_LIGHT = RED_LIGHT;
|
|
16209
|
+
exports.RED_MID = RED_MID;
|
|
14723
16210
|
exports.Radio = Radio;
|
|
14724
16211
|
exports.RadioGroup = RadioGroup;
|
|
14725
16212
|
exports.ReadinessCheck = ReadinessCheck;
|
|
14726
16213
|
exports.RestTimer = RestTimer;
|
|
16214
|
+
exports.RomProgressionChart = RomProgressionChart;
|
|
14727
16215
|
exports.SET_STRIP_VARIABLE_COLOR = SET_STRIP_VARIABLE_COLOR;
|
|
14728
16216
|
exports.SET_STRIP_ZONES = SET_STRIP_ZONES;
|
|
16217
|
+
exports.SILVER = SILVER;
|
|
16218
|
+
exports.STATE_LABEL = STATE_LABEL;
|
|
14729
16219
|
exports.SURFACE_LEVEL_TOKEN = SURFACE_LEVEL_TOKEN;
|
|
14730
16220
|
exports.ScaleIcon = ScaleIcon;
|
|
14731
16221
|
exports.Scatter = Scatter;
|
|
@@ -14774,6 +16264,7 @@ exports.Surface = Surface;
|
|
|
14774
16264
|
exports.SurfaceContext = SurfaceContext;
|
|
14775
16265
|
exports.SvgIcon = SvgIcon;
|
|
14776
16266
|
exports.Switch = Switch;
|
|
16267
|
+
exports.TONE_COLOR = TONE_COLOR;
|
|
14777
16268
|
exports.Tab = Tab;
|
|
14778
16269
|
exports.TabList = TabList;
|
|
14779
16270
|
exports.TabPanel = TabPanel;
|
|
@@ -14799,7 +16290,9 @@ exports.Treemap = Treemap;
|
|
|
14799
16290
|
exports.TrendingDownIcon = TrendingDownIcon;
|
|
14800
16291
|
exports.Typography = Typography;
|
|
14801
16292
|
exports.VStack = VStack;
|
|
16293
|
+
exports.VelocityHero = VelocityHero;
|
|
14802
16294
|
exports.VelocityStrip = VelocityStrip;
|
|
16295
|
+
exports.VerdictHero = VerdictHero;
|
|
14803
16296
|
exports.VoltrasMark = VoltrasMark;
|
|
14804
16297
|
exports.VolumeLandmarkBar = VolumeLandmarkBar;
|
|
14805
16298
|
exports.WeekRow = WeekRow;
|
|
@@ -14810,6 +16303,8 @@ exports.ZoneTrack = ZoneTrack;
|
|
|
14810
16303
|
exports.alpha = alpha;
|
|
14811
16304
|
exports.applyThemePreset = applyThemePreset;
|
|
14812
16305
|
exports.audiobookPreset = audiobookPreset;
|
|
16306
|
+
exports.auraForVerdict = auraForVerdict;
|
|
16307
|
+
exports.backgroundFrameDark = backgroundFrameDark;
|
|
14813
16308
|
exports.bestTextColor = bestTextColor;
|
|
14814
16309
|
exports.buttonSizes = buttonSizes;
|
|
14815
16310
|
exports.calculateMeanVelocity = calculateMeanVelocity;
|
|
@@ -14848,6 +16343,7 @@ exports.getFieldValidationProps = getFieldValidationProps;
|
|
|
14848
16343
|
exports.getGlowShadow = getGlowShadow;
|
|
14849
16344
|
exports.getHoverColors = getHoverColors;
|
|
14850
16345
|
exports.getLuminance = getLuminance;
|
|
16346
|
+
exports.getPressedRecessShadow = getPressedRecessShadow;
|
|
14851
16347
|
exports.getResultColor = getResultColor;
|
|
14852
16348
|
exports.getSemanticColors = getSemanticColors;
|
|
14853
16349
|
exports.getStatusColor = getStatusColor;
|
|
@@ -14855,6 +16351,7 @@ exports.getThemeCSSVars = getThemeCSSVars;
|
|
|
14855
16351
|
exports.getValidatedElevation = getValidatedElevation;
|
|
14856
16352
|
exports.getVelocityZoneColor = getVelocityZoneColor;
|
|
14857
16353
|
exports.getVelocityZoneName = getVelocityZoneName;
|
|
16354
|
+
exports.ghostLineColor = ghostLineColor;
|
|
14858
16355
|
exports.gluestackConfig = gluestackConfig;
|
|
14859
16356
|
exports.hasMaxLength = hasMaxLength;
|
|
14860
16357
|
exports.hasMinLength = hasMinLength;
|
|
@@ -14868,10 +16365,12 @@ exports.lightThemeCSSVars = lightThemeCSSVars;
|
|
|
14868
16365
|
exports.lighten = lighten;
|
|
14869
16366
|
exports.linearGradient = linearGradient;
|
|
14870
16367
|
exports.liveAuraColor = liveAuraColor;
|
|
16368
|
+
exports.mergePhaseSegments = mergePhaseSegments;
|
|
14871
16369
|
exports.neumorphicShadows = neumorphicShadows;
|
|
14872
16370
|
exports.onSurfaceColors = onSurfaceColors;
|
|
14873
16371
|
exports.paceTone = paceTone;
|
|
14874
16372
|
exports.paceToneColor = paceToneColor;
|
|
16373
|
+
exports.pressedLevel = pressedLevel;
|
|
14875
16374
|
exports.primitiveBorderRadius = primitiveBorderRadius;
|
|
14876
16375
|
exports.primitiveBreakpoints = primitiveBreakpoints;
|
|
14877
16376
|
exports.primitiveColors = primitiveColors;
|
|
@@ -14893,9 +16392,11 @@ exports.semanticColorsLight = semanticColorsLight;
|
|
|
14893
16392
|
exports.semanticTypography = semanticTypography;
|
|
14894
16393
|
exports.sequentialEffort = sequentialEffort;
|
|
14895
16394
|
exports.shadowColors = shadowColors;
|
|
16395
|
+
exports.smoothPath = smoothPath;
|
|
14896
16396
|
exports.statusPillColor = statusPillColor;
|
|
14897
16397
|
exports.surfaceBackground = surfaceBackground;
|
|
14898
16398
|
exports.surfaceGradient = surfaceGradient;
|
|
16399
|
+
exports.surfaceRampDark = surfaceRampDark;
|
|
14899
16400
|
exports.useOnSurfaceColor = useOnSurfaceColor;
|
|
14900
16401
|
exports.useSurface = useSurface;
|
|
14901
16402
|
exports.useSurfaceMode = useSurfaceMode;
|