@titan-design/react-ui 0.12.1 → 0.13.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/README.md +46 -47
- package/dist/bodymap.js +768 -701
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +770 -703
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +1319 -216
- package/dist/index.d.ts +1319 -216
- package/dist/index.js +5996 -4498
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5773 -4357
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-CvsFqNtx.d.mts → pages-CpW3OB3g.d.mts} +21 -3
- package/dist/{pages-B1hqnGIC.d.ts → pages-_3Ajpiqu.d.ts} +21 -3
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +1435 -1428
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +1443 -1436
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +195 -134
- package/dist/theme/index.d.ts +195 -134
- package/dist/theme/index.js +215 -300
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +202 -296
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +62 -5
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +62 -5
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/dist/theme/tokens.d.mts +73 -5
- package/dist/theme/tokens.d.ts +73 -5
- package/dist/theme/tokens.js +55 -7
- package/dist/theme/tokens.js.map +1 -1
- package/dist/theme/tokens.mjs +55 -8
- package/dist/theme/tokens.mjs.map +1 -1
- package/docs/audits/2026-09-08-critique-addenda.md +51 -0
- package/docs/audits/2026-09-08-library-critique.md +212 -0
- package/docs/library-roadmap.md +99 -0
- package/package.json +1 -1
- package/src/arch/arch-graph.freshness.test.ts +52 -0
- package/src/arch/arch-graph.json +5021 -1617
- package/src/components/custom/ActiveWork/CoChangeChip.stories.tsx +44 -0
- package/src/components/custom/ActiveWork/CoChangeChip.test.tsx +29 -0
- package/src/components/custom/ActiveWork/CoChangeChip.tsx +48 -0
- package/src/components/custom/ActiveWork/Eyebrow.stories.tsx +25 -0
- package/src/components/custom/ActiveWork/Eyebrow.test.tsx +16 -0
- package/src/components/custom/ActiveWork/Eyebrow.tsx +26 -0
- package/src/components/custom/ActiveWork/FileActivityDetail.stories.tsx +50 -0
- package/src/components/custom/ActiveWork/FileActivityDetail.test.tsx +66 -0
- package/src/components/custom/ActiveWork/FileActivityDetail.tsx +202 -0
- package/src/components/custom/ActiveWork/FileActivityRow.stories.tsx +70 -0
- package/src/components/custom/ActiveWork/FileActivityRow.test.tsx +67 -0
- package/src/components/custom/ActiveWork/FileActivityRow.tsx +130 -0
- package/src/components/custom/ActiveWork/FileHistoryExplorer.stories.tsx +84 -0
- package/src/components/custom/ActiveWork/FileHistoryExplorer.test.tsx +99 -0
- package/src/components/custom/ActiveWork/FileHistoryExplorer.tsx +138 -0
- package/src/components/custom/ActiveWork/FilePathLabel.stories.tsx +39 -0
- package/src/components/custom/ActiveWork/FilePathLabel.test.tsx +53 -0
- package/src/components/custom/ActiveWork/FilePathLabel.tsx +63 -0
- package/src/components/custom/ActiveWork/InitiativeBrief.stories.tsx +50 -0
- package/src/components/custom/ActiveWork/InitiativeBrief.test.tsx +62 -0
- package/src/components/custom/ActiveWork/InitiativeBrief.tsx +181 -0
- package/src/components/custom/ActiveWork/InitiativeCard.stories.tsx +73 -0
- package/src/components/custom/ActiveWork/InitiativeCard.test.tsx +68 -0
- package/src/components/custom/ActiveWork/InitiativeCard.tsx +136 -0
- package/src/components/custom/ActiveWork/InitiativeHeader.stories.tsx +59 -0
- package/src/components/custom/ActiveWork/InitiativeHeader.test.tsx +40 -0
- package/src/components/custom/ActiveWork/InitiativeHeader.tsx +73 -0
- package/src/components/custom/ActiveWork/InitiativeReader.stories.tsx +145 -0
- package/src/components/custom/ActiveWork/OpenLoops.stories.tsx +58 -0
- package/src/components/custom/ActiveWork/OpenLoops.test.tsx +61 -0
- package/src/components/custom/ActiveWork/OpenLoops.tsx +107 -0
- package/src/components/custom/ActiveWork/PortfolioOverview.stories.tsx +101 -0
- package/src/components/custom/ActiveWork/PortfolioOverview.test.tsx +81 -0
- package/src/components/custom/ActiveWork/PortfolioOverview.tsx +83 -0
- package/src/components/custom/ActiveWork/README.md +305 -0
- package/src/components/custom/ActiveWork/SessionDetail.stories.tsx +71 -0
- package/src/components/custom/ActiveWork/SessionDetail.test.tsx +84 -0
- package/src/components/custom/ActiveWork/SessionDetail.tsx +180 -0
- package/src/components/custom/ActiveWork/SessionList.stories.tsx +69 -0
- package/src/components/custom/ActiveWork/SessionList.test.tsx +78 -0
- package/src/components/custom/ActiveWork/SessionList.tsx +103 -0
- package/src/components/custom/ActiveWork/SessionListItem.stories.tsx +64 -0
- package/src/components/custom/ActiveWork/SessionListItem.test.tsx +62 -0
- package/src/components/custom/ActiveWork/SessionListItem.tsx +152 -0
- package/src/components/custom/ActiveWork/SessionReader.stories.tsx +113 -0
- package/src/components/custom/ActiveWork/SeverityLabel.stories.tsx +51 -0
- package/src/components/custom/ActiveWork/SeverityLabel.test.tsx +68 -0
- package/src/components/custom/ActiveWork/SeverityLabel.tsx +97 -0
- package/src/components/custom/ActiveWork/TaskRow.stories.tsx +63 -0
- package/src/components/custom/ActiveWork/TaskRow.test.tsx +112 -0
- package/src/components/custom/ActiveWork/TaskRow.tsx +176 -0
- package/src/components/custom/ActiveWork/TaskTable.stories.tsx +123 -0
- package/src/components/custom/ActiveWork/TaskTable.test.tsx +233 -0
- package/src/components/custom/ActiveWork/TaskTable.tsx +321 -0
- package/src/components/custom/ActiveWork/active-work-depth.test.tsx +102 -0
- package/src/components/custom/ActiveWork/file-history-fixture.ts +91 -0
- package/src/components/custom/ActiveWork/format-time.test.ts +23 -0
- package/src/components/custom/ActiveWork/format-time.ts +32 -0
- package/src/components/custom/ActiveWork/index.ts +19 -0
- package/src/components/custom/ActiveWork/initiative-fixture.ts +104 -0
- package/src/components/custom/ActiveWork/session-fixture.ts +267 -0
- package/src/components/custom/ActiveWork/session-linkers.ts +56 -0
- package/src/components/custom/ActiveWork/task-list-fixture.ts +97 -0
- package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +4 -3
- package/src/components/custom/DateTime/DateTime.stories.tsx +2 -1
- package/src/components/custom/DateTime/DateTime.test.tsx +3 -1
- package/src/components/custom/EmptyState/EmptyState.stories.tsx +22 -9
- package/src/components/custom/EmptyState/EmptyState.test.tsx +7 -13
- package/src/components/custom/EmptyState/EmptyState.tsx +4 -18
- package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +4 -3
- package/src/components/custom/Fatigue/FatigueLights.stories.tsx +2 -2
- package/src/components/custom/Fatigue/GhostSpark.stories.tsx +4 -3
- package/src/components/custom/Fatigue/LiveFatigueCard.stories.tsx +2 -2
- package/src/components/custom/Fatigue/LiveFatigueCard.tsx +8 -4
- package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +116 -6
- package/src/components/custom/Fatigue/LiveFatiguePanel.test.tsx +134 -0
- package/src/components/custom/Fatigue/LiveFatiguePanel.tsx +54 -83
- package/src/components/custom/Fatigue/README.md +40 -3
- package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +2 -2
- package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +2 -1
- package/src/components/custom/Fatigue/VelocityHero.stories.tsx +4 -3
- package/src/components/custom/Fatigue/VerdictHero.stories.tsx +2 -2
- package/src/components/custom/Fatigue/fatigue-mock.test.ts +0 -5
- package/src/components/custom/Fatigue/fatigue-mock.ts +2 -10
- package/src/components/custom/Fatigue/fatigue-tokens.ts +12 -0
- package/src/components/custom/Fatigue/index.ts +17 -5
- package/src/components/custom/Fatigue/panel-layout.test.ts +149 -0
- package/src/components/custom/Fatigue/panel-layout.ts +185 -0
- package/src/components/custom/Gauge/Gauge.stories.tsx +2 -2
- package/src/components/custom/Gauge/Gauge.test.tsx +1 -1
- package/src/components/custom/Gauge/Gauge.tsx +4 -1
- package/src/components/custom/Metric/Metric.stories.tsx +10 -1
- package/src/components/custom/Metric/Metric.test.tsx +1 -3
- package/src/components/custom/Metric/Metric.tsx +5 -20
- package/src/components/custom/Prose/MarkdownProse.stories.tsx +88 -0
- package/src/components/custom/Prose/MarkdownProse.test.tsx +87 -0
- package/src/components/custom/Prose/MarkdownProse.tsx +201 -0
- package/src/components/custom/Prose/README.md +58 -0
- package/src/components/custom/Prose/index.ts +1 -0
- package/src/components/custom/Scatter/Scatter.stories.tsx +46 -8
- package/src/components/custom/Scatter/Scatter.test.tsx +2 -2
- package/src/components/custom/Scatter/Scatter.tsx +116 -14
- package/src/components/custom/Sidebar/Sidebar.stories.tsx +40 -7
- package/src/components/custom/Sidebar/Sidebar.test.tsx +5 -1
- package/src/components/custom/Sidebar/Sidebar.tsx +16 -33
- package/src/components/custom/Table/Table.stories.tsx +98 -15
- package/src/components/custom/Table/Table.test.tsx +273 -86
- package/src/components/custom/Table/Table.tsx +159 -67
- package/src/components/custom/Table/column-fit.test.ts +75 -0
- package/src/components/custom/Table/column-fit.ts +116 -0
- package/src/components/custom/Table/index.ts +9 -0
- package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +8 -6
- package/src/components/custom/Treemap/Treemap.stories.tsx +2 -2
- package/src/components/custom/Treemap/Treemap.test.tsx +1 -1
- package/src/components/custom/Treemap/Treemap.tsx +7 -2
- package/src/components/custom/Typography/Typography.stories.tsx +28 -1
- package/src/components/custom/Typography/Typography.test.tsx +34 -0
- package/src/components/custom/Typography/Typography.tsx +6 -0
- package/src/components/custom/Typography/index.ts +1 -8
- package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +9 -1
- package/src/components/custom/Workout/BaseBadge.stories.tsx +19 -7
- package/src/components/custom/Workout/BaseBadge.test.tsx +31 -11
- package/src/components/custom/Workout/BaseBadge.tsx +3 -0
- package/src/components/custom/Workout/BodyMap.stories.tsx +4 -1
- package/src/components/custom/Workout/BodyMap.tsx +3 -1
- package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +16 -4
- package/src/components/custom/Workout/BodyMapDetailPanel.test.tsx +11 -15
- package/src/components/custom/Workout/BodyMapDetailPanel.tsx +45 -9
- package/src/components/custom/Workout/CapacityBandChart.stories.tsx +10 -8
- package/src/components/custom/Workout/CapacityBandChart.test.tsx +24 -51
- package/src/components/custom/Workout/CapacityBandChart.tsx +139 -108
- package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
- package/src/components/custom/Workout/DeviationBar.test.tsx +5 -2
- package/src/components/custom/Workout/DeviationBar.tsx +21 -16
- package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +182 -0
- package/src/components/custom/Workout/DualVelocityStrip.test.tsx +3 -2
- package/src/components/custom/Workout/ExerciseCard.stories.tsx +8 -7
- package/src/components/custom/Workout/ExerciseCard.test.tsx +13 -3
- package/src/components/custom/Workout/ExerciseCard.tsx +42 -51
- package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +7 -7
- package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +9 -1
- package/src/components/custom/Workout/ExerciseHeading.stories.tsx +8 -8
- package/src/components/custom/Workout/ExerciseHeading.tsx +8 -5
- package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +6 -4
- package/src/components/custom/Workout/ExerciseIndicator.test.tsx +27 -11
- package/src/components/custom/Workout/ExerciseIndicator.tsx +14 -15
- package/src/components/custom/Workout/FatigueMeter.stories.tsx +5 -5
- package/src/components/custom/Workout/InputBar.stories.tsx +4 -5
- package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
- package/src/components/custom/Workout/IntensityBar.test.tsx +5 -1
- package/src/components/custom/Workout/IntensityBar.tsx +4 -2
- package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +5 -4
- package/src/components/custom/Workout/MesoCard.stories.tsx +4 -1
- package/src/components/custom/Workout/MesoCard.test.tsx +7 -18
- package/src/components/custom/Workout/MesoCard.tsx +12 -9
- package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
- package/src/components/custom/Workout/MesoProgressBar.test.tsx +18 -11
- package/src/components/custom/Workout/MesoProgressBar.tsx +15 -13
- package/src/components/custom/Workout/MesoStatusCard.stories.tsx +2 -1
- package/src/components/custom/Workout/MesoStatusCard.test.tsx +14 -44
- package/src/components/custom/Workout/MesoStatusCard.tsx +9 -23
- package/src/components/custom/Workout/MetricTiles.stories.tsx +3 -2
- package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +2 -8
- package/src/components/custom/Workout/MuscleGroupChip.test.tsx +23 -31
- package/src/components/custom/Workout/MuscleGroupChip.tsx +28 -67
- package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +1 -1
- package/src/components/custom/Workout/PlaceholderStrip.test.tsx +17 -10
- package/src/components/custom/Workout/PlaceholderStrip.tsx +9 -8
- package/src/components/custom/Workout/PrBadge.stories.tsx +2 -1
- package/src/components/custom/Workout/PrBadge.test.tsx +3 -9
- package/src/components/custom/Workout/PrBadge.tsx +13 -17
- package/src/components/custom/Workout/PrHistoryModal.stories.tsx +4 -1
- package/src/components/custom/Workout/PrHistoryModal.test.tsx +13 -36
- package/src/components/custom/Workout/PrHistoryModal.tsx +8 -4
- package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +6 -1
- package/src/components/custom/Workout/README.md +11 -6
- package/src/components/custom/Workout/ReadinessCheck.stories.tsx +3 -2
- package/src/components/custom/Workout/ReadinessCheck.test.tsx +12 -18
- package/src/components/custom/Workout/ReadinessCheck.tsx +19 -3
- package/src/components/custom/Workout/RestTimer.stories.tsx +3 -2
- package/src/components/custom/Workout/ScheduleTiles.stories.tsx +3 -2
- package/src/components/custom/Workout/ScheduleTiles.test.tsx +3 -4
- package/src/components/custom/Workout/ScheduleTiles.tsx +4 -7
- package/src/components/custom/Workout/SegmentedBar.stories.tsx +5 -5
- package/src/components/custom/Workout/SegmentedBar.tsx +6 -2
- package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +5 -5
- package/src/components/custom/Workout/SessionHeader.stories.tsx +6 -6
- package/src/components/custom/Workout/SessionHeader.tsx +12 -12
- package/src/components/custom/Workout/SessionRail.stories.tsx +12 -11
- package/src/components/custom/Workout/SessionRail.tsx +7 -9
- package/src/components/custom/Workout/SetBar.stories.tsx +10 -6
- package/src/components/custom/Workout/SetBar.test.tsx +49 -4
- package/src/components/custom/Workout/SetBar.tsx +38 -12
- package/src/components/custom/Workout/SetRow.stories.tsx +3 -2
- package/src/components/custom/Workout/SetRow.test.tsx +16 -2
- package/src/components/custom/Workout/SetRow.tsx +61 -23
- package/src/components/custom/Workout/SetStrip.stories.tsx +60 -5
- package/src/components/custom/Workout/SetStrip.test.tsx +25 -0
- package/src/components/custom/Workout/SetStrip.tsx +24 -4
- package/src/components/custom/Workout/SetTableHeader.stories.tsx +6 -6
- package/src/components/custom/Workout/SetTableHeader.tsx +9 -12
- package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +7 -7
- package/src/components/custom/Workout/SetsRepsLoad.tsx +3 -5
- package/src/components/custom/Workout/Sparkline.stories.tsx +1 -1
- package/src/components/custom/Workout/Sparkline.test.tsx +37 -19
- package/src/components/custom/Workout/Sparkline.tsx +14 -18
- package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusDot.test.tsx +44 -5
- package/src/components/custom/Workout/StatusDot.tsx +93 -63
- package/src/components/custom/Workout/StatusPill.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusPill.test.tsx +6 -15
- package/src/components/custom/Workout/StatusPill.tsx +27 -38
- package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +9 -3
- package/src/components/custom/Workout/StrengthTrendChart.test.tsx +15 -37
- package/src/components/custom/Workout/StrengthTrendChart.tsx +31 -30
- package/src/components/custom/Workout/SupersetWrapper.stories.tsx +27 -64
- package/src/components/custom/Workout/SupersetWrapper.test.tsx +16 -14
- package/src/components/custom/Workout/SupersetWrapper.tsx +17 -21
- package/src/components/custom/Workout/TempoDisplay.stories.tsx +22 -7
- package/src/components/custom/Workout/TempoDisplay.test.tsx +18 -4
- package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +9 -1
- package/src/components/custom/Workout/VelocityStrip.compact.stories.tsx +4 -1
- package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +7 -4
- package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +4 -1
- package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +4 -1
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +12 -6
- package/src/components/custom/Workout/VelocityStrip.tsx +3 -0
- package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +6 -7
- package/src/components/custom/Workout/VolumeLandmarkBar.tsx +15 -6
- package/src/components/custom/Workout/WeekRow.stories.tsx +4 -1
- package/src/components/custom/Workout/WeekRow.test.tsx +18 -15
- package/src/components/custom/Workout/WeekRow.tsx +23 -16
- package/src/components/custom/Workout/WeightBadge.stories.tsx +2 -1
- package/src/components/custom/Workout/WeightBadge.test.tsx +50 -19
- package/src/components/custom/Workout/WeightBadge.tsx +28 -27
- package/src/components/custom/Workout/WorkoutCard.stories.tsx +6 -1
- package/src/components/custom/Workout/WorkoutCard.test.tsx +18 -0
- package/src/components/custom/Workout/WorkoutCard.tsx +35 -57
- package/src/components/custom/Workout/WorkoutPill.stories.tsx +5 -2
- package/src/components/custom/Workout/WorkoutPill.test.tsx +39 -7
- package/src/components/custom/Workout/WorkoutPill.tsx +88 -57
- package/src/components/custom/Workout/ZoneTrack.stories.tsx +7 -6
- package/src/components/custom/Workout/ZoneTrack.tsx +10 -9
- package/src/components/custom/Workout/index.ts +5 -0
- package/src/components/custom/Workout/metricText.stories.tsx +6 -5
- package/src/components/custom/Workout/metricText.tsx +4 -0
- package/src/components/custom/Workout/setHeadingKit.tsx +207 -26
- package/src/components/custom/charts/README.md +61 -0
- package/src/components/custom/charts/SetBarChart.test.tsx +30 -1
- package/src/components/custom/charts/SetBarChart.tsx +42 -4
- package/src/components/custom/charts/SparkBars.stories.tsx +55 -0
- package/src/components/custom/charts/SparkBars.test.tsx +69 -0
- package/src/components/custom/charts/SparkBars.tsx +85 -0
- package/src/components/custom/charts/index.ts +3 -0
- package/src/components/custom/index.ts +3 -0
- package/src/components/custom/stepper/Stepper.stories.tsx +13 -10
- package/src/components/custom/stepper/Stepper.test.tsx +1 -7
- package/src/components/custom/stepper/Stepper.tsx +13 -28
- package/src/components/icons/icons.stories.tsx +1 -1
- package/src/components/icons/icons.tsx +51 -0
- package/src/components/shell/AppShell.stories.tsx +150 -0
- package/src/components/shell/AppShell.test.tsx +70 -0
- package/src/components/shell/AppShell.tsx +90 -0
- package/src/components/shell/BrandLockup.stories.tsx +66 -5
- package/src/components/shell/BrandLockup.test.tsx +34 -1
- package/src/components/shell/BrandLockup.tsx +27 -11
- package/src/components/shell/NavItem.stories.tsx +4 -4
- package/src/components/shell/NavItem.test.tsx +17 -0
- package/src/components/shell/NavItem.tsx +17 -5
- package/src/components/shell/README.md +89 -26
- package/src/components/shell/SideNav.stories.tsx +38 -7
- package/src/components/shell/SideNav.test.tsx +16 -8
- package/src/components/shell/SideNav.tsx +17 -17
- package/src/components/shell/TopBar.stories.tsx +44 -19
- package/src/components/shell/TopBar.test.tsx +34 -12
- package/src/components/shell/TopBar.tsx +61 -40
- package/src/components/shell/brands.tsx +66 -0
- package/src/components/shell/index.ts +9 -8
- package/src/components/shell/{DeviceIndicator.stories.tsx → workout/DeviceIndicator.stories.tsx} +2 -2
- package/src/components/shell/{DeviceIndicator.tsx → workout/DeviceIndicator.tsx} +2 -2
- package/src/components/shell/{DeviceMenu.stories.tsx → workout/DeviceMenu.stories.tsx} +2 -2
- package/src/components/shell/{DeviceMenu.tsx → workout/DeviceMenu.tsx} +3 -3
- package/src/components/shell/{DeviceRow.stories.tsx → workout/DeviceRow.stories.tsx} +2 -2
- package/src/components/shell/{DeviceRow.tsx → workout/DeviceRow.tsx} +3 -3
- package/src/components/shell/{SessionStatePill.stories.tsx → workout/SessionStatePill.stories.tsx} +2 -2
- package/src/components/shell/{SessionStatePill.tsx → workout/SessionStatePill.tsx} +5 -3
- package/src/components/shell/workout/WorkoutShell.stories.tsx +71 -0
- package/src/components/shell/{DashboardShell.test.tsx → workout/WorkoutShell.test.tsx} +19 -8
- package/src/components/shell/workout/WorkoutShell.tsx +77 -0
- package/src/components/shell/workout/WorkoutTopBar.stories.tsx +58 -0
- package/src/components/shell/workout/WorkoutTopBar.test.tsx +39 -0
- package/src/components/shell/workout/WorkoutTopBar.tsx +52 -0
- package/src/components/shell/workout/index.ts +9 -0
- package/src/components/shell/workout/workoutNavItems.tsx +16 -0
- package/src/components/ui/README.md +110 -0
- package/src/components/ui/alert/Alert.stories.tsx +1 -1
- package/src/components/ui/autocomplete/Autocomplete.stories.tsx +11 -13
- package/src/components/ui/autocomplete/Autocomplete.test.tsx +7 -25
- package/src/components/ui/autocomplete/Autocomplete.tsx +21 -28
- package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
- package/src/components/ui/avatar/Avatar.tsx +2 -10
- package/src/components/ui/badge/Badge.stories.tsx +83 -28
- package/src/components/ui/badge/Badge.test.tsx +19 -3
- package/src/components/ui/badge/Badge.tsx +48 -55
- package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
- package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +4 -16
- package/src/components/ui/button/Button.stories.tsx +46 -12
- package/src/components/ui/button/Button.tsx +32 -25
- package/src/components/ui/card/Card.stories.tsx +90 -94
- package/src/components/ui/card/Card.test.tsx +80 -6
- package/src/components/ui/card/Card.tsx +150 -203
- package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
- package/src/components/ui/checkbox/Checkbox.tsx +6 -26
- package/src/components/ui/chip/Chip.stories.tsx +37 -13
- package/src/components/ui/chip/Chip.test.tsx +10 -6
- package/src/components/ui/chip/Chip.tsx +76 -91
- package/src/components/ui/collapse/Collapse.stories.tsx +23 -23
- package/src/components/ui/collapse/Collapse.tsx +6 -11
- package/src/components/ui/data-row/DataRow.stories.tsx +9 -2
- package/src/components/ui/data-row/DataRow.test.tsx +5 -15
- package/src/components/ui/data-row/DataRow.tsx +18 -7
- package/src/components/ui/divider/Divider.stories.tsx +1 -1
- package/src/components/ui/divider/Divider.tsx +1 -5
- package/src/components/ui/drawer/Drawer.stories.tsx +5 -16
- package/src/components/ui/drawer/Drawer.tsx +13 -30
- package/src/components/ui/floating-overlay-depth.test.tsx +143 -0
- package/src/components/ui/form-field/FormField.stories.tsx +6 -20
- package/src/components/ui/form-field/FormField.test.tsx +1 -5
- package/src/components/ui/form-field/FormField.tsx +10 -46
- package/src/components/ui/help-tip/HelpTip.stories.tsx +8 -19
- package/src/components/ui/help-tip/HelpTip.test.tsx +1 -3
- package/src/components/ui/help-tip/HelpTip.tsx +21 -22
- package/src/components/ui/help-tip/index.ts +7 -1
- package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
- package/src/components/ui/icon-box/IconBox.test.tsx +1 -7
- package/src/components/ui/icon-box/IconBox.tsx +2 -12
- package/src/components/ui/indicator/Indicator.stories.tsx +1 -1
- package/src/components/ui/indicator/Indicator.tsx +20 -14
- package/src/components/ui/input/Input.stories.tsx +3 -11
- package/src/components/ui/input/Input.test.tsx +2 -10
- package/src/components/ui/input/Input.tsx +10 -25
- package/src/components/ui/input/index.ts +7 -1
- package/src/components/ui/link/Link.stories.tsx +19 -7
- package/src/components/ui/list-item/ListItem.stories.tsx +11 -2
- package/src/components/ui/list-item/ListItem.tsx +2 -9
- package/src/components/ui/menu/Menu.stories.tsx +21 -15
- package/src/components/ui/menu/Menu.test.tsx +2 -11
- package/src/components/ui/menu/Menu.tsx +12 -14
- package/src/components/ui/menu/index.ts +8 -1
- package/src/components/ui/modal/Modal.stories.tsx +2 -2
- package/src/components/ui/modal/Modal.tsx +29 -19
- package/src/components/ui/pill/Pill.stories.tsx +58 -23
- package/src/components/ui/pill/Pill.test.tsx +101 -3
- package/src/components/ui/pill/Pill.tsx +164 -49
- package/src/components/ui/pill/index.ts +1 -1
- package/src/components/ui/popover/Popover.stories.tsx +5 -7
- package/src/components/ui/popover/Popover.test.tsx +1 -6
- package/src/components/ui/popover/Popover.tsx +20 -15
- package/src/components/ui/popover/index.ts +7 -1
- package/src/components/ui/progress/Progress.stories.tsx +3 -12
- package/src/components/ui/progress/Progress.test.tsx +1 -5
- package/src/components/ui/radio/Radio.stories.tsx +42 -21
- package/src/components/ui/radio/Radio.test.tsx +6 -2
- package/src/components/ui/radio/Radio.tsx +23 -22
- package/src/components/ui/section/Section.stories.tsx +5 -14
- package/src/components/ui/section/Section.test.tsx +1 -6
- package/src/components/ui/section/Section.tsx +1 -3
- package/src/components/ui/section/index.ts +1 -5
- package/src/components/ui/select/Select.stories.tsx +3 -13
- package/src/components/ui/select/Select.test.tsx +2 -11
- package/src/components/ui/select/Select.tsx +14 -14
- package/src/components/ui/skeleton/Skeleton.stories.tsx +3 -11
- package/src/components/ui/skeleton/Skeleton.tsx +3 -14
- package/src/components/ui/skeleton/index.ts +1 -7
- package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
- package/src/components/ui/spinner/Spinner.tsx +1 -4
- package/src/components/ui/stack/Stack.stories.tsx +1 -1
- package/src/components/ui/stack/Stack.tsx +1 -1
- package/src/components/ui/surface/Surface.stories.tsx +17 -5
- package/src/components/ui/surface/Surface.test.tsx +88 -28
- package/src/components/ui/surface/Surface.tsx +37 -87
- package/src/components/ui/surface/SurfaceContext.ts +22 -62
- package/src/components/ui/surface/index.ts +8 -0
- package/src/components/ui/surface/resolveSurface.ts +98 -0
- package/src/components/ui/surface/surface.contract.test.ts +6 -1
- package/src/components/ui/switch/Switch.stories.tsx +1 -1
- package/src/components/ui/switch/Switch.tsx +7 -6
- package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
- package/src/components/ui/tabs/Tabs.tsx +7 -22
- package/src/components/ui/tabs/index.ts +1 -7
- package/src/components/ui/tile/Tile.stories.tsx +1 -1
- package/src/components/ui/tile/Tile.tsx +3 -0
- package/src/components/ui/toast/Toast.stories.tsx +1 -1
- package/src/components/ui/toast/Toast.test.tsx +2 -8
- package/src/components/ui/toast/Toast.tsx +43 -53
- package/src/components/ui/toast/index.ts +0 -1
- package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +92 -71
- package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +3 -18
- package/src/components/ui/toolbar-button/ToolbarButton.tsx +25 -43
- package/src/components/ui/tooltip/Tooltip.stories.tsx +12 -3
- package/src/components/ui/tooltip/Tooltip.test.tsx +21 -3
- package/src/components/ui/tooltip/Tooltip.tsx +55 -36
- package/src/components/ui/tooltip/index.ts +1 -1
- package/src/components/ui/trigger/TriggerSurface.test.tsx +215 -0
- package/src/components/ui/trigger/TriggerSurface.tsx +81 -0
- package/src/components/ui/trigger/index.ts +2 -0
- package/src/stories/PresetShowcase.stories.tsx +100 -47
- package/src/test/no-device-internals.test.ts +3 -3
- package/src/theme/ChoosingTokens.stories.tsx +48 -0
- package/src/theme/ColorPalettes.stories.tsx +62 -45
- package/src/theme/ColorPrimitives.stories.tsx +35 -35
- package/src/theme/Depth.stories.tsx +132 -65
- package/src/theme/DepthCalibration.stories.tsx +23 -21
- package/src/theme/ThemeProvider.test.tsx +4 -4
- package/src/theme/barrel.ts +7 -6
- package/src/theme/color-stories.coverage.test.ts +1 -1
- package/src/theme/color-utils.ts +41 -29
- package/src/theme/config.completeness.test.ts +1 -1
- package/src/theme/config.ts +28 -0
- package/src/theme/elevation.test.ts +82 -56
- package/src/theme/elevation.ts +77 -561
- package/src/theme/global.css +30 -2
- package/src/theme/grey-ramp.test.ts +24 -8
- package/src/theme/lift.test.ts +39 -0
- package/src/theme/lift.ts +102 -0
- package/src/theme/materials.test.ts +5 -2
- package/src/theme/materials.ts +38 -4
- package/src/theme/native-theming-guard.test.ts +5 -5
- package/src/theme/presets/apply.ts +4 -0
- package/src/theme/presets/audiobook.ts +5 -2
- package/src/theme/presets/default.ts +4 -0
- package/src/theme/presets/index.ts +14 -1
- package/src/theme/presets/types.ts +1 -1
- package/src/theme/semantic-contrast.test.ts +13 -4
- package/src/theme/status-distinctness.test.ts +66 -0
- package/src/theme/surface-planes.test.ts +37 -0
- package/src/theme/surface-planes.ts +68 -0
- package/src/theme/token-export-guard.test.ts +1 -3
- package/src/theme/tokens/primitives.test.ts +15 -7
- package/src/theme/tokens/primitives.ts +65 -33
- package/src/theme/tokens/semantic.ts +71 -37
- package/src/theme/wash-ladder.test.ts +58 -0
- package/src/theme/workout-tokens.ts +1 -7
- package/src/types/jest-axe.d.ts +2 -6
- package/src/types/raw-md.d.ts +5 -0
- package/src/utils/colors.ts +4 -4
- package/src/utils/form.ts +1 -3
- package/src/utils/index.ts +2 -7
- package/src/utils/number-format.ts +17 -0
- package/src/utils/workout-format.ts +15 -6
- package/src/web-jsx/jsx-runtime.ts +1 -5
- package/tailwind.config.js +5 -0
- package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +0 -151
- package/src/components/custom/Workout/RowInventory.stories.tsx +0 -408
- package/src/components/custom/Workout/S3FullRail.stories.tsx +0 -46
- package/src/components/custom/Workout/S3SessionPace.stories.tsx +0 -95
- package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +0 -61
- package/src/components/custom/Workout/S3SetTypes.stories.tsx +0 -176
- package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +0 -240
- package/src/components/shell/DashboardShell.stories.tsx +0 -65
- package/src/components/shell/DashboardShell.tsx +0 -75
- package/src/components/shell/SessionRailSpecimen.stories.tsx +0 -753
- package/src/theme/elevation.stories.tsx +0 -346
- package/src/utils/useTheme.ts +0 -72
- /package/src/components/shell/{DeviceIndicator.test.tsx → workout/DeviceIndicator.test.tsx} +0 -0
- /package/src/components/shell/{DeviceMenu.test.tsx → workout/DeviceMenu.test.tsx} +0 -0
- /package/src/components/shell/{DeviceRow.test.tsx → workout/DeviceRow.test.tsx} +0 -0
- /package/src/components/shell/{SessionStatePill.test.tsx → workout/SessionStatePill.test.tsx} +0 -0
|
@@ -1,753 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* S3 · Session Rail — SPECIMEN (rapid-prototype, not yet a hardened component).
|
|
3
|
-
*
|
|
4
|
-
* A faithful React port of the R2/C3 converged rail
|
|
5
|
-
* (sources/design/drilldown-pass/r2-synthesis/gallery.html), brought into
|
|
6
|
-
* titan Storybook so we can iterate with the token system + real titan components + side-by-side.
|
|
7
|
-
* Raw hex lives here on purpose — it mirrors the gallery 1:1; tokenization happens at harden.
|
|
8
|
-
*
|
|
9
|
-
* Stories:
|
|
10
|
-
* - R2Baseline — the converged rail exactly as designed (live-session context)
|
|
11
|
-
* - ContextSwap — the same rail across live / historical / idle (NAV-D02)
|
|
12
|
-
* - Directions — DIRECTION A (this faithful rail) vs DIRECTION B (real titan ExerciseCard)
|
|
13
|
-
* rendered at the real rail width, to ground the "what is a rail item?" call.
|
|
14
|
-
*/
|
|
15
|
-
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
16
|
-
import { useState } from 'react'
|
|
17
|
-
import { ExerciseCard, type ExerciseCardProps } from '../custom/Workout/ExerciseCard'
|
|
18
|
-
import type { SetRowProps } from '../custom/Workout/SetRow'
|
|
19
|
-
|
|
20
|
-
// ----------------------------------------------------------------------------- specimen data
|
|
21
|
-
// Transcribed from the r2-synthesis gallery `EX` model (cable pull/push session, "Pull A").
|
|
22
|
-
type SpecSet = { reps: number; meanV: number; loss: number; live?: boolean }
|
|
23
|
-
type SpecExercise = {
|
|
24
|
-
id: string
|
|
25
|
-
name: string
|
|
26
|
-
wt: number
|
|
27
|
-
e1rm: number
|
|
28
|
-
pr?: boolean
|
|
29
|
-
cat: string
|
|
30
|
-
sets: SpecSet[]
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const SESSION: SpecExercise[] = [
|
|
34
|
-
{
|
|
35
|
-
id: 'cable-row',
|
|
36
|
-
name: 'Seated Cable Row',
|
|
37
|
-
wt: 145,
|
|
38
|
-
e1rm: 205,
|
|
39
|
-
pr: true,
|
|
40
|
-
cat: 'Back',
|
|
41
|
-
sets: [
|
|
42
|
-
{ reps: 8, meanV: 0.56, loss: 11 },
|
|
43
|
-
{ reps: 8, meanV: 0.54, loss: 15 },
|
|
44
|
-
{ reps: 8, meanV: 0.51, loss: 19 },
|
|
45
|
-
{ reps: 8, meanV: 0.49, loss: 0, live: true },
|
|
46
|
-
{ reps: 7, meanV: 0.47, loss: 26 },
|
|
47
|
-
],
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
id: 'lat-pull',
|
|
51
|
-
name: 'Lat Pulldown',
|
|
52
|
-
wt: 130,
|
|
53
|
-
e1rm: 188,
|
|
54
|
-
cat: 'Back',
|
|
55
|
-
sets: [
|
|
56
|
-
{ reps: 10, meanV: 0.64, loss: 9 },
|
|
57
|
-
{ reps: 10, meanV: 0.61, loss: 14 },
|
|
58
|
-
{ reps: 9, meanV: 0.58, loss: 17 },
|
|
59
|
-
{ reps: 8, meanV: 0.55, loss: 22 },
|
|
60
|
-
],
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
id: 'cable-press',
|
|
64
|
-
name: 'Cable Chest Press',
|
|
65
|
-
wt: 90,
|
|
66
|
-
e1rm: 150,
|
|
67
|
-
cat: 'Chest',
|
|
68
|
-
sets: [
|
|
69
|
-
{ reps: 10, meanV: 0.58, loss: 10 },
|
|
70
|
-
{ reps: 9, meanV: 0.55, loss: 15 },
|
|
71
|
-
{ reps: 8, meanV: 0.51, loss: 20 },
|
|
72
|
-
{ reps: 8, meanV: 0.48, loss: 24 },
|
|
73
|
-
],
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
id: 'cable-fly',
|
|
77
|
-
name: 'Cable Fly',
|
|
78
|
-
wt: 45,
|
|
79
|
-
e1rm: 78,
|
|
80
|
-
cat: 'Chest',
|
|
81
|
-
sets: [
|
|
82
|
-
{ reps: 12, meanV: 0.74, loss: 8 },
|
|
83
|
-
{ reps: 12, meanV: 0.71, loss: 12 },
|
|
84
|
-
{ reps: 11, meanV: 0.68, loss: 16 },
|
|
85
|
-
],
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
id: 'face-pull',
|
|
89
|
-
name: 'Face Pull',
|
|
90
|
-
wt: 35,
|
|
91
|
-
e1rm: 60,
|
|
92
|
-
cat: 'Rear Delt',
|
|
93
|
-
sets: [
|
|
94
|
-
{ reps: 15, meanV: 0.78, loss: 6 },
|
|
95
|
-
{ reps: 15, meanV: 0.74, loss: 10 },
|
|
96
|
-
{ reps: 14, meanV: 0.7, loss: 14 },
|
|
97
|
-
],
|
|
98
|
-
},
|
|
99
|
-
]
|
|
100
|
-
|
|
101
|
-
const LIVE_EX = 'cable-row'
|
|
102
|
-
const LIVE_SET_IDX = 3
|
|
103
|
-
const LIVE_TARGET = 8
|
|
104
|
-
const LIVE_REP = 5 // current rep of the running set
|
|
105
|
-
|
|
106
|
-
type RailContext = 'live' | 'historical' | 'idle'
|
|
107
|
-
|
|
108
|
-
// ----------------------------------------------------------------------------- helpers (gallery)
|
|
109
|
-
const velColor = (v: number): string =>
|
|
110
|
-
v < 0.5 ? '#ff4757' : v < 0.75 ? '#ffa502' : v < 1.0 ? '#ffd43b' : '#2ed573'
|
|
111
|
-
const f2 = (v: number): string => v.toFixed(2)
|
|
112
|
-
const lossClass = (l: number): string => (l >= 25 ? 'bad' : l >= 18 ? 'warn' : 'ok')
|
|
113
|
-
|
|
114
|
-
// ----------------------------------------------------------------------------- faithful CSS
|
|
115
|
-
// All selectors scoped under `.s3spec` so short class names never leak into the RNW canvas.
|
|
116
|
-
const RAIL_CSS = `
|
|
117
|
-
.s3spec{--brand:#FF7900;--brand-soft:rgba(255,121,0,.14);--spine:#131418;--stage:#0f1012;
|
|
118
|
-
--surface:#17181c;--raised:#1e2025;--border:#24262d;--border-strong:#31343d;
|
|
119
|
-
--hover:rgba(255,255,255,.04);--text:#E8EAED;--muted:#8D95A3;--dim:#5b616d;
|
|
120
|
-
--amber:#FFB020;--sky:#2196F3;--v-green:#2ed573;
|
|
121
|
-
--mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace;--ease:cubic-bezier(.22,1,.36,1);
|
|
122
|
-
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;color:var(--text)}
|
|
123
|
-
.s3spec .rail{width:246px;flex:0 0 246px;background:var(--spine);border-right:1px solid var(--border);
|
|
124
|
-
display:flex;flex-direction:column;min-height:0;height:100%}
|
|
125
|
-
.s3spec .railhead{padding:11px 13px 9px;border-bottom:1px solid var(--border)}
|
|
126
|
-
.s3spec .railctx{font-size:9px;font-weight:800;letter-spacing:1px;text-transform:uppercase;color:var(--dim);display:flex;align-items:center;gap:5px}
|
|
127
|
-
.s3spec .railctx .d{width:6px;height:6px;border-radius:50%;background:var(--v-green)}
|
|
128
|
-
.s3spec .railctx.hist .d{background:var(--sky)}
|
|
129
|
-
.s3spec .railtitle{font-size:14px;font-weight:800;margin-top:3px;letter-spacing:-.2px}
|
|
130
|
-
.s3spec .railmeta{font-size:10px;color:var(--muted);margin-top:2px;font-family:var(--mono)}
|
|
131
|
-
.s3spec .railscroll{flex:1;overflow-y:auto;overflow-x:hidden;padding:6px 8px}
|
|
132
|
-
.s3spec .railscroll::-webkit-scrollbar{width:6px}
|
|
133
|
-
.s3spec .railscroll::-webkit-scrollbar-thumb{background:#2a2d34;border-radius:3px}
|
|
134
|
-
.s3spec .rex{border-radius:9px;margin-bottom:3px;border:1px solid transparent;transition:background .15s}
|
|
135
|
-
.s3spec .rex.active{background:var(--brand-soft);border-color:rgba(255,121,0,.28)}
|
|
136
|
-
.s3spec .rexhead{padding:8px 9px;cursor:pointer;display:flex;flex-direction:column;gap:6px;border-radius:9px;transition:background .15s}
|
|
137
|
-
.s3spec .rexhead:hover{background:var(--hover)}
|
|
138
|
-
.s3spec .rextop{display:flex;align-items:center;gap:6px}
|
|
139
|
-
.s3spec .rexidx{font-family:var(--mono);font-size:10px;color:var(--dim);width:13px;flex:0 0 auto}
|
|
140
|
-
.s3spec .rexname{font-size:12px;font-weight:700;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
141
|
-
.s3spec .rextag{font-size:8px;font-family:var(--mono);text-transform:uppercase;letter-spacing:.5px;color:var(--v-green)}
|
|
142
|
-
.s3spec .rexe1{font-family:var(--mono);font-size:10px;color:var(--dim)}
|
|
143
|
-
.s3spec .rexchev{width:0;height:0;border-left:4px solid transparent;border-right:4px solid transparent;border-top:5px solid var(--dim);transition:transform .3s var(--ease)}
|
|
144
|
-
.s3spec .rex.open .rexchev{transform:rotate(180deg)}
|
|
145
|
-
.s3spec .rticks{display:flex;gap:3px;padding-left:19px}
|
|
146
|
-
.s3spec .rtick{flex:1;height:15px;border-radius:3px;background:#22252c;position:relative;overflow:hidden;transition:transform .12s}
|
|
147
|
-
.s3spec .rtick.up{background:none;border:1px dashed #3a3d44;opacity:.5}
|
|
148
|
-
.s3spec .rtick.live{box-shadow:0 0 0 2px var(--brand),0 0 10px rgba(255,121,0,.5);animation:s3tickpulse 1.6s infinite}
|
|
149
|
-
@keyframes s3tickpulse{0%,100%{box-shadow:0 0 0 2px var(--brand),0 0 3px rgba(255,121,0,.3)}50%{box-shadow:0 0 0 2px var(--brand),0 0 14px rgba(255,121,0,.7)}}
|
|
150
|
-
.s3spec .rtick .rf{position:absolute;left:0;right:0;bottom:0;border-radius:3px}
|
|
151
|
-
.s3spec .rtlbl{position:absolute;top:1px;left:0;right:0;text-align:center;font-family:var(--mono);font-size:7px;color:rgba(255,255,255,.6);z-index:2}
|
|
152
|
-
.s3spec .rexbody{overflow:hidden;transition:max-height .4s var(--ease),opacity .3s}
|
|
153
|
-
.s3spec .rsrow{display:flex;align-items:center;gap:7px;padding:6px 9px 6px 22px;font-family:var(--mono);font-size:11px;border-radius:7px}
|
|
154
|
-
.s3spec .rsrow .sn{color:var(--muted);width:30px}
|
|
155
|
-
.s3spec .rsrow .sv{font-weight:800}
|
|
156
|
-
.s3spec .rsrow .sl{margin-left:auto;font-size:10px}
|
|
157
|
-
.s3spec .rsrow .sl.ok{color:var(--v-green)}
|
|
158
|
-
.s3spec .rsrow .sl.warn{color:var(--amber)}
|
|
159
|
-
.s3spec .rsrow .sl.bad{color:#ff4757}
|
|
160
|
-
.s3spec .rsrow.livenow{color:var(--v-green)}
|
|
161
|
-
.s3spec .pace{border-top:1px solid var(--border);padding:11px 13px;background:linear-gradient(180deg,#141519,#101113)}
|
|
162
|
-
.s3spec .pacehead{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}
|
|
163
|
-
.s3spec .pacetitle{font-size:10px;font-weight:800;letter-spacing:.6px;text-transform:uppercase;color:var(--brand)}
|
|
164
|
-
.s3spec .pacetoggle{font-size:8px;font-family:var(--mono);color:var(--dim);border:1px solid var(--border-strong);border-radius:5px;padding:2px 5px;cursor:pointer;letter-spacing:.3px}
|
|
165
|
-
.s3spec .pacetime{display:flex;align-items:baseline;gap:6px;font-family:var(--mono)}
|
|
166
|
-
.s3spec .pacetime .big{font-size:20px;font-weight:800;letter-spacing:-.5px}
|
|
167
|
-
.s3spec .pacetime .of{font-size:11px;color:var(--muted)}
|
|
168
|
-
.s3spec .pacebar{height:6px;border-radius:3px;background:#22252c;margin:7px 0 3px;overflow:hidden;position:relative}
|
|
169
|
-
.s3spec .pacebar .pf{position:absolute;left:0;top:0;bottom:0;border-radius:3px}
|
|
170
|
-
.s3spec .pacebar .budget{position:absolute;top:-2px;bottom:-2px;width:2px;background:var(--text);opacity:.7}
|
|
171
|
-
.s3spec .pacemetrics{display:flex;gap:4px;margin:8px 0}
|
|
172
|
-
.s3spec .pm{flex:1;text-align:center;background:var(--raised);border-radius:7px;padding:6px 3px}
|
|
173
|
-
.s3spec .pm .k{font-size:8px;text-transform:uppercase;letter-spacing:.4px;color:var(--dim);font-weight:800}
|
|
174
|
-
.s3spec .pm .v{font-family:var(--mono);font-size:13px;font-weight:800;margin-top:2px}
|
|
175
|
-
.s3spec .pacesuggest{font-size:10.5px;line-height:1.4;border-radius:8px;padding:8px 9px;display:flex;gap:7px;align-items:flex-start}
|
|
176
|
-
.s3spec .pacesuggest.behind{background:rgba(255,176,32,.12);border:1px solid rgba(255,176,32,.35);color:var(--amber)}
|
|
177
|
-
.s3spec .pacesuggest.ahead{background:rgba(46,213,115,.1);border:1px solid rgba(46,213,115,.3);color:var(--v-green)}
|
|
178
|
-
.s3spec .pacesuggest .ic{flex:0 0 auto;font-size:12px}
|
|
179
|
-
.s3spec .pacesuggest b{color:var(--text)}
|
|
180
|
-
.s3spec .speccol{display:flex;flex-direction:column;height:100vh}
|
|
181
|
-
/* Inside a flex-COLUMN the rail flex-basis would set HEIGHT; override to fill height, keep 246 width. */
|
|
182
|
-
.s3spec .speccol > .rail{flex:1 1 auto;min-height:0}
|
|
183
|
-
.s3spec .speclabel{font-family:var(--mono);font-size:10px;color:var(--dim);letter-spacing:.4px;padding:6px 0 8px 2px;flex:0 0 auto}
|
|
184
|
-
`
|
|
185
|
-
|
|
186
|
-
// ----------------------------------------------------------------------------- render pieces
|
|
187
|
-
function TickRow({ ex, live }: { ex: SpecExercise; live: boolean }) {
|
|
188
|
-
const isLiveEx = live && ex.id === LIVE_EX
|
|
189
|
-
return (
|
|
190
|
-
<div className="rticks">
|
|
191
|
-
{ex.sets.map((st, s) => {
|
|
192
|
-
if (isLiveEx && s > LIVE_SET_IDX) return <div key={s} className="rtick up" />
|
|
193
|
-
if (isLiveEx && s === LIVE_SET_IDX && st.live) {
|
|
194
|
-
const pct = Math.round((LIVE_REP / LIVE_TARGET) * 100)
|
|
195
|
-
return (
|
|
196
|
-
<div key={s} className="rtick live">
|
|
197
|
-
<span className="rtlbl">
|
|
198
|
-
{LIVE_REP}/{LIVE_TARGET}
|
|
199
|
-
</span>
|
|
200
|
-
<div className="rf" style={{ height: `${pct}%`, background: 'var(--brand)' }} />
|
|
201
|
-
</div>
|
|
202
|
-
)
|
|
203
|
-
}
|
|
204
|
-
return (
|
|
205
|
-
<div key={s} className="rtick">
|
|
206
|
-
<span className="rtlbl">{f2(st.meanV)}</span>
|
|
207
|
-
<div
|
|
208
|
-
className="rf"
|
|
209
|
-
style={{ height: '100%', background: velColor(st.meanV), opacity: 0.85 }}
|
|
210
|
-
/>
|
|
211
|
-
</div>
|
|
212
|
-
)
|
|
213
|
-
})}
|
|
214
|
-
</div>
|
|
215
|
-
)
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function SetRows({ ex, live }: { ex: SpecExercise; live: boolean }) {
|
|
219
|
-
const isLiveEx = live && ex.id === LIVE_EX
|
|
220
|
-
return (
|
|
221
|
-
<>
|
|
222
|
-
{ex.sets.map((s, i) => {
|
|
223
|
-
if (isLiveEx && i > LIVE_SET_IDX)
|
|
224
|
-
return (
|
|
225
|
-
<div key={i} className="rsrow" style={{ opacity: 0.5 }}>
|
|
226
|
-
<span className="sn">S{i + 1}</span>
|
|
227
|
-
<span>upcoming</span>
|
|
228
|
-
</div>
|
|
229
|
-
)
|
|
230
|
-
if (isLiveEx && i === LIVE_SET_IDX && s.live)
|
|
231
|
-
return (
|
|
232
|
-
<div key={i} className="rsrow livenow">
|
|
233
|
-
<span className="sn">S{i + 1}</span>
|
|
234
|
-
<span className="sv">
|
|
235
|
-
{LIVE_REP}/{LIVE_TARGET}
|
|
236
|
-
</span>
|
|
237
|
-
<span className="sl">live</span>
|
|
238
|
-
</div>
|
|
239
|
-
)
|
|
240
|
-
return (
|
|
241
|
-
<div key={i} className="rsrow">
|
|
242
|
-
<span className="sn">S{i + 1}</span>
|
|
243
|
-
<span className="sv">{f2(s.meanV)}</span>
|
|
244
|
-
<span style={{ color: 'var(--muted)' }}>
|
|
245
|
-
{s.reps}×{ex.wt}
|
|
246
|
-
</span>
|
|
247
|
-
<span className={`sl ${lossClass(s.loss)}`}>−{s.loss}%</span>
|
|
248
|
-
</div>
|
|
249
|
-
)
|
|
250
|
-
})}
|
|
251
|
-
</>
|
|
252
|
-
)
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
function RailItem({
|
|
256
|
-
ex,
|
|
257
|
-
idx,
|
|
258
|
-
live,
|
|
259
|
-
open,
|
|
260
|
-
onToggle,
|
|
261
|
-
}: {
|
|
262
|
-
ex: SpecExercise
|
|
263
|
-
idx: number
|
|
264
|
-
live: boolean
|
|
265
|
-
open: boolean
|
|
266
|
-
onToggle: () => void
|
|
267
|
-
}) {
|
|
268
|
-
const isLiveEx = live && ex.id === LIVE_EX
|
|
269
|
-
const cls = ['rex']
|
|
270
|
-
if (open) cls.push('open')
|
|
271
|
-
if (isLiveEx) cls.push('active')
|
|
272
|
-
return (
|
|
273
|
-
<div className={cls.join(' ')}>
|
|
274
|
-
<div className="rexhead" onClick={onToggle}>
|
|
275
|
-
<div className="rextop">
|
|
276
|
-
<span className="rexidx">{idx + 1}</span>
|
|
277
|
-
<span className="rexname">{ex.name}</span>
|
|
278
|
-
{isLiveEx ? (
|
|
279
|
-
<span className="rextag">live</span>
|
|
280
|
-
) : (
|
|
281
|
-
<span className="rexe1">{ex.e1rm} e1RM</span>
|
|
282
|
-
)}
|
|
283
|
-
<span className="rexchev" />
|
|
284
|
-
</div>
|
|
285
|
-
<TickRow ex={ex} live={live} />
|
|
286
|
-
</div>
|
|
287
|
-
<div className="rexbody" style={{ maxHeight: open ? 400 : 0, opacity: open ? 1 : 0.4 }}>
|
|
288
|
-
<SetRows ex={ex} live={live} />
|
|
289
|
-
</div>
|
|
290
|
-
</div>
|
|
291
|
-
)
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
function PaceFooter({
|
|
295
|
-
context,
|
|
296
|
-
ahead,
|
|
297
|
-
onToggle,
|
|
298
|
-
}: {
|
|
299
|
-
context: RailContext
|
|
300
|
-
ahead: boolean
|
|
301
|
-
onToggle: () => void
|
|
302
|
-
}) {
|
|
303
|
-
if (context === 'idle') {
|
|
304
|
-
return (
|
|
305
|
-
<div className="pace">
|
|
306
|
-
<div className="pacehead">
|
|
307
|
-
<span className="pacetitle">Next session budget</span>
|
|
308
|
-
</div>
|
|
309
|
-
<div className="pacetime">
|
|
310
|
-
<span className="big">~52:00</span>
|
|
311
|
-
<span className="of">planned · Lower B</span>
|
|
312
|
-
</div>
|
|
313
|
-
<div className="pacesuggest ahead">
|
|
314
|
-
<span className="ic">◷</span>
|
|
315
|
-
<div>6 exercises · ~20 sets. Wall wakes to Live when the first set streams.</div>
|
|
316
|
-
</div>
|
|
317
|
-
</div>
|
|
318
|
-
)
|
|
319
|
-
}
|
|
320
|
-
const behind = !ahead
|
|
321
|
-
return (
|
|
322
|
-
<div className="pace">
|
|
323
|
-
<div className="pacehead">
|
|
324
|
-
<span className="pacetitle">Session Pace</span>
|
|
325
|
-
<span className="pacetoggle" onClick={onToggle}>
|
|
326
|
-
{behind ? 'show ahead ▸' : 'show behind ▸'}
|
|
327
|
-
</span>
|
|
328
|
-
</div>
|
|
329
|
-
<div className="pacetime">
|
|
330
|
-
<span className="big">42:18</span>
|
|
331
|
-
<span className="of">of 60:00 budget</span>
|
|
332
|
-
</div>
|
|
333
|
-
<div className="pacebar">
|
|
334
|
-
<div
|
|
335
|
-
className="pf"
|
|
336
|
-
style={{ width: '70%', background: behind ? 'var(--amber)' : 'var(--v-green)' }}
|
|
337
|
-
/>
|
|
338
|
-
<div className="budget" style={{ left: '100%' }} />
|
|
339
|
-
</div>
|
|
340
|
-
<div className="pacemetrics">
|
|
341
|
-
<div className="pm">
|
|
342
|
-
<div className="k">Volume</div>
|
|
343
|
-
<div className="v">76%</div>
|
|
344
|
-
</div>
|
|
345
|
-
<div className="pm">
|
|
346
|
-
<div className="k">Load</div>
|
|
347
|
-
<div className="v">7.3k</div>
|
|
348
|
-
</div>
|
|
349
|
-
<div className="pm">
|
|
350
|
-
<div className="k">Fatigue</div>
|
|
351
|
-
<div className="v" style={{ color: 'var(--amber)' }}>
|
|
352
|
-
MOD
|
|
353
|
-
</div>
|
|
354
|
-
</div>
|
|
355
|
-
</div>
|
|
356
|
-
{behind ? (
|
|
357
|
-
<div className="pacesuggest behind">
|
|
358
|
-
<span className="ic">▲</span>
|
|
359
|
-
<div>
|
|
360
|
-
<b>Behind pace</b> — ~9 min over budget at this rate. Drop <b>Face Pull</b> to 2 sets to
|
|
361
|
-
finish on time.
|
|
362
|
-
</div>
|
|
363
|
-
</div>
|
|
364
|
-
) : (
|
|
365
|
-
<div className="pacesuggest ahead">
|
|
366
|
-
<span className="ic">+</span>
|
|
367
|
-
<div>
|
|
368
|
-
<b>Ahead of pace</b> — ~7 min of headroom. Room to add a <b>Cable Fly</b> drop-set or a
|
|
369
|
-
back-off set on Row.
|
|
370
|
-
</div>
|
|
371
|
-
</div>
|
|
372
|
-
)}
|
|
373
|
-
</div>
|
|
374
|
-
)
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// Shared rail chrome header (context dot + title + meta) — reused by both the adapted rail and the
|
|
378
|
-
// "ExerciseCard exactly" comparison so only the item body differs.
|
|
379
|
-
function RailHeader({ context, meta }: { context: RailContext; meta?: string }) {
|
|
380
|
-
const hist = context === 'historical'
|
|
381
|
-
const ctxLabel =
|
|
382
|
-
context === 'historical'
|
|
383
|
-
? 'Reviewing · session Mar 11'
|
|
384
|
-
: context === 'idle'
|
|
385
|
-
? 'Last session'
|
|
386
|
-
: 'Live session'
|
|
387
|
-
const title = context === 'idle' ? 'Lower B · Volume (next)' : 'Pull A · Intensification'
|
|
388
|
-
const resolvedMeta =
|
|
389
|
-
meta ??
|
|
390
|
-
(context === 'historical'
|
|
391
|
-
? '22 sets · 48 min · complete'
|
|
392
|
-
: context === 'idle'
|
|
393
|
-
? 'ended 3 days ago'
|
|
394
|
-
: 'ex 1/5 · set 4 live')
|
|
395
|
-
return (
|
|
396
|
-
<div className="railhead">
|
|
397
|
-
<div className={`railctx${hist ? ' hist' : ''}`}>
|
|
398
|
-
<span className="d" />
|
|
399
|
-
{ctxLabel}
|
|
400
|
-
</div>
|
|
401
|
-
<div className="railtitle">{title}</div>
|
|
402
|
-
<div className="railmeta">{resolvedMeta}</div>
|
|
403
|
-
</div>
|
|
404
|
-
)
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// PIECE 1 · Live Session Plan — the context header + expandable exercise skeleton (set-tick progress).
|
|
408
|
-
function SessionPlan({ context = 'live' as RailContext }: { context?: RailContext }) {
|
|
409
|
-
const live = context === 'live'
|
|
410
|
-
const [open, setOpen] = useState<Record<string, boolean>>({ 'cable-row': true })
|
|
411
|
-
return (
|
|
412
|
-
<>
|
|
413
|
-
<RailHeader context={context} />
|
|
414
|
-
<div className="railscroll">
|
|
415
|
-
{SESSION.map((ex, i) => (
|
|
416
|
-
<RailItem
|
|
417
|
-
key={ex.id}
|
|
418
|
-
ex={ex}
|
|
419
|
-
idx={i}
|
|
420
|
-
live={live}
|
|
421
|
-
open={!!open[ex.id]}
|
|
422
|
-
onToggle={() => setOpen((o) => ({ ...o, [ex.id]: !o[ex.id] }))}
|
|
423
|
-
/>
|
|
424
|
-
))}
|
|
425
|
-
</div>
|
|
426
|
-
</>
|
|
427
|
-
)
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// The assembled rail = Live Session Plan (piece 1) + Session Pace (piece 2).
|
|
431
|
-
function R2Rail({ context = 'live' as RailContext }: { context?: RailContext }) {
|
|
432
|
-
const [ahead, setAhead] = useState(false)
|
|
433
|
-
return (
|
|
434
|
-
<div className="rail">
|
|
435
|
-
<SessionPlan context={context} />
|
|
436
|
-
<PaceFooter context={context} ahead={ahead} onToggle={() => setAhead((a) => !a)} />
|
|
437
|
-
</div>
|
|
438
|
-
)
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// ----------------------------------------------------------------------------- ExerciseCard rail (state mix)
|
|
442
|
-
const perRepVel = (s: SpecSet): number[] =>
|
|
443
|
-
Array.from({ length: s.reps }, (_, r) => +(s.meanV + 0.04 - r * 0.01).toFixed(2))
|
|
444
|
-
|
|
445
|
-
type CardStatus = 'finished' | 'active' | 'upcoming'
|
|
446
|
-
|
|
447
|
-
// index-aligned to SESSION: 2 finished · 1 active · 2 upcoming
|
|
448
|
-
const CARD_MIX: { ex: SpecExercise; status: CardStatus }[] = SESSION.map((ex, i) => ({
|
|
449
|
-
ex,
|
|
450
|
-
status: (i < 2 ? 'finished' : i === 2 ? 'active' : 'upcoming') as CardStatus,
|
|
451
|
-
}))
|
|
452
|
-
|
|
453
|
-
const completedSet = (ex: SpecExercise, i: number, s: SpecSet): SetRowProps => ({
|
|
454
|
-
state: 'done',
|
|
455
|
-
setNumber: i + 1,
|
|
456
|
-
reps: s.reps,
|
|
457
|
-
weight: ex.wt,
|
|
458
|
-
rpe: 7 + i * 0.5,
|
|
459
|
-
unit: 'lbs',
|
|
460
|
-
velocities: perRepVel(s),
|
|
461
|
-
})
|
|
462
|
-
|
|
463
|
-
// The active exercise's 3-set mix: done · LIVE · todo. The live set is now a
|
|
464
|
-
// first-class SetRow state — the compact velocity-height spotlight strip, brightened.
|
|
465
|
-
const activeSets = (ex: SpecExercise): SetRowProps[] => [
|
|
466
|
-
completedSet(ex, 0, ex.sets[0]),
|
|
467
|
-
{
|
|
468
|
-
state: 'live',
|
|
469
|
-
setNumber: 2,
|
|
470
|
-
unit: 'lbs',
|
|
471
|
-
target: { reps: 8, weight: ex.wt },
|
|
472
|
-
reps: 8,
|
|
473
|
-
weight: ex.wt,
|
|
474
|
-
velocities: [0.58, 0.55, 0.53, 0.5, 0.48],
|
|
475
|
-
},
|
|
476
|
-
{
|
|
477
|
-
state: 'todo',
|
|
478
|
-
setNumber: 3,
|
|
479
|
-
unit: 'lbs',
|
|
480
|
-
target: { reps: 8, weight: ex.wt },
|
|
481
|
-
},
|
|
482
|
-
]
|
|
483
|
-
|
|
484
|
-
// Real ExerciseCard props for an exercise given its status + whether it's open (onToggle added by caller).
|
|
485
|
-
function cardProps(
|
|
486
|
-
ex: SpecExercise,
|
|
487
|
-
status: CardStatus,
|
|
488
|
-
open: boolean
|
|
489
|
-
): Omit<ExerciseCardProps, 'onExpandedChange'> {
|
|
490
|
-
const base = { name: ex.name, isPR: ex.pr }
|
|
491
|
-
const summary = {
|
|
492
|
-
sets: status === 'active' ? 3 : ex.sets.length,
|
|
493
|
-
reps: ex.sets[0].reps,
|
|
494
|
-
weight: ex.wt,
|
|
495
|
-
unit: 'lbs' as const,
|
|
496
|
-
}
|
|
497
|
-
if (status === 'upcoming') {
|
|
498
|
-
// Closed → the dimmed upcoming row; open → an expanded card of planned (todo) sets.
|
|
499
|
-
if (!open) {
|
|
500
|
-
return {
|
|
501
|
-
...base,
|
|
502
|
-
upcoming: true,
|
|
503
|
-
prescription: `${ex.sets.length} × ${ex.sets[0].reps} @ RPE 8`,
|
|
504
|
-
previousBest: `${ex.sets[0].reps} × ${ex.wt} lb`,
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
return {
|
|
508
|
-
...base,
|
|
509
|
-
expanded: true,
|
|
510
|
-
summary,
|
|
511
|
-
tempo: [3, 1, 2, 0],
|
|
512
|
-
sets: ex.sets.map((s, i) => ({
|
|
513
|
-
state: 'todo',
|
|
514
|
-
setNumber: i + 1,
|
|
515
|
-
unit: 'lbs',
|
|
516
|
-
target: { reps: s.reps, weight: ex.wt },
|
|
517
|
-
})),
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
if (open) {
|
|
521
|
-
return {
|
|
522
|
-
...base,
|
|
523
|
-
expanded: true,
|
|
524
|
-
summary,
|
|
525
|
-
tempo: [3, 1, 2, 0],
|
|
526
|
-
sets: status === 'active' ? activeSets(ex) : ex.sets.map((s, i) => completedSet(ex, i, s)),
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
const doneSets = status === 'active' ? ex.sets.slice(0, 1) : ex.sets
|
|
530
|
-
return {
|
|
531
|
-
...base,
|
|
532
|
-
expanded: false,
|
|
533
|
-
summary,
|
|
534
|
-
totalPlannedSets: status === 'active' ? 3 : ex.sets.length,
|
|
535
|
-
setVelocities: doneSets.map(perRepVel),
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
// The session drawn with the REAL (responsive) ExerciseCard — interactive expand/collapse.
|
|
540
|
-
function ExerciseCardPlan() {
|
|
541
|
-
const [open, setOpen] = useState<Record<string, boolean>>({ [SESSION[2].id]: true })
|
|
542
|
-
return (
|
|
543
|
-
<>
|
|
544
|
-
<RailHeader context="live" meta="ex 3/5 · set 2 live" />
|
|
545
|
-
<div className="railscroll" style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
546
|
-
{CARD_MIX.map(({ ex, status }) => (
|
|
547
|
-
<ExerciseCard
|
|
548
|
-
key={ex.id}
|
|
549
|
-
{...cardProps(ex, status, !!open[ex.id])}
|
|
550
|
-
onExpandedChange={() => setOpen((o) => ({ ...o, [ex.id]: !o[ex.id] }))}
|
|
551
|
-
/>
|
|
552
|
-
))}
|
|
553
|
-
</div>
|
|
554
|
-
</>
|
|
555
|
-
)
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
// A proper, standalone ExerciseCard at native width — reference point (interactive).
|
|
559
|
-
function ReferenceCard() {
|
|
560
|
-
const [open, setOpen] = useState(true)
|
|
561
|
-
return (
|
|
562
|
-
<ExerciseCard
|
|
563
|
-
{...cardProps(SESSION[0], 'finished', open)}
|
|
564
|
-
onExpandedChange={() => setOpen((v) => !v)}
|
|
565
|
-
/>
|
|
566
|
-
)
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
// ----------------------------------------------------------------------------- stories
|
|
570
|
-
function Frame({ children }: { children: React.ReactNode }) {
|
|
571
|
-
return (
|
|
572
|
-
<div
|
|
573
|
-
className="s3spec"
|
|
574
|
-
style={{ display: 'flex', height: '100vh', background: 'var(--stage)' }}
|
|
575
|
-
>
|
|
576
|
-
<style>{RAIL_CSS}</style>
|
|
577
|
-
{children}
|
|
578
|
-
</div>
|
|
579
|
-
)
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
const meta: Meta = {
|
|
583
|
-
title: 'Lab/Specimens/Session Rail',
|
|
584
|
-
parameters: { layout: 'fullscreen' },
|
|
585
|
-
}
|
|
586
|
-
export default meta
|
|
587
|
-
type Story = StoryObj
|
|
588
|
-
|
|
589
|
-
/** The converged R2/C3 rail, live-session context — the "version we have", now in React. */
|
|
590
|
-
export const R2Baseline: Story = {
|
|
591
|
-
render: () => (
|
|
592
|
-
<Frame>
|
|
593
|
-
<R2Rail context="live" />
|
|
594
|
-
<div
|
|
595
|
-
style={{
|
|
596
|
-
flex: 1,
|
|
597
|
-
display: 'flex',
|
|
598
|
-
alignItems: 'center',
|
|
599
|
-
justifyContent: 'center',
|
|
600
|
-
color: 'var(--dim)',
|
|
601
|
-
fontFamily: 'var(--mono)',
|
|
602
|
-
fontSize: 12,
|
|
603
|
-
}}
|
|
604
|
-
>
|
|
605
|
-
main stage
|
|
606
|
-
</div>
|
|
607
|
-
</Frame>
|
|
608
|
-
),
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
/** NAV-D02 context-swap: the same rail across live / historical / idle, side by side. */
|
|
612
|
-
export const ContextSwap: Story = {
|
|
613
|
-
render: () => (
|
|
614
|
-
<Frame>
|
|
615
|
-
<div className="speccol">
|
|
616
|
-
<div className="speclabel">live</div>
|
|
617
|
-
<R2Rail context="live" />
|
|
618
|
-
</div>
|
|
619
|
-
<div className="speccol">
|
|
620
|
-
<div className="speclabel">historical (Review)</div>
|
|
621
|
-
<R2Rail context="historical" />
|
|
622
|
-
</div>
|
|
623
|
-
<div className="speccol">
|
|
624
|
-
<div className="speclabel">idle</div>
|
|
625
|
-
<R2Rail context="idle" />
|
|
626
|
-
</div>
|
|
627
|
-
</Frame>
|
|
628
|
-
),
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
const RAIL_DELTAS: [string, string][] = [
|
|
632
|
-
[
|
|
633
|
-
'Width / fit ✓ DONE',
|
|
634
|
-
'FIXED in the real component: SetRow fixed columns now flexShrink + PREV minWidth → the 5-col table renders correctly at 246px (was: "8×145" mangled per-char). No-op at 390px. This is the whole "make it reactive" ask.',
|
|
635
|
-
],
|
|
636
|
-
[
|
|
637
|
-
'Tick row',
|
|
638
|
-
'REUSE ExerciseCard’s collapsed strip-row (mini VelocityStrips + PlaceholderStrips) AS the set-tick row — already the same concept (see collapsed items in the left column).',
|
|
639
|
-
],
|
|
640
|
-
[
|
|
641
|
-
'Live set marker',
|
|
642
|
-
'ADD an in-progress tick (pulsing + “5/8” rep count). ExerciseCard only knows completed strips + placeholders — no live state.',
|
|
643
|
-
],
|
|
644
|
-
[
|
|
645
|
-
'Expanded body',
|
|
646
|
-
'OPTIONAL: swap the (now-fitting) 5-col input table for a read-only compact list (mean-con · reps×wt · loss%). Rail is read-only — density preference, no longer a fit blocker.',
|
|
647
|
-
],
|
|
648
|
-
[
|
|
649
|
-
'States',
|
|
650
|
-
'Add context-swap (live/historical/idle) + a “live” header tag + leading index; drop input affordances.',
|
|
651
|
-
],
|
|
652
|
-
]
|
|
653
|
-
|
|
654
|
-
/**
|
|
655
|
-
* ExerciseCard EXACTLY AS-IS vs ExerciseCard ADAPTED for the rail, both full 246px rails with pace.
|
|
656
|
-
* The middle panel lists the specific deltas. This is the reuse question made concrete.
|
|
657
|
-
*/
|
|
658
|
-
export const Directions: Story = {
|
|
659
|
-
name: 'ExerciseCard · exact vs adapted',
|
|
660
|
-
render: () => (
|
|
661
|
-
<Frame>
|
|
662
|
-
<div className="speccol">
|
|
663
|
-
<div className="speclabel">
|
|
664
|
-
ExerciseCard rail — state mix (2 finished · 1 active · 2 upcoming)
|
|
665
|
-
</div>
|
|
666
|
-
<div className="rail">
|
|
667
|
-
<ExerciseCardPlan />
|
|
668
|
-
</div>
|
|
669
|
-
</div>
|
|
670
|
-
<div className="speccol">
|
|
671
|
-
<div className="speclabel">old rail — adapted item (reference)</div>
|
|
672
|
-
<div className="rail">
|
|
673
|
-
<SessionPlan context="live" />
|
|
674
|
-
</div>
|
|
675
|
-
</div>
|
|
676
|
-
<div className="speccol" style={{ width: 340, padding: '6px 4px 0 14px', overflowY: 'auto' }}>
|
|
677
|
-
<div className="speclabel">reference · real ExerciseCard (native width)</div>
|
|
678
|
-
<div style={{ marginBottom: 22 }}>
|
|
679
|
-
<ReferenceCard />
|
|
680
|
-
</div>
|
|
681
|
-
<div className="speclabel">the rail modifications</div>
|
|
682
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 12, paddingRight: 8 }}>
|
|
683
|
-
{RAIL_DELTAS.map(([k, v], i) => (
|
|
684
|
-
<div key={i} style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
|
685
|
-
<span
|
|
686
|
-
style={{
|
|
687
|
-
fontSize: 11,
|
|
688
|
-
fontWeight: 800,
|
|
689
|
-
letterSpacing: 0.4,
|
|
690
|
-
textTransform: 'uppercase',
|
|
691
|
-
color: '#FF7900',
|
|
692
|
-
}}
|
|
693
|
-
>
|
|
694
|
-
{k}
|
|
695
|
-
</span>
|
|
696
|
-
<span style={{ fontSize: 12, lineHeight: 1.45, color: '#B8BEC9' }}>{v}</span>
|
|
697
|
-
</div>
|
|
698
|
-
))}
|
|
699
|
-
</div>
|
|
700
|
-
</div>
|
|
701
|
-
</Frame>
|
|
702
|
-
),
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
// ============================================================ decomposed pieces (for review)
|
|
706
|
-
|
|
707
|
-
/**
|
|
708
|
-
* PIECE 1 · LIVE SESSION PLAN — the context header + expandable exercise skeleton with per-set
|
|
709
|
-
* tick progress, reviewed in isolation across live / historical / idle (no pace footer).
|
|
710
|
-
*/
|
|
711
|
-
export const SessionPlanPiece: Story = {
|
|
712
|
-
name: 'Piece · Live Session Plan',
|
|
713
|
-
render: () => (
|
|
714
|
-
<Frame>
|
|
715
|
-
{(['live', 'historical', 'idle'] as RailContext[]).map((ctx) => (
|
|
716
|
-
<div className="speccol" key={ctx}>
|
|
717
|
-
<div className="speclabel">{ctx}</div>
|
|
718
|
-
<div className="rail">
|
|
719
|
-
<SessionPlan context={ctx} />
|
|
720
|
-
</div>
|
|
721
|
-
</div>
|
|
722
|
-
))}
|
|
723
|
-
</Frame>
|
|
724
|
-
),
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
/**
|
|
728
|
-
* PIECE 2 · SESSION PACE — the net-new pace tile in isolation, all states: behind / ahead / idle.
|
|
729
|
-
* VMCP-primary (NAV-D05): plan-aware trim (behind) / add (ahead); idle → next-session budget.
|
|
730
|
-
*/
|
|
731
|
-
export const SessionPacePiece: Story = {
|
|
732
|
-
name: 'Piece · Session Pace',
|
|
733
|
-
render: () => (
|
|
734
|
-
<Frame>
|
|
735
|
-
{(
|
|
736
|
-
[
|
|
737
|
-
['behind', 'live', false],
|
|
738
|
-
['ahead', 'live', true],
|
|
739
|
-
['idle', 'idle', false],
|
|
740
|
-
] as [string, RailContext, boolean][]
|
|
741
|
-
).map(([label, ctx, ahead]) => (
|
|
742
|
-
<div className="speccol" key={label} style={{ height: 'auto' }}>
|
|
743
|
-
<div className="speclabel">{label}</div>
|
|
744
|
-
<div
|
|
745
|
-
style={{ width: 246, background: 'var(--spine)', border: '1px solid var(--border)' }}
|
|
746
|
-
>
|
|
747
|
-
<PaceFooter context={ctx} ahead={ahead} onToggle={() => {}} />
|
|
748
|
-
</div>
|
|
749
|
-
</div>
|
|
750
|
-
))}
|
|
751
|
-
</Frame>
|
|
752
|
-
),
|
|
753
|
-
}
|