@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.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx as jsx$1, jsxs as jsxs$1, Fragment } from 'react/jsx-runtime';
|
|
|
2
2
|
import { Pressable, ActivityIndicator, Text, View, TextInput, Platform, StyleSheet, Easing, Image, Modal as Modal$1, ScrollView, Animated } from 'react-native';
|
|
3
3
|
import { clsx } from 'clsx';
|
|
4
4
|
import { twMerge } from 'tailwind-merge';
|
|
5
|
-
import React25, { forwardRef, useState, createContext, useContext, useMemo, useRef, useEffect, useCallback, useSyncExternalStore, Fragment as Fragment$1 } from 'react';
|
|
5
|
+
import React25, { forwardRef, useState, createContext, useContext, useMemo, useRef, useEffect, useCallback, useId, useSyncExternalStore, Fragment as Fragment$1 } from 'react';
|
|
6
6
|
|
|
7
7
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
8
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
@@ -333,6 +333,21 @@ var primitiveColors = {
|
|
|
333
333
|
black: "#000000",
|
|
334
334
|
transparent: "transparent"
|
|
335
335
|
};
|
|
336
|
+
var surfaceRampDark = {
|
|
337
|
+
inset: "#13100D",
|
|
338
|
+
// L*4.5 — sub-shell well / pressed
|
|
339
|
+
background: "#1C1916",
|
|
340
|
+
// L*9 — shell / frame
|
|
341
|
+
base: "#252321",
|
|
342
|
+
// L*13.5 — main content plane
|
|
343
|
+
elevated: "#2C2A28",
|
|
344
|
+
// L*17 — nav / rail
|
|
345
|
+
raised: "#31302F",
|
|
346
|
+
// L*20 — cards
|
|
347
|
+
overlay: "#373635"
|
|
348
|
+
// L*22.5 — hero / popover
|
|
349
|
+
};
|
|
350
|
+
var backgroundFrameDark = "#100D0A";
|
|
336
351
|
var primitiveRamps = {
|
|
337
352
|
red: {
|
|
338
353
|
50: "#FFF4F4",
|
|
@@ -800,10 +815,19 @@ var semanticColorsLight = {
|
|
|
800
815
|
"surface-overlay": primitiveColors.white,
|
|
801
816
|
"surface-input": primitiveColors.neutral[50],
|
|
802
817
|
// Input field background (filled variant)
|
|
818
|
+
// Deepest pressed pit (TD-surface-tokens, S-3) — light-mode counterpart of
|
|
819
|
+
// the dark ramp's `inset` plane; a placeholder pairing, not part of the
|
|
820
|
+
// dark-ramp investigation (surface.contract.test.ts is dark-mode only).
|
|
821
|
+
"surface-inset": primitiveColors.neutral[300],
|
|
803
822
|
// Background colors (background-*)
|
|
804
823
|
"background-base": "#EBEBEB",
|
|
805
824
|
"background-default": primitiveColors.white,
|
|
806
825
|
"background-subtle": primitiveColors.neutral[50],
|
|
826
|
+
// Frame/bezel chrome (TD-surface-tokens, S-3) — top bar + side nav shell,
|
|
827
|
+
// one step below `background-base` (darker still than `surface-inset`
|
|
828
|
+
// above, mirroring the dark-mode ordering). Placeholder pairing for light
|
|
829
|
+
// mode (see dark-mode note on `surface-inset` above).
|
|
830
|
+
"background-frame": primitiveColors.neutral[400],
|
|
807
831
|
// Border colors (border-*)
|
|
808
832
|
"border-default": "#E8E9EB",
|
|
809
833
|
"border-subtle": primitiveColors.neutral[100],
|
|
@@ -819,6 +843,11 @@ var semanticColorsLight = {
|
|
|
819
843
|
// Input field border on focus
|
|
820
844
|
"border-input-error": primitiveColors.red[600],
|
|
821
845
|
// Input field border on error
|
|
846
|
+
// Alpha hairline separators (surface-independent — composite toward black on
|
|
847
|
+
// light surfaces, mirroring the dark-mode white-alpha family). See §4/S-2.
|
|
848
|
+
"hairline-subtle": "rgba(0, 0, 0, 0.06)",
|
|
849
|
+
"hairline-default": "rgba(0, 0, 0, 0.09)",
|
|
850
|
+
"hairline-strong": "rgba(0, 0, 0, 0.14)",
|
|
822
851
|
// Interactive states (interactive-*)
|
|
823
852
|
"interactive-hover": "rgba(55, 65, 81, 0.04)",
|
|
824
853
|
"interactive-focus": "rgba(55, 65, 81, 0.12)",
|
|
@@ -911,25 +940,46 @@ var semanticColorsDark = {
|
|
|
911
940
|
"text-inverse": primitiveColors.neutral[900],
|
|
912
941
|
"text-link": "#828DF8",
|
|
913
942
|
"text-link-hover": primitiveColors.blue[400],
|
|
914
|
-
// Surface colors - dark backgrounds
|
|
915
|
-
|
|
916
|
-
//
|
|
917
|
-
|
|
918
|
-
//
|
|
919
|
-
|
|
920
|
-
//
|
|
921
|
-
|
|
922
|
-
//
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
//
|
|
926
|
-
"
|
|
927
|
-
//
|
|
928
|
-
"
|
|
929
|
-
//
|
|
930
|
-
"
|
|
931
|
-
//
|
|
932
|
-
|
|
943
|
+
// Surface colors - dark backgrounds — warm-tapered DERIVED ramp (TD-surface-tokens,
|
|
944
|
+
// S-1, re-spaced S-3). Shipped verbatim from `deriveSurfaceRamp()` — see
|
|
945
|
+
// `surfaceRampDark` in primitives.ts for the full derivation note. `surface-overlay`
|
|
946
|
+
// and `surface-elevated` are distinct (were both #191919 pre-S-1); `surface-input`
|
|
947
|
+
// tracks one plane above `surface-base`, same relative position as before the remap.
|
|
948
|
+
// `background-base` backs `Surface level="background"` (SurfaceContext.SURFACE_LEVEL_TOKEN),
|
|
949
|
+
// so it takes the ramp's `background` (shell/frame) role, NOT `inset`. The ramp's
|
|
950
|
+
// deepest `inset` plane is now promoted to `surface-inset` below (S-3) — still no
|
|
951
|
+
// `SurfaceLevel` member for it (follow-up, alongside the elevation.ts -1/-2 pressed
|
|
952
|
+
// levels — parallel workstream, not this branch).
|
|
953
|
+
"surface-base": surfaceRampDark.base,
|
|
954
|
+
// main surface (#252321, L*13.5)
|
|
955
|
+
"surface-elevated": surfaceRampDark.elevated,
|
|
956
|
+
// elevated surface (#2C2A28, L*17 — nav/rail)
|
|
957
|
+
"surface-raised": surfaceRampDark.raised,
|
|
958
|
+
// raised surface (#31302F, L*20 — cards)
|
|
959
|
+
"surface-overlay": surfaceRampDark.overlay,
|
|
960
|
+
// overlay surface (#373635, L*22.5 — hero/popover)
|
|
961
|
+
"surface-input": surfaceRampDark.elevated,
|
|
962
|
+
// input surface (#2C2A28 — one plane above base)
|
|
963
|
+
"surface-inset": surfaceRampDark.inset,
|
|
964
|
+
// deepest pressed pit (#13100D, L*4.5 — sub-shell well)
|
|
965
|
+
// Background colors — same ramp, the frame/shell end of it.
|
|
966
|
+
"background-base": surfaceRampDark.background,
|
|
967
|
+
// shell / frame (#1C1916, L*9 — Surface level="background")
|
|
968
|
+
"background-default": surfaceRampDark.base,
|
|
969
|
+
// main background (#252321, L*13.5 — matches surface-base)
|
|
970
|
+
"background-subtle": surfaceRampDark.elevated,
|
|
971
|
+
// subtle background (#2C2A28, L*17 — matches surface-elevated)
|
|
972
|
+
// Frame/bezel chrome (S-3) — the top bar + side nav shell; one step BELOW
|
|
973
|
+
// `background-base`, darker even than `surface-inset` — it exits the content
|
|
974
|
+
// ramp entirely rather than sitting in it. See `backgroundFrameDark` in
|
|
975
|
+
// primitives.ts.
|
|
976
|
+
"background-frame": backgroundFrameDark,
|
|
977
|
+
// frame / bezel (#100D0A, L*3.79)
|
|
978
|
+
// Border colors — `border-subtle` (#1C1C1C) previously collided with
|
|
979
|
+
// `surface-raised` (also #1C1C1C, an invisible border on raised cards). The
|
|
980
|
+
// re-spaced ramp above moved `surface-raised` to #2D2C2B, so this value is now
|
|
981
|
+
// distinct from every surface hex without needing to change it — see
|
|
982
|
+
// `surface.contract.test.ts` R2/R4.
|
|
933
983
|
"border-default": primitiveColors.charcoal[400],
|
|
934
984
|
// default border
|
|
935
985
|
"border-subtle": primitiveColors.charcoal[500],
|
|
@@ -943,6 +993,14 @@ var semanticColorsDark = {
|
|
|
943
993
|
"border-input-hover": primitiveColors.neutral[500],
|
|
944
994
|
"border-input-focus": "#828DF8",
|
|
945
995
|
"border-input-error": primitiveColors.red[500],
|
|
996
|
+
// Alpha hairline separators — the primary separation cue (§4/S-2). Self-
|
|
997
|
+
// normalizing: composites toward white by ~the same amount on ANY plane, so
|
|
998
|
+
// one family works at every elevation instead of per-surface border tokens.
|
|
999
|
+
// Shadows are demoted to floating-overlay use only (not shipped as a fill
|
|
1000
|
+
// separator here — see elevation.ts, follow-up S-4).
|
|
1001
|
+
"hairline-subtle": "rgba(255, 255, 255, 0.06)",
|
|
1002
|
+
"hairline-default": "rgba(255, 255, 255, 0.09)",
|
|
1003
|
+
"hairline-strong": "rgba(255, 255, 255, 0.14)",
|
|
946
1004
|
// Interactive states
|
|
947
1005
|
"interactive-hover": "rgba(255, 255, 255, 0.04)",
|
|
948
1006
|
"interactive-focus": "rgba(255, 255, 255, 0.12)",
|
|
@@ -1472,9 +1530,11 @@ var lightThemeCSSVars = {
|
|
|
1472
1530
|
"--color-surface-base": semanticColorsLight["surface-base"],
|
|
1473
1531
|
"--color-surface-elevated": semanticColorsLight["surface-elevated"],
|
|
1474
1532
|
"--color-surface-raised": semanticColorsLight["surface-raised"],
|
|
1533
|
+
"--color-surface-inset": semanticColorsLight["surface-inset"],
|
|
1475
1534
|
"--color-background-base": semanticColorsLight["background-base"],
|
|
1476
1535
|
"--color-background-default": semanticColorsLight["background-default"],
|
|
1477
1536
|
"--color-background-subtle": semanticColorsLight["background-subtle"],
|
|
1537
|
+
"--color-background-frame": semanticColorsLight["background-frame"],
|
|
1478
1538
|
"--color-border-default": semanticColorsLight["border-default"],
|
|
1479
1539
|
"--color-border-subtle": semanticColorsLight["border-subtle"],
|
|
1480
1540
|
"--color-border-strong": semanticColorsLight["border-strong"],
|
|
@@ -1531,6 +1591,9 @@ var lightThemeCSSVars = {
|
|
|
1531
1591
|
"--color-border-input-hover": semanticColorsLight["border-input-hover"],
|
|
1532
1592
|
"--color-border-input-focus": semanticColorsLight["border-input-focus"],
|
|
1533
1593
|
"--color-border-input-error": semanticColorsLight["border-input-error"],
|
|
1594
|
+
"--color-hairline-subtle": semanticColorsLight["hairline-subtle"],
|
|
1595
|
+
"--color-hairline-default": semanticColorsLight["hairline-default"],
|
|
1596
|
+
"--color-hairline-strong": semanticColorsLight["hairline-strong"],
|
|
1534
1597
|
"--color-interactive-disabled-text": semanticColorsLight["interactive-disabled-text"],
|
|
1535
1598
|
"--color-avatar-background": semanticColorsLight["avatar-background"],
|
|
1536
1599
|
"--color-avatar-text": semanticColorsLight["avatar-text"],
|
|
@@ -1570,9 +1633,11 @@ var darkThemeCSSVars = {
|
|
|
1570
1633
|
"--color-surface-base": semanticColorsDark["surface-base"],
|
|
1571
1634
|
"--color-surface-elevated": semanticColorsDark["surface-elevated"],
|
|
1572
1635
|
"--color-surface-raised": semanticColorsDark["surface-raised"],
|
|
1636
|
+
"--color-surface-inset": semanticColorsDark["surface-inset"],
|
|
1573
1637
|
"--color-background-base": semanticColorsDark["background-base"],
|
|
1574
1638
|
"--color-background-default": semanticColorsDark["background-default"],
|
|
1575
1639
|
"--color-background-subtle": semanticColorsDark["background-subtle"],
|
|
1640
|
+
"--color-background-frame": semanticColorsDark["background-frame"],
|
|
1576
1641
|
"--color-border-default": semanticColorsDark["border-default"],
|
|
1577
1642
|
"--color-border-subtle": semanticColorsDark["border-subtle"],
|
|
1578
1643
|
"--color-border-strong": semanticColorsDark["border-strong"],
|
|
@@ -1629,6 +1694,9 @@ var darkThemeCSSVars = {
|
|
|
1629
1694
|
"--color-border-input-hover": semanticColorsDark["border-input-hover"],
|
|
1630
1695
|
"--color-border-input-focus": semanticColorsDark["border-input-focus"],
|
|
1631
1696
|
"--color-border-input-error": semanticColorsDark["border-input-error"],
|
|
1697
|
+
"--color-hairline-subtle": semanticColorsDark["hairline-subtle"],
|
|
1698
|
+
"--color-hairline-default": semanticColorsDark["hairline-default"],
|
|
1699
|
+
"--color-hairline-strong": semanticColorsDark["hairline-strong"],
|
|
1632
1700
|
"--color-interactive-disabled-text": semanticColorsDark["interactive-disabled-text"],
|
|
1633
1701
|
"--color-avatar-background": semanticColorsDark["avatar-background"],
|
|
1634
1702
|
"--color-avatar-text": semanticColorsDark["avatar-text"],
|
|
@@ -2263,6 +2331,10 @@ function getElevationShadow(baseColor, level, theme, isHovered = false) {
|
|
|
2263
2331
|
elevationShadowCache.set(cacheKey, result);
|
|
2264
2332
|
return result;
|
|
2265
2333
|
}
|
|
2334
|
+
var PRESSED_ELEVATION_LEVEL = -1;
|
|
2335
|
+
function getPressedRecessShadow(fillColor, theme) {
|
|
2336
|
+
return getElevationShadow(fillColor, PRESSED_ELEVATION_LEVEL, theme);
|
|
2337
|
+
}
|
|
2266
2338
|
function getBaseSurfaceColor(theme) {
|
|
2267
2339
|
return theme === "light" ? semanticColorsLight["surface-raised"] : semanticColorsDark["surface-elevated"];
|
|
2268
2340
|
}
|
|
@@ -2565,6 +2637,7 @@ var SURFACE_LEVEL_TOKEN = {
|
|
|
2565
2637
|
raised: "surface-raised",
|
|
2566
2638
|
overlay: "surface-overlay"
|
|
2567
2639
|
};
|
|
2640
|
+
var PRESSED_RAMP = ["inset", "background", "base", "elevated", "raised", "overlay"];
|
|
2568
2641
|
var ON_SURFACE_TOKEN = {
|
|
2569
2642
|
primary: "text-primary",
|
|
2570
2643
|
secondary: "text-secondary",
|
|
@@ -2589,9 +2662,19 @@ function onSurfaceColors(mode) {
|
|
|
2589
2662
|
function useOnSurfaceColor(role = "primary") {
|
|
2590
2663
|
return getSemanticColors(useSurfaceMode())[ON_SURFACE_TOKEN[role]];
|
|
2591
2664
|
}
|
|
2665
|
+
function insetFloor(mode) {
|
|
2666
|
+
const colors = getSemanticColors(mode);
|
|
2667
|
+
return colors["surface-inset"] ?? surfaceRampDark.inset;
|
|
2668
|
+
}
|
|
2592
2669
|
function surfaceBackground(level, mode) {
|
|
2670
|
+
if (level === "inset") return insetFloor(mode);
|
|
2593
2671
|
return getSemanticColors(mode)[SURFACE_LEVEL_TOKEN[level]];
|
|
2594
2672
|
}
|
|
2673
|
+
function pressedLevel(parent) {
|
|
2674
|
+
const index = PRESSED_RAMP.indexOf(parent);
|
|
2675
|
+
const down = Math.max(0, index - 1);
|
|
2676
|
+
return PRESSED_RAMP[down];
|
|
2677
|
+
}
|
|
2595
2678
|
function getTheme() {
|
|
2596
2679
|
if (typeof document === "undefined") return "dark";
|
|
2597
2680
|
return document.documentElement.classList.contains("light") ? "light" : "dark";
|
|
@@ -5290,7 +5373,7 @@ function ToastProvider({
|
|
|
5290
5373
|
return id;
|
|
5291
5374
|
}, [defaultDuration, maxToasts]);
|
|
5292
5375
|
const removeToast = useCallback((id) => {
|
|
5293
|
-
setToasts((prev) => prev.filter((
|
|
5376
|
+
setToasts((prev) => prev.filter((t26) => t26.id !== id));
|
|
5294
5377
|
}, []);
|
|
5295
5378
|
const removeAllToasts = useCallback(() => {
|
|
5296
5379
|
setToasts([]);
|
|
@@ -6043,6 +6126,7 @@ function IconBox({
|
|
|
6043
6126
|
function Surface({
|
|
6044
6127
|
elevation = 0,
|
|
6045
6128
|
level,
|
|
6129
|
+
pressed = false,
|
|
6046
6130
|
glowColor,
|
|
6047
6131
|
glowIntensity,
|
|
6048
6132
|
theme,
|
|
@@ -6056,13 +6140,24 @@ function Surface({
|
|
|
6056
6140
|
const mode = theme ?? inherited.mode;
|
|
6057
6141
|
const isPlane = level != null;
|
|
6058
6142
|
const baseColor = getBaseSurfaceColor(mode);
|
|
6059
|
-
const
|
|
6060
|
-
|
|
6143
|
+
const resolvedLevel = pressed ? pressedLevel(inherited.level) : level ?? inherited.level;
|
|
6144
|
+
let backgroundColor;
|
|
6145
|
+
let shadowStyle;
|
|
6146
|
+
if (pressed) {
|
|
6147
|
+
backgroundColor = surfaceBackground(resolvedLevel, mode);
|
|
6148
|
+
shadowStyle = getPressedRecessShadow(backgroundColor, mode);
|
|
6149
|
+
} else if (isPlane) {
|
|
6150
|
+
backgroundColor = surfaceBackground(level, mode);
|
|
6151
|
+
shadowStyle = {};
|
|
6152
|
+
} else {
|
|
6153
|
+
backgroundColor = getElevationSurface(baseColor, elevation, mode);
|
|
6154
|
+
shadowStyle = getElevationShadow(baseColor, elevation, mode);
|
|
6155
|
+
}
|
|
6061
6156
|
const glowStyle = glowColor ? getGlowShadow(glowColor, glowIntensity) : {};
|
|
6062
|
-
const applyRounded = rounded ?? !isPlane;
|
|
6157
|
+
const applyRounded = rounded ?? (pressed || !isPlane);
|
|
6063
6158
|
const value = useMemo(
|
|
6064
|
-
() => ({ mode, level:
|
|
6065
|
-
[mode,
|
|
6159
|
+
() => ({ mode, level: resolvedLevel }),
|
|
6160
|
+
[mode, resolvedLevel]
|
|
6066
6161
|
);
|
|
6067
6162
|
return /* @__PURE__ */ jsx(SurfaceContext.Provider, { value, children: /* @__PURE__ */ jsx(
|
|
6068
6163
|
View,
|
|
@@ -7255,13 +7350,14 @@ function MetricGroup({
|
|
|
7255
7350
|
)
|
|
7256
7351
|
] }, i)) });
|
|
7257
7352
|
}
|
|
7353
|
+
var t = getSemanticColors("dark");
|
|
7258
7354
|
var baseBadgeSizeConfig = {
|
|
7259
7355
|
sm: { fontSize: 9, paddingH: 6, paddingV: 2, iconSize: 10 },
|
|
7260
7356
|
md: { fontSize: 10, paddingH: 8, paddingV: 2, iconSize: 12 },
|
|
7261
7357
|
lg: { fontSize: 12, paddingH: 10, paddingV: 4, iconSize: 14 }
|
|
7262
7358
|
};
|
|
7263
7359
|
var variantColors = {
|
|
7264
|
-
plain: { backgroundColor: "
|
|
7360
|
+
plain: { backgroundColor: t["surface-raised"], borderColor: "#1F1F1F" },
|
|
7265
7361
|
pr: {
|
|
7266
7362
|
backgroundColor: "rgba(255, 121, 0, 0.12)",
|
|
7267
7363
|
borderColor: "rgba(255, 121, 0, 0.3)"
|
|
@@ -7501,11 +7597,11 @@ function PrBadge({
|
|
|
7501
7597
|
}
|
|
7502
7598
|
return badge;
|
|
7503
7599
|
}
|
|
7504
|
-
var
|
|
7600
|
+
var t2 = getSemanticColors("dark");
|
|
7505
7601
|
var solidVariantColors = {
|
|
7506
|
-
success:
|
|
7507
|
-
warning:
|
|
7508
|
-
error:
|
|
7602
|
+
success: t2["status-success"],
|
|
7603
|
+
warning: t2["status-warning"],
|
|
7604
|
+
error: t2["status-error"],
|
|
7509
7605
|
neutral: "#6B7280"
|
|
7510
7606
|
};
|
|
7511
7607
|
var ringVariantStyles = {
|
|
@@ -7545,8 +7641,8 @@ var iconColors2 = {
|
|
|
7545
7641
|
warning: "#6B4000",
|
|
7546
7642
|
error: "#5C1A1A",
|
|
7547
7643
|
neutral: "#D1D5DB",
|
|
7548
|
-
"on-track":
|
|
7549
|
-
deviation:
|
|
7644
|
+
"on-track": t2["status-success"],
|
|
7645
|
+
deviation: t2["status-warning"],
|
|
7550
7646
|
future: "#6B7280"
|
|
7551
7647
|
};
|
|
7552
7648
|
var sizeDimensions = {
|
|
@@ -7867,19 +7963,19 @@ function MetricCell({ children, color, fontSize = 11 }) {
|
|
|
7867
7963
|
}
|
|
7868
7964
|
|
|
7869
7965
|
// src/components/custom/Workout/TempoDisplay.tsx
|
|
7870
|
-
var
|
|
7966
|
+
var t3 = getSemanticColors("dark");
|
|
7871
7967
|
var INTER = "Inter, sans-serif";
|
|
7872
7968
|
var TEXT_TERTIARY = "#6B7280";
|
|
7873
|
-
var STATUS_ERROR =
|
|
7874
|
-
var STATUS_SUCCESS =
|
|
7875
|
-
var STATUS_WARNING =
|
|
7969
|
+
var STATUS_ERROR = t3["status-error"];
|
|
7970
|
+
var STATUS_SUCCESS = t3["status-success"];
|
|
7971
|
+
var STATUS_WARNING = t3["status-warning"];
|
|
7876
7972
|
var ON_TARGET_MS = 100;
|
|
7877
7973
|
function getTempoFillPct(elapsedMs, targetMs) {
|
|
7878
7974
|
if (!targetMs) return 100;
|
|
7879
7975
|
return Math.min(100, elapsedMs / targetMs * 100);
|
|
7880
7976
|
}
|
|
7881
7977
|
function activeNumberTone(elapsedMs, targetMs) {
|
|
7882
|
-
if (targetMs == null) return
|
|
7978
|
+
if (targetMs == null) return t3["text-primary"];
|
|
7883
7979
|
const remainingMs = targetMs - elapsedMs;
|
|
7884
7980
|
if (remainingMs > ON_TARGET_MS) return STATUS_WARNING;
|
|
7885
7981
|
if (remainingMs >= -ON_TARGET_MS) return STATUS_SUCCESS;
|
|
@@ -8031,7 +8127,7 @@ function TempoDisplay({
|
|
|
8031
8127
|
style: {
|
|
8032
8128
|
flexDirection: "row",
|
|
8033
8129
|
alignItems: "center",
|
|
8034
|
-
backgroundColor: "
|
|
8130
|
+
backgroundColor: t3["surface-raised"],
|
|
8035
8131
|
paddingHorizontal: chromePadX,
|
|
8036
8132
|
paddingVertical: chromePadY,
|
|
8037
8133
|
borderRadius: chromeRadius
|
|
@@ -8046,7 +8142,7 @@ function TempoDisplay({
|
|
|
8046
8142
|
fontSize: labelFont,
|
|
8047
8143
|
fontWeight: "500",
|
|
8048
8144
|
// Live-muted green while a rep is running, tertiary at rest.
|
|
8049
|
-
color: live ?
|
|
8145
|
+
color: live ? t3["status-live-muted"] : TEXT_TERTIARY,
|
|
8050
8146
|
letterSpacing: 0.5,
|
|
8051
8147
|
textTransform: "uppercase",
|
|
8052
8148
|
marginRight: Math.round(fontSize * 0.5)
|
|
@@ -8104,7 +8200,7 @@ function TempoDisplay({
|
|
|
8104
8200
|
View,
|
|
8105
8201
|
{
|
|
8106
8202
|
style: {
|
|
8107
|
-
backgroundColor: "
|
|
8203
|
+
backgroundColor: t3["surface-overlay"],
|
|
8108
8204
|
borderRadius: 6,
|
|
8109
8205
|
paddingVertical: 8,
|
|
8110
8206
|
paddingHorizontal: 12,
|
|
@@ -8157,13 +8253,13 @@ function TempoDisplay({
|
|
|
8157
8253
|
}
|
|
8158
8254
|
);
|
|
8159
8255
|
}
|
|
8160
|
-
var
|
|
8256
|
+
var t4 = getSemanticColors("dark");
|
|
8161
8257
|
function getDotColor(deviation) {
|
|
8162
8258
|
const abs = Math.abs(deviation);
|
|
8163
|
-
if (deviation < -0.3) return
|
|
8259
|
+
if (deviation < -0.3) return t4["status-success"];
|
|
8164
8260
|
if (abs <= 0.3) return "#6B7280";
|
|
8165
|
-
if (abs <= 0.7) return
|
|
8166
|
-
return
|
|
8261
|
+
if (abs <= 0.7) return t4["status-warning"];
|
|
8262
|
+
return t4["status-error"];
|
|
8167
8263
|
}
|
|
8168
8264
|
function getDeviationDescription(deviation) {
|
|
8169
8265
|
if (deviation < -0.3) return "lighter than planned";
|
|
@@ -8231,19 +8327,19 @@ function DeviationBar({
|
|
|
8231
8327
|
}
|
|
8232
8328
|
);
|
|
8233
8329
|
}
|
|
8234
|
-
var
|
|
8330
|
+
var t5 = getSemanticColors("dark");
|
|
8235
8331
|
var TRACK_BG = "#333333";
|
|
8236
8332
|
var LABEL_COLOR = "#6B7280";
|
|
8237
8333
|
var TARGET_LINE_COLOR = "rgba(33, 150, 243, 0.5)";
|
|
8238
8334
|
var AT_TARGET_GLOW = "0 0 5px 1px rgba(33, 150, 243, 0.35), 0 0 10px 3px rgba(33, 150, 243, 0.15)";
|
|
8239
8335
|
var ZONE = {
|
|
8240
|
-
building:
|
|
8336
|
+
building: t5["status-success"],
|
|
8241
8337
|
// below target ramp-up
|
|
8242
|
-
approaching:
|
|
8338
|
+
approaching: t5["status-warning"],
|
|
8243
8339
|
// nearing target
|
|
8244
|
-
target:
|
|
8340
|
+
target: t5["brand-primary"],
|
|
8245
8341
|
// exactly at target
|
|
8246
|
-
over1:
|
|
8342
|
+
over1: t5["status-error"],
|
|
8247
8343
|
// mild over-target
|
|
8248
8344
|
over2: WORKOUT_TOKENS.intensity.over2,
|
|
8249
8345
|
// moderate over-target
|
|
@@ -8412,12 +8508,12 @@ var MESO_ACCENT_GRADIENT_LIGHT = primitiveRamps.orange[300];
|
|
|
8412
8508
|
var WORKOUT_PILL_DELOAD = primitiveRamps.magenta[600];
|
|
8413
8509
|
|
|
8414
8510
|
// src/components/custom/Workout/WorkoutPill.tsx
|
|
8415
|
-
var
|
|
8511
|
+
var t6 = getSemanticColors("dark");
|
|
8416
8512
|
var statusBgColors = {
|
|
8417
8513
|
completed: "rgba(46,213,115,0.15)",
|
|
8418
8514
|
current: "rgba(255,121,0,0.15)",
|
|
8419
8515
|
next: "transparent",
|
|
8420
|
-
upcoming: "
|
|
8516
|
+
upcoming: t6["surface-raised"],
|
|
8421
8517
|
missed: "rgba(209,67,67,0.1)",
|
|
8422
8518
|
deload: "rgba(186,41,150,0.15)"
|
|
8423
8519
|
};
|
|
@@ -8430,9 +8526,9 @@ var statusBorderStyles = {
|
|
|
8430
8526
|
deload: { borderWidth: 1, borderColor: "rgba(186,41,150,0.3)" }
|
|
8431
8527
|
};
|
|
8432
8528
|
var textColors = {
|
|
8433
|
-
completed:
|
|
8434
|
-
current:
|
|
8435
|
-
next:
|
|
8529
|
+
completed: t6["status-success"],
|
|
8530
|
+
current: t6["brand-primary"],
|
|
8531
|
+
next: t6["brand-primary"],
|
|
8436
8532
|
upcoming: "#6B7280",
|
|
8437
8533
|
missed: "rgba(209,67,67,0.7)",
|
|
8438
8534
|
deload: WORKOUT_PILL_DELOAD
|
|
@@ -8565,257 +8661,412 @@ function WorkoutPill({
|
|
|
8565
8661
|
}
|
|
8566
8662
|
return pill;
|
|
8567
8663
|
}
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
const
|
|
8664
|
+
|
|
8665
|
+
// src/theme/bar-paper.ts
|
|
8666
|
+
function grainOpacityForColor(hex) {
|
|
8667
|
+
const h = hex.replace("#", "");
|
|
8668
|
+
const full = h.length === 3 ? h.split("").map((ch) => ch + ch).join("") : h;
|
|
8669
|
+
const r = parseInt(full.slice(0, 2), 16);
|
|
8670
|
+
const g = parseInt(full.slice(2, 4), 16);
|
|
8671
|
+
const b = parseInt(full.slice(4, 6), 16);
|
|
8672
|
+
if ([r, g, b].some(Number.isNaN)) return 0.14;
|
|
8673
|
+
const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
8674
|
+
return Math.min(0.24, Math.max(0.04, 0.02 + 0.31 * lum));
|
|
8675
|
+
}
|
|
8676
|
+
function barGrain(color) {
|
|
8677
|
+
const op = grainOpacityForColor(color).toFixed(3);
|
|
8678
|
+
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")`;
|
|
8679
|
+
}
|
|
8680
|
+
function barPaper(color, flip = false) {
|
|
8681
|
+
return {
|
|
8682
|
+
backgroundImage: barGrain(color),
|
|
8683
|
+
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)"
|
|
8684
|
+
};
|
|
8685
|
+
}
|
|
8686
|
+
var ANIMATION_EASING = Easing.bezier(0.22, 1, 0.36, 1);
|
|
8687
|
+
var PEAK_OVERSHOOT = 1.12;
|
|
8688
|
+
function getReducedMotionPreference() {
|
|
8689
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
8690
|
+
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
8691
|
+
}
|
|
8692
|
+
function usePrefersReducedMotion() {
|
|
8693
|
+
const [reduced, setReduced] = useState(getReducedMotionPreference);
|
|
8573
8694
|
useEffect(() => {
|
|
8574
|
-
if (
|
|
8575
|
-
const
|
|
8695
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
8696
|
+
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
8697
|
+
const handler = () => setReduced(mq.matches);
|
|
8698
|
+
handler();
|
|
8699
|
+
mq.addEventListener?.("change", handler);
|
|
8700
|
+
return () => mq.removeEventListener?.("change", handler);
|
|
8701
|
+
}, []);
|
|
8702
|
+
return reduced;
|
|
8703
|
+
}
|
|
8704
|
+
function useLiveRepGrowth(active, liveRepIndex, liveVelocity, isNewPeak) {
|
|
8705
|
+
const prefersReducedMotion = usePrefersReducedMotion();
|
|
8706
|
+
const [liveGrowth] = useState(() => new Animated.Value(1));
|
|
8707
|
+
useEffect(() => {
|
|
8708
|
+
if (liveRepIndex == null || liveVelocity == null) return;
|
|
8709
|
+
if (prefersReducedMotion) {
|
|
8710
|
+
liveGrowth.setValue(1);
|
|
8711
|
+
return;
|
|
8712
|
+
}
|
|
8713
|
+
liveGrowth.setValue(0);
|
|
8714
|
+
if (isNewPeak) {
|
|
8576
8715
|
Animated.sequence([
|
|
8577
|
-
Animated.timing(
|
|
8578
|
-
toValue:
|
|
8579
|
-
duration:
|
|
8580
|
-
easing: Easing.
|
|
8716
|
+
Animated.timing(liveGrowth, {
|
|
8717
|
+
toValue: PEAK_OVERSHOOT,
|
|
8718
|
+
duration: 220,
|
|
8719
|
+
easing: Easing.out(Easing.quad),
|
|
8581
8720
|
useNativeDriver: false
|
|
8582
8721
|
}),
|
|
8583
|
-
Animated.
|
|
8584
|
-
toValue:
|
|
8585
|
-
|
|
8586
|
-
|
|
8722
|
+
Animated.spring(liveGrowth, {
|
|
8723
|
+
toValue: 1,
|
|
8724
|
+
friction: 5,
|
|
8725
|
+
tension: 140,
|
|
8587
8726
|
useNativeDriver: false
|
|
8588
8727
|
})
|
|
8589
|
-
])
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8728
|
+
]).start();
|
|
8729
|
+
} else {
|
|
8730
|
+
Animated.timing(liveGrowth, {
|
|
8731
|
+
toValue: 1,
|
|
8732
|
+
duration: 300,
|
|
8733
|
+
easing: ANIMATION_EASING,
|
|
8734
|
+
useNativeDriver: false
|
|
8735
|
+
}).start();
|
|
8736
|
+
}
|
|
8737
|
+
}, [active, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveGrowth]);
|
|
8738
|
+
return liveGrowth;
|
|
8595
8739
|
}
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8740
|
+
|
|
8741
|
+
// src/components/custom/charts/SetBarChart.tsx
|
|
8742
|
+
function mixHex(a, b, t26) {
|
|
8743
|
+
const parse = (h) => {
|
|
8744
|
+
const s = h.replace("#", "");
|
|
8745
|
+
return [parseInt(s.slice(0, 2), 16), parseInt(s.slice(2, 4), 16), parseInt(s.slice(4, 6), 16)];
|
|
8746
|
+
};
|
|
8747
|
+
const [ar, ag, ab] = parse(a);
|
|
8748
|
+
const [br, bg, bb] = parse(b);
|
|
8749
|
+
const ch = (x, y) => Math.round(x + (y - x) * t26).toString(16).padStart(2, "0");
|
|
8750
|
+
return `#${ch(ar, br)}${ch(ag, bg)}${ch(ab, bb)}`;
|
|
8751
|
+
}
|
|
8752
|
+
var VARIABLE_FILL = primitiveRamps.cyan[900];
|
|
8753
|
+
var CONTINUE_OUTLINE = primitiveRamps.cyan[800];
|
|
8754
|
+
var FIXED_MAX_VALUE = 1.15;
|
|
8755
|
+
var PEAK_HEADROOM = 1.03;
|
|
8756
|
+
function scaleDenominator(scale, maxValue) {
|
|
8757
|
+
return scale === "fixed" ? FIXED_MAX_VALUE : maxValue * PEAK_HEADROOM;
|
|
8758
|
+
}
|
|
8759
|
+
function valueLabelFontSize(height) {
|
|
8760
|
+
return Math.round(Math.max(8, Math.min(12, height * 0.11)));
|
|
8761
|
+
}
|
|
8762
|
+
var SET_BAR_DEFAULT_HEIGHT = 220;
|
|
8763
|
+
var LABEL_ROW_HEIGHT = 16;
|
|
8764
|
+
var LABEL_GAP = 3;
|
|
8765
|
+
var GAP_RATIO = 0.08;
|
|
8766
|
+
var MIN_BAR_GAP = 2;
|
|
8767
|
+
var CHUNK_NOTCH_RATIO = 0.25;
|
|
8768
|
+
var CHUNK_NOTCH_MIN_PX = 10;
|
|
8769
|
+
var BAR_MAX_WIDTH = 120;
|
|
8770
|
+
var LABEL_MIN_BAR_WIDTH = 30;
|
|
8771
|
+
var DEFAULT_BAR_RADIUS = 5;
|
|
8772
|
+
var MIN_BAR_HEIGHT = 4;
|
|
8773
|
+
var FLAT_BAR_FRACTION = 0.5;
|
|
8774
|
+
var STUB_HEIGHT = 9;
|
|
8775
|
+
var REP_GAP = 2;
|
|
8776
|
+
function computeBarLayout(plotWidth, count, gapRatio = GAP_RATIO) {
|
|
8777
|
+
const rawBarWidth = plotWidth > 0 && count > 0 ? plotWidth / (count + (count - 1) * gapRatio) : BAR_MAX_WIDTH;
|
|
8778
|
+
const barWidth = Math.min(BAR_MAX_WIDTH, rawBarWidth);
|
|
8779
|
+
const gap = plotWidth === 0 ? MIN_BAR_GAP : Math.max(MIN_BAR_GAP, gapRatio * barWidth);
|
|
8780
|
+
return { barWidth, gap };
|
|
8781
|
+
}
|
|
8782
|
+
function isRep(slot) {
|
|
8783
|
+
return slot.kind === "rep";
|
|
8784
|
+
}
|
|
8785
|
+
function repIndexByCell(cells) {
|
|
8786
|
+
let count = 0;
|
|
8787
|
+
return cells.map((slot) => isRep(slot) ? count++ : -1);
|
|
8788
|
+
}
|
|
8789
|
+
function SetBarChart({
|
|
8790
|
+
slots,
|
|
8791
|
+
colorFor,
|
|
8792
|
+
height,
|
|
8793
|
+
scale = "peak",
|
|
8794
|
+
scaleMax,
|
|
8795
|
+
orientation = "up",
|
|
8796
|
+
liveRepIndex,
|
|
8797
|
+
isNewPeak = false,
|
|
8798
|
+
targetReps,
|
|
8799
|
+
gapRatio = GAP_RATIO,
|
|
8800
|
+
barRadius = DEFAULT_BAR_RADIUS,
|
|
8801
|
+
cornerStyle = "top",
|
|
8802
|
+
flat = false,
|
|
8803
|
+
showValueLabels: showValueLabelsProp = false,
|
|
8804
|
+
formatValue: formatValue3 = String,
|
|
8805
|
+
todoVariant = "solid",
|
|
8806
|
+
minColumns,
|
|
8807
|
+
renderReference,
|
|
8808
|
+
renderBarOverlay,
|
|
8809
|
+
expandProgress,
|
|
8810
|
+
hideBaseline = false,
|
|
8811
|
+
testID,
|
|
8812
|
+
testIDPrefix = "setbar",
|
|
8813
|
+
accessibilityLabel: accessibilityLabel2,
|
|
8814
|
+
label,
|
|
8815
|
+
className,
|
|
8816
|
+
viewProps = {}
|
|
8817
|
+
}) {
|
|
8818
|
+
const targetPadded = Math.max(slots.length, targetReps ?? 0, minColumns ?? 0);
|
|
8819
|
+
const cells = targetPadded > slots.length ? [
|
|
8820
|
+
...slots,
|
|
8821
|
+
...Array.from({ length: targetPadded - slots.length }, () => ({ kind: "todo" }))
|
|
8822
|
+
] : slots;
|
|
8823
|
+
const repValues = cells.filter(isRep).map((s) => s.value ?? 0);
|
|
8824
|
+
const best = repValues.length > 0 ? Math.max(...repValues) : 0;
|
|
8825
|
+
const flip = orientation === "down";
|
|
8826
|
+
const flipStyle = flip ? { transform: [{ scaleY: -1 }] } : null;
|
|
8827
|
+
const placeholderColor = useOnSurfaceColor("tertiary");
|
|
8828
|
+
const surface = useSurface();
|
|
8829
|
+
const surfaceBg = surfaceBackground(surface.level, surface.mode);
|
|
8830
|
+
const solidTodoColor = mixHex(surfaceBg, placeholderColor, 0.55);
|
|
8831
|
+
const emptyColor = mixHex(surfaceBg, placeholderColor, 0.28);
|
|
8832
|
+
const barCorners = cornerStyle === "all" ? { borderRadius: barRadius } : { borderTopLeftRadius: barRadius, borderTopRightRadius: barRadius };
|
|
8833
|
+
const scaleDenom = scaleMax != null && scaleMax > 0 ? scaleMax * PEAK_HEADROOM : scaleDenominator(scale, best);
|
|
8834
|
+
const showValueLabels = showValueLabelsProp && !flat;
|
|
8835
|
+
const plotHeight = Math.max(0, height - (showValueLabels ? LABEL_ROW_HEIGHT + LABEL_GAP : 0));
|
|
8836
|
+
const yOf = (value) => scaleDenom > 0 ? value / scaleDenom * plotHeight : 0;
|
|
8837
|
+
const flatBarHeight = Math.max(MIN_BAR_HEIGHT, FLAT_BAR_FRACTION * plotHeight);
|
|
8838
|
+
const compactBarHeight = Math.max(MIN_BAR_HEIGHT, plotHeight);
|
|
8839
|
+
const valueBarHeight = (value) => scaleDenom > 0 ? Math.max(MIN_BAR_HEIGHT, Math.min(1, value / scaleDenom) * plotHeight) : MIN_BAR_HEIGHT;
|
|
8840
|
+
const barHeight = (value) => flat ? compactBarHeight : valueBarHeight(value);
|
|
8841
|
+
const liveValue = liveRepIndex != null && liveRepIndex < repValues.length ? repValues[liveRepIndex] : void 0;
|
|
8842
|
+
const liveGrowth = useLiveRepGrowth(true, liveRepIndex, liveValue, isNewPeak);
|
|
8843
|
+
const [plotW, setPlotW] = useState(0);
|
|
8844
|
+
const onPlotLayout = (e) => setPlotW(e.nativeEvent.layout.width);
|
|
8845
|
+
const totalCells = cells.length;
|
|
8846
|
+
const { barWidth, gap } = computeBarLayout(plotW, totalCells, gapRatio);
|
|
8847
|
+
const labelsCrowded = plotW > 0 && barWidth < LABEL_MIN_BAR_WIDTH;
|
|
8848
|
+
const peakRepIndex = repValues.reduce((b, v, i) => v > repValues[b] ? i : b, 0);
|
|
8849
|
+
const showBarLabel = (repIndex) => !labelsCrowded || repIndex === peakRepIndex || repIndex === liveRepIndex;
|
|
8850
|
+
const geometry = { scaleDenom, plotHeight, yOf, best, flip };
|
|
8851
|
+
const { style: externalStyle, ...restProps } = viewProps;
|
|
8852
|
+
const a11y = accessibilityLabel2 != null ? { accessibilityRole: "image", accessibilityLabel: accessibilityLabel2 } : {};
|
|
8853
|
+
const repIndices = repIndexByCell(cells);
|
|
8854
|
+
return /* @__PURE__ */ jsxs(
|
|
8855
|
+
View,
|
|
8856
|
+
{
|
|
8857
|
+
className,
|
|
8858
|
+
style: [{ height, flexDirection: "row" }, externalStyle],
|
|
8859
|
+
testID,
|
|
8860
|
+
...a11y,
|
|
8861
|
+
...restProps,
|
|
8862
|
+
children: [
|
|
8863
|
+
label ? /* @__PURE__ */ jsx(
|
|
8864
|
+
ChartSideRail,
|
|
8865
|
+
{
|
|
8866
|
+
sections: [{ label, testID: `${testIDPrefix}-side-label` }],
|
|
8867
|
+
sectionExtent: height
|
|
8868
|
+
}
|
|
8869
|
+
) : null,
|
|
8870
|
+
/* @__PURE__ */ jsxs(
|
|
8871
|
+
View,
|
|
8872
|
+
{
|
|
8873
|
+
onLayout: onPlotLayout,
|
|
8874
|
+
style: {
|
|
8875
|
+
flex: 1,
|
|
8876
|
+
flexDirection: "row",
|
|
8877
|
+
alignItems: "flex-end",
|
|
8878
|
+
gap,
|
|
8879
|
+
position: "relative",
|
|
8880
|
+
borderBottomWidth: hideBaseline ? 0 : 2,
|
|
8881
|
+
borderBottomColor: placeholderColor,
|
|
8882
|
+
...flipStyle
|
|
8883
|
+
},
|
|
8884
|
+
children: [
|
|
8885
|
+
renderReference?.(geometry),
|
|
8886
|
+
cells.map((slot, i) => {
|
|
8887
|
+
const isChunkBoundary = (slot.leadingGap ?? REP_GAP) > REP_GAP;
|
|
8888
|
+
const extraGap = isChunkBoundary ? Math.max(CHUNK_NOTCH_MIN_PX, CHUNK_NOTCH_RATIO * barWidth) : 0;
|
|
8889
|
+
const column = {
|
|
8890
|
+
flex: 1,
|
|
8891
|
+
maxWidth: BAR_MAX_WIDTH,
|
|
8892
|
+
height: "100%",
|
|
8893
|
+
alignItems: "center",
|
|
8894
|
+
justifyContent: "flex-end",
|
|
8895
|
+
marginLeft: extraGap
|
|
8896
|
+
};
|
|
8897
|
+
if (!isRep(slot)) {
|
|
8898
|
+
return /* @__PURE__ */ jsx(View, { accessibilityElementsHidden: true, style: column, children: renderStub(
|
|
8899
|
+
slot.kind,
|
|
8900
|
+
barCorners,
|
|
8901
|
+
// Flat (compact) stubs fill the plot like the compact bars; else the fixed short stub.
|
|
8902
|
+
flat ? compactBarHeight : STUB_HEIGHT,
|
|
8903
|
+
placeholderColor,
|
|
8904
|
+
testIDPrefix,
|
|
8905
|
+
todoVariant,
|
|
8906
|
+
solidTodoColor,
|
|
8907
|
+
emptyColor
|
|
8908
|
+
) }, i);
|
|
8909
|
+
}
|
|
8910
|
+
const repIndex = repIndices[i];
|
|
8911
|
+
const value = slot.value ?? 0;
|
|
8912
|
+
const isLive = liveRepIndex === repIndex;
|
|
8913
|
+
const color = colorFor(value);
|
|
8914
|
+
const barHeightStyle = expandProgress ? expandProgress.interpolate({
|
|
8915
|
+
inputRange: [0, 1],
|
|
8916
|
+
outputRange: [flatBarHeight, valueBarHeight(value)]
|
|
8917
|
+
}) : isLive ? liveGrowth.interpolate({ inputRange: [0, 1], outputRange: [0, barHeight(value)] }) : barHeight(value);
|
|
8918
|
+
const ownLabel = showValueLabels && !renderBarOverlay && showBarLabel(repIndex);
|
|
8919
|
+
const labelBottom = (expandProgress ? valueBarHeight(best) : barHeight(best)) + LABEL_GAP;
|
|
8920
|
+
return /* @__PURE__ */ jsxs(View, { accessibilityElementsHidden: renderBarOverlay == null, style: column, children: [
|
|
8921
|
+
ownLabel && // All labels pin to one aligned row JUST ABOVE THE PEAK BAR (not the container top),
|
|
8922
|
+
// in the muted on-surface-secondary tone — so the row hugs the bars, no floating void.
|
|
8923
|
+
/* @__PURE__ */ jsx(
|
|
8924
|
+
Animated.View,
|
|
8925
|
+
{
|
|
8926
|
+
style: {
|
|
8927
|
+
position: "absolute",
|
|
8928
|
+
bottom: labelBottom,
|
|
8929
|
+
left: 0,
|
|
8930
|
+
right: 0,
|
|
8931
|
+
alignItems: "center",
|
|
8932
|
+
...expandProgress ? { opacity: expandProgress } : null
|
|
8933
|
+
},
|
|
8934
|
+
pointerEvents: "none",
|
|
8935
|
+
children: /* @__PURE__ */ jsx(
|
|
8936
|
+
Text,
|
|
8937
|
+
{
|
|
8938
|
+
className: "text-text-secondary",
|
|
8939
|
+
style: [{ fontSize: valueLabelFontSize(height), fontWeight: "700" }, flipStyle],
|
|
8940
|
+
testID: `${testIDPrefix}-label-${repIndex}`,
|
|
8941
|
+
children: formatValue3(value)
|
|
8942
|
+
}
|
|
8943
|
+
)
|
|
8944
|
+
}
|
|
8945
|
+
),
|
|
8946
|
+
/* @__PURE__ */ jsx(
|
|
8947
|
+
Animated.View,
|
|
8948
|
+
{
|
|
8949
|
+
style: [
|
|
8950
|
+
{ width: "100%", height: barHeightStyle, backgroundColor: color },
|
|
8951
|
+
barCorners,
|
|
8952
|
+
barPaper(color, flip)
|
|
8953
|
+
],
|
|
8954
|
+
testID: `${testIDPrefix}-bar-${repIndex}`
|
|
8955
|
+
}
|
|
8956
|
+
),
|
|
8957
|
+
renderBarOverlay?.(repIndex, value)
|
|
8958
|
+
] }, i);
|
|
8959
|
+
})
|
|
8960
|
+
]
|
|
8961
|
+
}
|
|
8962
|
+
)
|
|
8963
|
+
]
|
|
8964
|
+
}
|
|
8965
|
+
);
|
|
8966
|
+
}
|
|
8967
|
+
function renderStub(kind, barCorners, stubHeight, placeholderColor, testIDPrefix, todoVariant, solidTodoColor, emptyColor) {
|
|
8968
|
+
const base = {
|
|
8969
|
+
width: "100%",
|
|
8970
|
+
height: stubHeight,
|
|
8971
|
+
...barCorners
|
|
8972
|
+
};
|
|
8973
|
+
if (kind === "variable") {
|
|
8974
|
+
return /* @__PURE__ */ jsx(
|
|
8975
|
+
View,
|
|
8976
|
+
{
|
|
8977
|
+
style: { ...base, backgroundColor: VARIABLE_FILL },
|
|
8978
|
+
testID: `${testIDPrefix}-slot-variable`
|
|
8979
|
+
}
|
|
8980
|
+
);
|
|
8981
|
+
}
|
|
8982
|
+
if (kind === "continue") {
|
|
8983
|
+
return /* @__PURE__ */ jsx(
|
|
8984
|
+
View,
|
|
8985
|
+
{
|
|
8986
|
+
style: { ...base, borderWidth: 1, borderColor: CONTINUE_OUTLINE },
|
|
8987
|
+
testID: `${testIDPrefix}-slot-continue`
|
|
8988
|
+
}
|
|
8989
|
+
);
|
|
8990
|
+
}
|
|
8991
|
+
if (kind === "empty") {
|
|
8992
|
+
return /* @__PURE__ */ jsx(
|
|
8993
|
+
View,
|
|
8994
|
+
{
|
|
8995
|
+
style: { ...base, backgroundColor: emptyColor },
|
|
8996
|
+
testID: `${testIDPrefix}-slot-empty`
|
|
8997
|
+
}
|
|
8998
|
+
);
|
|
8999
|
+
}
|
|
9000
|
+
const todoStyle = todoVariant === "solid" ? { ...base, backgroundColor: solidTodoColor } : { ...base, borderWidth: 1, borderStyle: "dashed", borderColor: placeholderColor };
|
|
9001
|
+
return /* @__PURE__ */ jsx(View, { style: todoStyle, testID: `${testIDPrefix}-slot-todo` });
|
|
9002
|
+
}
|
|
9003
|
+
function sideLabelFontSize(height) {
|
|
9004
|
+
return Math.round(Math.max(7, Math.min(13, height * 0.06)));
|
|
9005
|
+
}
|
|
9006
|
+
var SIDE_LABEL_INITIALS_BELOW = 70;
|
|
9007
|
+
function sideLabelText(label, extent) {
|
|
9008
|
+
if (extent >= SIDE_LABEL_INITIALS_BELOW) return label;
|
|
9009
|
+
return label.trim().split(/\s+/).map((word) => word.charAt(0)).join(" ").toUpperCase();
|
|
9010
|
+
}
|
|
9011
|
+
function ChartSideRail({
|
|
9012
|
+
sections,
|
|
9013
|
+
sectionExtent,
|
|
9014
|
+
variant = "hero"
|
|
9015
|
+
}) {
|
|
9016
|
+
if (!sections.some((s) => s.label)) return null;
|
|
9017
|
+
const isRail = variant === "rail";
|
|
9018
|
+
const labelLength = Math.max(0, sectionExtent - (isRail ? 0 : 6));
|
|
9019
|
+
const fontSize = isRail ? 7 : sideLabelFontSize(sectionExtent);
|
|
9020
|
+
const textStyle = {
|
|
9021
|
+
maxWidth: "100%",
|
|
9022
|
+
textAlign: "center",
|
|
9023
|
+
fontSize,
|
|
9024
|
+
fontWeight: "700",
|
|
9025
|
+
letterSpacing: isRail || fontSize < 10 ? 0 : 2,
|
|
9026
|
+
textTransform: "uppercase"
|
|
9027
|
+
};
|
|
9028
|
+
return /* @__PURE__ */ jsx(View, { style: { width: isRail ? 18 : 34 }, accessibilityElementsHidden: true, children: sections.map((s) => /* @__PURE__ */ jsx(
|
|
9029
|
+
View,
|
|
9030
|
+
{
|
|
9031
|
+
style: { height: sectionExtent, alignItems: "center", justifyContent: "center" },
|
|
9032
|
+
children: s.label ? /* @__PURE__ */ jsx(
|
|
8617
9033
|
View,
|
|
8618
9034
|
{
|
|
8619
9035
|
style: {
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
marginLeft: seg.leadingGap,
|
|
8624
|
-
borderRadius: radius,
|
|
8625
|
-
overflow: "hidden"
|
|
9036
|
+
width: labelLength,
|
|
9037
|
+
alignItems: "center",
|
|
9038
|
+
transform: [{ rotate: "-90deg" }]
|
|
8626
9039
|
},
|
|
8627
|
-
children:
|
|
8628
|
-
|
|
9040
|
+
children: /* @__PURE__ */ jsx(
|
|
9041
|
+
Text,
|
|
8629
9042
|
{
|
|
8630
|
-
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
outputRange: seg.pulseColor
|
|
8636
|
-
})
|
|
8637
|
-
} : {
|
|
8638
|
-
opacity: pulse.interpolate({
|
|
8639
|
-
inputRange: [0, 1],
|
|
8640
|
-
outputRange: [PULSE_MIN_OPACITY, 1]
|
|
8641
|
-
})
|
|
8642
|
-
}
|
|
8643
|
-
},
|
|
8644
|
-
accessibilityElementsHidden: true,
|
|
8645
|
-
testID
|
|
9043
|
+
className: "text-text-tertiary",
|
|
9044
|
+
style: textStyle,
|
|
9045
|
+
numberOfLines: 1,
|
|
9046
|
+
testID: s.testID,
|
|
9047
|
+
children: isRail ? s.label : sideLabelText(s.label, sectionExtent)
|
|
8646
9048
|
}
|
|
8647
|
-
)
|
|
8648
|
-
}
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
View,
|
|
8654
|
-
{
|
|
8655
|
-
style: {
|
|
8656
|
-
position: "absolute",
|
|
8657
|
-
left: `${marker.position * 100}%`,
|
|
8658
|
-
top: 0,
|
|
8659
|
-
bottom: 0,
|
|
8660
|
-
width: 2,
|
|
8661
|
-
backgroundColor: marker.color
|
|
8662
|
-
},
|
|
8663
|
-
accessibilityElementsHidden: true,
|
|
8664
|
-
testID: "segmented-bar-marker"
|
|
8665
|
-
}
|
|
8666
|
-
) : null
|
|
8667
|
-
] });
|
|
9049
|
+
)
|
|
9050
|
+
}
|
|
9051
|
+
) : null
|
|
9052
|
+
},
|
|
9053
|
+
s.testID
|
|
9054
|
+
)) });
|
|
8668
9055
|
}
|
|
8669
9056
|
|
|
8670
|
-
// src/components/custom/Workout/
|
|
8671
|
-
var
|
|
8672
|
-
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
|
|
8680
|
-
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
var MYO_UPCOMING_FADE = 0.6;
|
|
8684
|
-
var PULSE_RANGE = {
|
|
8685
|
-
[SET_STRIP_ZONES.slow]: [primitiveRamps.red[500], primitiveRamps.red[700]],
|
|
8686
|
-
[SET_STRIP_ZONES.moderate]: [primitiveRamps.orange[300], primitiveRamps.orange[500]],
|
|
8687
|
-
[SET_STRIP_ZONES.fast]: [primitiveRamps.amber[200], primitiveRamps.amber[400]],
|
|
8688
|
-
[SET_STRIP_ZONES.fastest]: [primitiveRamps.green[200], primitiveRamps.green[400]]
|
|
8689
|
-
};
|
|
8690
|
-
function velocityZoneColor(v) {
|
|
8691
|
-
if (v < 0.5) return SET_STRIP_ZONES.slow;
|
|
8692
|
-
if (v < 0.75) return SET_STRIP_ZONES.moderate;
|
|
8693
|
-
if (v < 1) return SET_STRIP_ZONES.fast;
|
|
8694
|
-
return SET_STRIP_ZONES.fastest;
|
|
8695
|
-
}
|
|
8696
|
-
function chunkedSegments(chunks, gap) {
|
|
8697
|
-
return chunks.flatMap(
|
|
8698
|
-
(vels, ci) => vels.map((v, ri) => ({
|
|
8699
|
-
color: velocityZoneColor(v),
|
|
8700
|
-
...ci > 0 && ri === 0 ? { leadingGap: gap } : {}
|
|
8701
|
-
}))
|
|
8702
|
-
);
|
|
8703
|
-
}
|
|
8704
|
-
function rangeSegments(floor, max, doneVels) {
|
|
8705
|
-
return Array.from({ length: max }, (_, i) => {
|
|
8706
|
-
if (i < doneVels.length) return { color: velocityZoneColor(doneVels[i]) };
|
|
8707
|
-
return { color: i < floor ? TODO_COLOR : SET_STRIP_VARIABLE_COLOR };
|
|
8708
|
-
});
|
|
8709
|
-
}
|
|
8710
|
-
function myoUpcomingSegments(activationLen, clusterCount) {
|
|
8711
|
-
const activation = Array.from({ length: activationLen }, () => ({ color: TODO_COLOR }));
|
|
8712
|
-
const trail = Array.from(
|
|
8713
|
-
{ length: clusterCount },
|
|
8714
|
-
(_, ci) => Array.from({ length: MYO_UPCOMING_CLUSTER_LEN }, (_2, ri) => ({
|
|
8715
|
-
color: SET_STRIP_VARIABLE_COLOR,
|
|
8716
|
-
opacity: MYO_UPCOMING_FADE ** ci,
|
|
8717
|
-
...ri === 0 ? { leadingGap: CLUSTER_GAP } : {}
|
|
8718
|
-
}))
|
|
8719
|
-
).flat();
|
|
8720
|
-
return [...activation, ...trail];
|
|
8721
|
-
}
|
|
8722
|
-
function setSegments(set) {
|
|
8723
|
-
if (set.status === "todo") {
|
|
8724
|
-
return [{ color: TODO_COLOR }];
|
|
8725
|
-
}
|
|
8726
|
-
if (set.status === "done") {
|
|
8727
|
-
return set.velocities.map((v) => ({ color: velocityZoneColor(v) }));
|
|
8728
|
-
}
|
|
8729
|
-
if (set.status === "range") {
|
|
8730
|
-
return rangeSegments(set.floor, set.max, set.doneVels);
|
|
8731
|
-
}
|
|
8732
|
-
if (set.status === "drop") {
|
|
8733
|
-
return chunkedSegments(set.subloads, NOTCH_GAP);
|
|
8734
|
-
}
|
|
8735
|
-
if (set.status === "myo") {
|
|
8736
|
-
return chunkedSegments([set.activation, ...set.clusters], CLUSTER_GAP);
|
|
8737
|
-
}
|
|
8738
|
-
if (set.status === "myo-upcoming") {
|
|
8739
|
-
return myoUpcomingSegments(set.activationLen, set.clusterCount ?? MYO_UPCOMING_CLUSTERS);
|
|
8740
|
-
}
|
|
8741
|
-
const performed = set.velocities.map((v) => {
|
|
8742
|
-
const color = velocityZoneColor(v);
|
|
8743
|
-
return { color, pulse: true, pulseColor: PULSE_RANGE[color] };
|
|
8744
|
-
});
|
|
8745
|
-
const remaining = Math.max(0, set.planned - set.velocities.length);
|
|
8746
|
-
return [...performed, ...Array.from({ length: remaining }, () => ({ color: TODO_COLOR }))];
|
|
8747
|
-
}
|
|
8748
|
-
function setBarSegmentTestID(seg) {
|
|
8749
|
-
if (seg.pulse) return "set-strip-pulse";
|
|
8750
|
-
if (seg.color === SET_STRIP_VARIABLE_COLOR) return "set-strip-variable";
|
|
8751
|
-
return seg.color === TODO_COLOR ? "set-strip-empty" : "set-strip-fill";
|
|
8752
|
-
}
|
|
8753
|
-
function SetBar({ set, height = 8 }) {
|
|
8754
|
-
return /* @__PURE__ */ jsx(
|
|
8755
|
-
SegmentedBar,
|
|
8756
|
-
{
|
|
8757
|
-
segments: setSegments(set),
|
|
8758
|
-
height,
|
|
8759
|
-
gap: 0,
|
|
8760
|
-
radius: 0,
|
|
8761
|
-
segmentTestID: setBarSegmentTestID,
|
|
8762
|
-
style: { flex: 1, minWidth: 0, borderRadius: 2, overflow: "hidden" },
|
|
8763
|
-
testID: "set-strip-set"
|
|
8764
|
-
}
|
|
8765
|
-
);
|
|
8766
|
-
}
|
|
8767
|
-
|
|
8768
|
-
// src/components/custom/Workout/SetStrip.tsx
|
|
8769
|
-
var SET_STRIP_GAP = 5;
|
|
8770
|
-
function setCategory(set) {
|
|
8771
|
-
switch (set.status) {
|
|
8772
|
-
case "done":
|
|
8773
|
-
case "drop":
|
|
8774
|
-
case "myo":
|
|
8775
|
-
return "done";
|
|
8776
|
-
case "active":
|
|
8777
|
-
return "active";
|
|
8778
|
-
case "range":
|
|
8779
|
-
return set.doneVels.length > 0 ? "active" : "upcoming";
|
|
8780
|
-
case "todo":
|
|
8781
|
-
case "myo-upcoming":
|
|
8782
|
-
return "upcoming";
|
|
8783
|
-
}
|
|
8784
|
-
}
|
|
8785
|
-
function describeSets(sets) {
|
|
8786
|
-
const done = sets.filter((s) => setCategory(s) === "done").length;
|
|
8787
|
-
const active = sets.filter((s) => setCategory(s) === "active").length;
|
|
8788
|
-
const upcoming = sets.filter((s) => setCategory(s) === "upcoming").length;
|
|
8789
|
-
return `Set progress: ${done} done, ${active} in progress, ${upcoming} upcoming`;
|
|
8790
|
-
}
|
|
8791
|
-
function SetStrip({ sets, height = 8, className, ...props }) {
|
|
8792
|
-
return /* @__PURE__ */ jsx(
|
|
8793
|
-
View,
|
|
8794
|
-
{
|
|
8795
|
-
className,
|
|
8796
|
-
style: { flexDirection: "row", width: "100%", height, gap: SET_STRIP_GAP },
|
|
8797
|
-
accessibilityRole: "image",
|
|
8798
|
-
accessibilityLabel: describeSets(sets),
|
|
8799
|
-
testID: "set-strip",
|
|
8800
|
-
...props,
|
|
8801
|
-
children: sets.map((set, i) => /* @__PURE__ */ jsx(SetBar, { set, height }, i))
|
|
8802
|
-
}
|
|
8803
|
-
);
|
|
8804
|
-
}
|
|
8805
|
-
|
|
8806
|
-
// src/components/custom/Workout/VelocityStrip.tsx
|
|
8807
|
-
var VEL_COLORS = WORKOUT_TOKENS.scale;
|
|
8808
|
-
var bandIdToEffortColor = {
|
|
8809
|
-
speed: sequentialEffort[0],
|
|
8810
|
-
// green-300
|
|
8811
|
-
power: sequentialEffort[2],
|
|
8812
|
-
// amber-300 (gold)
|
|
8813
|
-
strengthSpeed: sequentialEffort[3],
|
|
8814
|
-
// orange-400
|
|
8815
|
-
maximalStrength: sequentialEffort[4],
|
|
8816
|
-
// red-600
|
|
8817
|
-
grinding: sequentialEffort[5]
|
|
8818
|
-
// red-700 — the distinct 5th band
|
|
9057
|
+
// src/components/custom/Workout/VelocityStrip.tsx
|
|
9058
|
+
var VEL_COLORS = WORKOUT_TOKENS.scale;
|
|
9059
|
+
var bandIdToEffortColor = {
|
|
9060
|
+
speed: sequentialEffort[0],
|
|
9061
|
+
// green-300
|
|
9062
|
+
power: sequentialEffort[2],
|
|
9063
|
+
// amber-300 (gold)
|
|
9064
|
+
strengthSpeed: sequentialEffort[3],
|
|
9065
|
+
// orange-400
|
|
9066
|
+
maximalStrength: sequentialEffort[4],
|
|
9067
|
+
// red-600
|
|
9068
|
+
grinding: sequentialEffort[5]
|
|
9069
|
+
// red-700 — the distinct 5th band
|
|
8819
9070
|
};
|
|
8820
9071
|
function getVelocityZoneColor(velocity) {
|
|
8821
9072
|
if (velocity >= 1) return "vel-green";
|
|
@@ -8847,6 +9098,18 @@ function calculateMeanVelocity(velocities) {
|
|
|
8847
9098
|
const sum = velocities.reduce((acc, v) => acc + v, 0);
|
|
8848
9099
|
return sum / velocities.length;
|
|
8849
9100
|
}
|
|
9101
|
+
var VL_LOSS_THRESHOLDS = [10, 20, 30];
|
|
9102
|
+
function getVelocityLossColor(lossPct) {
|
|
9103
|
+
const [t1, t26, t32] = VL_LOSS_THRESHOLDS;
|
|
9104
|
+
if (lossPct < t1) return VEL_COLORS.green;
|
|
9105
|
+
if (lossPct < t26) return VEL_COLORS.yellow;
|
|
9106
|
+
if (lossPct < t32) return VEL_COLORS.orange;
|
|
9107
|
+
return VEL_COLORS.red;
|
|
9108
|
+
}
|
|
9109
|
+
function velocityLossForRep(velocity, best) {
|
|
9110
|
+
if (best <= 0) return 0;
|
|
9111
|
+
return Math.max(0, Math.round((best - velocity) / best * 100));
|
|
9112
|
+
}
|
|
8850
9113
|
function classifyBand(velocity, bands) {
|
|
8851
9114
|
for (const band of bands) {
|
|
8852
9115
|
if (band.max === null || velocity < band.max) return band;
|
|
@@ -8856,15 +9119,17 @@ function classifyBand(velocity, bands) {
|
|
|
8856
9119
|
function bandColor(band) {
|
|
8857
9120
|
return bandIdToEffortColor[band.id] ?? VEL_COLORS.green;
|
|
8858
9121
|
}
|
|
9122
|
+
function makeBarColorFor(zones) {
|
|
9123
|
+
const hasZones = zones != null && zones.length > 0;
|
|
9124
|
+
return (v) => hasZones ? bandColor(classifyBand(v, zones)) : zoneHexMap[getVelocityZoneColor(v)];
|
|
9125
|
+
}
|
|
8859
9126
|
function getLossStyle(loss) {
|
|
8860
9127
|
if (loss > 25) return { color: VEL_COLORS.red };
|
|
8861
9128
|
if (loss > 20) return { color: VEL_COLORS.orange };
|
|
8862
9129
|
return null;
|
|
8863
9130
|
}
|
|
8864
|
-
var
|
|
9131
|
+
var REP_GAP2 = 2;
|
|
8865
9132
|
var WIDE_GAP = 8;
|
|
8866
|
-
var TODO_COLOR2 = primitiveColors.charcoal[300];
|
|
8867
|
-
var CONTINUE_OUTLINE = primitiveRamps.cyan[800];
|
|
8868
9133
|
function deriveDoneVelocities(set) {
|
|
8869
9134
|
switch (set.type) {
|
|
8870
9135
|
case "drop":
|
|
@@ -8880,7 +9145,7 @@ function chunkedRepSlots(chunks) {
|
|
|
8880
9145
|
chunks.forEach((chunk) => {
|
|
8881
9146
|
chunk.forEach((velocity, ri) => {
|
|
8882
9147
|
const first = slots.length === 0;
|
|
8883
|
-
slots.push({ kind: "rep", velocity, leadingGap: first ? 0 : ri === 0 ? WIDE_GAP :
|
|
9148
|
+
slots.push({ kind: "rep", velocity, leadingGap: first ? 0 : ri === 0 ? WIDE_GAP : REP_GAP2 });
|
|
8884
9149
|
});
|
|
8885
9150
|
});
|
|
8886
9151
|
return slots;
|
|
@@ -8894,7 +9159,7 @@ function buildSlots(set) {
|
|
|
8894
9159
|
return {
|
|
8895
9160
|
kind: done ? "rep" : "todo",
|
|
8896
9161
|
velocity: done ? set.velocities[i] : void 0,
|
|
8897
|
-
leadingGap: i === 0 ? 0 :
|
|
9162
|
+
leadingGap: i === 0 ? 0 : REP_GAP2
|
|
8898
9163
|
};
|
|
8899
9164
|
});
|
|
8900
9165
|
}
|
|
@@ -8905,7 +9170,7 @@ function buildSlots(set) {
|
|
|
8905
9170
|
return {
|
|
8906
9171
|
kind,
|
|
8907
9172
|
velocity: kind === "rep" ? set.velocities[i] : void 0,
|
|
8908
|
-
leadingGap: i === 0 ? 0 :
|
|
9173
|
+
leadingGap: i === 0 ? 0 : REP_GAP2
|
|
8909
9174
|
};
|
|
8910
9175
|
});
|
|
8911
9176
|
}
|
|
@@ -8913,16 +9178,16 @@ function buildSlots(set) {
|
|
|
8913
9178
|
const reps = set.velocities.map((velocity, i) => ({
|
|
8914
9179
|
kind: "rep",
|
|
8915
9180
|
velocity,
|
|
8916
|
-
leadingGap: i === 0 ? 0 :
|
|
9181
|
+
leadingGap: i === 0 ? 0 : REP_GAP2
|
|
8917
9182
|
}));
|
|
8918
|
-
reps.push({ kind: "continue", leadingGap: reps.length === 0 ? 0 :
|
|
9183
|
+
reps.push({ kind: "continue", leadingGap: reps.length === 0 ? 0 : REP_GAP2 });
|
|
8919
9184
|
return reps;
|
|
8920
9185
|
}
|
|
8921
9186
|
case "drop":
|
|
8922
9187
|
return chunkedRepSlots(set.subloads);
|
|
8923
9188
|
case "myo": {
|
|
8924
9189
|
const slots = chunkedRepSlots([set.activation, ...set.clusters]);
|
|
8925
|
-
if (set.open) slots.push({ kind: "continue", leadingGap: slots.length === 0 ? 0 :
|
|
9190
|
+
if (set.open) slots.push({ kind: "continue", leadingGap: slots.length === 0 ? 0 : REP_GAP2 });
|
|
8926
9191
|
return slots;
|
|
8927
9192
|
}
|
|
8928
9193
|
case "cluster": {
|
|
@@ -8933,7 +9198,7 @@ function buildSlots(set) {
|
|
|
8933
9198
|
return {
|
|
8934
9199
|
kind: done ? "rep" : "todo",
|
|
8935
9200
|
velocity: done ? set.velocities[i] : void 0,
|
|
8936
|
-
leadingGap: i === 0 ? 0 : boundary ? WIDE_GAP :
|
|
9201
|
+
leadingGap: i === 0 ? 0 : boundary ? WIDE_GAP : REP_GAP2
|
|
8937
9202
|
};
|
|
8938
9203
|
});
|
|
8939
9204
|
}
|
|
@@ -8955,253 +9220,406 @@ function setAccessibilityLabel(set, repCount) {
|
|
|
8955
9220
|
return `Velocity strip, cluster set, groups of ${set.groupSize}, ${repCount} reps`;
|
|
8956
9221
|
}
|
|
8957
9222
|
}
|
|
8958
|
-
function slotAccessibilityLabel(slot, index) {
|
|
8959
|
-
switch (slot.kind) {
|
|
8960
|
-
case "rep":
|
|
8961
|
-
return `Rep ${index + 1}: ${formatVelocity(slot.velocity ?? 0)} meters per second`;
|
|
8962
|
-
case "todo":
|
|
8963
|
-
return `Rep ${index + 1}: planned`;
|
|
8964
|
-
case "variable":
|
|
8965
|
-
return `Rep ${index + 1}: variable`;
|
|
8966
|
-
case "continue":
|
|
8967
|
-
return "Keep going";
|
|
8968
|
-
}
|
|
8969
|
-
}
|
|
8970
|
-
var EXPANDED_TODO_STUB_PCT = 16;
|
|
8971
|
-
var EXPANDED_ENCODED_PCT = 45;
|
|
8972
9223
|
var EXPANDED_HEIGHT = 60;
|
|
8973
|
-
var
|
|
8974
|
-
var BARE_STUB_HEIGHT = 3;
|
|
8975
|
-
var HERO_HEIGHT = 220;
|
|
8976
|
-
var HERO_LABEL_HEADROOM = 20;
|
|
8977
|
-
var HERO_BAR_GAP = 8;
|
|
8978
|
-
var HERO_BAR_MAX_WIDTH = 120;
|
|
8979
|
-
var HERO_LABEL_MIN_BAR_WIDTH = 30;
|
|
8980
|
-
var HERO_BAR_RADIUS = 5;
|
|
8981
|
-
var HERO_MIN_BAR_HEIGHT = 4;
|
|
8982
|
-
var HERO_PENDING_COLOR = primitiveColors.charcoal[100];
|
|
9224
|
+
var COMPACT_HEIGHT = 8;
|
|
8983
9225
|
var HERO_REFERENCE_COLOR = primitiveColors.charcoal[0];
|
|
8984
|
-
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
|
|
8998
|
-
|
|
8999
|
-
|
|
9000
|
-
|
|
9001
|
-
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
return reduced;
|
|
9226
|
+
function DashedReferenceLine({
|
|
9227
|
+
anchor,
|
|
9228
|
+
offset,
|
|
9229
|
+
testID
|
|
9230
|
+
}) {
|
|
9231
|
+
const edge = anchor === "top" ? { top: offset } : { bottom: offset };
|
|
9232
|
+
return /* @__PURE__ */ jsx(
|
|
9233
|
+
View,
|
|
9234
|
+
{
|
|
9235
|
+
accessibilityElementsHidden: true,
|
|
9236
|
+
style: {
|
|
9237
|
+
position: "absolute",
|
|
9238
|
+
left: 0,
|
|
9239
|
+
right: 0,
|
|
9240
|
+
borderTopWidth: 1,
|
|
9241
|
+
borderStyle: "dashed",
|
|
9242
|
+
borderColor: HERO_REFERENCE_COLOR,
|
|
9243
|
+
pointerEvents: "none",
|
|
9244
|
+
...edge
|
|
9245
|
+
},
|
|
9246
|
+
testID
|
|
9247
|
+
}
|
|
9248
|
+
);
|
|
9008
9249
|
}
|
|
9009
|
-
var
|
|
9010
|
-
var
|
|
9011
|
-
var
|
|
9012
|
-
function
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
|
|
9019
|
-
|
|
9250
|
+
var BAND_LABEL_FONT = "monospace";
|
|
9251
|
+
var VL_LABEL_MIN_PLOT = 65;
|
|
9252
|
+
var VL_SEMANTIC = getSemanticColors("dark");
|
|
9253
|
+
function VelocityLossBands({
|
|
9254
|
+
best,
|
|
9255
|
+
scaleDenom,
|
|
9256
|
+
plotHeight,
|
|
9257
|
+
flip = false
|
|
9258
|
+
}) {
|
|
9259
|
+
if (best <= 0 || scaleDenom <= 0 || plotHeight <= 0) return null;
|
|
9260
|
+
const yOf = (v) => v / scaleDenom * plotHeight;
|
|
9261
|
+
const vl20 = best * 0.8;
|
|
9262
|
+
const vl30 = best * 0.7;
|
|
9263
|
+
const vlFont = Math.round(Math.max(7, Math.min(9, plotHeight * 0.05)));
|
|
9264
|
+
const showLabels = plotHeight >= VL_LABEL_MIN_PLOT;
|
|
9265
|
+
const band = (loV, hiV, color) => /* @__PURE__ */ jsx(
|
|
9266
|
+
View,
|
|
9267
|
+
{
|
|
9268
|
+
style: {
|
|
9269
|
+
position: "absolute",
|
|
9270
|
+
left: 0,
|
|
9271
|
+
right: 0,
|
|
9272
|
+
bottom: yOf(loV),
|
|
9273
|
+
height: Math.max(0, yOf(hiV) - yOf(loV)),
|
|
9274
|
+
backgroundColor: color
|
|
9275
|
+
}
|
|
9020
9276
|
}
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
|
|
9028
|
-
|
|
9029
|
-
|
|
9030
|
-
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9037
|
-
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
|
|
9277
|
+
);
|
|
9278
|
+
const threshold = (v, color, label) => /* @__PURE__ */ jsxs(
|
|
9279
|
+
View,
|
|
9280
|
+
{
|
|
9281
|
+
style: {
|
|
9282
|
+
position: "absolute",
|
|
9283
|
+
left: 0,
|
|
9284
|
+
right: 0,
|
|
9285
|
+
bottom: yOf(v),
|
|
9286
|
+
// Collapse to zero height so the dashed border lands EXACTLY on `yOf(v)` — the
|
|
9287
|
+
// band edge — instead of floating up half a text-row (the label then centres on
|
|
9288
|
+
// the line, breaking it). Matches DashedReferenceLine's bare-border anchoring.
|
|
9289
|
+
height: 0,
|
|
9290
|
+
flexDirection: "row",
|
|
9291
|
+
alignItems: "center"
|
|
9292
|
+
},
|
|
9293
|
+
children: [
|
|
9294
|
+
/* @__PURE__ */ jsx(View, { style: { flex: 9, borderTopWidth: 1, borderStyle: "dashed", borderColor: color } }),
|
|
9295
|
+
showLabels ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9296
|
+
/* @__PURE__ */ jsx(
|
|
9297
|
+
Text,
|
|
9298
|
+
{
|
|
9299
|
+
style: {
|
|
9300
|
+
marginHorizontal: 6,
|
|
9301
|
+
fontSize: vlFont,
|
|
9302
|
+
fontWeight: "800",
|
|
9303
|
+
fontFamily: BAND_LABEL_FONT,
|
|
9304
|
+
color,
|
|
9305
|
+
...flip ? { transform: [{ scaleY: -1 }] } : null
|
|
9306
|
+
},
|
|
9307
|
+
children: label
|
|
9308
|
+
}
|
|
9309
|
+
),
|
|
9310
|
+
/* @__PURE__ */ jsx(View, { style: { flex: 1, borderTopWidth: 1, borderStyle: "dashed", borderColor: color } })
|
|
9311
|
+
] }) : null
|
|
9312
|
+
]
|
|
9313
|
+
}
|
|
9314
|
+
);
|
|
9315
|
+
return /* @__PURE__ */ jsxs(
|
|
9316
|
+
View,
|
|
9317
|
+
{
|
|
9318
|
+
accessibilityElementsHidden: true,
|
|
9319
|
+
pointerEvents: "none",
|
|
9320
|
+
style: { position: "absolute", left: 0, right: 0, bottom: 0, top: 0 },
|
|
9321
|
+
testID: "velocity-loss-bands",
|
|
9322
|
+
children: [
|
|
9323
|
+
band(0, vl30, alpha(VL_SEMANTIC["status-error"], 0.09)),
|
|
9324
|
+
band(vl30, vl20, alpha(VL_SEMANTIC["status-warning"], 0.08)),
|
|
9325
|
+
threshold(vl20, alpha(VL_SEMANTIC["status-warning"], 0.75), "VL 20%"),
|
|
9326
|
+
threshold(vl30, alpha(VL_SEMANTIC["status-error"], 0.75), "VL 30%")
|
|
9327
|
+
]
|
|
9045
9328
|
}
|
|
9046
|
-
|
|
9047
|
-
return liveScale;
|
|
9329
|
+
);
|
|
9048
9330
|
}
|
|
9049
|
-
function
|
|
9050
|
-
|
|
9051
|
-
|
|
9052
|
-
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
9331
|
+
function velocityReferenceOverlay(g, showLossBands) {
|
|
9332
|
+
const referencePx = g.best > 0 && g.scaleDenom > 0 ? Math.min(g.plotHeight, g.yOf(g.best)) : 0;
|
|
9333
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9334
|
+
showLossBands && /* @__PURE__ */ jsx(
|
|
9335
|
+
VelocityLossBands,
|
|
9336
|
+
{
|
|
9337
|
+
best: g.best,
|
|
9338
|
+
scaleDenom: g.scaleDenom,
|
|
9339
|
+
plotHeight: g.plotHeight,
|
|
9340
|
+
flip: g.flip
|
|
9341
|
+
}
|
|
9342
|
+
),
|
|
9343
|
+
referencePx > 0 && /* @__PURE__ */ jsx(
|
|
9344
|
+
DashedReferenceLine,
|
|
9345
|
+
{
|
|
9346
|
+
anchor: "bottom",
|
|
9347
|
+
offset: referencePx,
|
|
9348
|
+
testID: "velocity-hero-reference"
|
|
9349
|
+
}
|
|
9350
|
+
)
|
|
9351
|
+
] });
|
|
9352
|
+
}
|
|
9353
|
+
var ANIMATION_DURATION = 400;
|
|
9354
|
+
var DUAL_HERO_HEIGHT = SET_BAR_DEFAULT_HEIGHT;
|
|
9355
|
+
var DUAL_RAIL_HEIGHT = 96;
|
|
9356
|
+
var DUAL_COMPACT_HEIGHT = 11.5;
|
|
9357
|
+
var DUAL_WING_GAP = 2;
|
|
9358
|
+
var COMPACT_FOLD_GAP = 1.5;
|
|
9359
|
+
var COMPACT_FOLD_HALF = 5;
|
|
9360
|
+
function streamDone(stream) {
|
|
9361
|
+
return stream.set ? deriveDoneVelocities(stream.set) : stream.velocities ?? [];
|
|
9362
|
+
}
|
|
9363
|
+
function streamNaturalSlots(stream, targetReps) {
|
|
9364
|
+
if (stream.set) {
|
|
9365
|
+
return buildSlots(stream.set).map((s) => ({
|
|
9366
|
+
kind: s.kind,
|
|
9367
|
+
value: s.velocity,
|
|
9368
|
+
leadingGap: s.leadingGap
|
|
9369
|
+
}));
|
|
9370
|
+
}
|
|
9371
|
+
const reps = (stream.velocities ?? []).map((v, i) => ({
|
|
9372
|
+
kind: "rep",
|
|
9373
|
+
value: v,
|
|
9374
|
+
leadingGap: i === 0 ? 0 : REP_GAP2
|
|
9375
|
+
}));
|
|
9376
|
+
const pad = Math.max(0, (targetReps ?? 0) - reps.length);
|
|
9377
|
+
return [...reps, ...Array.from({ length: pad }, () => ({ kind: "todo" }))];
|
|
9378
|
+
}
|
|
9379
|
+
function mergeColumnKind(l, r) {
|
|
9380
|
+
const kinds = [l?.kind, r?.kind];
|
|
9381
|
+
if (kinds.includes("variable")) return "variable";
|
|
9382
|
+
if (kinds.includes("continue")) return "continue";
|
|
9383
|
+
if (kinds.includes("rep")) return "rep";
|
|
9384
|
+
return "todo";
|
|
9385
|
+
}
|
|
9386
|
+
function sideColumnCell(slot, kind, leadingGap) {
|
|
9387
|
+
if (kind !== "rep") return { kind, leadingGap };
|
|
9388
|
+
return slot?.kind === "rep" ? { kind: "rep", value: slot.value, leadingGap } : { kind: "empty", leadingGap };
|
|
9389
|
+
}
|
|
9390
|
+
function alignDualSlots(left, right) {
|
|
9391
|
+
const n = Math.max(left.length, right.length);
|
|
9392
|
+
const L = [];
|
|
9393
|
+
const R = [];
|
|
9394
|
+
for (let i = 0; i < n; i++) {
|
|
9395
|
+
const lt = left[i];
|
|
9396
|
+
const rt = right[i];
|
|
9397
|
+
const kind = mergeColumnKind(lt, rt);
|
|
9398
|
+
const gap = Math.max(lt?.leadingGap ?? REP_GAP2, rt?.leadingGap ?? REP_GAP2);
|
|
9399
|
+
L.push(sideColumnCell(lt, kind, gap));
|
|
9400
|
+
R.push(sideColumnCell(rt, kind, gap));
|
|
9401
|
+
}
|
|
9402
|
+
return { left: L, right: R };
|
|
9403
|
+
}
|
|
9404
|
+
function DualVelocityHero({
|
|
9405
|
+
leftDone,
|
|
9406
|
+
rightDone,
|
|
9407
|
+
leftStream,
|
|
9408
|
+
rightStream,
|
|
9409
|
+
leftLabel,
|
|
9410
|
+
rightLabel,
|
|
9411
|
+
zones,
|
|
9412
|
+
barColor: barColor2,
|
|
9413
|
+
scale,
|
|
9056
9414
|
targetReps,
|
|
9415
|
+
liveRepIndex,
|
|
9057
9416
|
height,
|
|
9058
9417
|
className,
|
|
9418
|
+
label,
|
|
9059
9419
|
viewProps
|
|
9060
9420
|
}) {
|
|
9061
|
-
const
|
|
9062
|
-
const
|
|
9063
|
-
const
|
|
9064
|
-
|
|
9065
|
-
|
|
9066
|
-
|
|
9067
|
-
const pendingCount = Math.max(0, (targetReps ?? 0) - doneVelocities.length);
|
|
9068
|
-
const referencePx = referenceVelocity > 0 && scaleDenom > 0 ? Math.min(plotHeight, referenceVelocity / scaleDenom * plotHeight) : 0;
|
|
9069
|
-
const totalSlots = doneVelocities.length + pendingCount;
|
|
9070
|
-
const barWidth = plotW > 0 && totalSlots > 0 ? Math.min(HERO_BAR_MAX_WIDTH, (plotW - HERO_BAR_GAP * (totalSlots - 1)) / totalSlots) : HERO_BAR_MAX_WIDTH;
|
|
9071
|
-
const labelsCrowded = plotW > 0 && barWidth < HERO_LABEL_MIN_BAR_WIDTH;
|
|
9072
|
-
const heroGap = plotW === 0 ? HERO_BAR_GAP : barWidth < 16 ? 2 : barWidth < 28 ? 4 : HERO_BAR_GAP;
|
|
9073
|
-
const peakIndex = doneVelocities.reduce(
|
|
9074
|
-
(best, v, i) => v > doneVelocities[best] ? i : best,
|
|
9075
|
-
0
|
|
9076
|
-
);
|
|
9077
|
-
const showBarLabel = (i) => !labelsCrowded || i === peakIndex || i === liveRepIndex;
|
|
9078
|
-
const repCount = doneVelocities.length;
|
|
9079
|
-
const total = Math.max(repCount, targetReps ?? repCount);
|
|
9080
|
-
const label = referenceVelocity > 0 ? `Velocity chart, ${repCount} of ${total} reps, best ${formatVelocity(referenceVelocity)} meters per second` : `Velocity chart, ${repCount} of ${total} reps`;
|
|
9421
|
+
const plotHalf = (height - DUAL_WING_GAP) / 2;
|
|
9422
|
+
const sharedMax = Math.max(...leftDone, ...rightDone, 0);
|
|
9423
|
+
const aligned = alignDualSlots(
|
|
9424
|
+
leftStream ? streamNaturalSlots(leftStream, targetReps) : [],
|
|
9425
|
+
rightStream ? streamNaturalSlots(rightStream, targetReps) : []
|
|
9426
|
+
);
|
|
9081
9427
|
const { style: externalStyle, ...restProps } = viewProps;
|
|
9082
|
-
|
|
9428
|
+
const wing = (orientation, columns, done) => /* @__PURE__ */ jsx(
|
|
9429
|
+
VelocityStrip,
|
|
9430
|
+
{
|
|
9431
|
+
variant: "hero",
|
|
9432
|
+
orientation,
|
|
9433
|
+
velocities: done,
|
|
9434
|
+
columnSlots: columns,
|
|
9435
|
+
scale,
|
|
9436
|
+
scaleMax: scale === "fixed" ? void 0 : sharedMax,
|
|
9437
|
+
barColor: barColor2,
|
|
9438
|
+
zones,
|
|
9439
|
+
liveRepIndex,
|
|
9440
|
+
height: plotHalf,
|
|
9441
|
+
hideBaseline: true
|
|
9442
|
+
}
|
|
9443
|
+
);
|
|
9444
|
+
return /* @__PURE__ */ jsxs(
|
|
9083
9445
|
View,
|
|
9084
9446
|
{
|
|
9085
9447
|
className,
|
|
9086
|
-
style: [{ height }, externalStyle],
|
|
9448
|
+
style: [{ height, flexDirection: "row" }, externalStyle],
|
|
9087
9449
|
accessibilityRole: "image",
|
|
9088
9450
|
accessibilityLabel: label,
|
|
9089
|
-
testID: "velocity-strip
|
|
9451
|
+
testID: "dual-velocity-strip",
|
|
9090
9452
|
...restProps,
|
|
9091
|
-
children:
|
|
9092
|
-
|
|
9093
|
-
|
|
9094
|
-
|
|
9095
|
-
|
|
9096
|
-
|
|
9097
|
-
|
|
9098
|
-
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
9104
|
-
children:
|
|
9105
|
-
|
|
9106
|
-
|
|
9107
|
-
|
|
9108
|
-
|
|
9109
|
-
|
|
9110
|
-
|
|
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
|
-
|
|
9453
|
+
children: [
|
|
9454
|
+
/* @__PURE__ */ jsx(
|
|
9455
|
+
ChartSideRail,
|
|
9456
|
+
{
|
|
9457
|
+
sectionExtent: plotHalf,
|
|
9458
|
+
variant: "hero",
|
|
9459
|
+
sections: [
|
|
9460
|
+
{ label: leftLabel, testID: "dual-velocity-side-label-L" },
|
|
9461
|
+
{ label: rightLabel, testID: "dual-velocity-side-label-R" }
|
|
9462
|
+
]
|
|
9463
|
+
}
|
|
9464
|
+
),
|
|
9465
|
+
/* @__PURE__ */ jsxs(View, { style: { flex: 1, gap: DUAL_WING_GAP }, children: [
|
|
9466
|
+
/* @__PURE__ */ jsx(View, { accessibilityElementsHidden: true, testID: "dual-velocity-wing-up", children: wing("up", aligned.left, leftDone) }),
|
|
9467
|
+
/* @__PURE__ */ jsx(View, { accessibilityElementsHidden: true, testID: "dual-velocity-wing-down", children: wing("down", aligned.right, rightDone) })
|
|
9468
|
+
] })
|
|
9469
|
+
]
|
|
9470
|
+
}
|
|
9471
|
+
);
|
|
9472
|
+
}
|
|
9473
|
+
function DualVelocityRail({
|
|
9474
|
+
leftDone,
|
|
9475
|
+
rightDone,
|
|
9476
|
+
leftStream,
|
|
9477
|
+
rightStream,
|
|
9478
|
+
zones,
|
|
9479
|
+
barColor: barColor2,
|
|
9480
|
+
scale,
|
|
9481
|
+
targetReps,
|
|
9482
|
+
liveRepIndex,
|
|
9483
|
+
height,
|
|
9484
|
+
className,
|
|
9485
|
+
label,
|
|
9486
|
+
viewProps
|
|
9487
|
+
}) {
|
|
9488
|
+
const plotHalf = (height - DUAL_WING_GAP) / 2;
|
|
9489
|
+
const sharedMax = Math.max(...leftDone, ...rightDone, 0);
|
|
9490
|
+
const aligned = alignDualSlots(
|
|
9491
|
+
leftStream ? streamNaturalSlots(leftStream, targetReps) : [],
|
|
9492
|
+
rightStream ? streamNaturalSlots(rightStream, targetReps) : []
|
|
9493
|
+
);
|
|
9494
|
+
const { style: externalStyle, ...restProps } = viewProps;
|
|
9495
|
+
const wing = (orientation, columns, done) => /* @__PURE__ */ jsx(
|
|
9496
|
+
VelocityStrip,
|
|
9497
|
+
{
|
|
9498
|
+
variant: "expanded",
|
|
9499
|
+
showNumbers: false,
|
|
9500
|
+
showInfo: false,
|
|
9501
|
+
orientation,
|
|
9502
|
+
velocities: done,
|
|
9503
|
+
columnSlots: columns,
|
|
9504
|
+
scale,
|
|
9505
|
+
scaleMax: scale === "fixed" ? void 0 : sharedMax,
|
|
9506
|
+
barColor: barColor2,
|
|
9507
|
+
zones,
|
|
9508
|
+
liveRepIndex,
|
|
9509
|
+
height: plotHalf
|
|
9510
|
+
}
|
|
9511
|
+
);
|
|
9512
|
+
return /* @__PURE__ */ jsxs(
|
|
9513
|
+
View,
|
|
9514
|
+
{
|
|
9515
|
+
className,
|
|
9516
|
+
style: [{ height, flexDirection: "column", gap: DUAL_WING_GAP }, externalStyle],
|
|
9517
|
+
accessibilityRole: "image",
|
|
9518
|
+
accessibilityLabel: label,
|
|
9519
|
+
testID: "dual-velocity-strip",
|
|
9520
|
+
...restProps,
|
|
9521
|
+
children: [
|
|
9522
|
+
/* @__PURE__ */ jsx(View, { accessibilityElementsHidden: true, testID: "dual-velocity-wing-up", children: wing("up", aligned.left, leftDone) }),
|
|
9523
|
+
/* @__PURE__ */ jsx(View, { accessibilityElementsHidden: true, testID: "dual-velocity-wing-down", children: wing("down", aligned.right, rightDone) })
|
|
9524
|
+
]
|
|
9525
|
+
}
|
|
9526
|
+
);
|
|
9527
|
+
}
|
|
9528
|
+
function DualVelocityCompactStrip({
|
|
9529
|
+
leftDone,
|
|
9530
|
+
rightDone,
|
|
9531
|
+
leftStream,
|
|
9532
|
+
rightStream,
|
|
9533
|
+
zones,
|
|
9534
|
+
barColor: barColor2,
|
|
9535
|
+
targetReps,
|
|
9536
|
+
height,
|
|
9537
|
+
className,
|
|
9538
|
+
label,
|
|
9539
|
+
viewProps
|
|
9540
|
+
}) {
|
|
9541
|
+
const half = height === DUAL_COMPACT_HEIGHT ? COMPACT_FOLD_HALF : (height - COMPACT_FOLD_GAP) / 2;
|
|
9542
|
+
const aligned = alignDualSlots(
|
|
9543
|
+
leftStream ? streamNaturalSlots(leftStream, targetReps) : [],
|
|
9544
|
+
rightStream ? streamNaturalSlots(rightStream, targetReps) : []
|
|
9545
|
+
);
|
|
9546
|
+
const { style: externalStyle, ...restProps } = viewProps;
|
|
9547
|
+
const wing = (orientation, columns, done) => /* @__PURE__ */ jsx(
|
|
9548
|
+
VelocityStrip,
|
|
9549
|
+
{
|
|
9550
|
+
variant: "compact",
|
|
9551
|
+
orientation,
|
|
9552
|
+
velocities: done,
|
|
9553
|
+
columnSlots: columns,
|
|
9554
|
+
barColor: barColor2,
|
|
9555
|
+
zones,
|
|
9556
|
+
height: half
|
|
9557
|
+
}
|
|
9558
|
+
);
|
|
9559
|
+
return /* @__PURE__ */ jsxs(
|
|
9560
|
+
View,
|
|
9561
|
+
{
|
|
9562
|
+
className,
|
|
9563
|
+
style: [{ height, flexDirection: "column", gap: COMPACT_FOLD_GAP }, externalStyle],
|
|
9564
|
+
accessibilityRole: "image",
|
|
9565
|
+
accessibilityLabel: label,
|
|
9566
|
+
testID: "dual-velocity-strip",
|
|
9567
|
+
...restProps,
|
|
9568
|
+
children: [
|
|
9569
|
+
/* @__PURE__ */ jsx(View, { accessibilityElementsHidden: true, testID: "dual-velocity-wing-up", children: wing("up", aligned.left, leftDone) }),
|
|
9570
|
+
/* @__PURE__ */ jsx(View, { accessibilityElementsHidden: true, testID: "dual-velocity-wing-down", children: wing("down", aligned.right, rightDone) })
|
|
9571
|
+
]
|
|
9198
9572
|
}
|
|
9199
9573
|
);
|
|
9200
9574
|
}
|
|
9575
|
+
function DualVelocityStrip({
|
|
9576
|
+
left,
|
|
9577
|
+
right,
|
|
9578
|
+
zones,
|
|
9579
|
+
barColor: barColor2 = "loss",
|
|
9580
|
+
targetReps,
|
|
9581
|
+
liveRepIndex,
|
|
9582
|
+
variant = "hero",
|
|
9583
|
+
scale = "peak",
|
|
9584
|
+
height,
|
|
9585
|
+
className,
|
|
9586
|
+
...props
|
|
9587
|
+
}) {
|
|
9588
|
+
const resolvedHeight = height ?? (variant === "hero" ? DUAL_HERO_HEIGHT : variant === "compact" ? DUAL_COMPACT_HEIGHT : DUAL_RAIL_HEIGHT);
|
|
9589
|
+
const leftDone = streamDone(left);
|
|
9590
|
+
const rightDone = streamDone(right);
|
|
9591
|
+
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`;
|
|
9592
|
+
const shared = {
|
|
9593
|
+
leftDone,
|
|
9594
|
+
rightDone,
|
|
9595
|
+
leftStream: left,
|
|
9596
|
+
rightStream: right,
|
|
9597
|
+
leftLabel: left.label,
|
|
9598
|
+
rightLabel: right.label,
|
|
9599
|
+
zones,
|
|
9600
|
+
barColor: barColor2,
|
|
9601
|
+
scale,
|
|
9602
|
+
targetReps,
|
|
9603
|
+
height: resolvedHeight,
|
|
9604
|
+
className,
|
|
9605
|
+
label,
|
|
9606
|
+
viewProps: props
|
|
9607
|
+
};
|
|
9608
|
+
if (variant === "rail") return /* @__PURE__ */ jsx(DualVelocityRail, { ...shared, liveRepIndex });
|
|
9609
|
+
if (variant === "compact") return /* @__PURE__ */ jsx(DualVelocityCompactStrip, { ...shared });
|
|
9610
|
+
return /* @__PURE__ */ jsx(DualVelocityHero, { ...shared, liveRepIndex });
|
|
9611
|
+
}
|
|
9201
9612
|
function VelocityStrip({
|
|
9202
9613
|
velocities,
|
|
9203
9614
|
set,
|
|
9204
9615
|
zones,
|
|
9616
|
+
barColor: barColor2 = "loss",
|
|
9617
|
+
showLossBands,
|
|
9618
|
+
orientation = "up",
|
|
9619
|
+
scaleMax,
|
|
9620
|
+
hideBaseline,
|
|
9621
|
+
columnSlots,
|
|
9622
|
+
label,
|
|
9205
9623
|
liveRepIndex,
|
|
9206
9624
|
expanded = true,
|
|
9207
9625
|
onToggle,
|
|
@@ -9215,51 +9633,29 @@ function VelocityStrip({
|
|
|
9215
9633
|
className,
|
|
9216
9634
|
...props
|
|
9217
9635
|
}) {
|
|
9636
|
+
const lossBandsOn = showLossBands ?? barColor2 === "loss";
|
|
9218
9637
|
const doneVelocities = set ? deriveDoneVelocities(set) : velocities ?? [];
|
|
9219
|
-
const slots = set ? buildSlots(set) : doneVelocities.map((v, i) => ({
|
|
9220
|
-
kind: "rep",
|
|
9221
|
-
velocity: v,
|
|
9222
|
-
leadingGap: i === 0 ? 0 : REP_GAP
|
|
9223
|
-
}));
|
|
9224
9638
|
const maxVelocity = Math.max(...doneVelocities, 0);
|
|
9225
9639
|
const meanVelocity = calculateMeanVelocity(doneVelocities);
|
|
9226
9640
|
const loss = calculateVelocityLoss(doneVelocities);
|
|
9227
9641
|
const framed = showNumbers || showInfo;
|
|
9228
|
-
const scaleDenom = scaleDenominator(scale, maxVelocity);
|
|
9229
9642
|
const hasZones = zones != null && zones.length > 0;
|
|
9230
|
-
const
|
|
9231
|
-
const
|
|
9232
|
-
if (slot.kind === "rep") return barColorFor(slot.velocity ?? 0);
|
|
9233
|
-
if (slot.kind === "todo") return TODO_COLOR2;
|
|
9234
|
-
return SET_STRIP_VARIABLE_COLOR;
|
|
9235
|
-
};
|
|
9643
|
+
const zoneColorFor = makeBarColorFor(zones);
|
|
9644
|
+
const barColorFor = (v) => barColor2 === "loss" ? getVelocityLossColor(velocityLossForRep(v, maxVelocity)) : zoneColorFor(v);
|
|
9236
9645
|
const meanZone = hasZones ? classifyBand(meanVelocity, zones)?.label ?? "" : getVelocityZoneName(meanVelocity);
|
|
9237
|
-
const [
|
|
9238
|
-
const [labelOpacity] = useState(() => new Animated.Value(expanded ? 1 : 0));
|
|
9646
|
+
const [expandProgress] = useState(() => new Animated.Value(expanded ? 1 : 0));
|
|
9239
9647
|
const [infoOpacity] = useState(() => new Animated.Value(expanded ? 1 : 0));
|
|
9240
9648
|
const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
|
|
9241
9649
|
const isNewPeak = liveVelocity != null && maxVelocity > 0 && liveVelocity === maxVelocity;
|
|
9242
|
-
const liveScale = useLiveRepPop(
|
|
9243
|
-
variant === "expanded" && framed,
|
|
9244
|
-
liveRepIndex,
|
|
9245
|
-
liveVelocity,
|
|
9246
|
-
isNewPeak
|
|
9247
|
-
);
|
|
9248
9650
|
useEffect(() => {
|
|
9249
9651
|
if (variant !== "expanded" || !framed) return;
|
|
9250
9652
|
Animated.parallel([
|
|
9251
|
-
Animated.timing(
|
|
9252
|
-
toValue: expanded ?
|
|
9653
|
+
Animated.timing(expandProgress, {
|
|
9654
|
+
toValue: expanded ? 1 : 0,
|
|
9253
9655
|
duration: ANIMATION_DURATION,
|
|
9254
9656
|
easing: ANIMATION_EASING,
|
|
9255
9657
|
useNativeDriver: false
|
|
9256
9658
|
}),
|
|
9257
|
-
Animated.timing(labelOpacity, {
|
|
9258
|
-
toValue: expanded ? 1 : 0,
|
|
9259
|
-
duration: 300,
|
|
9260
|
-
delay: expanded ? 150 : 0,
|
|
9261
|
-
useNativeDriver: false
|
|
9262
|
-
}),
|
|
9263
9659
|
Animated.timing(infoOpacity, {
|
|
9264
9660
|
toValue: expanded ? 1 : 0,
|
|
9265
9661
|
duration: 300,
|
|
@@ -9267,250 +9663,176 @@ function VelocityStrip({
|
|
|
9267
9663
|
useNativeDriver: false
|
|
9268
9664
|
})
|
|
9269
9665
|
]).start();
|
|
9270
|
-
}, [expanded, variant, framed,
|
|
9666
|
+
}, [expanded, variant, framed, expandProgress, infoOpacity]);
|
|
9271
9667
|
if (set == null && velocities == null) return null;
|
|
9272
9668
|
const repCount = doneVelocities.length;
|
|
9273
9669
|
const miniLabel = set ? setAccessibilityLabel(set, repCount) : `Velocity strip, ${repCount} reps`;
|
|
9274
9670
|
if (variant === "hero") {
|
|
9275
|
-
const heroHeight = height === EXPANDED_HEIGHT ?
|
|
9671
|
+
const heroHeight = height === EXPANDED_HEIGHT && columnSlots == null ? SET_BAR_DEFAULT_HEIGHT : height;
|
|
9672
|
+
const heroSlots = columnSlots ?? (set ? buildSlots(set).map((s) => ({
|
|
9673
|
+
kind: s.kind,
|
|
9674
|
+
value: s.velocity,
|
|
9675
|
+
leadingGap: s.leadingGap
|
|
9676
|
+
})) : doneVelocities.map((v) => ({ kind: "rep", value: v })));
|
|
9677
|
+
const total = Math.max(repCount, targetReps ?? repCount);
|
|
9678
|
+
const heroLabel = maxVelocity > 0 ? `Velocity chart, ${repCount} of ${total} reps, best ${formatVelocity(maxVelocity)} meters per second` : `Velocity chart, ${repCount} of ${total} reps`;
|
|
9276
9679
|
return /* @__PURE__ */ jsx(
|
|
9277
|
-
|
|
9680
|
+
SetBarChart,
|
|
9278
9681
|
{
|
|
9279
|
-
|
|
9280
|
-
barColorFor,
|
|
9281
|
-
|
|
9282
|
-
|
|
9682
|
+
slots: heroSlots,
|
|
9683
|
+
colorFor: barColorFor,
|
|
9684
|
+
height: heroHeight,
|
|
9685
|
+
scale,
|
|
9686
|
+
scaleMax,
|
|
9687
|
+
orientation,
|
|
9283
9688
|
liveRepIndex,
|
|
9284
9689
|
isNewPeak,
|
|
9285
|
-
targetReps,
|
|
9286
|
-
|
|
9690
|
+
targetReps: set || columnSlots ? void 0 : targetReps,
|
|
9691
|
+
label,
|
|
9692
|
+
showValueLabels: true,
|
|
9693
|
+
formatValue: formatVelocity,
|
|
9694
|
+
renderReference: (g) => velocityReferenceOverlay(g, lossBandsOn),
|
|
9695
|
+
hideBaseline: true,
|
|
9696
|
+
testID: "velocity-strip-hero",
|
|
9697
|
+
testIDPrefix: "velocity",
|
|
9698
|
+
accessibilityLabel: heroLabel,
|
|
9287
9699
|
className,
|
|
9288
9700
|
viewProps: props
|
|
9289
9701
|
}
|
|
9290
9702
|
);
|
|
9291
9703
|
}
|
|
9292
|
-
if (variant === "
|
|
9293
|
-
const
|
|
9704
|
+
if (variant === "compact") {
|
|
9705
|
+
const compactSlots = columnSlots ?? (set ? buildSlots(set).map((s) => ({
|
|
9706
|
+
kind: s.kind,
|
|
9707
|
+
value: s.velocity,
|
|
9708
|
+
leadingGap: s.leadingGap
|
|
9709
|
+
})) : doneVelocities.map((v) => ({ kind: "rep", value: v })));
|
|
9710
|
+
const compactHeight = height === EXPANDED_HEIGHT ? COMPACT_HEIGHT : height;
|
|
9294
9711
|
return /* @__PURE__ */ jsx(
|
|
9295
|
-
|
|
9712
|
+
SetBarChart,
|
|
9296
9713
|
{
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9714
|
+
slots: compactSlots,
|
|
9715
|
+
colorFor: barColorFor,
|
|
9716
|
+
height: compactHeight,
|
|
9717
|
+
scale,
|
|
9718
|
+
scaleMax,
|
|
9719
|
+
orientation,
|
|
9720
|
+
flat: true,
|
|
9721
|
+
barRadius: 2,
|
|
9722
|
+
cornerStyle: "all",
|
|
9723
|
+
targetReps: set || columnSlots ? void 0 : targetReps,
|
|
9724
|
+
label,
|
|
9725
|
+
hideBaseline: true,
|
|
9726
|
+
testID: "velocity-strip-compact",
|
|
9727
|
+
testIDPrefix: "velocity",
|
|
9303
9728
|
accessibilityLabel: miniLabel,
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
children: slots.map((slot, i) => /* @__PURE__ */ jsx(
|
|
9307
|
-
View,
|
|
9308
|
-
{
|
|
9309
|
-
style: {
|
|
9310
|
-
flex: 1,
|
|
9311
|
-
backgroundColor: slotColor(slot),
|
|
9312
|
-
borderRadius: 1,
|
|
9313
|
-
minWidth: 4,
|
|
9314
|
-
height: "100%",
|
|
9315
|
-
// The container's uniform 2px gap covers rep spacing; a wide slot adds
|
|
9316
|
-
// only the EXTRA (WIDE_GAP − REP_GAP) so the notch totals WIDE_GAP.
|
|
9317
|
-
marginLeft: Math.max(0, slot.leadingGap - REP_GAP),
|
|
9318
|
-
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
9319
|
-
},
|
|
9320
|
-
accessibilityElementsHidden: true,
|
|
9321
|
-
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`
|
|
9322
|
-
},
|
|
9323
|
-
i
|
|
9324
|
-
))
|
|
9729
|
+
className,
|
|
9730
|
+
viewProps: props
|
|
9325
9731
|
}
|
|
9326
9732
|
);
|
|
9327
9733
|
}
|
|
9328
9734
|
if (!framed) {
|
|
9329
|
-
const
|
|
9735
|
+
const spotlightSlots = columnSlots ?? (set ? buildSlots(set).map((s) => ({
|
|
9736
|
+
kind: s.kind,
|
|
9737
|
+
value: s.velocity,
|
|
9738
|
+
leadingGap: s.leadingGap
|
|
9739
|
+
})) : doneVelocities.map((v) => ({ kind: "rep", value: v })));
|
|
9330
9740
|
return /* @__PURE__ */ jsx(
|
|
9331
|
-
|
|
9741
|
+
SetBarChart,
|
|
9332
9742
|
{
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9743
|
+
slots: spotlightSlots,
|
|
9744
|
+
colorFor: barColorFor,
|
|
9745
|
+
height,
|
|
9746
|
+
scale,
|
|
9747
|
+
scaleMax,
|
|
9748
|
+
orientation,
|
|
9749
|
+
liveRepIndex,
|
|
9750
|
+
isNewPeak,
|
|
9751
|
+
barRadius: 2,
|
|
9752
|
+
cornerStyle: "top",
|
|
9753
|
+
targetReps: set ? void 0 : targetReps,
|
|
9754
|
+
label,
|
|
9755
|
+
hideBaseline: true,
|
|
9756
|
+
testID: "velocity-strip-spotlight",
|
|
9757
|
+
testIDPrefix: "velocity",
|
|
9339
9758
|
accessibilityLabel: miniLabel,
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
children: slots.map((slot, i) => /* @__PURE__ */ jsx(
|
|
9343
|
-
View,
|
|
9344
|
-
{
|
|
9345
|
-
style: {
|
|
9346
|
-
flex: 1,
|
|
9347
|
-
minWidth: 4,
|
|
9348
|
-
height: bareSlotHeight(slot, height, scaleDenom),
|
|
9349
|
-
// Top-rounded to match the framed chart's bars (the "rounded tops from
|
|
9350
|
-
// the numbers one"); square bottoms since bars sit on the baseline.
|
|
9351
|
-
borderTopLeftRadius: 2,
|
|
9352
|
-
borderTopRightRadius: 2,
|
|
9353
|
-
backgroundColor: slotColor(slot),
|
|
9354
|
-
marginLeft: Math.max(0, slot.leadingGap - REP_GAP),
|
|
9355
|
-
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
9356
|
-
},
|
|
9357
|
-
accessibilityElementsHidden: true,
|
|
9358
|
-
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`
|
|
9359
|
-
},
|
|
9360
|
-
i
|
|
9361
|
-
))
|
|
9759
|
+
className,
|
|
9760
|
+
viewProps: props
|
|
9362
9761
|
}
|
|
9363
9762
|
);
|
|
9364
9763
|
}
|
|
9365
9764
|
const stripLabel = set ? `${setAccessibilityLabel(set, repCount)}, tap to ${expanded ? "collapse" : "expand"}` : `Velocity chart for set, ${repCount} reps, tap to ${expanded ? "collapse" : "expand"}`;
|
|
9366
9765
|
const hasInteractiveContainer = onToggle != null;
|
|
9367
9766
|
const hasInteractiveReps = onRepPress != null && expanded;
|
|
9368
|
-
const
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9767
|
+
const framedSlots = set ? buildSlots(set).map((sl) => ({
|
|
9768
|
+
kind: sl.kind,
|
|
9769
|
+
value: sl.velocity,
|
|
9770
|
+
leadingGap: sl.leadingGap
|
|
9771
|
+
})) : doneVelocities.map((v) => ({ kind: "rep", value: v }));
|
|
9772
|
+
const needsBarOverlay = showNumbers || onRepPress != null;
|
|
9773
|
+
const renderFramedBarOverlay = needsBarOverlay ? (repIndex, value) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9774
|
+
showNumbers && /* @__PURE__ */ jsx(
|
|
9775
|
+
Animated.View,
|
|
9373
9776
|
{
|
|
9374
9777
|
style: {
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9778
|
+
opacity: expandProgress,
|
|
9779
|
+
position: "absolute",
|
|
9780
|
+
top: -13,
|
|
9781
|
+
left: 0,
|
|
9782
|
+
right: 0,
|
|
9783
|
+
alignItems: "center"
|
|
9379
9784
|
},
|
|
9785
|
+
accessibilityElementsHidden: true,
|
|
9786
|
+
pointerEvents: "none",
|
|
9380
9787
|
children: /* @__PURE__ */ jsx(
|
|
9381
|
-
|
|
9788
|
+
Text,
|
|
9382
9789
|
{
|
|
9383
|
-
|
|
9384
|
-
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
borderTopRightRadius: 2,
|
|
9388
|
-
backgroundColor: slotColor(slot),
|
|
9389
|
-
...slot.kind === "continue" ? { borderWidth: 1, borderColor: CONTINUE_OUTLINE } : {}
|
|
9390
|
-
},
|
|
9391
|
-
testID: slot.kind === "rep" ? `velocity-bar-${i}` : `velocity-slot-${slot.kind}`,
|
|
9392
|
-
accessibilityRole: "image",
|
|
9393
|
-
accessibilityLabel: slotAccessibilityLabel(slot, i)
|
|
9790
|
+
className: "text-text-secondary",
|
|
9791
|
+
style: { fontSize: 8, fontWeight: "600" },
|
|
9792
|
+
testID: `velocity-label-${repIndex}`,
|
|
9793
|
+
children: formatVelocity(value)
|
|
9394
9794
|
}
|
|
9395
9795
|
)
|
|
9396
|
-
}
|
|
9397
|
-
|
|
9398
|
-
|
|
9399
|
-
|
|
9796
|
+
}
|
|
9797
|
+
),
|
|
9798
|
+
onRepPress && expanded && /* @__PURE__ */ jsx(
|
|
9799
|
+
Pressable,
|
|
9800
|
+
{
|
|
9801
|
+
style: { position: "absolute", top: 0, bottom: 0, left: 0, right: 0 },
|
|
9802
|
+
onPress: () => onRepPress(repIndex, value),
|
|
9803
|
+
accessibilityRole: "button",
|
|
9804
|
+
accessibilityLabel: `Rep ${repIndex + 1}: ${formatVelocity(value)} meters per second, tap for details`,
|
|
9805
|
+
testID: `velocity-bar-pressable-${repIndex}`
|
|
9806
|
+
}
|
|
9807
|
+
)
|
|
9808
|
+
] }) : void 0;
|
|
9400
9809
|
const stripContent = /* @__PURE__ */ jsxs(
|
|
9401
9810
|
Animated.View,
|
|
9402
9811
|
{
|
|
9403
|
-
className: [className,
|
|
9404
|
-
style:
|
|
9405
|
-
{
|
|
9406
|
-
height: heightAnim,
|
|
9407
|
-
width: "100%",
|
|
9408
|
-
gap: 2,
|
|
9409
|
-
borderRadius: expanded ? 6 : 2,
|
|
9410
|
-
paddingTop: expanded ? 16 : 0,
|
|
9411
|
-
paddingBottom: expanded ? 24 : 0,
|
|
9412
|
-
// No horizontal inset — the framed chart's bars span full width, matching
|
|
9413
|
-
// the bare spotlight and mini. Numbers/info sit in the vertical padding.
|
|
9414
|
-
paddingHorizontal: 0,
|
|
9415
|
-
paddingVertical: expanded ? void 0 : 8,
|
|
9416
|
-
overflow: expanded ? "visible" : "hidden"
|
|
9417
|
-
}
|
|
9418
|
-
],
|
|
9812
|
+
className: [className, "bg-surface-raised"].filter(Boolean).join(" "),
|
|
9813
|
+
style: { width: "100%", borderRadius: 6, paddingTop: 16, paddingBottom: showInfo ? 8 : 4 },
|
|
9419
9814
|
accessibilityRole: hasInteractiveContainer || hasInteractiveReps ? "none" : "button",
|
|
9420
9815
|
accessibilityLabel: hasInteractiveContainer || hasInteractiveReps ? void 0 : stripLabel,
|
|
9421
9816
|
testID: "velocity-strip",
|
|
9422
9817
|
...props,
|
|
9423
9818
|
children: [
|
|
9424
|
-
/* @__PURE__ */ jsx(
|
|
9425
|
-
|
|
9426
|
-
|
|
9427
|
-
|
|
9428
|
-
|
|
9429
|
-
|
|
9430
|
-
|
|
9431
|
-
|
|
9432
|
-
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
backgroundColor: barBackground
|
|
9442
|
-
};
|
|
9443
|
-
const barInner = isLive ? /* @__PURE__ */ jsx(
|
|
9444
|
-
Animated.View,
|
|
9445
|
-
{
|
|
9446
|
-
style: [barStyle, { transform: [{ scale: liveScale }] }],
|
|
9447
|
-
testID: `velocity-bar-${i}`,
|
|
9448
|
-
accessibilityRole: "image",
|
|
9449
|
-
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second${liveLabelSuffix}`
|
|
9450
|
-
}
|
|
9451
|
-
) : /* @__PURE__ */ jsx(
|
|
9452
|
-
View,
|
|
9453
|
-
{
|
|
9454
|
-
style: barStyle,
|
|
9455
|
-
testID: `velocity-bar-${i}`,
|
|
9456
|
-
accessibilityRole: "image",
|
|
9457
|
-
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second`
|
|
9458
|
-
}
|
|
9459
|
-
);
|
|
9460
|
-
const bar = /* @__PURE__ */ jsxs(
|
|
9461
|
-
View,
|
|
9462
|
-
{
|
|
9463
|
-
style: {
|
|
9464
|
-
flex: 1,
|
|
9465
|
-
height: "100%",
|
|
9466
|
-
justifyContent: "flex-end",
|
|
9467
|
-
position: "relative"
|
|
9468
|
-
},
|
|
9469
|
-
children: [
|
|
9470
|
-
expanded && showNumbers && /* @__PURE__ */ jsx(
|
|
9471
|
-
Animated.View,
|
|
9472
|
-
{
|
|
9473
|
-
style: {
|
|
9474
|
-
opacity: labelOpacity,
|
|
9475
|
-
alignItems: "center",
|
|
9476
|
-
position: "absolute",
|
|
9477
|
-
top: -13,
|
|
9478
|
-
left: 0,
|
|
9479
|
-
right: 0
|
|
9480
|
-
},
|
|
9481
|
-
accessibilityElementsHidden: true,
|
|
9482
|
-
children: /* @__PURE__ */ jsx(
|
|
9483
|
-
Text,
|
|
9484
|
-
{
|
|
9485
|
-
className: "text-text-secondary",
|
|
9486
|
-
style: { fontSize: 8, fontWeight: "600" },
|
|
9487
|
-
testID: `velocity-label-${i}`,
|
|
9488
|
-
children: formatVelocity(v)
|
|
9489
|
-
}
|
|
9490
|
-
)
|
|
9491
|
-
}
|
|
9492
|
-
),
|
|
9493
|
-
barInner
|
|
9494
|
-
]
|
|
9495
|
-
},
|
|
9496
|
-
i
|
|
9497
|
-
);
|
|
9498
|
-
if (onRepPress && expanded) {
|
|
9499
|
-
return /* @__PURE__ */ jsx(
|
|
9500
|
-
Pressable,
|
|
9501
|
-
{
|
|
9502
|
-
style: { flex: 1, height: "100%" },
|
|
9503
|
-
onPress: () => onRepPress(i, v),
|
|
9504
|
-
accessibilityRole: "button",
|
|
9505
|
-
accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second, tap for details`,
|
|
9506
|
-
testID: `velocity-bar-pressable-${i}`,
|
|
9507
|
-
children: bar
|
|
9508
|
-
},
|
|
9509
|
-
i
|
|
9510
|
-
);
|
|
9511
|
-
}
|
|
9512
|
-
return bar;
|
|
9513
|
-
}) }),
|
|
9819
|
+
/* @__PURE__ */ jsx(
|
|
9820
|
+
SetBarChart,
|
|
9821
|
+
{
|
|
9822
|
+
slots: framedSlots,
|
|
9823
|
+
colorFor: barColorFor,
|
|
9824
|
+
height,
|
|
9825
|
+
scale,
|
|
9826
|
+
scaleMax,
|
|
9827
|
+
expandProgress,
|
|
9828
|
+
renderBarOverlay: renderFramedBarOverlay,
|
|
9829
|
+
targetReps: set ? void 0 : targetReps,
|
|
9830
|
+
barRadius: 2,
|
|
9831
|
+
cornerStyle: "top",
|
|
9832
|
+
hideBaseline: true,
|
|
9833
|
+
testIDPrefix: "velocity"
|
|
9834
|
+
}
|
|
9835
|
+
),
|
|
9514
9836
|
expanded && showInfo && /* @__PURE__ */ jsxs(
|
|
9515
9837
|
Animated.View,
|
|
9516
9838
|
{
|
|
@@ -9518,10 +9840,8 @@ function VelocityStrip({
|
|
|
9518
9840
|
flexDirection: "row",
|
|
9519
9841
|
justifyContent: "space-between",
|
|
9520
9842
|
opacity: infoOpacity,
|
|
9521
|
-
|
|
9522
|
-
|
|
9523
|
-
left: 6,
|
|
9524
|
-
right: 6
|
|
9843
|
+
marginTop: 6,
|
|
9844
|
+
paddingHorizontal: 6
|
|
9525
9845
|
},
|
|
9526
9846
|
testID: "velocity-info-row",
|
|
9527
9847
|
children: [
|
|
@@ -9529,10 +9849,7 @@ function VelocityStrip({
|
|
|
9529
9849
|
Text,
|
|
9530
9850
|
{
|
|
9531
9851
|
className: "text-text-secondary",
|
|
9532
|
-
style: {
|
|
9533
|
-
fontSize: 10,
|
|
9534
|
-
fontFamily: "Inter, sans-serif"
|
|
9535
|
-
},
|
|
9852
|
+
style: { fontSize: 10, fontFamily: "Inter, sans-serif" },
|
|
9536
9853
|
children: [
|
|
9537
9854
|
meanZone,
|
|
9538
9855
|
" ",
|
|
@@ -9547,11 +9864,7 @@ function VelocityStrip({
|
|
|
9547
9864
|
Text,
|
|
9548
9865
|
{
|
|
9549
9866
|
className: "text-text-secondary",
|
|
9550
|
-
style: {
|
|
9551
|
-
fontSize: 10,
|
|
9552
|
-
fontFamily: "Inter, sans-serif",
|
|
9553
|
-
...getLossStyle(loss)
|
|
9554
|
-
},
|
|
9867
|
+
style: { fontSize: 10, fontFamily: "Inter, sans-serif", ...getLossStyle(loss) },
|
|
9555
9868
|
children: [
|
|
9556
9869
|
"Loss: ",
|
|
9557
9870
|
loss,
|
|
@@ -9579,13 +9892,13 @@ function VelocityStrip({
|
|
|
9579
9892
|
}
|
|
9580
9893
|
return stripContent;
|
|
9581
9894
|
}
|
|
9582
|
-
var
|
|
9895
|
+
var t7 = getSemanticColors("dark");
|
|
9583
9896
|
var dotColorMap = {
|
|
9584
9897
|
untrained: "#2C2C2C",
|
|
9585
|
-
behind:
|
|
9586
|
-
ontrack:
|
|
9587
|
-
target:
|
|
9588
|
-
over:
|
|
9898
|
+
behind: t7["brand-secondary"],
|
|
9899
|
+
ontrack: t7["status-success"],
|
|
9900
|
+
target: t7["brand-primary"],
|
|
9901
|
+
over: t7["status-error"]
|
|
9589
9902
|
};
|
|
9590
9903
|
function MuscleGroupChip({
|
|
9591
9904
|
name,
|
|
@@ -9859,6 +10172,7 @@ function RowStrip({ set }) {
|
|
|
9859
10172
|
showInfo: false,
|
|
9860
10173
|
height: 24,
|
|
9861
10174
|
scale: "fixed",
|
|
10175
|
+
liveRepIndex: set.velocities.length - 1,
|
|
9862
10176
|
set: { type: "straight", velocities: set.velocities, planned: set.target.reps },
|
|
9863
10177
|
zones
|
|
9864
10178
|
}
|
|
@@ -9868,7 +10182,9 @@ function RowStrip({ set }) {
|
|
|
9868
10182
|
return /* @__PURE__ */ jsx(
|
|
9869
10183
|
VelocityStrip,
|
|
9870
10184
|
{
|
|
9871
|
-
variant: "
|
|
10185
|
+
variant: "compact",
|
|
10186
|
+
height: 8,
|
|
10187
|
+
hideBaseline: true,
|
|
9872
10188
|
set: { type: "straight", velocities, planned: plannedReps(set) },
|
|
9873
10189
|
zones
|
|
9874
10190
|
}
|
|
@@ -10288,7 +10604,7 @@ function RestTimer({
|
|
|
10288
10604
|
}
|
|
10289
10605
|
);
|
|
10290
10606
|
}
|
|
10291
|
-
var
|
|
10607
|
+
var t8 = getSemanticColors("dark");
|
|
10292
10608
|
var TIMES = "\xD7";
|
|
10293
10609
|
var AT = "@";
|
|
10294
10610
|
function SetsRepsLoad({
|
|
@@ -10300,8 +10616,8 @@ function SetsRepsLoad({
|
|
|
10300
10616
|
className,
|
|
10301
10617
|
...props
|
|
10302
10618
|
}) {
|
|
10303
|
-
const value =
|
|
10304
|
-
const sep =
|
|
10619
|
+
const value = t8["text-primary"];
|
|
10620
|
+
const sep = t8["text-tertiary"];
|
|
10305
10621
|
return /* @__PURE__ */ jsxs(
|
|
10306
10622
|
View,
|
|
10307
10623
|
{
|
|
@@ -10321,6 +10637,107 @@ function SetsRepsLoad({
|
|
|
10321
10637
|
}
|
|
10322
10638
|
);
|
|
10323
10639
|
}
|
|
10640
|
+
var PULSE_HALF_MS = 950;
|
|
10641
|
+
var PULSE_MIN_OPACITY = 0.45;
|
|
10642
|
+
var SEGMENTED_BAR_GAP = 5;
|
|
10643
|
+
function usePulse2(active) {
|
|
10644
|
+
const [value] = useState(() => new Animated.Value(0));
|
|
10645
|
+
useEffect(() => {
|
|
10646
|
+
if (!active) return;
|
|
10647
|
+
const loop = Animated.loop(
|
|
10648
|
+
Animated.sequence([
|
|
10649
|
+
Animated.timing(value, {
|
|
10650
|
+
toValue: 1,
|
|
10651
|
+
duration: PULSE_HALF_MS,
|
|
10652
|
+
easing: Easing.inOut(Easing.ease),
|
|
10653
|
+
useNativeDriver: false
|
|
10654
|
+
}),
|
|
10655
|
+
Animated.timing(value, {
|
|
10656
|
+
toValue: 0,
|
|
10657
|
+
duration: PULSE_HALF_MS,
|
|
10658
|
+
easing: Easing.inOut(Easing.ease),
|
|
10659
|
+
useNativeDriver: false
|
|
10660
|
+
})
|
|
10661
|
+
])
|
|
10662
|
+
);
|
|
10663
|
+
loop.start();
|
|
10664
|
+
return () => loop.stop();
|
|
10665
|
+
}, [active, value]);
|
|
10666
|
+
return value;
|
|
10667
|
+
}
|
|
10668
|
+
function SegmentedBar({
|
|
10669
|
+
segments,
|
|
10670
|
+
height = 8,
|
|
10671
|
+
gap = SEGMENTED_BAR_GAP,
|
|
10672
|
+
radius = 2,
|
|
10673
|
+
marker = null,
|
|
10674
|
+
segmentTestID,
|
|
10675
|
+
style,
|
|
10676
|
+
...props
|
|
10677
|
+
}) {
|
|
10678
|
+
const pulse = usePulse2(segments.some((s) => s.pulse));
|
|
10679
|
+
return /* @__PURE__ */ jsxs(View, { style: [{ flexDirection: "row", height, gap, position: "relative" }, style], ...props, children: [
|
|
10680
|
+
segments.map((seg, i) => {
|
|
10681
|
+
const fillStyle = {
|
|
10682
|
+
width: `${(seg.fill ?? 1) * 100}%`,
|
|
10683
|
+
height: "100%",
|
|
10684
|
+
backgroundColor: seg.color,
|
|
10685
|
+
opacity: seg.opacity
|
|
10686
|
+
};
|
|
10687
|
+
const testID = segmentTestID?.(seg, i) ?? "segmented-bar-segment";
|
|
10688
|
+
return /* @__PURE__ */ jsx(
|
|
10689
|
+
View,
|
|
10690
|
+
{
|
|
10691
|
+
style: {
|
|
10692
|
+
flex: seg.weight ?? 1,
|
|
10693
|
+
minWidth: 0,
|
|
10694
|
+
height: "100%",
|
|
10695
|
+
marginLeft: seg.leadingGap,
|
|
10696
|
+
borderRadius: radius,
|
|
10697
|
+
overflow: "hidden"
|
|
10698
|
+
},
|
|
10699
|
+
children: seg.pulse ? /* @__PURE__ */ jsx(
|
|
10700
|
+
Animated.View,
|
|
10701
|
+
{
|
|
10702
|
+
style: {
|
|
10703
|
+
...fillStyle,
|
|
10704
|
+
...seg.pulseColor ? {
|
|
10705
|
+
backgroundColor: pulse.interpolate({
|
|
10706
|
+
inputRange: [0, 1],
|
|
10707
|
+
outputRange: seg.pulseColor
|
|
10708
|
+
})
|
|
10709
|
+
} : {
|
|
10710
|
+
opacity: pulse.interpolate({
|
|
10711
|
+
inputRange: [0, 1],
|
|
10712
|
+
outputRange: [PULSE_MIN_OPACITY, 1]
|
|
10713
|
+
})
|
|
10714
|
+
}
|
|
10715
|
+
},
|
|
10716
|
+
accessibilityElementsHidden: true,
|
|
10717
|
+
testID
|
|
10718
|
+
}
|
|
10719
|
+
) : /* @__PURE__ */ jsx(View, { style: fillStyle, accessibilityElementsHidden: true, testID })
|
|
10720
|
+
},
|
|
10721
|
+
i
|
|
10722
|
+
);
|
|
10723
|
+
}),
|
|
10724
|
+
marker ? /* @__PURE__ */ jsx(
|
|
10725
|
+
View,
|
|
10726
|
+
{
|
|
10727
|
+
style: {
|
|
10728
|
+
position: "absolute",
|
|
10729
|
+
left: `${marker.position * 100}%`,
|
|
10730
|
+
top: 0,
|
|
10731
|
+
bottom: 0,
|
|
10732
|
+
width: 2,
|
|
10733
|
+
backgroundColor: marker.color
|
|
10734
|
+
},
|
|
10735
|
+
accessibilityElementsHidden: true,
|
|
10736
|
+
testID: "segmented-bar-marker"
|
|
10737
|
+
}
|
|
10738
|
+
) : null
|
|
10739
|
+
] });
|
|
10740
|
+
}
|
|
10324
10741
|
|
|
10325
10742
|
// src/components/custom/Workout/paceTone.ts
|
|
10326
10743
|
function paceTone(progress, target) {
|
|
@@ -10414,14 +10831,148 @@ function ScheduleTiles({ when, now, gap = 1, ...props }) {
|
|
|
10414
10831
|
)
|
|
10415
10832
|
] });
|
|
10416
10833
|
}
|
|
10417
|
-
|
|
10834
|
+
|
|
10835
|
+
// src/components/custom/Workout/SetBar.tsx
|
|
10836
|
+
var SET_STRIP_ZONES = {
|
|
10837
|
+
slow: primitiveRamps.red[600],
|
|
10838
|
+
moderate: primitiveRamps.orange[400],
|
|
10839
|
+
fast: primitiveRamps.amber[300],
|
|
10840
|
+
fastest: primitiveRamps.green[300]
|
|
10841
|
+
};
|
|
10842
|
+
var TODO_COLOR = primitiveColors.charcoal[300];
|
|
10843
|
+
var SET_STRIP_VARIABLE_COLOR = primitiveRamps.cyan[900];
|
|
10844
|
+
var NOTCH_GAP = 2;
|
|
10845
|
+
var CLUSTER_GAP = 3;
|
|
10846
|
+
var MYO_UPCOMING_CLUSTERS = 3;
|
|
10847
|
+
var MYO_UPCOMING_CLUSTER_LEN = 5;
|
|
10848
|
+
var MYO_UPCOMING_FADE = 0.6;
|
|
10849
|
+
var PULSE_RANGE = {
|
|
10850
|
+
[SET_STRIP_ZONES.slow]: [primitiveRamps.red[500], primitiveRamps.red[700]],
|
|
10851
|
+
[SET_STRIP_ZONES.moderate]: [primitiveRamps.orange[300], primitiveRamps.orange[500]],
|
|
10852
|
+
[SET_STRIP_ZONES.fast]: [primitiveRamps.amber[200], primitiveRamps.amber[400]],
|
|
10853
|
+
[SET_STRIP_ZONES.fastest]: [primitiveRamps.green[200], primitiveRamps.green[400]]
|
|
10854
|
+
};
|
|
10855
|
+
function velocityZoneColor(v) {
|
|
10856
|
+
if (v < 0.5) return SET_STRIP_ZONES.slow;
|
|
10857
|
+
if (v < 0.75) return SET_STRIP_ZONES.moderate;
|
|
10858
|
+
if (v < 1) return SET_STRIP_ZONES.fast;
|
|
10859
|
+
return SET_STRIP_ZONES.fastest;
|
|
10860
|
+
}
|
|
10861
|
+
function chunkedSegments(chunks, gap) {
|
|
10862
|
+
return chunks.flatMap(
|
|
10863
|
+
(vels, ci) => vels.map((v, ri) => ({
|
|
10864
|
+
color: velocityZoneColor(v),
|
|
10865
|
+
...ci > 0 && ri === 0 ? { leadingGap: gap } : {}
|
|
10866
|
+
}))
|
|
10867
|
+
);
|
|
10868
|
+
}
|
|
10869
|
+
function rangeSegments(floor, max, doneVels) {
|
|
10870
|
+
return Array.from({ length: max }, (_, i) => {
|
|
10871
|
+
if (i < doneVels.length) return { color: velocityZoneColor(doneVels[i]) };
|
|
10872
|
+
return { color: i < floor ? TODO_COLOR : SET_STRIP_VARIABLE_COLOR };
|
|
10873
|
+
});
|
|
10874
|
+
}
|
|
10875
|
+
function myoUpcomingSegments(activationLen, clusterCount) {
|
|
10876
|
+
const activation = Array.from({ length: activationLen }, () => ({ color: TODO_COLOR }));
|
|
10877
|
+
const trail = Array.from(
|
|
10878
|
+
{ length: clusterCount },
|
|
10879
|
+
(_, ci) => Array.from({ length: MYO_UPCOMING_CLUSTER_LEN }, (_2, ri) => ({
|
|
10880
|
+
color: SET_STRIP_VARIABLE_COLOR,
|
|
10881
|
+
opacity: MYO_UPCOMING_FADE ** ci,
|
|
10882
|
+
...ri === 0 ? { leadingGap: CLUSTER_GAP } : {}
|
|
10883
|
+
}))
|
|
10884
|
+
).flat();
|
|
10885
|
+
return [...activation, ...trail];
|
|
10886
|
+
}
|
|
10887
|
+
function setSegments(set) {
|
|
10888
|
+
if (set.status === "todo") {
|
|
10889
|
+
return [{ color: TODO_COLOR }];
|
|
10890
|
+
}
|
|
10891
|
+
if (set.status === "done") {
|
|
10892
|
+
return set.velocities.map((v) => ({ color: velocityZoneColor(v) }));
|
|
10893
|
+
}
|
|
10894
|
+
if (set.status === "range") {
|
|
10895
|
+
return rangeSegments(set.floor, set.max, set.doneVels);
|
|
10896
|
+
}
|
|
10897
|
+
if (set.status === "drop") {
|
|
10898
|
+
return chunkedSegments(set.subloads, NOTCH_GAP);
|
|
10899
|
+
}
|
|
10900
|
+
if (set.status === "myo") {
|
|
10901
|
+
return chunkedSegments([set.activation, ...set.clusters], CLUSTER_GAP);
|
|
10902
|
+
}
|
|
10903
|
+
if (set.status === "myo-upcoming") {
|
|
10904
|
+
return myoUpcomingSegments(set.activationLen, set.clusterCount ?? MYO_UPCOMING_CLUSTERS);
|
|
10905
|
+
}
|
|
10906
|
+
const performed = set.velocities.map((v) => {
|
|
10907
|
+
const color = velocityZoneColor(v);
|
|
10908
|
+
return { color, pulse: true, pulseColor: PULSE_RANGE[color] };
|
|
10909
|
+
});
|
|
10910
|
+
const remaining = Math.max(0, set.planned - set.velocities.length);
|
|
10911
|
+
return [...performed, ...Array.from({ length: remaining }, () => ({ color: TODO_COLOR }))];
|
|
10912
|
+
}
|
|
10913
|
+
function setBarSegmentTestID(seg) {
|
|
10914
|
+
if (seg.pulse) return "set-strip-pulse";
|
|
10915
|
+
if (seg.color === SET_STRIP_VARIABLE_COLOR) return "set-strip-variable";
|
|
10916
|
+
return seg.color === TODO_COLOR ? "set-strip-empty" : "set-strip-fill";
|
|
10917
|
+
}
|
|
10918
|
+
function SetBar({ set, height = 8 }) {
|
|
10919
|
+
return /* @__PURE__ */ jsx(
|
|
10920
|
+
SegmentedBar,
|
|
10921
|
+
{
|
|
10922
|
+
segments: setSegments(set),
|
|
10923
|
+
height,
|
|
10924
|
+
gap: 0,
|
|
10925
|
+
radius: 0,
|
|
10926
|
+
segmentTestID: setBarSegmentTestID,
|
|
10927
|
+
style: { flex: 1, minWidth: 0, borderRadius: 2, overflow: "hidden" },
|
|
10928
|
+
testID: "set-strip-set"
|
|
10929
|
+
}
|
|
10930
|
+
);
|
|
10931
|
+
}
|
|
10932
|
+
var SET_STRIP_GAP = 5;
|
|
10933
|
+
function setCategory(set) {
|
|
10934
|
+
switch (set.status) {
|
|
10935
|
+
case "done":
|
|
10936
|
+
case "drop":
|
|
10937
|
+
case "myo":
|
|
10938
|
+
return "done";
|
|
10939
|
+
case "active":
|
|
10940
|
+
return "active";
|
|
10941
|
+
case "range":
|
|
10942
|
+
return set.doneVels.length > 0 ? "active" : "upcoming";
|
|
10943
|
+
case "todo":
|
|
10944
|
+
case "myo-upcoming":
|
|
10945
|
+
return "upcoming";
|
|
10946
|
+
}
|
|
10947
|
+
}
|
|
10948
|
+
function describeSets(sets) {
|
|
10949
|
+
const done = sets.filter((s) => setCategory(s) === "done").length;
|
|
10950
|
+
const active = sets.filter((s) => setCategory(s) === "active").length;
|
|
10951
|
+
const upcoming = sets.filter((s) => setCategory(s) === "upcoming").length;
|
|
10952
|
+
return `Set progress: ${done} done, ${active} in progress, ${upcoming} upcoming`;
|
|
10953
|
+
}
|
|
10954
|
+
function SetStrip({ sets, height = 8, className, ...props }) {
|
|
10955
|
+
return /* @__PURE__ */ jsx(
|
|
10956
|
+
View,
|
|
10957
|
+
{
|
|
10958
|
+
className,
|
|
10959
|
+
style: { flexDirection: "row", width: "100%", height, gap: SET_STRIP_GAP },
|
|
10960
|
+
accessibilityRole: "image",
|
|
10961
|
+
accessibilityLabel: describeSets(sets),
|
|
10962
|
+
testID: "set-strip",
|
|
10963
|
+
...props,
|
|
10964
|
+
children: sets.map((set, i) => /* @__PURE__ */ jsx(SetBar, { set, height }, i))
|
|
10965
|
+
}
|
|
10966
|
+
);
|
|
10967
|
+
}
|
|
10968
|
+
var t9 = getSemanticColors("dark");
|
|
10418
10969
|
var INDICATOR_CONFIG = {
|
|
10419
|
-
imbalance: { Icon: ScaleIcon, color:
|
|
10420
|
-
overshoot: { Icon: AlertTriangleIcon, color:
|
|
10421
|
-
"velocity-loss": { Icon: TrendingDownIcon, color:
|
|
10422
|
-
"missed-reps": { Icon: CircleSlashIcon, color:
|
|
10423
|
-
pr: { Icon: AwardIcon, color:
|
|
10424
|
-
info: { Icon: InfoIcon, color:
|
|
10970
|
+
imbalance: { Icon: ScaleIcon, color: t9["status-error"], label: "Left/right imbalance" },
|
|
10971
|
+
overshoot: { Icon: AlertTriangleIcon, color: t9["status-error"], label: "Load overshoot" },
|
|
10972
|
+
"velocity-loss": { Icon: TrendingDownIcon, color: t9["status-warning"], label: "Velocity loss" },
|
|
10973
|
+
"missed-reps": { Icon: CircleSlashIcon, color: t9["status-warning"], label: "Missed reps" },
|
|
10974
|
+
pr: { Icon: AwardIcon, color: t9["status-success"], label: "Personal record" },
|
|
10975
|
+
info: { Icon: InfoIcon, color: t9["status-info"], label: "More info" }
|
|
10425
10976
|
};
|
|
10426
10977
|
var INDICATOR_PRECEDENCE = [
|
|
10427
10978
|
"imbalance",
|
|
@@ -10716,7 +11267,9 @@ function CollapsedCard({
|
|
|
10716
11267
|
{
|
|
10717
11268
|
velocities,
|
|
10718
11269
|
zones: velocityZones,
|
|
10719
|
-
variant: "
|
|
11270
|
+
variant: "compact",
|
|
11271
|
+
height: 8,
|
|
11272
|
+
hideBaseline: true,
|
|
10720
11273
|
testID: `exercise-card-velocity-strip-${i}`
|
|
10721
11274
|
},
|
|
10722
11275
|
i
|
|
@@ -11015,6 +11568,7 @@ function SessionRail({
|
|
|
11015
11568
|
next,
|
|
11016
11569
|
stripHeight = 8,
|
|
11017
11570
|
width = DEFAULT_WIDTH,
|
|
11571
|
+
expandedIndex,
|
|
11018
11572
|
onExercisePress,
|
|
11019
11573
|
className,
|
|
11020
11574
|
style,
|
|
@@ -11055,11 +11609,28 @@ function SessionRail({
|
|
|
11055
11609
|
borderBottomWidth: i < exercises.length - 1 ? 1 : 0,
|
|
11056
11610
|
borderBottomColor: DIVIDER
|
|
11057
11611
|
},
|
|
11058
|
-
children: /* @__PURE__ */ jsx(
|
|
11059
|
-
|
|
11612
|
+
children: i === expandedIndex && ex.sets ? /* @__PURE__ */ jsx(
|
|
11613
|
+
ExerciseCard,
|
|
11060
11614
|
{
|
|
11061
11615
|
name: ex.name,
|
|
11062
|
-
|
|
11616
|
+
expanded: true,
|
|
11617
|
+
summary: {
|
|
11618
|
+
sets: ex.summary.sets,
|
|
11619
|
+
reps: ex.summary.reps,
|
|
11620
|
+
// ExerciseCard's summary takes a numeric load; a string load is
|
|
11621
|
+
// an unset/discovery weight, which has no expanded form yet.
|
|
11622
|
+
weight: typeof ex.summary.weight === "number" ? ex.summary.weight : 0,
|
|
11623
|
+
unit: ex.summary.unit
|
|
11624
|
+
},
|
|
11625
|
+
tempo: ex.tempo,
|
|
11626
|
+
sets: ex.sets,
|
|
11627
|
+
onExpandedChange: () => onExercisePress?.(ex, i)
|
|
11628
|
+
}
|
|
11629
|
+
) : /* @__PURE__ */ jsx(
|
|
11630
|
+
ExerciseCardHeading,
|
|
11631
|
+
{
|
|
11632
|
+
name: ex.name,
|
|
11633
|
+
sets: ex.summary.sets,
|
|
11063
11634
|
reps: ex.summary.reps,
|
|
11064
11635
|
load: ex.summary.weight,
|
|
11065
11636
|
unit: ex.summary.unit,
|
|
@@ -11083,7 +11654,7 @@ function SessionRail({
|
|
|
11083
11654
|
var DEFAULTS = {
|
|
11084
11655
|
label: "SS",
|
|
11085
11656
|
color: getSemanticColors("dark")["brand-primary"],
|
|
11086
|
-
bgBase: "
|
|
11657
|
+
bgBase: getSemanticColors("dark")["background-base"]
|
|
11087
11658
|
};
|
|
11088
11659
|
function SupersetWrapper({
|
|
11089
11660
|
label = DEFAULTS.label,
|
|
@@ -11342,10 +11913,10 @@ function WeekRow({
|
|
|
11342
11913
|
}
|
|
11343
11914
|
);
|
|
11344
11915
|
}
|
|
11345
|
-
var
|
|
11916
|
+
var t10 = getSemanticColors("dark");
|
|
11346
11917
|
var COLORS = {
|
|
11347
|
-
statusSuccess:
|
|
11348
|
-
brandPrimary:
|
|
11918
|
+
statusSuccess: t10["status-success"],
|
|
11919
|
+
brandPrimary: t10["brand-primary"],
|
|
11349
11920
|
borderDefault: "#1F1F1F",
|
|
11350
11921
|
brandPrimarySubtle: "rgba(255,121,0,0.06)"
|
|
11351
11922
|
};
|
|
@@ -11829,12 +12400,12 @@ function PrHistoryModal({
|
|
|
11829
12400
|
}
|
|
11830
12401
|
);
|
|
11831
12402
|
}
|
|
11832
|
-
var
|
|
11833
|
-
var BRAND_PRIMARY9 =
|
|
12403
|
+
var t11 = getSemanticColors("dark");
|
|
12404
|
+
var BRAND_PRIMARY9 = t11["brand-primary"];
|
|
11834
12405
|
var BRAND_PRIMARY_SUBTLE = "rgba(255,121,0,0.12)";
|
|
11835
|
-
var STATUS_SUCCESS2 =
|
|
11836
|
-
var STATUS_WARNING2 =
|
|
11837
|
-
var STATUS_ERROR2 =
|
|
12406
|
+
var STATUS_SUCCESS2 = t11["status-success"];
|
|
12407
|
+
var STATUS_WARNING2 = t11["status-warning"];
|
|
12408
|
+
var STATUS_ERROR2 = t11["status-error"];
|
|
11838
12409
|
var EMOJI_SETS = {
|
|
11839
12410
|
sleep: ["\u{1F634}", "\u{1F610}", "\u{1F642}", "\u{1F60A}", "\u{1F929}"],
|
|
11840
12411
|
soreness: ["\u{1F635}", "\u{1F623}", "\u{1F610}", "\u{1F642}", "\u{1F4AA}"],
|
|
@@ -12024,13 +12595,13 @@ function ReadinessCheck({
|
|
|
12024
12595
|
)
|
|
12025
12596
|
] }) });
|
|
12026
12597
|
}
|
|
12027
|
-
var
|
|
12028
|
-
var BRAND_PRIMARY10 =
|
|
12598
|
+
var t12 = getSemanticColors("dark");
|
|
12599
|
+
var BRAND_PRIMARY10 = t12["brand-primary"];
|
|
12029
12600
|
var BRAND_PRIMARY_DARK2 = MESO_ACCENT_GRADIENT_DARK;
|
|
12030
12601
|
var BRAND_PRIMARY_LIGHT2 = MESO_ACCENT_GRADIENT_LIGHT;
|
|
12031
|
-
var SUCCESS =
|
|
12032
|
-
var WARNING =
|
|
12033
|
-
var ERROR =
|
|
12602
|
+
var SUCCESS = t12["status-success"];
|
|
12603
|
+
var WARNING = t12["status-warning"];
|
|
12604
|
+
var ERROR = t12["status-error"];
|
|
12034
12605
|
var ACCENT_STOPS2 = [BRAND_PRIMARY_DARK2, BRAND_PRIMARY10, BRAND_PRIMARY_LIGHT2];
|
|
12035
12606
|
var CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor("surface-elevated")} 0%, ${resolveColor("surface-raised")} 100%)`;
|
|
12036
12607
|
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%)";
|
|
@@ -12401,8 +12972,8 @@ var ERROR_PILL_BORDER = "rgba(209,67,67,0.20)";
|
|
|
12401
12972
|
var PLOT_LEFT = 26;
|
|
12402
12973
|
var TOOLTIP_WIDTH = 124;
|
|
12403
12974
|
function timeOf(dateStr) {
|
|
12404
|
-
const
|
|
12405
|
-
return Number.isNaN(
|
|
12975
|
+
const t26 = Date.parse(dateStr);
|
|
12976
|
+
return Number.isNaN(t26) ? 0 : t26;
|
|
12406
12977
|
}
|
|
12407
12978
|
function formatValue(value) {
|
|
12408
12979
|
return `${Math.round(value)}`;
|
|
@@ -12891,10 +13462,10 @@ function StrengthTrendChart({
|
|
|
12891
13462
|
}
|
|
12892
13463
|
);
|
|
12893
13464
|
}
|
|
12894
|
-
var
|
|
12895
|
-
var STATUS_SUCCESS4 =
|
|
12896
|
-
var STATUS_WARNING4 =
|
|
12897
|
-
var STATUS_INFO =
|
|
13465
|
+
var t13 = getSemanticColors("dark");
|
|
13466
|
+
var STATUS_SUCCESS4 = t13["status-success"];
|
|
13467
|
+
var STATUS_WARNING4 = t13["status-warning"];
|
|
13468
|
+
var STATUS_INFO = t13["status-info"];
|
|
12898
13469
|
var DOT_BORDER = "#F3F4F6";
|
|
12899
13470
|
var BAND_FILL = "rgba(46,213,115,0.1)";
|
|
12900
13471
|
var BAND_EDGE = "rgba(46,213,115,0.45)";
|
|
@@ -12937,8 +13508,8 @@ function interpEdge(pixels, x, key) {
|
|
|
12937
13508
|
const a = pixels[i];
|
|
12938
13509
|
const b = pixels[i + 1];
|
|
12939
13510
|
if (x >= a.x && x <= b.x) {
|
|
12940
|
-
const
|
|
12941
|
-
return a[key] +
|
|
13511
|
+
const t26 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
|
|
13512
|
+
return a[key] + t26 * (b[key] - a[key]);
|
|
12942
13513
|
}
|
|
12943
13514
|
}
|
|
12944
13515
|
return pixels[pixels.length - 1][key];
|
|
@@ -13268,13 +13839,13 @@ function CapacityBandChart({
|
|
|
13268
13839
|
}
|
|
13269
13840
|
);
|
|
13270
13841
|
}
|
|
13271
|
-
var
|
|
13842
|
+
var t14 = getSemanticColors("dark");
|
|
13272
13843
|
function glowShadow(color) {
|
|
13273
13844
|
return `0 0 5px 1px ${alpha(color, 0.3)}, 0 0 10px 3px ${alpha(color, 0.12)}`;
|
|
13274
13845
|
}
|
|
13275
13846
|
var DEFAULT_TRACK_COLOR = primitiveColors.charcoal[200];
|
|
13276
13847
|
var DEFAULT_MARKER_COLOR2 = primitiveColors.white;
|
|
13277
|
-
var TICK_COLOR =
|
|
13848
|
+
var TICK_COLOR = t14["text-tertiary"];
|
|
13278
13849
|
var ZONE_SIZES = {
|
|
13279
13850
|
default: {
|
|
13280
13851
|
trackHeight: 14,
|
|
@@ -13457,7 +14028,7 @@ function ZoneTrack({
|
|
|
13457
14028
|
),
|
|
13458
14029
|
ticks?.map((tick, i) => {
|
|
13459
14030
|
const emphasized = tick.emphasized === true;
|
|
13460
|
-
const lineColor = tick.color ?? (emphasized ?
|
|
14031
|
+
const lineColor = tick.color ?? (emphasized ? t14["brand-primary"] : TICK_COLOR);
|
|
13461
14032
|
const lineWidth = emphasized ? s.tickLineEmphasized : s.tickLineNormal;
|
|
13462
14033
|
return /* @__PURE__ */ jsx(
|
|
13463
14034
|
View,
|
|
@@ -13488,7 +14059,7 @@ function ZoneTrack({
|
|
|
13488
14059
|
if (tick.label == null) return null;
|
|
13489
14060
|
const emphasized = tick.emphasized === true;
|
|
13490
14061
|
if (!showTickLabel(i, emphasized)) return null;
|
|
13491
|
-
const labelColor = tick.color ?? (emphasized ?
|
|
14062
|
+
const labelColor = tick.color ?? (emphasized ? t14["brand-primary"] : TICK_COLOR);
|
|
13492
14063
|
const labelNode = /* @__PURE__ */ jsx(
|
|
13493
14064
|
Text,
|
|
13494
14065
|
{
|
|
@@ -13544,14 +14115,14 @@ function FatigueMeter({
|
|
|
13544
14115
|
className,
|
|
13545
14116
|
...props
|
|
13546
14117
|
}) {
|
|
13547
|
-
const [t1,
|
|
14118
|
+
const [t1, t26, t32] = thresholds;
|
|
13548
14119
|
const zones = [
|
|
13549
14120
|
{ upTo: t1, color: zoneColors[0] },
|
|
13550
|
-
{ upTo:
|
|
14121
|
+
{ upTo: t26, color: zoneColors[1] },
|
|
13551
14122
|
{ upTo: t32, color: zoneColors[2] },
|
|
13552
14123
|
{ upTo: max, color: zoneColors[3] }
|
|
13553
14124
|
];
|
|
13554
|
-
const tickValues = [0, t1,
|
|
14125
|
+
const tickValues = [0, t1, t26, t32, max];
|
|
13555
14126
|
const ticks = tickValues.map((v, i) => ({ value: v, label: labels[i] }));
|
|
13556
14127
|
return /* @__PURE__ */ jsx(
|
|
13557
14128
|
ZoneTrack,
|
|
@@ -13674,7 +14245,7 @@ function VolumeLandmarkBar({
|
|
|
13674
14245
|
}
|
|
13675
14246
|
);
|
|
13676
14247
|
}
|
|
13677
|
-
var
|
|
14248
|
+
var t15 = getSemanticColors("dark");
|
|
13678
14249
|
var statusToken = {
|
|
13679
14250
|
productive: "status-success",
|
|
13680
14251
|
threshold: "status-warning",
|
|
@@ -13691,7 +14262,7 @@ var defaultLabel = {
|
|
|
13691
14262
|
stop: "Stop"
|
|
13692
14263
|
};
|
|
13693
14264
|
function statusPillColor(status) {
|
|
13694
|
-
return
|
|
14265
|
+
return t15[statusToken[status]];
|
|
13695
14266
|
}
|
|
13696
14267
|
function StatusPill2({ status, label, className, style, ...props }) {
|
|
13697
14268
|
const color = statusPillColor(status);
|
|
@@ -13724,7 +14295,7 @@ function StatusPill2({ status, label, className, style, ...props }) {
|
|
|
13724
14295
|
}
|
|
13725
14296
|
);
|
|
13726
14297
|
}
|
|
13727
|
-
var
|
|
14298
|
+
var t16 = getSemanticColors("dark");
|
|
13728
14299
|
var categoryToken = {
|
|
13729
14300
|
productive: null,
|
|
13730
14301
|
threshold: "status-warning",
|
|
@@ -13737,7 +14308,7 @@ var floodOpacity = {
|
|
|
13737
14308
|
};
|
|
13738
14309
|
function liveAuraColor(category) {
|
|
13739
14310
|
const token = categoryToken[category];
|
|
13740
|
-
return token ?
|
|
14311
|
+
return token ? t16[token] : null;
|
|
13741
14312
|
}
|
|
13742
14313
|
function LiveAuraFrame({
|
|
13743
14314
|
category,
|
|
@@ -13766,9 +14337,9 @@ function LiveAuraFrame({
|
|
|
13766
14337
|
position: "relative",
|
|
13767
14338
|
overflow: "hidden",
|
|
13768
14339
|
borderRadius: 10,
|
|
13769
|
-
backgroundColor:
|
|
14340
|
+
backgroundColor: t16["background-base"],
|
|
13770
14341
|
borderWidth: 1,
|
|
13771
|
-
borderColor:
|
|
14342
|
+
borderColor: t16["border-default"]
|
|
13772
14343
|
},
|
|
13773
14344
|
style
|
|
13774
14345
|
],
|
|
@@ -13789,6 +14360,905 @@ function LiveAuraFrame({
|
|
|
13789
14360
|
}
|
|
13790
14361
|
);
|
|
13791
14362
|
}
|
|
14363
|
+
function VelocityHero({
|
|
14364
|
+
velocities,
|
|
14365
|
+
targetReps,
|
|
14366
|
+
liveRepIndex,
|
|
14367
|
+
width,
|
|
14368
|
+
height = 300
|
|
14369
|
+
}) {
|
|
14370
|
+
return /* @__PURE__ */ jsx(View, { testID: "velocity-hero", style: width != null ? { width, height } : { flex: 1, height }, children: /* @__PURE__ */ jsx(
|
|
14371
|
+
VelocityStrip,
|
|
14372
|
+
{
|
|
14373
|
+
variant: "hero",
|
|
14374
|
+
velocities,
|
|
14375
|
+
liveRepIndex: liveRepIndex ?? velocities.length - 1,
|
|
14376
|
+
targetReps,
|
|
14377
|
+
height,
|
|
14378
|
+
scale: "peak"
|
|
14379
|
+
}
|
|
14380
|
+
) });
|
|
14381
|
+
}
|
|
14382
|
+
|
|
14383
|
+
// src/components/custom/Fatigue/fatigue-tokens.ts
|
|
14384
|
+
var t17 = getSemanticColors("dark");
|
|
14385
|
+
var FONT_HEAD = '"Space Grotesk", sans-serif';
|
|
14386
|
+
var FONT_UI = '"Nunito Sans", sans-serif';
|
|
14387
|
+
var FONT_MONO = "monospace";
|
|
14388
|
+
var TONE_COLOR = {
|
|
14389
|
+
ok: t17["status-success"],
|
|
14390
|
+
warn: t17["status-warning"],
|
|
14391
|
+
alarm: t17["status-error"]
|
|
14392
|
+
};
|
|
14393
|
+
var STATE_LABEL = {
|
|
14394
|
+
good: "Good",
|
|
14395
|
+
slowing: "Slowing",
|
|
14396
|
+
grinding: "Grinding",
|
|
14397
|
+
"form-breakdown": "Form breaking down"
|
|
14398
|
+
};
|
|
14399
|
+
var PHASE_AXIS_COLOR = {
|
|
14400
|
+
eccentric: primitiveRamps.magenta[800],
|
|
14401
|
+
concentric: primitiveRamps.cyan[800],
|
|
14402
|
+
hold: primitiveColors.charcoal[100],
|
|
14403
|
+
idle: primitiveColors.charcoal[200]
|
|
14404
|
+
};
|
|
14405
|
+
var PHASE_AXIS_BASE_COLOR = {
|
|
14406
|
+
eccentric: primitiveRamps.magenta[950],
|
|
14407
|
+
concentric: primitiveRamps.cyan[950],
|
|
14408
|
+
hold: primitiveColors.charcoal[300],
|
|
14409
|
+
idle: primitiveColors.charcoal[200]
|
|
14410
|
+
};
|
|
14411
|
+
var PACING_TONE = {
|
|
14412
|
+
ahead: primitiveRamps.amber[300],
|
|
14413
|
+
onPace: primitiveRamps.green[300],
|
|
14414
|
+
over: primitiveRamps.red[300]
|
|
14415
|
+
};
|
|
14416
|
+
var SILVER = primitiveColors.neutral[300];
|
|
14417
|
+
var RED_LIGHT = primitiveRamps.red[400];
|
|
14418
|
+
var RED_MID = primitiveRamps.red[600];
|
|
14419
|
+
var RED_DEEP = primitiveRamps.red[800];
|
|
14420
|
+
var GRIND_THRESHOLD = 0.35;
|
|
14421
|
+
var DRIFT_GREY = primitiveColors.neutral[600];
|
|
14422
|
+
function clamp012(v) {
|
|
14423
|
+
return Math.min(1, Math.max(0, v));
|
|
14424
|
+
}
|
|
14425
|
+
function hexToRgb2(hex) {
|
|
14426
|
+
const h = hex.replace("#", "");
|
|
14427
|
+
const f = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
|
|
14428
|
+
return [parseInt(f.slice(0, 2), 16), parseInt(f.slice(2, 4), 16), parseInt(f.slice(4, 6), 16)];
|
|
14429
|
+
}
|
|
14430
|
+
function mixHex2(a, b, ratio) {
|
|
14431
|
+
const [ar, ag, ab] = hexToRgb2(a);
|
|
14432
|
+
const [br, bg, bb] = hexToRgb2(b);
|
|
14433
|
+
const m = (x, y) => Math.round(x + (y - x) * clamp012(ratio));
|
|
14434
|
+
return `#${[m(ar, br), m(ag, bg), m(ab, bb)].map((v) => v.toString(16).padStart(2, "0")).join("")}`;
|
|
14435
|
+
}
|
|
14436
|
+
function ghostLineColor(tempoDeviation, grindSignature) {
|
|
14437
|
+
if (grindSignature >= GRIND_THRESHOLD) {
|
|
14438
|
+
const severity = clamp012((grindSignature - GRIND_THRESHOLD) / (1 - GRIND_THRESHOLD));
|
|
14439
|
+
return severity < 0.5 ? mixHex2(RED_LIGHT, RED_MID, severity * 2) : mixHex2(RED_MID, RED_DEEP, (severity - 0.5) * 2);
|
|
14440
|
+
}
|
|
14441
|
+
return mixHex2(SILVER, DRIFT_GREY, (tempoDeviation ?? 0) * 0.55);
|
|
14442
|
+
}
|
|
14443
|
+
function auraForVerdict(state) {
|
|
14444
|
+
if (state == null || state === "good") return "productive";
|
|
14445
|
+
if (state === "form-breakdown") return "stop";
|
|
14446
|
+
return "threshold";
|
|
14447
|
+
}
|
|
14448
|
+
|
|
14449
|
+
// src/components/custom/Fatigue/VerdictHero.tsx
|
|
14450
|
+
var t18 = getSemanticColors("dark");
|
|
14451
|
+
function formatRpe(rpe) {
|
|
14452
|
+
if (rpe == null) return "\u2014";
|
|
14453
|
+
return (Math.round(rpe * 2) / 2).toFixed(1);
|
|
14454
|
+
}
|
|
14455
|
+
function VerdictHero({ rpe, verdict }) {
|
|
14456
|
+
const tone = verdict ? TONE_COLOR[verdict.tone] : t18["text-tertiary"];
|
|
14457
|
+
const word = verdict ? STATE_LABEL[verdict.state] : "Warming up";
|
|
14458
|
+
return /* @__PURE__ */ jsxs(View, { style: { gap: 6 }, testID: "verdict-hero", children: [
|
|
14459
|
+
/* @__PURE__ */ jsx(
|
|
14460
|
+
Text,
|
|
14461
|
+
{
|
|
14462
|
+
style: {
|
|
14463
|
+
fontSize: 9,
|
|
14464
|
+
letterSpacing: 1.5,
|
|
14465
|
+
fontFamily: FONT_MONO,
|
|
14466
|
+
color: t18["text-tertiary"]
|
|
14467
|
+
},
|
|
14468
|
+
children: "FATIGUE"
|
|
14469
|
+
}
|
|
14470
|
+
),
|
|
14471
|
+
/* @__PURE__ */ jsxs(View, { style: { gap: 2 }, children: [
|
|
14472
|
+
/* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "flex-end", gap: 7 }, children: [
|
|
14473
|
+
/* @__PURE__ */ jsx(
|
|
14474
|
+
Text,
|
|
14475
|
+
{
|
|
14476
|
+
style: {
|
|
14477
|
+
fontSize: 62,
|
|
14478
|
+
fontWeight: "900",
|
|
14479
|
+
fontFamily: FONT_HEAD,
|
|
14480
|
+
color: tone,
|
|
14481
|
+
lineHeight: 60
|
|
14482
|
+
},
|
|
14483
|
+
accessibilityLabel: `RPE ${formatRpe(rpe)}`,
|
|
14484
|
+
children: formatRpe(rpe)
|
|
14485
|
+
}
|
|
14486
|
+
),
|
|
14487
|
+
/* @__PURE__ */ jsx(
|
|
14488
|
+
Text,
|
|
14489
|
+
{
|
|
14490
|
+
style: {
|
|
14491
|
+
fontSize: 18,
|
|
14492
|
+
fontWeight: "800",
|
|
14493
|
+
color: tone,
|
|
14494
|
+
marginBottom: 9,
|
|
14495
|
+
fontFamily: FONT_HEAD
|
|
14496
|
+
},
|
|
14497
|
+
children: "RPE"
|
|
14498
|
+
}
|
|
14499
|
+
)
|
|
14500
|
+
] }),
|
|
14501
|
+
/* @__PURE__ */ jsx(Text, { style: { fontSize: 17, fontWeight: "800", fontFamily: FONT_HEAD, color: tone }, children: word })
|
|
14502
|
+
] })
|
|
14503
|
+
] });
|
|
14504
|
+
}
|
|
14505
|
+
var t19 = getSemanticColors("dark");
|
|
14506
|
+
var TONE_VARIANT = {
|
|
14507
|
+
ok: "success",
|
|
14508
|
+
warn: "warning",
|
|
14509
|
+
alarm: "error"
|
|
14510
|
+
};
|
|
14511
|
+
var TONE_WORD = { ok: "ok", warn: "watch", alarm: "alarm" };
|
|
14512
|
+
function Light({
|
|
14513
|
+
label,
|
|
14514
|
+
tone,
|
|
14515
|
+
detail
|
|
14516
|
+
}) {
|
|
14517
|
+
const variant = tone ? TONE_VARIANT[tone] : "neutral";
|
|
14518
|
+
const word = tone ? TONE_WORD[tone] : "warming up";
|
|
14519
|
+
return /* @__PURE__ */ jsx(Tooltip, { label: `${detail} \xB7 ${word}`, placement: "bottom", children: /* @__PURE__ */ jsxs(
|
|
14520
|
+
View,
|
|
14521
|
+
{
|
|
14522
|
+
accessibilityLabel: `${detail}, ${word}`,
|
|
14523
|
+
style: { flexDirection: "row", alignItems: "center", gap: 5 },
|
|
14524
|
+
children: [
|
|
14525
|
+
/* @__PURE__ */ jsx(StatusDot, { variant, size: "sm", glow: true }),
|
|
14526
|
+
/* @__PURE__ */ jsx(
|
|
14527
|
+
Text,
|
|
14528
|
+
{
|
|
14529
|
+
style: {
|
|
14530
|
+
fontSize: 9,
|
|
14531
|
+
letterSpacing: 0.6,
|
|
14532
|
+
fontFamily: FONT_MONO,
|
|
14533
|
+
color: t19["text-secondary"]
|
|
14534
|
+
},
|
|
14535
|
+
children: label
|
|
14536
|
+
}
|
|
14537
|
+
)
|
|
14538
|
+
]
|
|
14539
|
+
}
|
|
14540
|
+
) });
|
|
14541
|
+
}
|
|
14542
|
+
function FatigueLights({ dimensions, spread = false }) {
|
|
14543
|
+
return /* @__PURE__ */ jsxs(
|
|
14544
|
+
View,
|
|
14545
|
+
{
|
|
14546
|
+
testID: "fatigue-lights",
|
|
14547
|
+
style: {
|
|
14548
|
+
flexDirection: "row",
|
|
14549
|
+
gap: spread ? 0 : 16,
|
|
14550
|
+
alignItems: "center",
|
|
14551
|
+
justifyContent: spread ? "space-between" : "flex-start"
|
|
14552
|
+
},
|
|
14553
|
+
children: [
|
|
14554
|
+
/* @__PURE__ */ jsx(Light, { label: "VEL", tone: dimensions?.velocityLoss ?? null, detail: "Velocity loss" }),
|
|
14555
|
+
/* @__PURE__ */ jsx(Light, { label: "ROM", tone: dimensions?.rom ?? null, detail: "ROM depth" }),
|
|
14556
|
+
/* @__PURE__ */ jsx(Light, { label: "TEMPO", tone: dimensions?.tempo ?? null, detail: "Tempo" })
|
|
14557
|
+
]
|
|
14558
|
+
}
|
|
14559
|
+
);
|
|
14560
|
+
}
|
|
14561
|
+
function barColor(romM, working, short) {
|
|
14562
|
+
if (short != null && romM < short) return RED_DEEP;
|
|
14563
|
+
if (working != null && romM < working) return RED_LIGHT;
|
|
14564
|
+
return SILVER;
|
|
14565
|
+
}
|
|
14566
|
+
function romReferenceOverlay(g, workingStandardM, shortThresholdM) {
|
|
14567
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
14568
|
+
shortThresholdM != null && /* @__PURE__ */ jsx(
|
|
14569
|
+
View,
|
|
14570
|
+
{
|
|
14571
|
+
pointerEvents: "none",
|
|
14572
|
+
style: {
|
|
14573
|
+
position: "absolute",
|
|
14574
|
+
left: 0,
|
|
14575
|
+
right: 0,
|
|
14576
|
+
bottom: 0,
|
|
14577
|
+
height: g.yOf(shortThresholdM),
|
|
14578
|
+
backgroundColor: alpha(RED_DEEP, 0.06)
|
|
14579
|
+
}
|
|
14580
|
+
}
|
|
14581
|
+
),
|
|
14582
|
+
workingStandardM != null && /* @__PURE__ */ jsx(
|
|
14583
|
+
View,
|
|
14584
|
+
{
|
|
14585
|
+
pointerEvents: "none",
|
|
14586
|
+
style: {
|
|
14587
|
+
position: "absolute",
|
|
14588
|
+
left: 0,
|
|
14589
|
+
right: 0,
|
|
14590
|
+
bottom: g.yOf(workingStandardM),
|
|
14591
|
+
borderTopWidth: 1,
|
|
14592
|
+
borderStyle: "dashed",
|
|
14593
|
+
borderColor: alpha(SILVER, 0.4)
|
|
14594
|
+
}
|
|
14595
|
+
}
|
|
14596
|
+
),
|
|
14597
|
+
shortThresholdM != null && /* @__PURE__ */ jsx(
|
|
14598
|
+
View,
|
|
14599
|
+
{
|
|
14600
|
+
pointerEvents: "none",
|
|
14601
|
+
style: {
|
|
14602
|
+
position: "absolute",
|
|
14603
|
+
left: 0,
|
|
14604
|
+
right: 0,
|
|
14605
|
+
bottom: g.yOf(shortThresholdM),
|
|
14606
|
+
borderTopWidth: 1,
|
|
14607
|
+
borderStyle: "dashed",
|
|
14608
|
+
borderColor: alpha(RED_DEEP, 0.4)
|
|
14609
|
+
}
|
|
14610
|
+
}
|
|
14611
|
+
)
|
|
14612
|
+
] });
|
|
14613
|
+
}
|
|
14614
|
+
function RomProgressionChart({
|
|
14615
|
+
points,
|
|
14616
|
+
workingStandardM,
|
|
14617
|
+
shortThresholdM,
|
|
14618
|
+
barHeight = 44,
|
|
14619
|
+
maxReps = 12,
|
|
14620
|
+
plannedReps: plannedReps2
|
|
14621
|
+
}) {
|
|
14622
|
+
const shown = points.slice(-maxReps);
|
|
14623
|
+
const slots = shown.map((p) => ({ kind: "rep", value: p.romM }));
|
|
14624
|
+
const scaleMax = Math.max(...shown.map((p) => p.romM), workingStandardM ?? 0, shortThresholdM ?? 0) || void 0;
|
|
14625
|
+
return /* @__PURE__ */ jsx(View, { testID: "rom-progression", children: /* @__PURE__ */ jsx(
|
|
14626
|
+
SetBarChart,
|
|
14627
|
+
{
|
|
14628
|
+
slots,
|
|
14629
|
+
colorFor: (romM) => barColor(romM, workingStandardM, shortThresholdM),
|
|
14630
|
+
height: barHeight,
|
|
14631
|
+
scaleMax,
|
|
14632
|
+
barRadius: 2,
|
|
14633
|
+
hideBaseline: true,
|
|
14634
|
+
targetReps: plannedReps2,
|
|
14635
|
+
renderReference: (g) => romReferenceOverlay(g, workingStandardM, shortThresholdM),
|
|
14636
|
+
testIDPrefix: "rom"
|
|
14637
|
+
}
|
|
14638
|
+
) });
|
|
14639
|
+
}
|
|
14640
|
+
|
|
14641
|
+
// src/components/custom/Fatigue/tempo-pacing.ts
|
|
14642
|
+
var t20 = getSemanticColors("dark");
|
|
14643
|
+
var ON_TARGET_MS2 = 100;
|
|
14644
|
+
function phaseFillFraction(elapsedMs, targetMs) {
|
|
14645
|
+
if (targetMs == null || targetMs <= 0) return 1;
|
|
14646
|
+
return Math.min(1, Math.max(0, elapsedMs / targetMs));
|
|
14647
|
+
}
|
|
14648
|
+
function pacingTone(elapsedMs, targetMs) {
|
|
14649
|
+
if (targetMs == null || targetMs <= 0) return t20["text-primary"];
|
|
14650
|
+
const remainingMs = targetMs - elapsedMs;
|
|
14651
|
+
if (remainingMs > ON_TARGET_MS2) return PACING_TONE.ahead;
|
|
14652
|
+
if (remainingMs >= -ON_TARGET_MS2) return PACING_TONE.onPace;
|
|
14653
|
+
return PACING_TONE.over;
|
|
14654
|
+
}
|
|
14655
|
+
function prescribedSegments(tempo) {
|
|
14656
|
+
if (tempo == null) return [];
|
|
14657
|
+
const [ecc, pauseBottom, con, pauseTop] = tempo;
|
|
14658
|
+
const order = [
|
|
14659
|
+
["eccentric", ecc],
|
|
14660
|
+
["hold", pauseBottom],
|
|
14661
|
+
["concentric", con],
|
|
14662
|
+
["hold", pauseTop]
|
|
14663
|
+
];
|
|
14664
|
+
const out = [];
|
|
14665
|
+
let t26 = 0;
|
|
14666
|
+
for (const [phase, seconds] of order) {
|
|
14667
|
+
const ms = seconds * 1e3;
|
|
14668
|
+
if (ms > 0) out.push({ phase, startMs: t26, endMs: t26 + ms });
|
|
14669
|
+
t26 += ms;
|
|
14670
|
+
}
|
|
14671
|
+
return out;
|
|
14672
|
+
}
|
|
14673
|
+
function phaseTargetsMs(phases, tempo) {
|
|
14674
|
+
if (tempo == null) return phases.map(() => null);
|
|
14675
|
+
const [eccS, pauseBottomS, conS, pauseTopS] = tempo;
|
|
14676
|
+
let seenConcentric = false;
|
|
14677
|
+
return phases.map((phase) => {
|
|
14678
|
+
switch (phase) {
|
|
14679
|
+
case "eccentric":
|
|
14680
|
+
return eccS * 1e3;
|
|
14681
|
+
case "concentric":
|
|
14682
|
+
seenConcentric = true;
|
|
14683
|
+
return conS * 1e3;
|
|
14684
|
+
case "hold":
|
|
14685
|
+
return (seenConcentric ? pauseTopS : pauseBottomS) * 1e3;
|
|
14686
|
+
default:
|
|
14687
|
+
return null;
|
|
14688
|
+
}
|
|
14689
|
+
});
|
|
14690
|
+
}
|
|
14691
|
+
|
|
14692
|
+
// src/components/custom/Fatigue/GhostBand.tsx
|
|
14693
|
+
var t21 = getSemanticColors("dark");
|
|
14694
|
+
var BAND_H = 16;
|
|
14695
|
+
var BAND_GAP = 4;
|
|
14696
|
+
var PHASE_LABEL = {
|
|
14697
|
+
eccentric: "ECC",
|
|
14698
|
+
concentric: "CON"
|
|
14699
|
+
};
|
|
14700
|
+
var LABEL_CH_PX = 7;
|
|
14701
|
+
var LABEL_PAD_PX = 6;
|
|
14702
|
+
var WELL_SHADE = primitiveColors.black;
|
|
14703
|
+
var WELL_FLOOR_LIGHT = primitiveColors.white;
|
|
14704
|
+
function labelFits(label, segW) {
|
|
14705
|
+
return segW >= label.length * LABEL_CH_PX + LABEL_PAD_PX;
|
|
14706
|
+
}
|
|
14707
|
+
function GhostBand({
|
|
14708
|
+
segments,
|
|
14709
|
+
x,
|
|
14710
|
+
top,
|
|
14711
|
+
height = BAND_H,
|
|
14712
|
+
showLabels = false,
|
|
14713
|
+
labelColor = t21["text-primary"],
|
|
14714
|
+
targetTempoSeconds = null,
|
|
14715
|
+
prescribed = false
|
|
14716
|
+
}) {
|
|
14717
|
+
const rawId = useId();
|
|
14718
|
+
const safeId = rawId.replace(/[^a-zA-Z0-9]/g, "");
|
|
14719
|
+
const clipId = `ghost-band-${safeId}`;
|
|
14720
|
+
const wellId = `ghost-band-well-${safeId}`;
|
|
14721
|
+
const drawn = segments.filter((seg) => x(seg.endMs) - x(seg.startMs) > 0);
|
|
14722
|
+
if (drawn.length === 0) return null;
|
|
14723
|
+
const bandLeft = x(drawn[0].startMs);
|
|
14724
|
+
const bandRight = x(drawn[drawn.length - 1].endMs);
|
|
14725
|
+
const bandW = bandRight - bandLeft;
|
|
14726
|
+
if (bandW <= 0) return null;
|
|
14727
|
+
const pacing = targetTempoSeconds != null && !prescribed;
|
|
14728
|
+
const targetsMs = phaseTargetsMs(
|
|
14729
|
+
drawn.map((seg) => seg.phase),
|
|
14730
|
+
targetTempoSeconds
|
|
14731
|
+
);
|
|
14732
|
+
const runs = drawn.map((seg, i) => {
|
|
14733
|
+
const left = x(seg.startMs);
|
|
14734
|
+
const right = i === drawn.length - 1 ? bandRight : x(drawn[i + 1].startMs);
|
|
14735
|
+
const width = Math.max(0, right - left);
|
|
14736
|
+
const targetMs = targetsMs[i];
|
|
14737
|
+
const elapsedMs = seg.endMs - seg.startMs;
|
|
14738
|
+
return {
|
|
14739
|
+
phase: seg.phase,
|
|
14740
|
+
left,
|
|
14741
|
+
width,
|
|
14742
|
+
// Prescribed → nothing performed, so nothing filled. No target at all → the run reads
|
|
14743
|
+
// complete rather than perpetually empty.
|
|
14744
|
+
fillWidth: prescribed ? 0 : pacing ? width * phaseFillFraction(elapsedMs, targetMs) : width,
|
|
14745
|
+
labelTone: pacing ? pacingTone(elapsedMs, targetMs) : labelColor
|
|
14746
|
+
};
|
|
14747
|
+
});
|
|
14748
|
+
return /* @__PURE__ */ jsxs("g", { children: [
|
|
14749
|
+
/* @__PURE__ */ jsxs("defs", { children: [
|
|
14750
|
+
/* @__PURE__ */ jsx("clipPath", { id: clipId, children: /* @__PURE__ */ jsx("rect", { x: bandLeft, y: top, width: bandW, height, rx: 2 }) }),
|
|
14751
|
+
/* @__PURE__ */ jsxs("linearGradient", { id: wellId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
14752
|
+
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: WELL_SHADE, stopOpacity: 0.6 }),
|
|
14753
|
+
/* @__PURE__ */ jsx("stop", { offset: "42%", stopColor: WELL_SHADE, stopOpacity: 0.12 }),
|
|
14754
|
+
/* @__PURE__ */ jsx("stop", { offset: "88%", stopColor: WELL_SHADE, stopOpacity: 0.02 }),
|
|
14755
|
+
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: WELL_FLOOR_LIGHT, stopOpacity: 0.07 })
|
|
14756
|
+
] })
|
|
14757
|
+
] }),
|
|
14758
|
+
/* @__PURE__ */ jsxs("g", { clipPath: `url(#${clipId})`, children: [
|
|
14759
|
+
/* @__PURE__ */ jsx(
|
|
14760
|
+
"rect",
|
|
14761
|
+
{
|
|
14762
|
+
x: bandLeft,
|
|
14763
|
+
y: top,
|
|
14764
|
+
width: bandW,
|
|
14765
|
+
height,
|
|
14766
|
+
fill: PHASE_AXIS_COLOR.idle,
|
|
14767
|
+
"data-testid": "ghost-band-floor"
|
|
14768
|
+
}
|
|
14769
|
+
),
|
|
14770
|
+
runs.map((run, i) => /* @__PURE__ */ jsxs("g", { children: [
|
|
14771
|
+
/* @__PURE__ */ jsx(
|
|
14772
|
+
"rect",
|
|
14773
|
+
{
|
|
14774
|
+
x: run.left,
|
|
14775
|
+
y: top,
|
|
14776
|
+
width: run.width,
|
|
14777
|
+
height,
|
|
14778
|
+
fill: PHASE_AXIS_BASE_COLOR[run.phase],
|
|
14779
|
+
"data-testid": "ghost-band-base"
|
|
14780
|
+
}
|
|
14781
|
+
),
|
|
14782
|
+
/* @__PURE__ */ jsx(
|
|
14783
|
+
"rect",
|
|
14784
|
+
{
|
|
14785
|
+
x: run.left,
|
|
14786
|
+
y: top,
|
|
14787
|
+
width: run.width,
|
|
14788
|
+
height,
|
|
14789
|
+
fill: `url(#${wellId})`,
|
|
14790
|
+
"data-testid": "ghost-band-well"
|
|
14791
|
+
}
|
|
14792
|
+
),
|
|
14793
|
+
/* @__PURE__ */ jsx(
|
|
14794
|
+
"rect",
|
|
14795
|
+
{
|
|
14796
|
+
x: run.left,
|
|
14797
|
+
y: top,
|
|
14798
|
+
width: run.fillWidth,
|
|
14799
|
+
height,
|
|
14800
|
+
fill: PHASE_AXIS_COLOR[run.phase],
|
|
14801
|
+
"data-testid": "ghost-band-fill"
|
|
14802
|
+
}
|
|
14803
|
+
)
|
|
14804
|
+
] }, i)),
|
|
14805
|
+
showLabels && runs.map((run, i) => {
|
|
14806
|
+
const label = PHASE_LABEL[run.phase];
|
|
14807
|
+
if (!label || !labelFits(label, run.width)) return null;
|
|
14808
|
+
return /* @__PURE__ */ jsx(
|
|
14809
|
+
"text",
|
|
14810
|
+
{
|
|
14811
|
+
x: run.left + run.width / 2,
|
|
14812
|
+
y: top + height / 2,
|
|
14813
|
+
textAnchor: "middle",
|
|
14814
|
+
dominantBaseline: "central",
|
|
14815
|
+
fill: run.labelTone,
|
|
14816
|
+
fontSize: 8,
|
|
14817
|
+
fontWeight: 800,
|
|
14818
|
+
letterSpacing: 1,
|
|
14819
|
+
fontFamily: FONT_UI,
|
|
14820
|
+
children: label
|
|
14821
|
+
},
|
|
14822
|
+
i
|
|
14823
|
+
);
|
|
14824
|
+
})
|
|
14825
|
+
] })
|
|
14826
|
+
] });
|
|
14827
|
+
}
|
|
14828
|
+
var t22 = getSemanticColors("dark");
|
|
14829
|
+
var PARCH = t22["text-primary"];
|
|
14830
|
+
function smoothPath(pts) {
|
|
14831
|
+
if (pts.length < 2) return pts.length ? `M ${pts[0][0]} ${pts[0][1]}` : "";
|
|
14832
|
+
let d = `M ${pts[0][0].toFixed(1)} ${pts[0][1].toFixed(1)}`;
|
|
14833
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
14834
|
+
const p0 = pts[i - 1] ?? pts[i];
|
|
14835
|
+
const p1 = pts[i];
|
|
14836
|
+
const p2 = pts[i + 1];
|
|
14837
|
+
const p3 = pts[i + 2] ?? p2;
|
|
14838
|
+
const c1x = p1[0] + (p2[0] - p0[0]) / 6;
|
|
14839
|
+
const c1y = p1[1] + (p2[1] - p0[1]) / 6;
|
|
14840
|
+
const c2x = p2[0] - (p3[0] - p1[0]) / 6;
|
|
14841
|
+
const c2y = p2[1] - (p3[1] - p1[1]) / 6;
|
|
14842
|
+
d += ` C ${c1x.toFixed(1)} ${c1y.toFixed(1)}, ${c2x.toFixed(1)} ${c2y.toFixed(1)}, ${p2[0].toFixed(1)} ${p2[1].toFixed(1)}`;
|
|
14843
|
+
}
|
|
14844
|
+
return d;
|
|
14845
|
+
}
|
|
14846
|
+
var BLUR_STD_DEV = 2.6;
|
|
14847
|
+
function GhostBloom({
|
|
14848
|
+
current,
|
|
14849
|
+
ghosts = [],
|
|
14850
|
+
tint,
|
|
14851
|
+
baseline,
|
|
14852
|
+
orientation = "up",
|
|
14853
|
+
ghostStroke = (i) => alpha(PARCH, 0.1 + i * 0.015),
|
|
14854
|
+
lineWidth = 3.5
|
|
14855
|
+
}) {
|
|
14856
|
+
const rawId = useId();
|
|
14857
|
+
const blurId = `bloom-blur-${rawId.replace(/[^a-zA-Z0-9]/g, "")}`;
|
|
14858
|
+
const toY = (mag) => orientation === "down" ? baseline + mag : baseline - mag;
|
|
14859
|
+
const toAbs = (pts) => pts.map(([px, mag]) => [px, toY(mag)]);
|
|
14860
|
+
const curD = smoothPath(toAbs(current));
|
|
14861
|
+
const ghostDs = ghosts.map((g) => smoothPath(toAbs(g)));
|
|
14862
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
14863
|
+
/* @__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 }) }) }),
|
|
14864
|
+
ghostDs.map((d, i) => /* @__PURE__ */ jsx(
|
|
14865
|
+
"path",
|
|
14866
|
+
{
|
|
14867
|
+
d,
|
|
14868
|
+
fill: "none",
|
|
14869
|
+
stroke: ghostStroke(i),
|
|
14870
|
+
strokeWidth: 1.5,
|
|
14871
|
+
strokeLinejoin: "round"
|
|
14872
|
+
},
|
|
14873
|
+
i
|
|
14874
|
+
)),
|
|
14875
|
+
/* @__PURE__ */ jsx(
|
|
14876
|
+
"path",
|
|
14877
|
+
{
|
|
14878
|
+
d: curD,
|
|
14879
|
+
fill: "none",
|
|
14880
|
+
stroke: alpha(primitiveColors.black, 0.3),
|
|
14881
|
+
strokeWidth: lineWidth + 3,
|
|
14882
|
+
strokeLinejoin: "round",
|
|
14883
|
+
strokeLinecap: "round",
|
|
14884
|
+
filter: `url(#${blurId})`
|
|
14885
|
+
}
|
|
14886
|
+
),
|
|
14887
|
+
/* @__PURE__ */ jsx(
|
|
14888
|
+
"path",
|
|
14889
|
+
{
|
|
14890
|
+
d: curD,
|
|
14891
|
+
fill: "none",
|
|
14892
|
+
stroke: tint,
|
|
14893
|
+
strokeWidth: lineWidth,
|
|
14894
|
+
strokeLinejoin: "round",
|
|
14895
|
+
strokeLinecap: "round"
|
|
14896
|
+
}
|
|
14897
|
+
)
|
|
14898
|
+
] });
|
|
14899
|
+
}
|
|
14900
|
+
|
|
14901
|
+
// src/components/custom/Fatigue/GhostSpark.tsx
|
|
14902
|
+
function GhostSpark({
|
|
14903
|
+
curves,
|
|
14904
|
+
width,
|
|
14905
|
+
height = 172,
|
|
14906
|
+
targetTempoSeconds = null
|
|
14907
|
+
}) {
|
|
14908
|
+
const w = width;
|
|
14909
|
+
const h = height;
|
|
14910
|
+
const padL = 12;
|
|
14911
|
+
const padR = 8;
|
|
14912
|
+
const padTop = 10;
|
|
14913
|
+
const padBot = 6;
|
|
14914
|
+
if (curves.length === 0) {
|
|
14915
|
+
const prescribed = prescribedSegments(targetTempoSeconds);
|
|
14916
|
+
if (prescribed.length === 0) {
|
|
14917
|
+
return /* @__PURE__ */ jsx(View, { testID: "ghost-spark", style: { width: w, height: h } });
|
|
14918
|
+
}
|
|
14919
|
+
const totalMs = prescribed[prescribed.length - 1].endMs;
|
|
14920
|
+
const bandTopEmpty = h - padBot - BAND_H;
|
|
14921
|
+
const xEmpty = (ms) => padL + ms / (totalMs * 1.04) * (w - padL - padR);
|
|
14922
|
+
return /* @__PURE__ */ jsx(View, { testID: "ghost-spark", style: { paddingHorizontal: 4 }, children: /* @__PURE__ */ jsx("svg", { width: w, height: h, children: /* @__PURE__ */ jsx(
|
|
14923
|
+
GhostBand,
|
|
14924
|
+
{
|
|
14925
|
+
segments: prescribed,
|
|
14926
|
+
x: xEmpty,
|
|
14927
|
+
top: bandTopEmpty,
|
|
14928
|
+
showLabels: true,
|
|
14929
|
+
targetTempoSeconds,
|
|
14930
|
+
prescribed: true
|
|
14931
|
+
}
|
|
14932
|
+
) }) });
|
|
14933
|
+
}
|
|
14934
|
+
const cur = curves[curves.length - 1];
|
|
14935
|
+
const allSamples = curves.flatMap((c) => c.samples);
|
|
14936
|
+
const vmax = Math.max(0.01, ...allSamples.map((s) => s.velocityMps)) * 1.06;
|
|
14937
|
+
const axisMaxT = Math.max(1, ...curves.map((c) => c.samples[c.samples.length - 1]?.tMs ?? 0)) * 1.04;
|
|
14938
|
+
const bandBottom = h - padBot;
|
|
14939
|
+
const bandTop = bandBottom - BAND_H;
|
|
14940
|
+
const baseline = bandTop - BAND_GAP;
|
|
14941
|
+
const plotH = Math.max(1, baseline - padTop);
|
|
14942
|
+
const x = (ms) => padL + ms / axisMaxT * (w - padL - padR);
|
|
14943
|
+
const mag = (v) => clamp012(v / vmax) * plotH;
|
|
14944
|
+
const lineTint = ghostLineColor(cur.tempoDeviation, cur.grindSignature);
|
|
14945
|
+
const curPts = cur.samples.map((s) => [x(s.tMs), mag(s.velocityMps)]);
|
|
14946
|
+
const ghostPts = curves.slice(0, -1).map((c) => c.samples.map((s) => [x(s.tMs), mag(s.velocityMps)]));
|
|
14947
|
+
return /* @__PURE__ */ jsx(View, { testID: "ghost-spark", style: { paddingHorizontal: 4 }, children: /* @__PURE__ */ jsxs("svg", { width: w, height: h, children: [
|
|
14948
|
+
/* @__PURE__ */ jsx(
|
|
14949
|
+
GhostBloom,
|
|
14950
|
+
{
|
|
14951
|
+
current: curPts,
|
|
14952
|
+
ghosts: ghostPts,
|
|
14953
|
+
tint: lineTint,
|
|
14954
|
+
baseline,
|
|
14955
|
+
orientation: "up"
|
|
14956
|
+
}
|
|
14957
|
+
),
|
|
14958
|
+
/* @__PURE__ */ jsx(
|
|
14959
|
+
GhostBand,
|
|
14960
|
+
{
|
|
14961
|
+
segments: cur.phaseSegments,
|
|
14962
|
+
x,
|
|
14963
|
+
top: bandTop,
|
|
14964
|
+
showLabels: true,
|
|
14965
|
+
targetTempoSeconds
|
|
14966
|
+
}
|
|
14967
|
+
)
|
|
14968
|
+
] }) });
|
|
14969
|
+
}
|
|
14970
|
+
|
|
14971
|
+
// src/components/custom/Fatigue/LiveFatigueCard.tsx
|
|
14972
|
+
var t23 = getSemanticColors("dark");
|
|
14973
|
+
var PAD = 18;
|
|
14974
|
+
var GHOST_GUTTER = 4;
|
|
14975
|
+
function LiveFatigueCard({ model, width = 318, height }) {
|
|
14976
|
+
const chartW = width - PAD * 2 - GHOST_GUTTER * 2;
|
|
14977
|
+
const chartH = height != null ? Math.round(Math.min(240, Math.max(168, height * 0.4))) : 172;
|
|
14978
|
+
return /* @__PURE__ */ jsxs(
|
|
14979
|
+
Surface,
|
|
14980
|
+
{
|
|
14981
|
+
level: "base",
|
|
14982
|
+
testID: "live-fatigue-card",
|
|
14983
|
+
style: {
|
|
14984
|
+
width,
|
|
14985
|
+
height,
|
|
14986
|
+
borderRadius: 14,
|
|
14987
|
+
padding: PAD,
|
|
14988
|
+
borderWidth: 1,
|
|
14989
|
+
borderColor: t23["hairline-default"],
|
|
14990
|
+
...barPaper(t23["surface-base"])
|
|
14991
|
+
},
|
|
14992
|
+
children: [
|
|
14993
|
+
/* @__PURE__ */ jsxs(View, { style: { gap: 12 }, children: [
|
|
14994
|
+
/* @__PURE__ */ jsx(VerdictHero, { rpe: model.rpe, verdict: model.verdict }),
|
|
14995
|
+
/* @__PURE__ */ jsx(FatigueLights, { dimensions: model.verdict?.dimensions ?? null })
|
|
14996
|
+
] }),
|
|
14997
|
+
/* @__PURE__ */ jsx(View, { style: { flex: 1, minHeight: 16 } }),
|
|
14998
|
+
/* @__PURE__ */ jsx(
|
|
14999
|
+
RomProgressionChart,
|
|
15000
|
+
{
|
|
15001
|
+
points: model.romProgression,
|
|
15002
|
+
workingStandardM: model.romWorkingStandardM,
|
|
15003
|
+
shortThresholdM: model.romShortThresholdM,
|
|
15004
|
+
plannedReps: model.plannedReps
|
|
15005
|
+
}
|
|
15006
|
+
),
|
|
15007
|
+
/* @__PURE__ */ jsx(View, { style: { flex: 1, minHeight: 16 } }),
|
|
15008
|
+
/* @__PURE__ */ jsx(
|
|
15009
|
+
GhostSpark,
|
|
15010
|
+
{
|
|
15011
|
+
curves: model.velocityCurves,
|
|
15012
|
+
width: chartW,
|
|
15013
|
+
height: chartH,
|
|
15014
|
+
targetTempoSeconds: model.targetTempoSeconds
|
|
15015
|
+
}
|
|
15016
|
+
)
|
|
15017
|
+
]
|
|
15018
|
+
}
|
|
15019
|
+
);
|
|
15020
|
+
}
|
|
15021
|
+
|
|
15022
|
+
// src/components/custom/Fatigue/LiveFatiguePanel.tsx
|
|
15023
|
+
var t24 = getSemanticColors("dark");
|
|
15024
|
+
function ExerciseHeaderLite({ header }) {
|
|
15025
|
+
return /* @__PURE__ */ jsxs(
|
|
15026
|
+
View,
|
|
15027
|
+
{
|
|
15028
|
+
style: {
|
|
15029
|
+
flexDirection: "row",
|
|
15030
|
+
alignItems: "flex-end",
|
|
15031
|
+
justifyContent: "space-between",
|
|
15032
|
+
paddingHorizontal: 24,
|
|
15033
|
+
paddingTop: 18,
|
|
15034
|
+
paddingBottom: 14,
|
|
15035
|
+
borderBottomWidth: 1,
|
|
15036
|
+
borderColor: alpha(t24["text-primary"], 0.08)
|
|
15037
|
+
},
|
|
15038
|
+
children: [
|
|
15039
|
+
/* @__PURE__ */ jsxs(View, { style: { gap: 2 }, children: [
|
|
15040
|
+
/* @__PURE__ */ jsx(
|
|
15041
|
+
Text,
|
|
15042
|
+
{
|
|
15043
|
+
style: {
|
|
15044
|
+
fontSize: 28,
|
|
15045
|
+
fontWeight: "700",
|
|
15046
|
+
fontFamily: FONT_HEAD,
|
|
15047
|
+
color: t24["text-primary"]
|
|
15048
|
+
},
|
|
15049
|
+
children: header.title
|
|
15050
|
+
}
|
|
15051
|
+
),
|
|
15052
|
+
header.subtitle && /* @__PURE__ */ jsx(
|
|
15053
|
+
Text,
|
|
15054
|
+
{
|
|
15055
|
+
style: {
|
|
15056
|
+
fontSize: 13,
|
|
15057
|
+
fontWeight: "600",
|
|
15058
|
+
fontFamily: FONT_UI,
|
|
15059
|
+
color: t24["text-secondary"]
|
|
15060
|
+
},
|
|
15061
|
+
children: header.subtitle
|
|
15062
|
+
}
|
|
15063
|
+
)
|
|
15064
|
+
] }),
|
|
15065
|
+
header.meta && /* @__PURE__ */ jsx(
|
|
15066
|
+
Text,
|
|
15067
|
+
{
|
|
15068
|
+
style: {
|
|
15069
|
+
fontSize: 13,
|
|
15070
|
+
fontWeight: "800",
|
|
15071
|
+
fontFamily: FONT_MONO,
|
|
15072
|
+
color: t24["text-tertiary"]
|
|
15073
|
+
},
|
|
15074
|
+
children: header.meta
|
|
15075
|
+
}
|
|
15076
|
+
)
|
|
15077
|
+
]
|
|
15078
|
+
}
|
|
15079
|
+
);
|
|
15080
|
+
}
|
|
15081
|
+
function LiveFatiguePanel({
|
|
15082
|
+
model,
|
|
15083
|
+
velocity,
|
|
15084
|
+
header,
|
|
15085
|
+
aura,
|
|
15086
|
+
bodyHeight = 508,
|
|
15087
|
+
cardWidth = 318
|
|
15088
|
+
}) {
|
|
15089
|
+
const category = aura ?? auraForVerdict(model.verdict?.state ?? null);
|
|
15090
|
+
const heroH = bodyHeight - 26;
|
|
15091
|
+
return /* @__PURE__ */ jsx(
|
|
15092
|
+
LiveAuraFrame,
|
|
15093
|
+
{
|
|
15094
|
+
category,
|
|
15095
|
+
style: { borderRadius: 0, borderWidth: 0 },
|
|
15096
|
+
testID: "live-fatigue-panel",
|
|
15097
|
+
children: /* @__PURE__ */ jsxs(View, { style: { flex: 1 }, children: [
|
|
15098
|
+
header && /* @__PURE__ */ jsx(ExerciseHeaderLite, { header }),
|
|
15099
|
+
/* @__PURE__ */ jsxs(View, { style: { padding: 24, flexDirection: "row", gap: 18, alignItems: "stretch" }, children: [
|
|
15100
|
+
/* @__PURE__ */ jsxs(View, { style: { flex: 1, gap: 8 }, children: [
|
|
15101
|
+
/* @__PURE__ */ jsx(
|
|
15102
|
+
Text,
|
|
15103
|
+
{
|
|
15104
|
+
style: {
|
|
15105
|
+
fontSize: 9,
|
|
15106
|
+
letterSpacing: 1.2,
|
|
15107
|
+
fontFamily: FONT_MONO,
|
|
15108
|
+
color: t24["text-tertiary"]
|
|
15109
|
+
},
|
|
15110
|
+
children: "VELOCITY \xB7 this set"
|
|
15111
|
+
}
|
|
15112
|
+
),
|
|
15113
|
+
/* @__PURE__ */ jsx(
|
|
15114
|
+
VelocityHero,
|
|
15115
|
+
{
|
|
15116
|
+
velocities: velocity.velocities,
|
|
15117
|
+
targetReps: velocity.targetReps,
|
|
15118
|
+
liveRepIndex: velocity.liveRepIndex,
|
|
15119
|
+
height: heroH
|
|
15120
|
+
}
|
|
15121
|
+
)
|
|
15122
|
+
] }),
|
|
15123
|
+
/* @__PURE__ */ jsx(LiveFatigueCard, { model, width: cardWidth, height: bodyHeight })
|
|
15124
|
+
] })
|
|
15125
|
+
] })
|
|
15126
|
+
}
|
|
15127
|
+
);
|
|
15128
|
+
}
|
|
15129
|
+
var t25 = getSemanticColors("dark");
|
|
15130
|
+
function phaseAt(segments, ms) {
|
|
15131
|
+
const hit = segments.find((s) => ms >= s.startMs && ms < s.endMs);
|
|
15132
|
+
return hit ? hit.phase : null;
|
|
15133
|
+
}
|
|
15134
|
+
function mergePhaseSegments(a, b) {
|
|
15135
|
+
if (a.length === 0) return b;
|
|
15136
|
+
if (b.length === 0) return a;
|
|
15137
|
+
const bounds = Array.from(new Set([...a, ...b].flatMap((s) => [s.startMs, s.endMs]))).sort(
|
|
15138
|
+
(p, q) => p - q
|
|
15139
|
+
);
|
|
15140
|
+
const out = [];
|
|
15141
|
+
for (let i = 0; i < bounds.length - 1; i++) {
|
|
15142
|
+
const startMs = bounds[i];
|
|
15143
|
+
const endMs = bounds[i + 1];
|
|
15144
|
+
if (endMs <= startMs) continue;
|
|
15145
|
+
const mid = (startMs + endMs) / 2;
|
|
15146
|
+
const pa = phaseAt(a, mid);
|
|
15147
|
+
const pb = phaseAt(b, mid);
|
|
15148
|
+
const phase = pa == null ? pb ?? "idle" : pb == null ? pa : pa === pb ? pa : "idle";
|
|
15149
|
+
const last = out[out.length - 1];
|
|
15150
|
+
if (last && last.phase === phase && last.endMs === startMs) last.endMs = endMs;
|
|
15151
|
+
else out.push({ phase, startMs, endMs });
|
|
15152
|
+
}
|
|
15153
|
+
return out;
|
|
15154
|
+
}
|
|
15155
|
+
function lastTMs(curves) {
|
|
15156
|
+
return Math.max(0, ...curves.map((c) => c.samples[c.samples.length - 1]?.tMs ?? 0));
|
|
15157
|
+
}
|
|
15158
|
+
function DualGhostSpark({
|
|
15159
|
+
left,
|
|
15160
|
+
right,
|
|
15161
|
+
width,
|
|
15162
|
+
height = 232,
|
|
15163
|
+
leftLabel = "LEFT",
|
|
15164
|
+
rightLabel = "RIGHT",
|
|
15165
|
+
showDeviceLabels = true,
|
|
15166
|
+
targetTempoSeconds = null
|
|
15167
|
+
}) {
|
|
15168
|
+
const w = width;
|
|
15169
|
+
const h = height;
|
|
15170
|
+
const padL = 14;
|
|
15171
|
+
const padR = 14;
|
|
15172
|
+
const padTop = 22;
|
|
15173
|
+
const padBot = 22;
|
|
15174
|
+
if (left.length === 0 && right.length === 0) {
|
|
15175
|
+
return /* @__PURE__ */ jsx(View, { testID: "dual-ghost-spark", style: { width: w, height: h } });
|
|
15176
|
+
}
|
|
15177
|
+
const mid = padTop + (h - padTop - padBot) / 2;
|
|
15178
|
+
const bandTop = mid - BAND_H / 2;
|
|
15179
|
+
const baseUp = bandTop - BAND_GAP;
|
|
15180
|
+
const baseDown = mid + BAND_H / 2 + BAND_GAP;
|
|
15181
|
+
const wingH = Math.max(1, Math.min(baseUp - padTop, h - padBot - baseDown));
|
|
15182
|
+
const allSamples = [...left, ...right].flatMap((c) => c.samples);
|
|
15183
|
+
const vmax = Math.max(0.01, ...allSamples.map((s) => s.velocityMps)) * 1.06;
|
|
15184
|
+
const axisMaxT = Math.max(1, lastTMs(left), lastTMs(right)) * 1.04;
|
|
15185
|
+
const x = (ms) => padL + ms / axisMaxT * (w - padL - padR);
|
|
15186
|
+
const mag = (v) => clamp012(v / vmax) * wingH;
|
|
15187
|
+
const toPts = (c) => c.samples.map((s) => [x(s.tMs), mag(s.velocityMps)]);
|
|
15188
|
+
const wing = (curves) => {
|
|
15189
|
+
const cur = curves[curves.length - 1];
|
|
15190
|
+
return {
|
|
15191
|
+
cur,
|
|
15192
|
+
current: cur ? toPts(cur) : [],
|
|
15193
|
+
ghosts: curves.slice(0, -1).map(toPts),
|
|
15194
|
+
tint: cur ? ghostLineColor(cur.tempoDeviation, cur.grindSignature) : t25["text-tertiary"]
|
|
15195
|
+
};
|
|
15196
|
+
};
|
|
15197
|
+
const up = wing(left);
|
|
15198
|
+
const down = wing(right);
|
|
15199
|
+
const segments = mergePhaseSegments(up.cur?.phaseSegments ?? [], down.cur?.phaseSegments ?? []);
|
|
15200
|
+
return /* @__PURE__ */ jsx(View, { testID: "dual-ghost-spark", style: { paddingHorizontal: 4 }, children: /* @__PURE__ */ jsxs("svg", { width: w, height: h, children: [
|
|
15201
|
+
up.current.length > 0 && /* @__PURE__ */ jsx("g", { "data-testid": "dual-ghost-bloom-left", children: /* @__PURE__ */ jsx(
|
|
15202
|
+
GhostBloom,
|
|
15203
|
+
{
|
|
15204
|
+
current: up.current,
|
|
15205
|
+
ghosts: up.ghosts,
|
|
15206
|
+
tint: up.tint,
|
|
15207
|
+
baseline: baseUp,
|
|
15208
|
+
orientation: "up"
|
|
15209
|
+
}
|
|
15210
|
+
) }),
|
|
15211
|
+
down.current.length > 0 && /* @__PURE__ */ jsx("g", { "data-testid": "dual-ghost-bloom-right", children: /* @__PURE__ */ jsx(
|
|
15212
|
+
GhostBloom,
|
|
15213
|
+
{
|
|
15214
|
+
current: down.current,
|
|
15215
|
+
ghosts: down.ghosts,
|
|
15216
|
+
tint: down.tint,
|
|
15217
|
+
baseline: baseDown,
|
|
15218
|
+
orientation: "down"
|
|
15219
|
+
}
|
|
15220
|
+
) }),
|
|
15221
|
+
/* @__PURE__ */ jsx(
|
|
15222
|
+
GhostBand,
|
|
15223
|
+
{
|
|
15224
|
+
segments,
|
|
15225
|
+
x,
|
|
15226
|
+
top: bandTop,
|
|
15227
|
+
showLabels: true,
|
|
15228
|
+
labelColor: alpha(t25["text-primary"], 0.92),
|
|
15229
|
+
targetTempoSeconds
|
|
15230
|
+
}
|
|
15231
|
+
),
|
|
15232
|
+
showDeviceLabels && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
15233
|
+
/* @__PURE__ */ jsx(
|
|
15234
|
+
"text",
|
|
15235
|
+
{
|
|
15236
|
+
x: padL,
|
|
15237
|
+
y: padTop - 9,
|
|
15238
|
+
fontSize: 9,
|
|
15239
|
+
fontWeight: 800,
|
|
15240
|
+
letterSpacing: 1,
|
|
15241
|
+
fontFamily: FONT_UI,
|
|
15242
|
+
fill: t25["text-tertiary"],
|
|
15243
|
+
children: leftLabel
|
|
15244
|
+
}
|
|
15245
|
+
),
|
|
15246
|
+
/* @__PURE__ */ jsx(
|
|
15247
|
+
"text",
|
|
15248
|
+
{
|
|
15249
|
+
x: padL,
|
|
15250
|
+
y: h - padBot + 15,
|
|
15251
|
+
fontSize: 9,
|
|
15252
|
+
fontWeight: 800,
|
|
15253
|
+
letterSpacing: 1,
|
|
15254
|
+
fontFamily: FONT_UI,
|
|
15255
|
+
fill: t25["text-tertiary"],
|
|
15256
|
+
children: rightLabel
|
|
15257
|
+
}
|
|
15258
|
+
)
|
|
15259
|
+
] })
|
|
15260
|
+
] }) });
|
|
15261
|
+
}
|
|
13792
15262
|
var PALETTE = DATAVIZ_CATEGORICAL_PALETTE;
|
|
13793
15263
|
function scaleValue(v, scale) {
|
|
13794
15264
|
if (v <= 0) return 0;
|
|
@@ -13877,7 +15347,7 @@ function Treemap({
|
|
|
13877
15347
|
const rects = squarify(weighted, width, height);
|
|
13878
15348
|
return /* @__PURE__ */ jsx(View, { className: cn("relative", className), style: { width, height }, ...props, children: rects.map((r) => {
|
|
13879
15349
|
const selected = r.datum.id === selectedId;
|
|
13880
|
-
const
|
|
15350
|
+
const labelFits2 = r.w > 54 && r.h > 22;
|
|
13881
15351
|
const pressable = r.datum.id !== "__more__";
|
|
13882
15352
|
return /* @__PURE__ */ jsx(
|
|
13883
15353
|
Pressable,
|
|
@@ -13899,7 +15369,7 @@ function Treemap({
|
|
|
13899
15369
|
padding: 4,
|
|
13900
15370
|
overflow: "hidden"
|
|
13901
15371
|
},
|
|
13902
|
-
children:
|
|
15372
|
+
children: labelFits2 && /* @__PURE__ */ jsx(Text, { numberOfLines: 2, className: "text-[10px] font-semibold text-[#0b0b0b]", children: r.datum.label ?? r.datum.id })
|
|
13903
15373
|
},
|
|
13904
15374
|
r.datum.id
|
|
13905
15375
|
);
|
|
@@ -13995,28 +15465,28 @@ function Scatter({
|
|
|
13995
15465
|
testID: "scatter-canvas",
|
|
13996
15466
|
style: { position: "absolute", top: 0, left: 0, width, height, overflow: "hidden" },
|
|
13997
15467
|
children: [
|
|
13998
|
-
ticksOf(yd, TICK_COUNT).map((
|
|
13999
|
-
const y = toY(
|
|
15468
|
+
ticksOf(yd, TICK_COUNT).map((t26, i) => {
|
|
15469
|
+
const y = toY(t26);
|
|
14000
15470
|
return /* @__PURE__ */ jsx(
|
|
14001
15471
|
View,
|
|
14002
15472
|
{
|
|
14003
15473
|
accessibilityElementsHidden: true,
|
|
14004
15474
|
testID: "scatter-gridline-y",
|
|
14005
15475
|
style: { position: "absolute", left: PLOT_LEFT2, width: innerW, top: y, height: 1, backgroundColor: GRID_LINE2 },
|
|
14006
|
-
children: /* @__PURE__ */ jsx(Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", left: -PLOT_LEFT2, top: -6, width: PLOT_LEFT2 - 6, textAlign: "right" }, children: formatTick(
|
|
15476
|
+
children: /* @__PURE__ */ jsx(Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", left: -PLOT_LEFT2, top: -6, width: PLOT_LEFT2 - 6, textAlign: "right" }, children: formatTick(t26) })
|
|
14007
15477
|
},
|
|
14008
15478
|
`y-${i}`
|
|
14009
15479
|
);
|
|
14010
15480
|
}),
|
|
14011
|
-
ticksOf(xd, TICK_COUNT).map((
|
|
14012
|
-
const x = toX(
|
|
15481
|
+
ticksOf(xd, TICK_COUNT).map((t26, i) => {
|
|
15482
|
+
const x = toX(t26);
|
|
14013
15483
|
return /* @__PURE__ */ jsx(
|
|
14014
15484
|
View,
|
|
14015
15485
|
{
|
|
14016
15486
|
accessibilityElementsHidden: true,
|
|
14017
15487
|
testID: "scatter-gridline-x",
|
|
14018
15488
|
style: { position: "absolute", top: PLOT_TOP, height: innerH, left: x, width: 1, backgroundColor: GRID_LINE2 },
|
|
14019
|
-
children: /* @__PURE__ */ jsx(Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", top: innerH + 4, left: -16, width: 32, textAlign: "center" }, children: formatTick(
|
|
15489
|
+
children: /* @__PURE__ */ jsx(Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", top: innerH + 4, left: -16, width: 32, textAlign: "center" }, children: formatTick(t26) })
|
|
14020
15490
|
},
|
|
14021
15491
|
`x-${i}`
|
|
14022
15492
|
);
|
|
@@ -14089,7 +15559,7 @@ var DEFAULT_THRESHOLDS2 = [
|
|
|
14089
15559
|
var ARC_START = 135;
|
|
14090
15560
|
var ARC_SWEEP = 270;
|
|
14091
15561
|
var SEGMENTS = 40;
|
|
14092
|
-
function
|
|
15562
|
+
function clamp013(n) {
|
|
14093
15563
|
if (Number.isNaN(n)) return 0;
|
|
14094
15564
|
return Math.max(0, Math.min(1, n));
|
|
14095
15565
|
}
|
|
@@ -14134,7 +15604,7 @@ function Gauge2({
|
|
|
14134
15604
|
...props
|
|
14135
15605
|
}) {
|
|
14136
15606
|
const span = max - min || 1;
|
|
14137
|
-
const fill =
|
|
15607
|
+
const fill = clamp013((value - min) / span);
|
|
14138
15608
|
const activeColor = (f) => color ?? bandColor2(f, thresholds, min, max);
|
|
14139
15609
|
const ticks = buildTicks(fill, size, activeColor);
|
|
14140
15610
|
const displayColor = color ?? bandColor2(fill, thresholds, min, max);
|
|
@@ -14151,23 +15621,23 @@ function Gauge2({
|
|
|
14151
15621
|
testID: "gauge",
|
|
14152
15622
|
...props,
|
|
14153
15623
|
children: [
|
|
14154
|
-
ticks.map((
|
|
15624
|
+
ticks.map((t26) => /* @__PURE__ */ jsx(
|
|
14155
15625
|
View,
|
|
14156
15626
|
{
|
|
14157
15627
|
accessibilityElementsHidden: true,
|
|
14158
15628
|
testID: "gauge-segment",
|
|
14159
15629
|
style: {
|
|
14160
15630
|
position: "absolute",
|
|
14161
|
-
left:
|
|
14162
|
-
top:
|
|
15631
|
+
left: t26.left - tickThickness / 2,
|
|
15632
|
+
top: t26.top - tickLength / 2,
|
|
14163
15633
|
width: tickThickness,
|
|
14164
15634
|
height: tickLength,
|
|
14165
15635
|
borderRadius: tickThickness / 2,
|
|
14166
|
-
backgroundColor:
|
|
14167
|
-
transform: [{ rotate:
|
|
15636
|
+
backgroundColor: t26.color,
|
|
15637
|
+
transform: [{ rotate: t26.rotate }]
|
|
14168
15638
|
}
|
|
14169
15639
|
},
|
|
14170
|
-
|
|
15640
|
+
t26.key
|
|
14171
15641
|
)),
|
|
14172
15642
|
/* @__PURE__ */ jsxs(View, { style: { position: "absolute", top: 0, left: 0, width: size, height: size }, className: "items-center justify-center", children: [
|
|
14173
15643
|
/* @__PURE__ */ jsxs(View, { className: "flex-row items-baseline gap-0.5", children: [
|
|
@@ -14587,6 +16057,6 @@ function composeValidators(...validators) {
|
|
|
14587
16057
|
};
|
|
14588
16058
|
}
|
|
14589
16059
|
|
|
14590
|
-
export { Accordion, AccordionButton, AccordionItem, AccordionPanel, ActivityIcon, Alert, AlertDescription, AlertTitle, AlertTriangleIcon, Autocomplete, Avatar, AvatarBadge, AvatarGroup, AwardIcon, Badge, BadgeText, BaseBadge, BluetoothIcon, BrandLockup, BreadcrumbItem, Breadcrumbs, Button, ButtonIcon, ButtonSpinner, ButtonText, CATEGORICAL_CVD_SAFE_MAX, CapacityBandChart, Caption, Card, CardContent, CardDescription, CardFooter, CardHeader, CardSkeleton, CardTitle, Checkbox, CheckboxGroup, Chip, CircleSlashIcon, CircularProgress, CircularTimer, Collapse, CollapseButton, CollapseContent, DashboardShell, DataRow, DateTime, DeviationBar, DeviceIndicator, DeviceMenu, DeviceRow, Divider, Drawer, DrawerBody, DrawerFooter, DrawerHeader, DumbbellIcon, EmptyState, ExerciseCard, ExerciseCardHeading, ExerciseHeading, ExerciseIndicator, FatigueMeter, FormActions, FormField, FormRow, FormSection, Gauge2 as Gauge, HStack, Heading, HelpTip, HistoryIcon, INDICATOR_PRECEDENCE, IconBox, Indicator, InfoIcon, Input, InputBar, InputGroup, IntensityBar, Label, LabelWithHelp, LayersIcon, Link, ListItem, ListItemContent, ListItemDivider, ListItemIcon, ListItemTrailing, LiveAuraFrame, METRIC_FONT, Menu, MenuDivider, MenuGroup, MenuItem, MenuList, MenuTrigger, MesoCard, MesoProgressBar, MesoStatusCard, Metric, MetricCell, MetricGroup, MetricTiles, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalTitle, MuscleGroupChip, NavItem, Overline, Paragraph, PasswordInput, PersonStandingIcon, Pill, PlaceholderStrip, Popover, PopoverCloseButton, PopoverContent, PopoverTrigger, PrBadge, PrHistoryModal, Progress, ProgressSteps, Radio, RadioGroup, ReadinessCheck, RestTimer, SET_STRIP_VARIABLE_COLOR, SET_STRIP_ZONES, SURFACE_LEVEL_TOKEN, ScaleIcon, Scatter, ScheduleTiles, Section, SectionContent, SectionHeader, SegmentedBar, SegmentedProgressBar, Select, SessionHeader, SessionRail, SessionStatePill, SetBar, SetRow, SetStrip, SetTableHeader, SetsRepsLoad, SideNav, Sidebar, SidebarContent, SidebarDivider, SidebarFooter, SidebarHeader, SidebarItem, SidebarSection, Skeleton, SkeletonCard, SkeletonCircle, SkeletonListItem, SkeletonText, Sparkline, Spinner, Stack, StarIcon, StatusDot, StatusPill2 as StatusPill, StepContent, StepIndicator, StepLabel, Stepper, Step as StepperStep, StrengthTrendChart, SupersetWrapper, Surface, SurfaceContext, SvgIcon, Switch, Tab, TabList, TabPanel, TabPanels, Table, TableBody, TableCell, TableHeader, TableHeaderCell, TablePagination, TableRow, Tabs, TempoDisplay, Tile, TimerReadout, Toast, ToastProvider, ToolbarButton, ToolbarButtonGroup, Tooltip, TopBar, Treemap, TrendingDownIcon, Typography, VStack, VelocityStrip, VoltrasMark, VolumeLandmarkBar, WeekRow, WeightBadge, WorkoutCard, WorkoutPill, ZoneTrack, alpha, applyThemePreset, audiobookPreset, bestTextColor, buttonSizes, calculateMeanVelocity, calculateVelocityLoss, categoricalPalette, cn, componentElevationRanges, composeValidators, createFieldId, createFlatShadow, createPressedHoverShadow, createPressedShadow, createRaisedHoverShadow, createRaisedShadow, darkThemeCSSVars, darken, defaultNavItems, defaultPreset, discreteRainbow, divergingScale, elevationSystem, formatDateTime, formatDuration, formatPrescription, formatSignedPct, formatVelocity, getBaseSurfaceColor, getCategoricalColor, getContrastText, getDiscreteRainbowColor, getElevationConfig, getElevationShadow, getElevationSurface, getFieldAriaProps, getFieldValidationProps, getGlowShadow, getHoverColors, getLuminance, getResultColor, getSemanticColors, getStatusColor, getThemeCSSVars, getValidatedElevation, getVelocityZoneColor, getVelocityZoneName, gluestackConfig, hasMaxLength, hasMinLength, hexToRgb, hsvToRgb, isCssPropertyManifest, isDark, isEmpty, isValidEmail, lightThemeCSSVars, lighten, linearGradient, liveAuraColor, neumorphicShadows, onSurfaceColors, paceTone, paceToneColor, primitiveBorderRadius, primitiveBreakpoints, primitiveColors, primitiveRamps, primitiveShadows, primitiveSpacing, primitiveTypography, primitiveZIndex, resolveColor, resolveIndicator, rgbToHex, rgbToHsv, roundRpe, roundTempo, roundWeight, rpeColor, semanticColorsDark, semanticColorsLight, semanticTypography, sequentialEffort, shadowColors, statusPillColor, surfaceBackground, surfaceGradient, useOnSurfaceColor, useSurface, useSurfaceMode, useTable, useTimer, useToast, useToolbarButton, validateCssPropertyManifest, validationRules, velocityZoneColor };
|
|
16060
|
+
export { Accordion, AccordionButton, AccordionItem, AccordionPanel, ActivityIcon, Alert, AlertDescription, AlertTitle, AlertTriangleIcon, Autocomplete, Avatar, AvatarBadge, AvatarGroup, AwardIcon, BAND_GAP, BAND_H, Badge, BadgeText, BaseBadge, BluetoothIcon, BrandLockup, BreadcrumbItem, Breadcrumbs, Button, ButtonIcon, ButtonSpinner, ButtonText, CATEGORICAL_CVD_SAFE_MAX, CapacityBandChart, Caption, Card, CardContent, CardDescription, CardFooter, CardHeader, CardSkeleton, CardTitle, Checkbox, CheckboxGroup, Chip, CircleSlashIcon, CircularProgress, CircularTimer, Collapse, CollapseButton, CollapseContent, DRIFT_GREY, DashboardShell, DataRow, DateTime, DeviationBar, DeviceIndicator, DeviceMenu, DeviceRow, Divider, Drawer, DrawerBody, DrawerFooter, DrawerHeader, DualGhostSpark, DualVelocityStrip, DumbbellIcon, EmptyState, ExerciseCard, ExerciseCardHeading, ExerciseHeading, ExerciseIndicator, FatigueLights, FatigueMeter, FormActions, FormField, FormRow, FormSection, GRIND_THRESHOLD, Gauge2 as Gauge, GhostBand, GhostBloom, GhostSpark, HStack, Heading, HelpTip, HistoryIcon, INDICATOR_PRECEDENCE, IconBox, Indicator, InfoIcon, Input, InputBar, InputGroup, IntensityBar, Label, LabelWithHelp, LayersIcon, Link, ListItem, ListItemContent, ListItemDivider, ListItemIcon, ListItemTrailing, LiveAuraFrame, LiveFatigueCard, LiveFatiguePanel, METRIC_FONT, Menu, MenuDivider, MenuGroup, MenuItem, MenuList, MenuTrigger, MesoCard, MesoProgressBar, MesoStatusCard, Metric, MetricCell, MetricGroup, MetricTiles, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalTitle, MuscleGroupChip, NavItem, Overline, PHASE_AXIS_COLOR, PRESSED_ELEVATION_LEVEL, Paragraph, PasswordInput, PersonStandingIcon, Pill, PlaceholderStrip, Popover, PopoverCloseButton, PopoverContent, PopoverTrigger, PrBadge, PrHistoryModal, Progress, ProgressSteps, RED_DEEP, RED_LIGHT, RED_MID, Radio, RadioGroup, ReadinessCheck, RestTimer, RomProgressionChart, SET_STRIP_VARIABLE_COLOR, SET_STRIP_ZONES, SILVER, STATE_LABEL, SURFACE_LEVEL_TOKEN, ScaleIcon, Scatter, ScheduleTiles, Section, SectionContent, SectionHeader, SegmentedBar, SegmentedProgressBar, Select, SessionHeader, SessionRail, SessionStatePill, SetBar, SetRow, SetStrip, SetTableHeader, SetsRepsLoad, SideNav, Sidebar, SidebarContent, SidebarDivider, SidebarFooter, SidebarHeader, SidebarItem, SidebarSection, Skeleton, SkeletonCard, SkeletonCircle, SkeletonListItem, SkeletonText, Sparkline, Spinner, Stack, StarIcon, StatusDot, StatusPill2 as StatusPill, StepContent, StepIndicator, StepLabel, Stepper, Step as StepperStep, StrengthTrendChart, SupersetWrapper, Surface, SurfaceContext, SvgIcon, Switch, TONE_COLOR, Tab, TabList, TabPanel, TabPanels, Table, TableBody, TableCell, TableHeader, TableHeaderCell, TablePagination, TableRow, Tabs, TempoDisplay, Tile, TimerReadout, Toast, ToastProvider, ToolbarButton, ToolbarButtonGroup, Tooltip, TopBar, Treemap, TrendingDownIcon, Typography, VStack, VelocityHero, VelocityStrip, VerdictHero, VoltrasMark, VolumeLandmarkBar, WeekRow, WeightBadge, WorkoutCard, WorkoutPill, ZoneTrack, alpha, applyThemePreset, audiobookPreset, auraForVerdict, backgroundFrameDark, bestTextColor, buttonSizes, calculateMeanVelocity, calculateVelocityLoss, categoricalPalette, cn, componentElevationRanges, composeValidators, createFieldId, createFlatShadow, createPressedHoverShadow, createPressedShadow, createRaisedHoverShadow, createRaisedShadow, darkThemeCSSVars, darken, defaultNavItems, defaultPreset, discreteRainbow, divergingScale, elevationSystem, formatDateTime, formatDuration, formatPrescription, formatSignedPct, formatVelocity, getBaseSurfaceColor, getCategoricalColor, getContrastText, getDiscreteRainbowColor, getElevationConfig, getElevationShadow, getElevationSurface, getFieldAriaProps, getFieldValidationProps, getGlowShadow, getHoverColors, getLuminance, getPressedRecessShadow, getResultColor, getSemanticColors, getStatusColor, getThemeCSSVars, getValidatedElevation, getVelocityZoneColor, getVelocityZoneName, ghostLineColor, gluestackConfig, hasMaxLength, hasMinLength, hexToRgb, hsvToRgb, isCssPropertyManifest, isDark, isEmpty, isValidEmail, lightThemeCSSVars, lighten, linearGradient, liveAuraColor, mergePhaseSegments, neumorphicShadows, onSurfaceColors, paceTone, paceToneColor, pressedLevel, primitiveBorderRadius, primitiveBreakpoints, primitiveColors, primitiveRamps, primitiveShadows, primitiveSpacing, primitiveTypography, primitiveZIndex, resolveColor, resolveIndicator, rgbToHex, rgbToHsv, roundRpe, roundTempo, roundWeight, rpeColor, semanticColorsDark, semanticColorsLight, semanticTypography, sequentialEffort, shadowColors, smoothPath, statusPillColor, surfaceBackground, surfaceGradient, surfaceRampDark, useOnSurfaceColor, useSurface, useSurfaceMode, useTable, useTimer, useToast, useToolbarButton, validateCssPropertyManifest, validationRules, velocityZoneColor };
|
|
14591
16061
|
//# sourceMappingURL=index.mjs.map
|
|
14592
16062
|
//# sourceMappingURL=index.mjs.map
|