@titan-design/react-ui 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/MesoStatusCard-CQk71hSi.d.mts +77 -0
  2. package/dist/MesoStatusCard-CQk71hSi.d.ts +77 -0
  3. package/dist/bodymap.d.mts +246 -3
  4. package/dist/bodymap.d.ts +246 -3
  5. package/dist/bodymap.mjs +1911 -6
  6. package/dist/bodymap.mjs.map +1 -1
  7. package/dist/index.d.mts +31 -738
  8. package/dist/index.d.ts +31 -738
  9. package/dist/index.js +894 -1692
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +10287 -8203
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/pages-D7Jlssvp.d.ts +791 -0
  14. package/dist/pages-MO0JCySL.d.mts +791 -0
  15. package/dist/pages.d.mts +4 -0
  16. package/dist/pages.d.ts +4 -0
  17. package/dist/pages.js +6646 -0
  18. package/dist/pages.js.map +1 -0
  19. package/dist/pages.mjs +6626 -0
  20. package/dist/pages.mjs.map +1 -0
  21. package/dist/theme/index.d.mts +12 -12
  22. package/dist/theme/index.d.ts +12 -12
  23. package/dist/theme/index.js +30 -30
  24. package/dist/theme/index.js.map +1 -1
  25. package/dist/theme/index.mjs +1880 -2
  26. package/dist/theme/index.mjs.map +1 -1
  27. package/dist/theme/tokens-css.mjs +573 -1
  28. package/dist/theme/tokens-css.mjs.map +1 -1
  29. package/package.json +9 -7
  30. package/src/components/custom/Workout/BaseBadge.stories.tsx +3 -3
  31. package/src/components/custom/Workout/BaseBadge.test.tsx +1 -1
  32. package/src/components/custom/Workout/BaseBadge.tsx +1 -1
  33. package/src/components/custom/Workout/ExerciseCard.tsx +5 -1
  34. package/src/components/custom/Workout/ExerciseDetailPage.tsx +17 -4
  35. package/src/components/custom/Workout/IntensityBar.test.tsx +112 -60
  36. package/src/components/custom/Workout/IntensityBar.tsx +109 -58
  37. package/src/components/custom/Workout/PlaceholderStrip.tsx +5 -9
  38. package/src/components/custom/Workout/PrBadge.tsx +3 -3
  39. package/src/components/custom/Workout/PrHistoryModal.tsx +2 -2
  40. package/src/components/custom/Workout/README.md +50 -0
  41. package/src/components/custom/Workout/RestTimer.test.tsx +41 -10
  42. package/src/components/custom/Workout/RestTimer.tsx +48 -43
  43. package/src/components/custom/Workout/SetRow.tsx +10 -1
  44. package/src/components/custom/Workout/TempoDisplay.stories.tsx +69 -2
  45. package/src/components/custom/Workout/TempoDisplay.test.tsx +35 -0
  46. package/src/components/custom/Workout/TempoDisplay.tsx +104 -1
  47. package/src/components/custom/Workout/VelocityStrip.test.tsx +89 -2
  48. package/src/components/custom/Workout/VelocityStrip.tsx +203 -38
  49. package/src/components/custom/Workout/WeekRow.test.tsx +2 -2
  50. package/src/components/custom/Workout/WeightBadge.test.tsx +2 -2
  51. package/src/components/custom/Workout/WeightBadge.tsx +2 -2
  52. package/src/components/custom/Workout/icons.tsx +68 -0
  53. package/src/components/custom/Workout/index.ts +38 -47
  54. package/src/index.ts +3 -2
  55. package/src/pages.ts +61 -0
  56. package/src/test/stories-smoke.test.tsx +50 -0
  57. package/src/theme/barrel.ts +53 -0
  58. package/src/theme/index.ts +8 -43
  59. package/src/theme/native-theming-guard.test.ts +117 -0
  60. package/dist/bodymap-BhG55xHM.d.mts +0 -318
  61. package/dist/bodymap-BhG55xHM.d.ts +0 -318
  62. package/dist/chunk-2SISKTWM.mjs +0 -1027
  63. package/dist/chunk-2SISKTWM.mjs.map +0 -1
  64. package/dist/chunk-7XPB4NAO.mjs +0 -1013
  65. package/dist/chunk-7XPB4NAO.mjs.map +0 -1
  66. package/dist/chunk-SNVKL5WZ.mjs +0 -883
  67. package/dist/chunk-SNVKL5WZ.mjs.map +0 -1
@@ -0,0 +1,791 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ViewProps } from 'react-native';
3
+ import { c as MesoStatusCardProps } from './MesoStatusCard-CQk71hSi.mjs';
4
+
5
+ type PRType = 'e1rm' | 'weight' | 'reps' | 'volume' | 'velocity';
6
+ interface PrBadgeProps extends ViewProps {
7
+ /** PR type determines auto-generated label */
8
+ type?: PRType;
9
+ /** Display label (e.g., "PR e1RM", "PR 5RM"). Auto-generated from type if omitted. */
10
+ label?: string;
11
+ /** Show only the star icon, no text label */
12
+ compact?: boolean;
13
+ /** Trigger pop animation on mount */
14
+ animate?: boolean;
15
+ className?: string;
16
+ }
17
+ declare function PrBadge({ type, label: labelProp, compact, animate, className, ...props }: PrBadgeProps): react_jsx_runtime.JSX.Element;
18
+
19
+ /**
20
+ * Spec statuses: completed | current | upcoming | deload.
21
+ * `next` and `missed` are additive extras supported by this implementation.
22
+ */
23
+ type WorkoutPillStatus = 'completed' | 'current' | 'upcoming' | 'deload' | 'next' | 'missed';
24
+ interface WorkoutPillProps extends ViewProps {
25
+ name: string;
26
+ status: WorkoutPillStatus;
27
+ /** Force pulsing animation. Defaults to pulsing only on `current` status. */
28
+ pulse?: boolean;
29
+ onPress?: () => void;
30
+ highlighted?: boolean;
31
+ className?: string;
32
+ }
33
+ declare function WorkoutPill({ name, status, pulse, onPress, highlighted, className, ...props }: WorkoutPillProps): react_jsx_runtime.JSX.Element;
34
+
35
+ /**
36
+ * Structural velocity-zone band accepted from an upstream analytics source
37
+ * (e.g. workout-analytics' `VelocityZones.bands`).
38
+ *
39
+ * Deliberately a plain structural shape — titan never imports the analytics
40
+ * package (same policy as TempoBar's presentational phase key). Any object with
41
+ * this shape can be passed, so `zones={waVelocityZones.bands}` works directly.
42
+ * Bands are ordered slow → fast, contiguous, and cover `[0, ∞)` with the top
43
+ * band's `max === null`. Bands carry NO color — color is a UI concern resolved
44
+ * here via {@link zoneIdToScaleToken}.
45
+ */
46
+ interface VelocityZoneBandProp {
47
+ /** Stable zone identity (e.g. WA's `VelocityZoneId`). Drives color mapping. */
48
+ id: string;
49
+ /** Human-readable label shown in the summary row. */
50
+ label: string;
51
+ /** Inclusive lower bound (m/s mean concentric velocity). */
52
+ min: number;
53
+ /** Exclusive upper bound (m/s); `null` marks the open top band. */
54
+ max: number | null;
55
+ }
56
+ interface VelocityStripProps extends ViewProps {
57
+ /**
58
+ * Per-rep MEAN concentric velocity (m/s), one entry per rep (brain WA-D02).
59
+ * Drives both bar height and — in the default path — zone color. Callers must
60
+ * feed mean (not peak) concentric velocity so bar color, height, and the
61
+ * summary label all describe one metric.
62
+ */
63
+ velocities: number[];
64
+ /**
65
+ * Optional velocity-zone bands (shape-compatible with WA's
66
+ * `VelocityZones.bands`). When provided, bar color and the summary zone label
67
+ * come from these bands via {@link zoneIdToScaleToken}. When omitted, the
68
+ * built-in default scale (≥1.0 / ≥0.75 / ≥0.5) is used — identical to prior
69
+ * releases. Colors NEVER come from the bands themselves.
70
+ */
71
+ zones?: readonly VelocityZoneBandProp[];
72
+ /**
73
+ * Live mode: index of the most-recently-completed rep. That bar animates in
74
+ * with a "pop"; if it is also the current set peak (a new best), it "bounces"
75
+ * instead. Only the latest bar animates. Honors `prefers-reduced-motion`.
76
+ * Full variant only.
77
+ */
78
+ liveRepIndex?: number;
79
+ expanded?: boolean;
80
+ onToggle?: () => void;
81
+ onRepPress?: (index: number, velocity: number) => void;
82
+ variant?: 'full' | 'mini';
83
+ showInfo?: boolean;
84
+ className?: string;
85
+ }
86
+ declare function getVelocityZoneColor(velocity: number): string;
87
+ declare function getVelocityZoneName(velocity: number): string;
88
+ /**
89
+ * Velocity loss for a set, as a whole percentage. Uses the running-best rep as
90
+ * the reference (matching WA-02.05 / brain WA-D01): `(vBest − vLast) / vBest`,
91
+ * clamped to ≥ 0 so a set that ends on its best rep reports 0 loss.
92
+ */
93
+ declare function calculateVelocityLoss(velocities: number[]): number;
94
+ /** Arithmetic mean of the per-rep mean-concentric velocities. */
95
+ declare function calculateMeanVelocity(velocities: number[]): number;
96
+ declare function VelocityStrip({ velocities, zones, liveRepIndex, expanded, onToggle, onRepPress, variant, showInfo, className, ...props }: VelocityStripProps): react_jsx_runtime.JSX.Element;
97
+
98
+ type SetRowMode = 'active' | 'completed' | 'history';
99
+ interface SetRowProps {
100
+ mode: SetRowMode;
101
+ setNumber: number;
102
+ previous?: {
103
+ reps: number;
104
+ weight: number;
105
+ } | null;
106
+ reps: number | null;
107
+ weight: number | null;
108
+ rpe?: number | null;
109
+ unit: 'lbs' | 'kg';
110
+ /** Per-rep MEAN concentric velocities (m/s). */
111
+ velocities?: number[];
112
+ /** Optional velocity-zone bands (WA `VelocityZones.bands`); default scale when absent. */
113
+ velocityZones?: readonly VelocityZoneBandProp[];
114
+ /** Live mode: index of the newest rep bar to animate (pop / new-peak bounce). */
115
+ velocityLiveRepIndex?: number;
116
+ velocityExpanded?: boolean;
117
+ onVelocityToggle?: () => void;
118
+ setType?: string;
119
+ prBadges?: Array<{
120
+ type: PRType;
121
+ label: string;
122
+ }>;
123
+ isNextSet?: boolean;
124
+ targets?: {
125
+ reps: number;
126
+ weight: number;
127
+ };
128
+ }
129
+ declare function SetRow({ mode, setNumber, previous, reps, weight, rpe, unit, velocities, velocityZones, velocityLiveRepIndex, velocityExpanded, onVelocityToggle, setType, prBadges, isNextSet, targets, }: SetRowProps): react_jsx_runtime.JSX.Element;
130
+
131
+ type ExerciseCardState = 'collapsed' | 'expanded' | 'upcoming';
132
+ interface ExerciseCardProps {
133
+ name: string;
134
+ state: ExerciseCardState;
135
+ onToggle: () => void;
136
+ onNavigateDetail?: () => void;
137
+ summary?: {
138
+ sets: number;
139
+ reps: number | string;
140
+ weight: number;
141
+ unit: 'lbs' | 'kg';
142
+ };
143
+ e1rm?: {
144
+ value: number;
145
+ unit: 'lbs' | 'kg';
146
+ };
147
+ isPR?: boolean;
148
+ setVelocities?: number[][];
149
+ /** Optional velocity-zone bands shared across this exercise's sets (WA bands). */
150
+ velocityZones?: readonly VelocityZoneBandProp[];
151
+ totalPlannedSets?: number;
152
+ sets?: SetRowProps[];
153
+ tempo?: [number, number, number, number];
154
+ prescription?: string;
155
+ previousBest?: string;
156
+ supersetPosition?: 'first' | 'last' | 'middle' | null;
157
+ supersetColor?: string;
158
+ }
159
+ declare function ExerciseCard(props: ExerciseCardProps): react_jsx_runtime.JSX.Element;
160
+
161
+ type MesoStatus = 'completed' | 'current' | 'upcoming';
162
+ interface Meso {
163
+ id: string;
164
+ name: string;
165
+ weekCount: number;
166
+ status: MesoStatus;
167
+ /**
168
+ * 1-based week index of progress within a `current` meso.
169
+ * Drives the solid fill width. Ignored for non-current mesos.
170
+ */
171
+ currentWeek?: number;
172
+ }
173
+ interface MesoProgressBarProps extends ViewProps {
174
+ mesos: Meso[];
175
+ activeMesoId: string | null;
176
+ onMesoPress: (mesoId: string) => void;
177
+ className?: string;
178
+ }
179
+ /**
180
+ * Segmented horizontal bar of mesocycles. Each segment is tappable and its
181
+ * flex width is proportional to the meso's `weekCount`. The active segment is
182
+ * highlighted with a 2px brand border to stay in sync with MesoCard selection.
183
+ *
184
+ * @example
185
+ * <MesoProgressBar
186
+ * mesos={[
187
+ * { id: 'm1', name: 'Accumulation', weekCount: 4, status: 'completed' },
188
+ * { id: 'm2', name: 'Intensification', weekCount: 3, status: 'current', currentWeek: 2 },
189
+ * { id: 'm3', name: 'Peak', weekCount: 2, status: 'upcoming' },
190
+ * ]}
191
+ * activeMesoId="m2"
192
+ * onMesoPress={(id) => console.log(id)}
193
+ * />
194
+ */
195
+ declare function MesoProgressBar({ mesos, activeMesoId, onMesoPress, className, ...props }: MesoProgressBarProps): react_jsx_runtime.JSX.Element;
196
+
197
+ interface WeekRowWorkout {
198
+ name: string;
199
+ status: WorkoutPillStatus;
200
+ onPress?: () => void;
201
+ }
202
+ interface WeekRowProps extends ViewProps {
203
+ /** Week index within the mesocycle (1-based) */
204
+ weekNumber: number;
205
+ /** Total weeks in the mesocycle */
206
+ totalWeeks: number;
207
+ /** Workouts scheduled for this week */
208
+ workouts: WeekRowWorkout[];
209
+ /** Planned intensity for the week, 0-1 */
210
+ intensityLevel: number;
211
+ /** Optional MRV/overexertion threshold, 0-1 */
212
+ intensityThreshold?: number;
213
+ /** Highlights this row as the active week */
214
+ isCurrent?: boolean;
215
+ /** Renders this row as a deload week (purple tint, deload pills) */
216
+ isDeload?: boolean;
217
+ className?: string;
218
+ }
219
+ /**
220
+ * A single week within a mesocycle, showing the week number, its workout pills,
221
+ * and a right-aligned vertical intensity indicator.
222
+ *
223
+ * @example
224
+ * <WeekRow
225
+ * weekNumber={1}
226
+ * totalWeeks={4}
227
+ * workouts={[
228
+ * { name: 'Upper', status: 'completed' },
229
+ * { name: 'Lower', status: 'current' },
230
+ * ]}
231
+ * intensityLevel={0.55}
232
+ * intensityThreshold={0.8}
233
+ * isCurrent
234
+ * />
235
+ */
236
+ declare function WeekRow({ weekNumber, totalWeeks, workouts, intensityLevel, intensityThreshold, isCurrent, isDeload, className, ...props }: WeekRowProps): react_jsx_runtime.JSX.Element;
237
+
238
+ type WorkoutStatus = 'completed' | 'today' | 'upcoming';
239
+ /** Spec volume-status vocabulary; mapped to MuscleGroupChip's VolumeStatus internally. */
240
+ type WorkoutMuscleVolumeStatus = 'under' | 'maintenance' | 'productive' | 'over';
241
+ interface WorkoutMuscleGroup {
242
+ /** Muscle group identifier (free-form to match plan data). */
243
+ group: string;
244
+ /** Display label, may be abbreviated. */
245
+ label: string;
246
+ /** Volume status relative to landmarks. */
247
+ volumeStatus?: WorkoutMuscleVolumeStatus;
248
+ }
249
+ interface WorkoutCardProps extends ViewProps {
250
+ name: string;
251
+ date: string;
252
+ /** e.g. "45 min" */
253
+ duration?: string;
254
+ status: WorkoutStatus;
255
+ muscleGroups: WorkoutMuscleGroup[];
256
+ totalSets: number;
257
+ /** Total lbs/kg moved across the workout. */
258
+ totalVolume?: number;
259
+ unit: 'lbs' | 'kg';
260
+ exercises?: ExerciseCardProps[];
261
+ expanded?: boolean;
262
+ onToggle?: () => void;
263
+ className?: string;
264
+ }
265
+ /**
266
+ * A workout within a week: name, date, duration, muscle-group pills, and a
267
+ * stats summary. When expandable it renders contained ExerciseCards on expand.
268
+ * Status drives a colored left border, today adds a brand tint, and upcoming
269
+ * is dimmed.
270
+ *
271
+ * @example
272
+ * <WorkoutCard
273
+ * name="Upper A"
274
+ * date="Mon, Jun 23"
275
+ * duration="45 min"
276
+ * status="today"
277
+ * muscleGroups={[{ group: 'chest', label: 'Chest', volumeStatus: 'productive' }]}
278
+ * totalSets={18}
279
+ * totalVolume={12450}
280
+ * unit="lbs"
281
+ * expanded
282
+ * onToggle={() => {}}
283
+ * exercises={exercises}
284
+ * />
285
+ */
286
+ declare function WorkoutCard({ name, date, duration, status, muscleGroups, totalSets, totalVolume, unit, exercises, expanded, onToggle, className, ...props }: WorkoutCardProps): react_jsx_runtime.JSX.Element;
287
+
288
+ interface MesoVolumeHeatmapEntry {
289
+ /** Muscle group identifier (free-form to match plan data). */
290
+ group: string;
291
+ /** Planned volume intensity for the group, 0-100. */
292
+ percentage: number;
293
+ }
294
+ interface MesoCardProps extends ViewProps {
295
+ /** Mesocycle name, e.g. "Accumulation". */
296
+ name: string;
297
+ /** Training goal, e.g. "Hypertrophy", "Strength", "Peaking". */
298
+ goal: string;
299
+ /** Training split, e.g. "Upper/Lower", "Push/Pull/Legs". */
300
+ split: string;
301
+ /** Week range label, e.g. "Weeks 1-4". */
302
+ weekRange: string;
303
+ /** Current week within the mesocycle (1-based). */
304
+ currentWeek?: number;
305
+ /** Total weeks in the mesocycle. */
306
+ totalWeeks: number;
307
+ /** Weeks rendered as a WeekRow list when expanded. */
308
+ weeks: WeekRowProps[];
309
+ /** Whether the card is expanded to reveal the week list. */
310
+ expanded?: boolean;
311
+ /** Toggles expansion; makes the header a button when provided. */
312
+ onToggle?: () => void;
313
+ /** Whether this meso is highlighted in the MesoProgressBar. */
314
+ highlighted?: boolean;
315
+ /** Volume heatmap data per muscle group for the meso. */
316
+ volumeHeatmap?: MesoVolumeHeatmapEntry[];
317
+ className?: string;
318
+ }
319
+ /**
320
+ * A mesocycle card with name, goal, split, week range, an optional volume
321
+ * heatmap strip, and an expandable WeekRow list. Highlighting animates the
322
+ * border toward brand-primary with a subtle glow to stay in sync with the
323
+ * MesoProgressBar.
324
+ *
325
+ * @example
326
+ * <MesoCard
327
+ * name="Accumulation"
328
+ * goal="Hypertrophy"
329
+ * split="Upper/Lower"
330
+ * weekRange="Weeks 1-4"
331
+ * currentWeek={2}
332
+ * totalWeeks={4}
333
+ * weeks={weeks}
334
+ * volumeHeatmap={[{ group: 'chest', percentage: 80 }]}
335
+ * expanded
336
+ * onToggle={() => {}}
337
+ * highlighted
338
+ * />
339
+ */
340
+ declare function MesoCard({ name, goal, split, weekRange, currentWeek, totalWeeks, weeks, expanded, onToggle, highlighted, volumeHeatmap, className, ...props }: MesoCardProps): react_jsx_runtime.JSX.Element;
341
+
342
+ interface StrengthTrendDataPoint {
343
+ /** ISO date string for the session. */
344
+ date: string;
345
+ /** Estimated one-rep max in the chart's unit. */
346
+ e1rm: number;
347
+ /** Marks a personal-record session (rendered with a star). */
348
+ isPR?: boolean;
349
+ /** Optional human label shown in the tooltip instead of the raw date. */
350
+ sessionLabel?: string;
351
+ }
352
+ interface StrengthTrendChartMesoBoundary {
353
+ /** ISO date where the mesocycle boundary falls. */
354
+ date: string;
355
+ /** Short label, e.g. "Hypertrophy". */
356
+ label: string;
357
+ }
358
+ interface StrengthTrendChartProps extends ViewProps {
359
+ /** Actual e1RM data points, chronological. */
360
+ data: StrengthTrendDataPoint[];
361
+ /** Projected/planned e1RM trajectory, drawn as a dashed line. */
362
+ projection?: StrengthTrendDataPoint[];
363
+ /** Chart plot width in pixels. */
364
+ width: number;
365
+ /** Chart plot height in pixels (120-160 for the compact variant). */
366
+ height: number;
367
+ /** Mesocycle boundary markers (vertical guides + labels). */
368
+ mesoBoundaries?: StrengthTrendChartMesoBoundary[];
369
+ /** Called when an actual data point is tapped. */
370
+ onPointPress?: (point: StrengthTrendDataPoint) => void;
371
+ /** Unit for display and labels. */
372
+ unit: 'lbs' | 'kg';
373
+ /** Animate the left-to-right line draw on mount (default true). */
374
+ animateOnMount?: boolean;
375
+ className?: string;
376
+ }
377
+ /**
378
+ * Estimated-1RM line chart over time with an optional dashed plan-projection
379
+ * line and PR star markers. Rendered entirely with absolutely-positioned
380
+ * Views (no SVG), following the Sparkline approach. Tapping an actual point
381
+ * opens a tooltip and fires `onPointPress`. A trend pill below the chart
382
+ * summarizes the percentage change for the current mesocycle.
383
+ *
384
+ * @example
385
+ * <StrengthTrendChart
386
+ * data={[{ date: '2026-01-01', e1rm: 225 }, { date: '2026-02-01', e1rm: 245, isPR: true }]}
387
+ * projection={[{ date: '2026-02-01', e1rm: 245 }, { date: '2026-03-01', e1rm: 260 }]}
388
+ * width={320}
389
+ * height={150}
390
+ * unit="lbs"
391
+ * />
392
+ */
393
+ declare function StrengthTrendChart({ data, projection, width, height, mesoBoundaries, onPointPress, unit, animateOnMount, className, ...props }: StrengthTrendChartProps): react_jsx_runtime.JSX.Element;
394
+
395
+ type WorkoutDotStatus = 'within' | 'above' | 'below';
396
+ interface CapacityBandDataPoint {
397
+ /** ISO-ish date string (e.g. "2026-06-01"). */
398
+ date: string;
399
+ /** Lower bound of the capacity band (MEV equivalent). */
400
+ bandLow: number;
401
+ /** Upper bound of the capacity band (MRV equivalent). */
402
+ bandHigh: number;
403
+ }
404
+ interface WorkoutDot {
405
+ date: string;
406
+ /** Actual session load score. */
407
+ load: number;
408
+ /** Position of the session relative to the band. */
409
+ status: WorkoutDotStatus;
410
+ }
411
+ interface CapacityBandProjection {
412
+ /** Band shape over the next days if training continues (rises). */
413
+ withTraining: CapacityBandDataPoint[];
414
+ /** Band shape over the next days if resting (drops). */
415
+ withRest: CapacityBandDataPoint[];
416
+ }
417
+ interface CapacityBandChartProps extends ViewProps {
418
+ /** Capacity band shape over time. */
419
+ band: CapacityBandDataPoint[];
420
+ /** Workout sessions plotted as dots. */
421
+ workouts: WorkoutDot[];
422
+ /** Optional forward projection (next few days). */
423
+ projection?: CapacityBandProjection;
424
+ /** Chart width in px. */
425
+ width: number;
426
+ /** Chart height in px. */
427
+ height: number;
428
+ /** Called when a workout dot is tapped. */
429
+ onWorkoutPress?: (workout: WorkoutDot) => void;
430
+ className?: string;
431
+ }
432
+ /**
433
+ * Gentler-Streak-inspired fatigue visualization. Renders a shaded capacity
434
+ * band (between a rolling MEV/MRV estimate), workout sessions as colored dots
435
+ * positioned by load, and an optional dashed forward projection that diverges
436
+ * for "keep training" (rises) versus "rest" (drops). View-based, no SVG:
437
+ * the band fill is a run of vertical columns and the edges/projection are
438
+ * rotated line Views (same technique as Sparkline).
439
+ *
440
+ * @example
441
+ * <CapacityBandChart
442
+ * band={band}
443
+ * workouts={workouts}
444
+ * projection={{ withTraining, withRest }}
445
+ * width={320}
446
+ * height={180}
447
+ * onWorkoutPress={(w) => openSession(w)}
448
+ * />
449
+ */
450
+ declare function CapacityBandChart({ band, workouts, projection, width, height, onWorkoutPress, className, ...props }: CapacityBandChartProps): react_jsx_runtime.JSX.Element;
451
+
452
+ /** A single workout within a planned week, plus its exercise breakdown. */
453
+ interface PlanWorkout {
454
+ /** Stable identifier used for drill-down selection. */
455
+ id: string;
456
+ name: string;
457
+ date: string;
458
+ duration?: string;
459
+ status: WorkoutStatus;
460
+ muscleGroups: WorkoutMuscleGroup[];
461
+ totalSets: number;
462
+ totalVolume?: number;
463
+ unit: 'lbs' | 'kg';
464
+ /** Exercises revealed when the workout is opened. */
465
+ exercises: ExerciseCardProps[];
466
+ }
467
+ /** A training week inside a mesocycle. */
468
+ interface PlanWeek {
469
+ /** 1-based week index within the mesocycle. */
470
+ weekNumber: number;
471
+ /** Planned intensity for the week, 0-1. */
472
+ intensityLevel: number;
473
+ /** Optional MRV/overexertion threshold, 0-1. */
474
+ intensityThreshold?: number;
475
+ isDeload?: boolean;
476
+ isCurrent?: boolean;
477
+ workouts: PlanWorkout[];
478
+ }
479
+ /** A mesocycle: the top level of the drill-down. */
480
+ interface PlanMeso {
481
+ id: string;
482
+ name: string;
483
+ goal: string;
484
+ split: string;
485
+ weekRange: string;
486
+ weekCount: number;
487
+ currentWeek?: number;
488
+ status: MesoStatus;
489
+ volumeHeatmap?: MesoVolumeHeatmapEntry[];
490
+ weeks: PlanWeek[];
491
+ }
492
+ /** Current depth of the meso -> week -> workout drill-down. */
493
+ type ProgramNavLevel = 'meso' | 'week' | 'workout';
494
+ /** The navigation cursor within the active mesocycle. */
495
+ interface ProgramSelection {
496
+ weekNumber: number | null;
497
+ workoutId: string | null;
498
+ }
499
+ /** One tappable step in the breadcrumb trail. */
500
+ interface ProgramBreadcrumb {
501
+ key: string;
502
+ label: string;
503
+ level: ProgramNavLevel;
504
+ }
505
+ interface ProgramPlanningPageProps extends ViewProps {
506
+ /** Page heading. */
507
+ title?: string;
508
+ /** Mesocycles that make up the program. */
509
+ mesos: PlanMeso[];
510
+ className?: string;
511
+ }
512
+ /** Derive the current drill-down level from the selection cursor (pure). */
513
+ declare function deriveNavLevel(selection: ProgramSelection): ProgramNavLevel;
514
+ /** Project the mesocycles onto MesoProgressBar segment data (pure). */
515
+ declare function toProgressBarMesos(mesos: PlanMeso[]): Meso[];
516
+ /** Look up a meso by id, or null. */
517
+ declare function findMeso(mesos: PlanMeso[], id: string | null): PlanMeso | null;
518
+ /** Look up a week within a meso, or null. */
519
+ declare function findWeek(meso: PlanMeso | null, weekNumber: number | null): PlanWeek | null;
520
+ /** Look up a workout within a week, or null. */
521
+ declare function findWorkout(week: PlanWeek | null, workoutId: string | null): PlanWorkout | null;
522
+ /** Build the breadcrumb trail for the active drill path (pure). */
523
+ declare function buildBreadcrumbs(meso: PlanMeso, weekNumber: number | null, workout: PlanWorkout | null): ProgramBreadcrumb[];
524
+ /**
525
+ * ProgramPlanningPage — a meso -> week -> workout drill-down over an entire
526
+ * training program. A `MesoProgressBar` pins the mesocycle timeline to the top;
527
+ * the body swaps between three levels: expandable `MesoCard`s (with inline
528
+ * `WeekRow`s), a week's `WorkoutCard` list, and a single opened `WorkoutCard`
529
+ * with inline `ExerciseCard` expansion. A breadcrumb trail tracks and rewinds
530
+ * the drill path. Selection is page-level state; children keep their own styles.
531
+ *
532
+ * @example
533
+ * <ProgramPlanningPage mesos={mesos} />
534
+ */
535
+ declare function ProgramPlanningPage({ title, mesos, className, ...props }: ProgramPlanningPageProps): react_jsx_runtime.JSX.Element;
536
+
537
+ /** Which tab of the exercise detail view is showing. */
538
+ type ExerciseDetailTab = 'progress' | 'history' | 'advanced';
539
+ /** Tabs in display order. */
540
+ declare const EXERCISE_DETAIL_TABS: Array<{
541
+ key: ExerciseDetailTab;
542
+ label: string;
543
+ }>;
544
+ /** Identity + headline stats for the exercise being inspected. */
545
+ interface ExerciseDetailHeader {
546
+ /** Exercise name, e.g. "Barbell Bench Press". */
547
+ name: string;
548
+ /** Context line, e.g. "Chest · Barbell". */
549
+ subtitle: string;
550
+ /** Unit used across the page. */
551
+ unit: 'lbs' | 'kg';
552
+ /** Current estimated one-rep max, shown as a header pill. */
553
+ currentE1rm?: number;
554
+ }
555
+ /**
556
+ * A single expandable entry — one progression week or one logged session.
557
+ * Projected onto an `ExerciseCard`, so it collapses to a summary and expands
558
+ * to its `SetRow` breakdown.
559
+ */
560
+ interface ExerciseDetailEntry {
561
+ /** Stable id for inline-expansion selection. */
562
+ id: string;
563
+ /** Row title, e.g. "Week 3" or "Mon, Jun 16". */
564
+ title: string;
565
+ /** Collapsed summary (sets × reps @ weight). */
566
+ summary?: ExerciseCardProps['summary'];
567
+ /** Estimated one-rep max badge. */
568
+ e1rm?: ExerciseCardProps['e1rm'];
569
+ /** Flags a personal-record entry. */
570
+ isPR?: boolean;
571
+ /** Prescribed tempo, revealed when expanded. */
572
+ tempo?: ExerciseCardProps['tempo'];
573
+ /** Per-set breakdown revealed when expanded. */
574
+ sets: SetRowProps[];
575
+ }
576
+ /** Strength-trend inputs for the Progress tab chart. */
577
+ interface ExerciseTrend {
578
+ /** Measured e1RM data points, chronological. */
579
+ data: StrengthTrendDataPoint[];
580
+ /** Planned/projected trajectory (dashed line). */
581
+ projection?: StrengthTrendDataPoint[];
582
+ /** Mesocycle boundary guides. */
583
+ mesoBoundaries?: StrengthTrendChartMesoBoundary[];
584
+ }
585
+ /** One set's velocity trace for the Advanced tab breakdown. */
586
+ interface ExerciseVbtSet {
587
+ /** Row label, e.g. "Set 1". */
588
+ label: string;
589
+ /** Per-rep mean concentric velocities (m/s). */
590
+ velocities: number[];
591
+ }
592
+ /** Velocity-based-training inputs for the Advanced tab. */
593
+ interface ExerciseVbt {
594
+ /** Capacity band shape over time. */
595
+ band: CapacityBandDataPoint[];
596
+ /** Sessions plotted against the band. */
597
+ workouts: WorkoutDot[];
598
+ /** Optional forward band projection. */
599
+ projection?: CapacityBandProjection;
600
+ /** Per-set velocity traces for the most recent session. */
601
+ sets: ExerciseVbtSet[];
602
+ /** Optional velocity-zone bands for this exercise (WA `VelocityZones.bands`). */
603
+ zones?: readonly VelocityZoneBandProp[];
604
+ }
605
+ /** Page-level roll-up of the logged sessions. */
606
+ interface ExerciseDetailStats {
607
+ /** Number of logged sessions. */
608
+ sessions: number;
609
+ /** Best estimated one-rep max seen, or null if none recorded. */
610
+ bestE1rm: number | null;
611
+ /** Count of PR entries. */
612
+ prCount: number;
613
+ }
614
+ /** Aggregate velocity readout for the Advanced tab. */
615
+ interface VbtSummary {
616
+ /** Mean concentric velocity across every logged rep. */
617
+ meanVelocity: number;
618
+ /** Worst within-set velocity loss (%), i.e. the most-fatigued set. */
619
+ velocityLoss: number;
620
+ }
621
+ interface ExerciseDetailPageProps extends ViewProps {
622
+ /** Exercise identity + headline stats. */
623
+ exercise: ExerciseDetailHeader;
624
+ /** Mesocycle context banner (Progress tab). */
625
+ meso: MesoStatusCardProps;
626
+ /** Strength trend inputs (Progress tab). */
627
+ trend: ExerciseTrend;
628
+ /** Week-by-week progression entries (Progress tab). */
629
+ progression: ExerciseDetailEntry[];
630
+ /** All logged sessions (History tab). */
631
+ history: ExerciseDetailEntry[];
632
+ /** Velocity-based-training inputs (Advanced tab). */
633
+ vbt: ExerciseVbt;
634
+ className?: string;
635
+ }
636
+ /** Roll up logged sessions into the page summary (pure, testable). */
637
+ declare function deriveExerciseStats(entries: ExerciseDetailEntry[]): ExerciseDetailStats;
638
+ /** Project an entry onto ExerciseCard props, wiring inline expansion (pure). */
639
+ declare function toExerciseCardProps(entry: ExerciseDetailEntry, expanded: boolean, onToggle: () => void): ExerciseCardProps;
640
+ /** Aggregate per-set velocity traces into the Advanced readout (pure). */
641
+ declare function summarizeVbt(sets: ExerciseVbtSet[]): VbtSummary;
642
+ /**
643
+ * ExerciseDetailPage — a tabbed per-exercise view. A shared header (name,
644
+ * context line, current e1RM pill) sits above a Progress / History / Advanced
645
+ * tab bar. Progress composes the `MesoStatusCard` context banner, a stats
646
+ * strip, a `StrengthTrendChart`, and a week-by-week progression list with
647
+ * inline `ExerciseCard` expansion. History lists every logged session with the
648
+ * same inline expansion. Advanced surfaces VBT data via a `CapacityBandChart`,
649
+ * a velocity summary, and a per-set `VelocityStrip` breakdown. Tab and
650
+ * per-list expansion are page-level state; children keep their own styles.
651
+ *
652
+ * @example
653
+ * <ExerciseDetailPage
654
+ * exercise={exercise}
655
+ * meso={meso}
656
+ * trend={trend}
657
+ * progression={progression}
658
+ * history={history}
659
+ * vbt={vbt}
660
+ * />
661
+ */
662
+ declare function ExerciseDetailPage({ exercise, meso, trend, progression, history, vbt, className, ...props }: ExerciseDetailPageProps): react_jsx_runtime.JSX.Element;
663
+
664
+ /** Where an exercise sits in the during-workout flow. */
665
+ type ActiveExerciseStatus = 'completed' | 'active' | 'upcoming';
666
+ /**
667
+ * One exercise in the running workout. Projected onto an `ExerciseCard`, whose
668
+ * visual state is driven by `status` and the page's zoom focus: completed and
669
+ * active exercises collapse/expand, upcoming ones stay dimmed.
670
+ */
671
+ interface ActiveWorkoutExercise {
672
+ /** Stable id for zoom-focus selection. */
673
+ id: string;
674
+ /** Exercise name, e.g. "Barbell Bench Press". */
675
+ name: string;
676
+ /** Position in the workout flow. */
677
+ status: ActiveExerciseStatus;
678
+ /** Unit used for this exercise's loads. */
679
+ unit: 'lbs' | 'kg';
680
+ /** Total sets prescribed for this exercise. */
681
+ totalPlannedSets: number;
682
+ /** Collapsed summary (sets × reps @ weight) for a completed exercise. */
683
+ summary?: ExerciseCardProps['summary'];
684
+ /** Estimated one-rep max badge. */
685
+ e1rm?: ExerciseCardProps['e1rm'];
686
+ /** Flags a personal-record exercise. */
687
+ isPR?: boolean;
688
+ /** Prescribed tempo, revealed when expanded. */
689
+ tempo?: ExerciseCardProps['tempo'];
690
+ /** Per-rep velocities for each logged set (drives collapsed strips + set count). */
691
+ setVelocities?: number[][];
692
+ /** Per-set breakdown revealed when expanded. */
693
+ sets?: SetRowProps[];
694
+ /** One-line prescription for an upcoming exercise. */
695
+ prescription?: string;
696
+ /** Previous-best hint for an upcoming exercise. */
697
+ previousBest?: string;
698
+ /** Groups consecutive exercises under a shared `SupersetWrapper`. */
699
+ supersetId?: string;
700
+ }
701
+ /** A superset grouping consecutive exercises that share its id. */
702
+ interface ActiveWorkoutSuperset {
703
+ /** Matches `ActiveWorkoutExercise.supersetId`. */
704
+ id: string;
705
+ /** Corner badge label, e.g. "SS1". */
706
+ label: string;
707
+ /** Accent color for the wrapper rail + label. */
708
+ color?: string;
709
+ }
710
+ /** Live-set inputs shown in the bottom `InputBar`. */
711
+ interface ActiveWorkoutInput {
712
+ /** Current reps field value. */
713
+ reps: string;
714
+ /** Current weight field value. */
715
+ weight: string;
716
+ }
717
+ /** Rest-timer inputs shown in the bottom `RestTimer`. */
718
+ interface ActiveWorkoutRest {
719
+ /** Prescribed rest length, seconds. */
720
+ totalSeconds: number;
721
+ /** Elapsed rest, milliseconds. */
722
+ elapsedMs: number;
723
+ /** Next-set hint, e.g. "Set 3 · 8 × 195 lbs". */
724
+ nextSetInfo?: string;
725
+ }
726
+ /** Derived, page-level roll-up of workout progress. */
727
+ interface WorkoutProgress {
728
+ /** Number of exercises marked completed. */
729
+ completedExercises: number;
730
+ /** Total exercises in the workout. */
731
+ totalExercises: number;
732
+ /** Sets logged so far across all exercises. */
733
+ completedSets: number;
734
+ /** Total sets prescribed across all exercises. */
735
+ totalSets: number;
736
+ /** Fraction of prescribed sets logged, 0-1. */
737
+ fraction: number;
738
+ }
739
+ /** An ordered render group: a lone exercise or a superset of exercises. */
740
+ type WorkoutGroup = {
741
+ type: 'single';
742
+ exercise: ActiveWorkoutExercise;
743
+ } | {
744
+ type: 'superset';
745
+ superset: ActiveWorkoutSuperset;
746
+ exercises: ActiveWorkoutExercise[];
747
+ };
748
+ interface ActiveWorkoutPageProps extends ViewProps {
749
+ /** Workout name shown in the header. */
750
+ title?: string;
751
+ /** Context line under the title, e.g. "Push Day A · Week 6". */
752
+ subtitle?: string;
753
+ /** Exercises in flow order. */
754
+ exercises: ActiveWorkoutExercise[];
755
+ /** Superset groupings referenced by `exercise.supersetId`. */
756
+ supersets?: ActiveWorkoutSuperset[];
757
+ /** Seed reps/weight for the live `InputBar`. */
758
+ input?: ActiveWorkoutInput;
759
+ /** Rest-timer state; when resting, the timer replaces the input bar. */
760
+ rest?: ActiveWorkoutRest;
761
+ /** Start the page in the resting state (timer showing). */
762
+ initialResting?: boolean;
763
+ className?: string;
764
+ }
765
+ /** Count the sets logged for an exercise (pure). */
766
+ declare function countCompletedSets(exercise: ActiveWorkoutExercise): number;
767
+ /** Roll up the exercises into the header progress readout (pure, testable). */
768
+ declare function deriveWorkoutProgress(exercises: ActiveWorkoutExercise[]): WorkoutProgress;
769
+ /** The active exercise anchors the bottom bar; find it, or null (pure). */
770
+ declare function findActiveExercise(exercises: ActiveWorkoutExercise[]): ActiveWorkoutExercise | null;
771
+ /** Map an exercise's status + focus onto an `ExerciseCard` visual state (pure). */
772
+ declare function statusToCardState(status: ActiveExerciseStatus, focused: boolean): ExerciseCardState;
773
+ /** Project an exercise onto ExerciseCard props, wiring zoom focus (pure). */
774
+ declare function toActiveCardProps(exercise: ActiveWorkoutExercise, focused: boolean, onToggle: () => void, supersetPosition?: ExerciseCardProps['supersetPosition'], supersetColor?: string): ExerciseCardProps;
775
+ /** Fold consecutive same-superset exercises into render groups (pure). */
776
+ declare function groupExercises(exercises: ActiveWorkoutExercise[], supersets: ActiveWorkoutSuperset[]): WorkoutGroup[];
777
+ /**
778
+ * ActiveWorkoutPage — the during-workout screen, built as a zoom hierarchy over
779
+ * `ExerciseCard`s. A header progress bar rolls up completed sets; the flow list
780
+ * dims upcoming exercises, collapses finished ones to their velocity strips, and
781
+ * expands exactly one focused exercise to its `SetRow` breakdown. Consecutive
782
+ * superset exercises stitch together inside a `SupersetWrapper`. A pinned bottom
783
+ * bar toggles between the live `InputBar` (log the next set) and the `RestTimer`.
784
+ * Focus and rest are page-level state; children keep their own styles.
785
+ *
786
+ * @example
787
+ * <ActiveWorkoutPage title="Push Day A" exercises={exercises} />
788
+ */
789
+ declare function ActiveWorkoutPage({ title, subtitle, exercises, supersets, input, rest, initialResting, className, ...props }: ActiveWorkoutPageProps): react_jsx_runtime.JSX.Element;
790
+
791
+ export { type WorkoutCardProps as $, type ActiveExerciseStatus as A, type PlanWorkout as B, CapacityBandChart as C, PrBadge as D, ExerciseCard as E, type PrBadgeProps as F, type ProgramBreadcrumb as G, type ProgramNavLevel as H, type ProgramPlanningPageProps as I, type ProgramSelection as J, type SetRowMode as K, type SetRowProps as L, type Meso as M, StrengthTrendChart as N, type StrengthTrendChartMesoBoundary as O, type PRType as P, type StrengthTrendChartProps as Q, type StrengthTrendDataPoint as R, SetRow as S, VelocityStrip as T, type VelocityStripProps as U, type VbtSummary as V, type VelocityZoneBandProp as W, WeekRow as X, type WeekRowProps as Y, type WeekRowWorkout as Z, WorkoutCard as _, type ActiveWorkoutExercise as a, type WorkoutDot as a0, type WorkoutDotStatus as a1, type WorkoutGroup as a2, type WorkoutMuscleGroup as a3, type WorkoutMuscleVolumeStatus as a4, WorkoutPill as a5, type WorkoutPillProps as a6, type WorkoutPillStatus as a7, type WorkoutProgress as a8, type WorkoutStatus as a9, calculateMeanVelocity as aa, calculateVelocityLoss as ab, getVelocityZoneColor as ac, getVelocityZoneName as ad, ActiveWorkoutPage as ae, EXERCISE_DETAIL_TABS as af, ExerciseDetailPage as ag, ProgramPlanningPage as ah, buildBreadcrumbs as ai, countCompletedSets as aj, deriveExerciseStats as ak, deriveNavLevel as al, deriveWorkoutProgress as am, findActiveExercise as an, findMeso as ao, findWeek as ap, findWorkout as aq, groupExercises as ar, statusToCardState as as, summarizeVbt as at, toActiveCardProps as au, toExerciseCardProps as av, toProgressBarMesos as aw, type ActiveWorkoutInput as b, type ActiveWorkoutPageProps as c, type ActiveWorkoutRest as d, type ActiveWorkoutSuperset as e, type CapacityBandChartProps as f, type CapacityBandDataPoint as g, type CapacityBandProjection as h, type ExerciseCardProps as i, type ExerciseCardState as j, type ExerciseDetailEntry as k, type ExerciseDetailHeader as l, type ExerciseDetailPageProps as m, type ExerciseDetailStats as n, type ExerciseDetailTab as o, type ExerciseTrend as p, type ExerciseVbt as q, type ExerciseVbtSet as r, MesoCard as s, type MesoCardProps as t, MesoProgressBar as u, type MesoProgressBarProps as v, type MesoStatus as w, type MesoVolumeHeatmapEntry as x, type PlanMeso as y, type PlanWeek as z };