@titan-design/react-ui 0.12.1 → 0.14.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 (531) hide show
  1. package/README.md +60 -47
  2. package/dist/bodymap.js +856 -736
  3. package/dist/bodymap.js.map +1 -1
  4. package/dist/bodymap.mjs +858 -738
  5. package/dist/bodymap.mjs.map +1 -1
  6. package/dist/index.d.mts +1431 -237
  7. package/dist/index.d.ts +1431 -237
  8. package/dist/index.js +6506 -4781
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +6280 -4642
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/{pages-B1hqnGIC.d.ts → pages-CsjsUn0G.d.ts} +51 -16
  13. package/dist/{pages-CvsFqNtx.d.mts → pages-ZbT_pD_U.d.mts} +51 -16
  14. package/dist/pages.d.mts +1 -1
  15. package/dist/pages.d.ts +1 -1
  16. package/dist/pages.js +1919 -1937
  17. package/dist/pages.js.map +1 -1
  18. package/dist/pages.mjs +1927 -1945
  19. package/dist/pages.mjs.map +1 -1
  20. package/dist/theme/index.d.mts +225 -140
  21. package/dist/theme/index.d.ts +225 -140
  22. package/dist/theme/index.js +298 -319
  23. package/dist/theme/index.js.map +1 -1
  24. package/dist/theme/index.mjs +283 -315
  25. package/dist/theme/index.mjs.map +1 -1
  26. package/dist/theme/tokens-css.js +145 -24
  27. package/dist/theme/tokens-css.js.map +1 -1
  28. package/dist/theme/tokens-css.mjs +145 -24
  29. package/dist/theme/tokens-css.mjs.map +1 -1
  30. package/dist/theme/tokens.d.mts +128 -11
  31. package/dist/theme/tokens.d.ts +128 -11
  32. package/dist/theme/tokens.js +126 -26
  33. package/dist/theme/tokens.js.map +1 -1
  34. package/dist/theme/tokens.mjs +124 -27
  35. package/dist/theme/tokens.mjs.map +1 -1
  36. package/docs/audits/2026-09-08-critique-addenda.md +51 -0
  37. package/docs/audits/2026-09-08-library-critique.md +212 -0
  38. package/docs/library-roadmap.md +99 -0
  39. package/docs/render-testing-pattern.md +130 -0
  40. package/package.json +1 -1
  41. package/src/arch/Architecture.stories.tsx +3 -1
  42. package/src/arch/arch-graph.freshness.test.ts +52 -0
  43. package/src/arch/arch-graph.json +4989 -1583
  44. package/src/components/custom/ActiveWork/CoChangeChip.stories.tsx +44 -0
  45. package/src/components/custom/ActiveWork/CoChangeChip.test.tsx +29 -0
  46. package/src/components/custom/ActiveWork/CoChangeChip.tsx +48 -0
  47. package/src/components/custom/ActiveWork/Eyebrow.stories.tsx +25 -0
  48. package/src/components/custom/ActiveWork/Eyebrow.test.tsx +16 -0
  49. package/src/components/custom/ActiveWork/Eyebrow.tsx +26 -0
  50. package/src/components/custom/ActiveWork/FileActivityDetail.stories.tsx +50 -0
  51. package/src/components/custom/ActiveWork/FileActivityDetail.test.tsx +66 -0
  52. package/src/components/custom/ActiveWork/FileActivityDetail.tsx +202 -0
  53. package/src/components/custom/ActiveWork/FileActivityRow.stories.tsx +70 -0
  54. package/src/components/custom/ActiveWork/FileActivityRow.test.tsx +67 -0
  55. package/src/components/custom/ActiveWork/FileActivityRow.tsx +130 -0
  56. package/src/components/custom/ActiveWork/FileHistoryExplorer.stories.tsx +84 -0
  57. package/src/components/custom/ActiveWork/FileHistoryExplorer.test.tsx +99 -0
  58. package/src/components/custom/ActiveWork/FileHistoryExplorer.tsx +138 -0
  59. package/src/components/custom/ActiveWork/FilePathLabel.stories.tsx +39 -0
  60. package/src/components/custom/ActiveWork/FilePathLabel.test.tsx +53 -0
  61. package/src/components/custom/ActiveWork/FilePathLabel.tsx +63 -0
  62. package/src/components/custom/ActiveWork/InitiativeBrief.stories.tsx +50 -0
  63. package/src/components/custom/ActiveWork/InitiativeBrief.test.tsx +62 -0
  64. package/src/components/custom/ActiveWork/InitiativeBrief.tsx +182 -0
  65. package/src/components/custom/ActiveWork/InitiativeCard.stories.tsx +73 -0
  66. package/src/components/custom/ActiveWork/InitiativeCard.test.tsx +68 -0
  67. package/src/components/custom/ActiveWork/InitiativeCard.tsx +136 -0
  68. package/src/components/custom/ActiveWork/InitiativeHeader.stories.tsx +59 -0
  69. package/src/components/custom/ActiveWork/InitiativeHeader.test.tsx +40 -0
  70. package/src/components/custom/ActiveWork/InitiativeHeader.tsx +73 -0
  71. package/src/components/custom/ActiveWork/InitiativeReader.stories.tsx +145 -0
  72. package/src/components/custom/ActiveWork/OpenLoops.stories.tsx +58 -0
  73. package/src/components/custom/ActiveWork/OpenLoops.test.tsx +61 -0
  74. package/src/components/custom/ActiveWork/OpenLoops.tsx +107 -0
  75. package/src/components/custom/ActiveWork/PortfolioOverview.stories.tsx +101 -0
  76. package/src/components/custom/ActiveWork/PortfolioOverview.test.tsx +81 -0
  77. package/src/components/custom/ActiveWork/PortfolioOverview.tsx +83 -0
  78. package/src/components/custom/ActiveWork/README.md +305 -0
  79. package/src/components/custom/ActiveWork/SessionDetail.stories.tsx +71 -0
  80. package/src/components/custom/ActiveWork/SessionDetail.test.tsx +84 -0
  81. package/src/components/custom/ActiveWork/SessionDetail.tsx +180 -0
  82. package/src/components/custom/ActiveWork/SessionList.stories.tsx +69 -0
  83. package/src/components/custom/ActiveWork/SessionList.test.tsx +78 -0
  84. package/src/components/custom/ActiveWork/SessionList.tsx +103 -0
  85. package/src/components/custom/ActiveWork/SessionListItem.stories.tsx +64 -0
  86. package/src/components/custom/ActiveWork/SessionListItem.test.tsx +62 -0
  87. package/src/components/custom/ActiveWork/SessionListItem.tsx +152 -0
  88. package/src/components/custom/ActiveWork/SessionReader.stories.tsx +113 -0
  89. package/src/components/custom/ActiveWork/SeverityLabel.stories.tsx +51 -0
  90. package/src/components/custom/ActiveWork/SeverityLabel.test.tsx +68 -0
  91. package/src/components/custom/ActiveWork/SeverityLabel.tsx +97 -0
  92. package/src/components/custom/ActiveWork/TaskRow.stories.tsx +63 -0
  93. package/src/components/custom/ActiveWork/TaskRow.test.tsx +112 -0
  94. package/src/components/custom/ActiveWork/TaskRow.tsx +176 -0
  95. package/src/components/custom/ActiveWork/TaskTable.stories.tsx +123 -0
  96. package/src/components/custom/ActiveWork/TaskTable.test.tsx +233 -0
  97. package/src/components/custom/ActiveWork/TaskTable.tsx +321 -0
  98. package/src/components/custom/ActiveWork/active-work-depth.test.tsx +102 -0
  99. package/src/components/custom/ActiveWork/file-history-fixture.ts +91 -0
  100. package/src/components/custom/ActiveWork/format-time.test.ts +23 -0
  101. package/src/components/custom/ActiveWork/format-time.ts +32 -0
  102. package/src/components/custom/ActiveWork/index.ts +19 -0
  103. package/src/components/custom/ActiveWork/initiative-fixture.ts +104 -0
  104. package/src/components/custom/ActiveWork/session-fixture.ts +267 -0
  105. package/src/components/custom/ActiveWork/session-linkers.ts +56 -0
  106. package/src/components/custom/ActiveWork/task-list-fixture.ts +97 -0
  107. package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +12 -6
  108. package/src/components/custom/DateTime/DateTime.stories.tsx +2 -1
  109. package/src/components/custom/DateTime/DateTime.test.tsx +3 -1
  110. package/src/components/custom/EmptyState/EmptyState.stories.tsx +22 -9
  111. package/src/components/custom/EmptyState/EmptyState.test.tsx +7 -13
  112. package/src/components/custom/EmptyState/EmptyState.tsx +4 -18
  113. package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +4 -3
  114. package/src/components/custom/Fatigue/FatigueLights.stories.tsx +2 -2
  115. package/src/components/custom/Fatigue/GhostSpark.stories.tsx +4 -3
  116. package/src/components/custom/Fatigue/LiveFatigueCard.stories.tsx +2 -2
  117. package/src/components/custom/Fatigue/LiveFatigueCard.tsx +8 -4
  118. package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +116 -6
  119. package/src/components/custom/Fatigue/LiveFatiguePanel.test.tsx +134 -0
  120. package/src/components/custom/Fatigue/LiveFatiguePanel.tsx +54 -83
  121. package/src/components/custom/Fatigue/README.md +40 -3
  122. package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +2 -2
  123. package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +2 -1
  124. package/src/components/custom/Fatigue/VelocityHero.stories.tsx +4 -3
  125. package/src/components/custom/Fatigue/VerdictHero.stories.tsx +2 -2
  126. package/src/components/custom/Fatigue/fatigue-mock.test.ts +0 -5
  127. package/src/components/custom/Fatigue/fatigue-mock.ts +2 -10
  128. package/src/components/custom/Fatigue/fatigue-tokens.ts +12 -0
  129. package/src/components/custom/Fatigue/index.ts +17 -5
  130. package/src/components/custom/Fatigue/panel-layout.test.ts +149 -0
  131. package/src/components/custom/Fatigue/panel-layout.ts +185 -0
  132. package/src/components/custom/Gauge/Gauge.stories.tsx +6 -3
  133. package/src/components/custom/Gauge/Gauge.test.tsx +1 -1
  134. package/src/components/custom/Gauge/Gauge.tsx +7 -2
  135. package/src/components/custom/Metric/Metric.stories.tsx +10 -1
  136. package/src/components/custom/Metric/Metric.test.tsx +1 -3
  137. package/src/components/custom/Metric/Metric.tsx +5 -20
  138. package/src/components/custom/Prose/MarkdownProse.stories.tsx +88 -0
  139. package/src/components/custom/Prose/MarkdownProse.test.tsx +87 -0
  140. package/src/components/custom/Prose/MarkdownProse.tsx +201 -0
  141. package/src/components/custom/Prose/README.md +58 -0
  142. package/src/components/custom/Prose/index.ts +1 -0
  143. package/src/components/custom/Scatter/Scatter.stories.tsx +55 -14
  144. package/src/components/custom/Scatter/Scatter.test.tsx +2 -2
  145. package/src/components/custom/Scatter/Scatter.tsx +122 -18
  146. package/src/components/custom/Sidebar/Sidebar.stories.tsx +41 -8
  147. package/src/components/custom/Sidebar/Sidebar.test.tsx +5 -1
  148. package/src/components/custom/Sidebar/Sidebar.tsx +16 -33
  149. package/src/components/custom/Table/Table.stories.tsx +99 -16
  150. package/src/components/custom/Table/Table.test.tsx +273 -86
  151. package/src/components/custom/Table/Table.tsx +159 -67
  152. package/src/components/custom/Table/column-fit.test.ts +75 -0
  153. package/src/components/custom/Table/column-fit.ts +116 -0
  154. package/src/components/custom/Table/index.ts +9 -0
  155. package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +8 -6
  156. package/src/components/custom/Treemap/Treemap.stories.tsx +15 -12
  157. package/src/components/custom/Treemap/Treemap.test.tsx +1 -1
  158. package/src/components/custom/Treemap/Treemap.tsx +11 -3
  159. package/src/components/custom/Typography/Typography.stories.tsx +28 -1
  160. package/src/components/custom/Typography/Typography.test.tsx +34 -0
  161. package/src/components/custom/Typography/Typography.tsx +6 -0
  162. package/src/components/custom/Typography/index.ts +1 -8
  163. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +11 -2
  164. package/src/components/custom/Workout/BaseBadge.stories.tsx +27 -11
  165. package/src/components/custom/Workout/BaseBadge.test.tsx +31 -11
  166. package/src/components/custom/Workout/BaseBadge.tsx +6 -2
  167. package/src/components/custom/Workout/BodyMap.stories.tsx +4 -1
  168. package/src/components/custom/Workout/BodyMap.tsx +8 -4
  169. package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +28 -6
  170. package/src/components/custom/Workout/BodyMapDetailPanel.test.tsx +11 -15
  171. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +50 -12
  172. package/src/components/custom/Workout/CapacityBandChart.stories.tsx +12 -9
  173. package/src/components/custom/Workout/CapacityBandChart.test.tsx +24 -51
  174. package/src/components/custom/Workout/CapacityBandChart.tsx +144 -111
  175. package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
  176. package/src/components/custom/Workout/DeviationBar.test.tsx +10 -4
  177. package/src/components/custom/Workout/DeviationBar.tsx +24 -16
  178. package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +183 -0
  179. package/src/components/custom/Workout/DualVelocityStrip.test.tsx +32 -18
  180. package/src/components/custom/Workout/ExerciseCard.stories.tsx +12 -11
  181. package/src/components/custom/Workout/ExerciseCard.test.tsx +52 -22
  182. package/src/components/custom/Workout/ExerciseCard.tsx +99 -183
  183. package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +95 -12
  184. package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +189 -1
  185. package/src/components/custom/Workout/ExerciseCardHeading.tsx +167 -18
  186. package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +9 -1
  187. package/src/components/custom/Workout/ExerciseDetailPage.tsx +3 -2
  188. package/src/components/custom/Workout/ExerciseHeading.stories.tsx +8 -8
  189. package/src/components/custom/Workout/ExerciseHeading.tsx +194 -46
  190. package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +12 -9
  191. package/src/components/custom/Workout/ExerciseIndicator.test.tsx +27 -11
  192. package/src/components/custom/Workout/ExerciseIndicator.tsx +14 -15
  193. package/src/components/custom/Workout/FatigueMeter.stories.tsx +5 -5
  194. package/src/components/custom/Workout/InputBar.stories.tsx +4 -5
  195. package/src/components/custom/Workout/InputBar.tsx +4 -2
  196. package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
  197. package/src/components/custom/Workout/IntensityBar.test.tsx +5 -1
  198. package/src/components/custom/Workout/IntensityBar.tsx +8 -3
  199. package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +5 -4
  200. package/src/components/custom/Workout/MesoCard.stories.tsx +4 -1
  201. package/src/components/custom/Workout/MesoCard.test.tsx +7 -18
  202. package/src/components/custom/Workout/MesoCard.tsx +14 -10
  203. package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
  204. package/src/components/custom/Workout/MesoProgressBar.test.tsx +18 -11
  205. package/src/components/custom/Workout/MesoProgressBar.tsx +15 -13
  206. package/src/components/custom/Workout/MesoStatusCard.stories.tsx +2 -1
  207. package/src/components/custom/Workout/MesoStatusCard.test.tsx +14 -44
  208. package/src/components/custom/Workout/MesoStatusCard.tsx +20 -33
  209. package/src/components/custom/Workout/MetricTiles.stories.tsx +3 -2
  210. package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +2 -8
  211. package/src/components/custom/Workout/MuscleGroupChip.test.tsx +23 -31
  212. package/src/components/custom/Workout/MuscleGroupChip.tsx +28 -67
  213. package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +9 -4
  214. package/src/components/custom/Workout/PlaceholderStrip.test.tsx +17 -10
  215. package/src/components/custom/Workout/PlaceholderStrip.tsx +9 -8
  216. package/src/components/custom/Workout/PrBadge.stories.tsx +2 -1
  217. package/src/components/custom/Workout/PrBadge.test.tsx +3 -9
  218. package/src/components/custom/Workout/PrBadge.tsx +13 -17
  219. package/src/components/custom/Workout/PrHistoryModal.stories.tsx +16 -3
  220. package/src/components/custom/Workout/PrHistoryModal.test.tsx +13 -36
  221. package/src/components/custom/Workout/PrHistoryModal.tsx +8 -4
  222. package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +6 -1
  223. package/src/components/custom/Workout/README.md +68 -17
  224. package/src/components/custom/Workout/ReadinessCheck.stories.tsx +3 -2
  225. package/src/components/custom/Workout/ReadinessCheck.test.tsx +12 -18
  226. package/src/components/custom/Workout/ReadinessCheck.tsx +22 -4
  227. package/src/components/custom/Workout/RestTimer.stories.tsx +16 -4
  228. package/src/components/custom/Workout/RestTimer.tsx +4 -2
  229. package/src/components/custom/Workout/ScheduleTiles.stories.tsx +3 -2
  230. package/src/components/custom/Workout/ScheduleTiles.test.tsx +3 -4
  231. package/src/components/custom/Workout/ScheduleTiles.tsx +4 -7
  232. package/src/components/custom/Workout/SegmentedBar.stories.tsx +21 -17
  233. package/src/components/custom/Workout/SegmentedBar.tsx +10 -5
  234. package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +5 -5
  235. package/src/components/custom/Workout/SessionHeader.stories.tsx +6 -6
  236. package/src/components/custom/Workout/SessionHeader.tsx +12 -12
  237. package/src/components/custom/Workout/SessionRail.stories.tsx +12 -11
  238. package/src/components/custom/Workout/SessionRail.tsx +7 -9
  239. package/src/components/custom/Workout/SetBar.stories.tsx +10 -6
  240. package/src/components/custom/Workout/SetBar.test.tsx +49 -4
  241. package/src/components/custom/Workout/SetBar.tsx +38 -12
  242. package/src/components/custom/Workout/SetRow.stories.tsx +3 -2
  243. package/src/components/custom/Workout/SetRow.test.tsx +16 -2
  244. package/src/components/custom/Workout/SetRow.tsx +61 -23
  245. package/src/components/custom/Workout/SetStrip.stories.tsx +63 -7
  246. package/src/components/custom/Workout/SetStrip.test.tsx +25 -0
  247. package/src/components/custom/Workout/SetStrip.tsx +24 -4
  248. package/src/components/custom/Workout/SetTableHeader.stories.tsx +6 -6
  249. package/src/components/custom/Workout/SetTableHeader.tsx +9 -12
  250. package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +7 -7
  251. package/src/components/custom/Workout/SetsRepsLoad.tsx +24 -12
  252. package/src/components/custom/Workout/Sparkline.stories.tsx +14 -11
  253. package/src/components/custom/Workout/Sparkline.test.tsx +37 -19
  254. package/src/components/custom/Workout/Sparkline.tsx +14 -18
  255. package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
  256. package/src/components/custom/Workout/StatusDot.test.tsx +44 -5
  257. package/src/components/custom/Workout/StatusDot.tsx +93 -63
  258. package/src/components/custom/Workout/StatusPill.stories.tsx +1 -1
  259. package/src/components/custom/Workout/StatusPill.test.tsx +6 -15
  260. package/src/components/custom/Workout/StatusPill.tsx +27 -38
  261. package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +11 -4
  262. package/src/components/custom/Workout/StrengthTrendChart.test.tsx +15 -37
  263. package/src/components/custom/Workout/StrengthTrendChart.tsx +39 -36
  264. package/src/components/custom/Workout/SupersetWrapper.stories.tsx +27 -64
  265. package/src/components/custom/Workout/SupersetWrapper.test.tsx +16 -14
  266. package/src/components/custom/Workout/SupersetWrapper.tsx +17 -21
  267. package/src/components/custom/Workout/TempoDisplay.stories.tsx +26 -10
  268. package/src/components/custom/Workout/TempoDisplay.test.tsx +18 -4
  269. package/src/components/custom/Workout/TempoDisplay.tsx +2 -0
  270. package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +9 -1
  271. package/src/components/custom/Workout/VelocityStrip.compact.stories.tsx +4 -1
  272. package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +10 -6
  273. package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +5 -2
  274. package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +44 -1
  275. package/src/components/custom/Workout/VelocityStrip.stories.tsx +12 -6
  276. package/src/components/custom/Workout/VelocityStrip.test.tsx +48 -6
  277. package/src/components/custom/Workout/VelocityStrip.tsx +26 -14
  278. package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +6 -7
  279. package/src/components/custom/Workout/VolumeLandmarkBar.tsx +15 -6
  280. package/src/components/custom/Workout/WeekRow.stories.tsx +4 -1
  281. package/src/components/custom/Workout/WeekRow.test.tsx +18 -15
  282. package/src/components/custom/Workout/WeekRow.tsx +23 -16
  283. package/src/components/custom/Workout/WeightBadge.stories.tsx +2 -1
  284. package/src/components/custom/Workout/WeightBadge.test.tsx +50 -19
  285. package/src/components/custom/Workout/WeightBadge.tsx +28 -27
  286. package/src/components/custom/Workout/WorkoutCard.stories.tsx +6 -1
  287. package/src/components/custom/Workout/WorkoutCard.test.tsx +18 -0
  288. package/src/components/custom/Workout/WorkoutCard.tsx +35 -57
  289. package/src/components/custom/Workout/WorkoutPill.stories.tsx +5 -2
  290. package/src/components/custom/Workout/WorkoutPill.test.tsx +39 -7
  291. package/src/components/custom/Workout/WorkoutPill.tsx +88 -57
  292. package/src/components/custom/Workout/ZoneTrack.stories.tsx +10 -7
  293. package/src/components/custom/Workout/ZoneTrack.tsx +10 -9
  294. package/src/components/custom/Workout/exerciseRowState.ts +29 -0
  295. package/src/components/custom/Workout/index.ts +17 -2
  296. package/src/components/custom/Workout/metricText.stories.tsx +15 -13
  297. package/src/components/custom/Workout/metricText.tsx +8 -2
  298. package/src/components/custom/Workout/setHeadingKit.tsx +219 -36
  299. package/src/components/custom/Workout/velocity-story-kit.tsx +2 -2
  300. package/src/components/custom/charts/README.md +66 -0
  301. package/src/components/custom/charts/SetBarChart.test.tsx +46 -1
  302. package/src/components/custom/charts/SetBarChart.tsx +56 -9
  303. package/src/components/custom/charts/SparkBars.stories.tsx +55 -0
  304. package/src/components/custom/charts/SparkBars.test.tsx +69 -0
  305. package/src/components/custom/charts/SparkBars.tsx +85 -0
  306. package/src/components/custom/charts/flatBarGeometry.test.ts +41 -0
  307. package/src/components/custom/charts/flatBarGeometry.ts +32 -0
  308. package/src/components/custom/charts/index.ts +3 -0
  309. package/src/components/custom/index.ts +3 -0
  310. package/src/components/custom/stepper/Stepper.stories.tsx +13 -10
  311. package/src/components/custom/stepper/Stepper.test.tsx +1 -7
  312. package/src/components/custom/stepper/Stepper.tsx +21 -34
  313. package/src/components/icons/icons.stories.tsx +1 -1
  314. package/src/components/icons/icons.tsx +51 -0
  315. package/src/components/shell/AppShell.stories.tsx +150 -0
  316. package/src/components/shell/AppShell.test.tsx +70 -0
  317. package/src/components/shell/AppShell.tsx +90 -0
  318. package/src/components/shell/BrandLockup.stories.tsx +66 -5
  319. package/src/components/shell/BrandLockup.test.tsx +34 -1
  320. package/src/components/shell/BrandLockup.tsx +27 -11
  321. package/src/components/shell/NavItem.stories.tsx +4 -4
  322. package/src/components/shell/NavItem.test.tsx +17 -0
  323. package/src/components/shell/NavItem.tsx +17 -5
  324. package/src/components/shell/README.md +89 -26
  325. package/src/components/shell/SideNav.stories.tsx +38 -7
  326. package/src/components/shell/SideNav.test.tsx +16 -8
  327. package/src/components/shell/SideNav.tsx +17 -17
  328. package/src/components/shell/TopBar.stories.tsx +44 -19
  329. package/src/components/shell/TopBar.test.tsx +34 -12
  330. package/src/components/shell/TopBar.tsx +61 -40
  331. package/src/components/shell/brands.tsx +66 -0
  332. package/src/components/shell/index.ts +9 -8
  333. package/src/components/shell/{DeviceIndicator.stories.tsx → workout/DeviceIndicator.stories.tsx} +2 -2
  334. package/src/components/shell/{DeviceIndicator.tsx → workout/DeviceIndicator.tsx} +2 -2
  335. package/src/components/shell/{DeviceMenu.stories.tsx → workout/DeviceMenu.stories.tsx} +2 -2
  336. package/src/components/shell/{DeviceMenu.tsx → workout/DeviceMenu.tsx} +3 -3
  337. package/src/components/shell/{DeviceRow.stories.tsx → workout/DeviceRow.stories.tsx} +2 -2
  338. package/src/components/shell/{DeviceRow.tsx → workout/DeviceRow.tsx} +3 -3
  339. package/src/components/shell/{SessionStatePill.stories.tsx → workout/SessionStatePill.stories.tsx} +2 -2
  340. package/src/components/shell/{SessionStatePill.tsx → workout/SessionStatePill.tsx} +5 -3
  341. package/src/components/shell/workout/WorkoutShell.stories.tsx +71 -0
  342. package/src/components/shell/{DashboardShell.test.tsx → workout/WorkoutShell.test.tsx} +19 -8
  343. package/src/components/shell/workout/WorkoutShell.tsx +77 -0
  344. package/src/components/shell/workout/WorkoutTopBar.stories.tsx +58 -0
  345. package/src/components/shell/workout/WorkoutTopBar.test.tsx +39 -0
  346. package/src/components/shell/workout/WorkoutTopBar.tsx +52 -0
  347. package/src/components/shell/workout/index.ts +9 -0
  348. package/src/components/shell/workout/workoutNavItems.tsx +16 -0
  349. package/src/components/ui/README.md +110 -0
  350. package/src/components/ui/alert/Alert.stories.tsx +15 -5
  351. package/src/components/ui/alert/Alert.tsx +24 -3
  352. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +11 -13
  353. package/src/components/ui/autocomplete/Autocomplete.test.tsx +7 -25
  354. package/src/components/ui/autocomplete/Autocomplete.tsx +21 -28
  355. package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
  356. package/src/components/ui/avatar/Avatar.tsx +2 -10
  357. package/src/components/ui/badge/Badge.stories.tsx +83 -28
  358. package/src/components/ui/badge/Badge.test.tsx +19 -3
  359. package/src/components/ui/badge/Badge.tsx +48 -55
  360. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  361. package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +4 -16
  362. package/src/components/ui/button/Button.stories.tsx +46 -12
  363. package/src/components/ui/button/Button.tsx +44 -37
  364. package/src/components/ui/card/Card.stories.tsx +90 -94
  365. package/src/components/ui/card/Card.test.tsx +80 -6
  366. package/src/components/ui/card/Card.tsx +150 -203
  367. package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
  368. package/src/components/ui/checkbox/Checkbox.tsx +7 -27
  369. package/src/components/ui/chip/Chip.stories.tsx +37 -13
  370. package/src/components/ui/chip/Chip.test.tsx +10 -6
  371. package/src/components/ui/chip/Chip.tsx +76 -91
  372. package/src/components/ui/collapse/Collapse.stories.tsx +23 -23
  373. package/src/components/ui/collapse/Collapse.tsx +6 -11
  374. package/src/components/ui/data-row/DataRow.stories.tsx +14 -3
  375. package/src/components/ui/data-row/DataRow.test.tsx +5 -15
  376. package/src/components/ui/data-row/DataRow.tsx +18 -7
  377. package/src/components/ui/divider/Divider.stories.tsx +1 -1
  378. package/src/components/ui/divider/Divider.tsx +1 -5
  379. package/src/components/ui/drawer/Drawer.stories.tsx +5 -16
  380. package/src/components/ui/drawer/Drawer.tsx +13 -27
  381. package/src/components/ui/floating-overlay-depth.test.tsx +143 -0
  382. package/src/components/ui/form-field/FormField.stories.tsx +6 -20
  383. package/src/components/ui/form-field/FormField.test.tsx +1 -5
  384. package/src/components/ui/form-field/FormField.tsx +10 -46
  385. package/src/components/ui/help-tip/HelpTip.stories.tsx +8 -19
  386. package/src/components/ui/help-tip/HelpTip.test.tsx +1 -3
  387. package/src/components/ui/help-tip/HelpTip.tsx +21 -22
  388. package/src/components/ui/help-tip/index.ts +7 -1
  389. package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
  390. package/src/components/ui/icon-box/IconBox.test.tsx +1 -7
  391. package/src/components/ui/icon-box/IconBox.tsx +10 -18
  392. package/src/components/ui/indicator/Indicator.stories.tsx +5 -4
  393. package/src/components/ui/indicator/Indicator.tsx +20 -14
  394. package/src/components/ui/input/Input.stories.tsx +3 -11
  395. package/src/components/ui/input/Input.test.tsx +2 -10
  396. package/src/components/ui/input/Input.tsx +10 -25
  397. package/src/components/ui/input/index.ts +7 -1
  398. package/src/components/ui/link/Link.stories.tsx +19 -7
  399. package/src/components/ui/list-item/ListItem.stories.tsx +17 -5
  400. package/src/components/ui/list-item/ListItem.tsx +2 -9
  401. package/src/components/ui/menu/Menu.stories.tsx +21 -15
  402. package/src/components/ui/menu/Menu.test.tsx +2 -11
  403. package/src/components/ui/menu/Menu.tsx +12 -14
  404. package/src/components/ui/menu/index.ts +8 -1
  405. package/src/components/ui/modal/Modal.stories.tsx +2 -2
  406. package/src/components/ui/modal/Modal.tsx +31 -19
  407. package/src/components/ui/pill/Pill.stories.tsx +111 -23
  408. package/src/components/ui/pill/Pill.test.tsx +101 -3
  409. package/src/components/ui/pill/Pill.tsx +168 -49
  410. package/src/components/ui/pill/index.ts +1 -1
  411. package/src/components/ui/popover/Popover.stories.tsx +6 -8
  412. package/src/components/ui/popover/Popover.test.tsx +1 -6
  413. package/src/components/ui/popover/Popover.tsx +20 -15
  414. package/src/components/ui/popover/index.ts +7 -1
  415. package/src/components/ui/progress/Progress.stories.tsx +3 -12
  416. package/src/components/ui/progress/Progress.test.tsx +1 -5
  417. package/src/components/ui/progress/Progress.tsx +9 -6
  418. package/src/components/ui/radio/Radio.stories.tsx +42 -21
  419. package/src/components/ui/radio/Radio.test.tsx +6 -2
  420. package/src/components/ui/radio/Radio.tsx +23 -22
  421. package/src/components/ui/section/Section.stories.tsx +12 -21
  422. package/src/components/ui/section/Section.test.tsx +1 -6
  423. package/src/components/ui/section/Section.tsx +1 -3
  424. package/src/components/ui/section/index.ts +1 -5
  425. package/src/components/ui/select/Select.stories.tsx +3 -13
  426. package/src/components/ui/select/Select.test.tsx +2 -11
  427. package/src/components/ui/select/Select.tsx +16 -15
  428. package/src/components/ui/skeleton/Skeleton.stories.tsx +3 -11
  429. package/src/components/ui/skeleton/Skeleton.tsx +3 -14
  430. package/src/components/ui/skeleton/index.ts +1 -7
  431. package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
  432. package/src/components/ui/spinner/Spinner.tsx +4 -5
  433. package/src/components/ui/stack/Stack.stories.tsx +8 -8
  434. package/src/components/ui/stack/Stack.tsx +1 -1
  435. package/src/components/ui/surface/Surface.stories.tsx +92 -33
  436. package/src/components/ui/surface/Surface.test.tsx +88 -28
  437. package/src/components/ui/surface/Surface.tsx +37 -87
  438. package/src/components/ui/surface/SurfaceContext.ts +22 -62
  439. package/src/components/ui/surface/index.ts +8 -0
  440. package/src/components/ui/surface/resolveSurface.ts +98 -0
  441. package/src/components/ui/surface/surface.contract.test.ts +6 -1
  442. package/src/components/ui/switch/Switch.stories.tsx +1 -1
  443. package/src/components/ui/switch/Switch.tsx +7 -6
  444. package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
  445. package/src/components/ui/tabs/Tabs.tsx +8 -21
  446. package/src/components/ui/tabs/index.ts +1 -7
  447. package/src/components/ui/tile/Tile.stories.tsx +1 -1
  448. package/src/components/ui/tile/Tile.tsx +3 -0
  449. package/src/components/ui/toast/Toast.stories.tsx +1 -1
  450. package/src/components/ui/toast/Toast.test.tsx +2 -8
  451. package/src/components/ui/toast/Toast.tsx +49 -57
  452. package/src/components/ui/toast/index.ts +0 -1
  453. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +92 -71
  454. package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +3 -18
  455. package/src/components/ui/toolbar-button/ToolbarButton.tsx +38 -49
  456. package/src/components/ui/tooltip/Tooltip.stories.tsx +12 -3
  457. package/src/components/ui/tooltip/Tooltip.test.tsx +21 -3
  458. package/src/components/ui/tooltip/Tooltip.tsx +55 -36
  459. package/src/components/ui/tooltip/index.ts +1 -1
  460. package/src/components/ui/trigger/TriggerSurface.test.tsx +215 -0
  461. package/src/components/ui/trigger/TriggerSurface.tsx +81 -0
  462. package/src/components/ui/trigger/index.ts +2 -0
  463. package/src/stories/PresetShowcase.stories.tsx +101 -48
  464. package/src/test/no-device-internals.test.ts +3 -3
  465. package/src/test/no-raw-color.test.ts +39 -0
  466. package/src/theme/Alpha.stories.tsx +103 -0
  467. package/src/theme/ChoosingTokens.stories.tsx +48 -0
  468. package/src/theme/ColorPalettes.stories.tsx +62 -45
  469. package/src/theme/ColorPrimitives.stories.tsx +35 -35
  470. package/src/theme/Depth.stories.tsx +132 -65
  471. package/src/theme/DepthCalibration.stories.tsx +23 -21
  472. package/src/theme/ProposedTokensVW82.stories.tsx +414 -0
  473. package/src/theme/ThemeProvider.test.tsx +4 -4
  474. package/src/theme/barrel.ts +7 -6
  475. package/src/theme/color-stories.coverage.test.ts +1 -1
  476. package/src/theme/color-utils.ts +41 -29
  477. package/src/theme/config.completeness.test.ts +1 -1
  478. package/src/theme/config.ts +40 -0
  479. package/src/theme/elevation.test.ts +82 -56
  480. package/src/theme/elevation.ts +77 -561
  481. package/src/theme/global.css +45 -5
  482. package/src/theme/grey-ramp.test.ts +24 -8
  483. package/src/theme/lift.test.ts +39 -0
  484. package/src/theme/lift.ts +102 -0
  485. package/src/theme/materials.test.ts +5 -2
  486. package/src/theme/materials.ts +38 -4
  487. package/src/theme/native-theming-guard.test.ts +5 -5
  488. package/src/theme/presets/apply.ts +4 -0
  489. package/src/theme/presets/audiobook.ts +5 -2
  490. package/src/theme/presets/default.ts +4 -0
  491. package/src/theme/presets/index.ts +14 -1
  492. package/src/theme/presets/types.ts +1 -1
  493. package/src/theme/semantic-contrast.test.ts +13 -4
  494. package/src/theme/status-distinctness.test.ts +66 -0
  495. package/src/theme/subtle-tone-weight.test.ts +163 -0
  496. package/src/theme/surface-planes.test.ts +37 -0
  497. package/src/theme/surface-planes.ts +68 -0
  498. package/src/theme/tailwind-var-opacity.test.ts +163 -0
  499. package/src/theme/token-export-guard.test.ts +1 -3
  500. package/src/theme/tokens/primitives.test.ts +15 -7
  501. package/src/theme/tokens/primitives.ts +98 -33
  502. package/src/theme/tokens/semantic-hex-guard.test.ts +322 -0
  503. package/src/theme/tokens/semantic.ts +124 -54
  504. package/src/theme/wash-ladder.test.ts +58 -0
  505. package/src/theme/workout-tokens.ts +1 -7
  506. package/src/types/jest-axe.d.ts +2 -6
  507. package/src/types/raw-md.d.ts +5 -0
  508. package/src/utils/colors.alpha-adoption.test.ts +408 -0
  509. package/src/utils/colors.ts +4 -4
  510. package/src/utils/form.ts +1 -3
  511. package/src/utils/index.ts +2 -7
  512. package/src/utils/number-format.ts +17 -0
  513. package/src/utils/workout-format.ts +15 -6
  514. package/src/web-jsx/jsx-runtime.ts +1 -5
  515. package/tailwind.config.js +55 -9
  516. package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +0 -151
  517. package/src/components/custom/Workout/RowInventory.stories.tsx +0 -408
  518. package/src/components/custom/Workout/S3FullRail.stories.tsx +0 -46
  519. package/src/components/custom/Workout/S3SessionPace.stories.tsx +0 -95
  520. package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +0 -61
  521. package/src/components/custom/Workout/S3SetTypes.stories.tsx +0 -176
  522. package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +0 -240
  523. package/src/components/shell/DashboardShell.stories.tsx +0 -65
  524. package/src/components/shell/DashboardShell.tsx +0 -75
  525. package/src/components/shell/SessionRailSpecimen.stories.tsx +0 -753
  526. package/src/theme/elevation.stories.tsx +0 -346
  527. package/src/utils/useTheme.ts +0 -72
  528. /package/src/components/shell/{DeviceIndicator.test.tsx → workout/DeviceIndicator.test.tsx} +0 -0
  529. /package/src/components/shell/{DeviceMenu.test.tsx → workout/DeviceMenu.test.tsx} +0 -0
  530. /package/src/components/shell/{DeviceRow.test.tsx → workout/DeviceRow.test.tsx} +0 -0
  531. /package/src/components/shell/{SessionStatePill.test.tsx → workout/SessionStatePill.test.tsx} +0 -0
@@ -1,16 +1,13 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
2
  import { useState } from 'react'
3
- import { View, Text, Pressable } from 'react-native'
4
- import { VelocityStrip, type VelocityZoneBandProp } from './VelocityStrip'
5
- import { PlaceholderStrip } from './PlaceholderStrip'
6
- import { PrBadge } from './PrBadge'
3
+ import { View } from 'react-native'
4
+ import { type VelocityZoneBandProp } from './VelocityStrip'
7
5
  import { SetRow, type SetRowProps } from './SetRow'
8
6
  import { type SetStripSet } from './SetStrip'
9
7
  import { SetTableHeader } from './SetTableHeader'
10
8
  import { ExerciseCardHeading } from './ExerciseCardHeading'
11
9
  import { type ExerciseIndicatorKind } from './ExerciseIndicator'
12
- import { roundWeight } from '../../../utils/workout-format'
13
- import { getSemanticColors } from '../../../theme/tokens/semantic'
10
+ import { resolveColor } from '../../../theme/resolve-color'
14
11
 
15
12
  export interface ExerciseCardProps {
16
13
  name: string
@@ -33,12 +30,19 @@ export interface ExerciseCardProps {
33
30
  unit: 'lbs' | 'kg'
34
31
  }
35
32
  isPR?: boolean
36
- /** Collapsed glance: per-set logged velocities (drives the mini strips). */
33
+ /**
34
+ * Collapsed glance: per-set logged velocities. Projected onto `setStates` for the
35
+ * row's `SetStrip` — pass `setStates` directly for anything richer than done/todo.
36
+ */
37
37
  setVelocities?: number[][]
38
- /** Optional velocity-zone bands shared across this exercise's sets (WA bands). */
38
+ /** Velocity-zone bands shared across this exercise's sets (WA bands). Expanded body only. */
39
39
  velocityZones?: readonly VelocityZoneBandProp[]
40
- /** Collapsed glance: total planned sets (placeholder strips fill the remainder). */
40
+ /** Collapsed glance: total planned sets (todo bars fill the remainder). */
41
41
  totalPlannedSets?: number
42
+ /** The row the user has chosen — a persistent wash on the collapsed / upcoming row. */
43
+ isSelected?: boolean
44
+ /** This exercise is being performed right now — the name takes the live tone. */
45
+ isLive?: boolean
42
46
  /** Expanded body: the per-set rows (done / live / todo). */
43
47
  sets?: SetRowProps[]
44
48
  tempo?: [number, number, number, number]
@@ -46,11 +50,14 @@ export interface ExerciseCardProps {
46
50
  previousBest?: string
47
51
  supersetPosition?: 'first' | 'last' | 'middle' | null
48
52
  supersetColor?: string
49
- /** Expanded header per-set strip override; derived from `sets` when omitted. */
53
+ /**
54
+ * Per-set strip override for the collapsed and expanded rows; derived from
55
+ * `setVelocities` (collapsed) or `sets` (expanded) when omitted.
56
+ */
50
57
  setStates?: SetStripSet[]
51
- /** A small PR / issue / info chip in the title line (expanded header). */
58
+ /** A small PR / issue / info chip in the title line. */
52
59
  indicator?: ExerciseIndicatorKind
53
- /** Expanded header strip height in px. Default 8. */
60
+ /** Row strip height in px. Default 8. */
54
61
  stripHeight?: number
55
62
  }
56
63
 
@@ -104,19 +111,28 @@ function getSupersetMargin(
104
111
  return {}
105
112
  }
106
113
 
107
- function formatSummary(summary: ExerciseCardProps['summary']): string {
108
- if (!summary) return ''
109
- return `${summary.sets}×${summary.reps} @ ${roundWeight(summary.weight)} ${summary.unit}`
114
+ /** The chrome a card wraps its row in: superset corner radii + the inter-card margin. */
115
+ function supersetChrome(position: ExerciseCardProps['supersetPosition']): Record<string, number> {
116
+ return { ...getSupersetBorderRadius(position), ...getSupersetMargin(position) }
110
117
  }
111
118
 
112
- function formatAccessibilityLabel(
113
- name: string,
114
- summary: ExerciseCardProps['summary'],
115
- prescription: ExerciseCardProps['prescription']
116
- ): string {
117
- if (summary) return `${name}, ${formatSummary(summary)}`
118
- if (prescription) return `${name}, ${prescription}`
119
- return name
119
+ /** Project the collapsed glance's velocities + planned total onto the strip's per-set states. */
120
+ function deriveCollapsedSetStates(
121
+ setVelocities: number[][] | undefined,
122
+ totalPlannedSets: number | undefined,
123
+ plannedReps: number
124
+ ): SetStripSet[] {
125
+ const done = (setVelocities ?? []).map(
126
+ (velocities): SetStripSet => ({ status: 'done', velocities })
127
+ )
128
+ const remaining = Math.max(0, (totalPlannedSets ?? 0) - done.length)
129
+ return [
130
+ ...done,
131
+ ...Array.from(
132
+ { length: remaining },
133
+ (): SetStripSet => ({ status: 'todo', planned: plannedReps })
134
+ ),
135
+ ]
120
136
  }
121
137
 
122
138
  function CollapsedCard({
@@ -124,92 +140,40 @@ function CollapsedCard({
124
140
  onToggle,
125
141
  summary,
126
142
  isPR,
143
+ indicator,
127
144
  setVelocities,
128
- velocityZones,
145
+ setStates,
129
146
  totalPlannedSets,
147
+ stripHeight,
130
148
  supersetPosition,
149
+ isSelected,
150
+ isLive,
131
151
  }: CardBodyProps) {
132
- const [pressed, setPressed] = useState(false)
133
- const completedSets = setVelocities?.length ?? 0
134
- const remaining = Math.max(0, (totalPlannedSets ?? 0) - completedSets)
135
- const borderRadius = getSupersetBorderRadius(supersetPosition)
136
- const supersetMargin = getSupersetMargin(supersetPosition)
137
-
138
- return (
139
- <Pressable
140
- onPress={onToggle}
141
- onPressIn={() => setPressed(true)}
142
- onPressOut={() => setPressed(false)}
143
- accessibilityRole="button"
144
- accessibilityLabel={formatAccessibilityLabel(name, summary, undefined)}
145
- testID="exercise-card"
146
- >
147
- <View
148
- className={pressed ? 'bg-surface-raised' : undefined}
149
- style={{
150
- padding: 12,
151
- paddingHorizontal: 14,
152
- cursor: 'pointer',
153
- ...borderRadius,
154
- ...supersetMargin,
155
- }}
156
- >
157
- <View className="flex-row items-center" testID="exercise-card-header">
158
- <Text
159
- className="text-text-primary"
160
- style={{
161
- fontSize: 14,
162
- fontFamily: '"Space Grotesk", sans-serif',
163
- fontWeight: '700',
164
- }}
165
- testID="exercise-card-name"
166
- >
167
- {name}
168
- </Text>
169
- <View className="flex-1" />
170
- {summary && (
171
- <Text
172
- className="text-text-secondary"
173
- style={{
174
- fontSize: 12,
175
- fontFamily: 'Inter, sans-serif',
176
- marginRight: 8,
177
- }}
178
- testID="exercise-card-summary"
179
- >
180
- {formatSummary(summary)}
181
- </Text>
182
- )}
183
- {isPR && (
184
- <View style={{ marginLeft: 4 }}>
185
- <PrBadge type="e1rm" compact animate={false} />
186
- </View>
187
- )}
188
- </View>
152
+ const plannedReps = typeof summary?.reps === 'number' ? summary.reps : 0
153
+ const row = {
154
+ density: 'compact' as const,
155
+ name,
156
+ indicator: indicator ?? (isPR ? ('pr' as const) : undefined),
157
+ setStates: setStates ?? deriveCollapsedSetStates(setVelocities, totalPlannedSets, plannedReps),
158
+ stripHeight,
159
+ isSelected,
160
+ isLive,
161
+ onPress: onToggle,
162
+ style: supersetChrome(supersetPosition),
163
+ }
189
164
 
190
- {(completedSets > 0 || remaining > 0) && (
191
- <View className="flex-row" style={{ marginTop: 6, gap: 4 }} testID="exercise-card-strips">
192
- {setVelocities?.map((velocities, i) => (
193
- <VelocityStrip
194
- key={i}
195
- velocities={velocities}
196
- zones={velocityZones}
197
- variant="compact"
198
- height={8}
199
- hideBaseline
200
- testID={`exercise-card-velocity-strip-${i}`}
201
- />
202
- ))}
203
- {Array.from({ length: remaining }, (_, i) => (
204
- <PlaceholderStrip
205
- key={`placeholder-${i}`}
206
- testID={`exercise-card-placeholder-${i}`}
207
- />
208
- ))}
209
- </View>
210
- )}
211
- </View>
212
- </Pressable>
165
+ // Two call shapes, not a conditional spread: the prescription union only narrows
166
+ // when every field of a member is present at the call site.
167
+ return summary ? (
168
+ <ExerciseCardHeading
169
+ {...row}
170
+ sets={summary.sets}
171
+ reps={summary.reps}
172
+ load={summary.weight}
173
+ unit={summary.unit}
174
+ />
175
+ ) : (
176
+ <ExerciseCardHeading {...row} />
213
177
  )
214
178
  }
215
179
 
@@ -218,10 +182,6 @@ function CollapsedCard({
218
182
  // is the real ExerciseCardHeading, and the revealed body drops PREV and renders
219
183
  // one real SetRow per set (muted done/todo, brightened live spotlight).
220
184
 
221
- const DARK = getSemanticColors('dark')
222
- /** The header↔body seam. */
223
- const BODY_DIVIDER = DARK['hairline-subtle']
224
-
225
185
  /** Project the set rows onto the heading strip's per-set state (done / active / todo). */
226
186
  function deriveHeaderSetStates(sets: SetRowProps[]): SetStripSet[] {
227
187
  return sets.map((set): SetStripSet => {
@@ -248,9 +208,8 @@ function ExpandedCard({
248
208
  stripHeight = 8,
249
209
  velocityZones,
250
210
  supersetPosition,
211
+ isLive,
251
212
  }: CardBodyProps) {
252
- const borderRadius = getSupersetBorderRadius(supersetPosition)
253
- const supersetMargin = getSupersetMargin(supersetPosition)
254
213
  // Summary is the card-level authority for the weight column; fall back to the
255
214
  // first set's unit, then lbs. (Mixed per-set units keep the card-level label.)
256
215
  const unit = summary?.unit ?? sets?.[0]?.unit ?? 'lbs'
@@ -263,11 +222,7 @@ function ExpandedCard({
263
222
  return (
264
223
  <View
265
224
  className="bg-surface-elevated border-hairline"
266
- style={{
267
- borderWidth: 1,
268
- ...borderRadius,
269
- ...supersetMargin,
270
- }}
225
+ style={{ borderWidth: 1, ...supersetChrome(supersetPosition) }}
271
226
  testID="exercise-card"
272
227
  >
273
228
  <ExerciseCardHeading
@@ -280,12 +235,18 @@ function ExpandedCard({
280
235
  indicator={headerIndicator}
281
236
  setStates={headerStates}
282
237
  stripHeight={stripHeight}
238
+ isLive={isLive}
283
239
  onPress={onToggle}
284
240
  testID="exercise-card-heading"
285
241
  />
286
242
 
243
+ {/* The header↔body seam. */}
287
244
  <View
288
- style={{ borderTopWidth: 1, borderTopColor: BODY_DIVIDER, paddingBottom: 6 }}
245
+ style={{
246
+ borderTopWidth: 1,
247
+ borderTopColor: resolveColor('hairline-subtle'),
248
+ paddingBottom: 6,
249
+ }}
289
250
  testID="exercise-card-body"
290
251
  >
291
252
  <SetTableHeader unit={unit} showPrevious={false} testID="exercise-card-column-headers" />
@@ -306,81 +267,36 @@ function UpcomingCard({
306
267
  name,
307
268
  prescription,
308
269
  previousBest,
270
+ indicator,
309
271
  supersetPosition,
272
+ isSelected,
310
273
  onToggle,
311
274
  }: CardBodyProps) {
312
- const borderRadius = getSupersetBorderRadius(supersetPosition)
313
- const supersetMargin = getSupersetMargin(supersetPosition)
275
+ const row = {
276
+ density: 'upcoming' as const,
277
+ name,
278
+ previousBest,
279
+ indicator,
280
+ isSelected,
281
+ onPress: onToggle,
282
+ style: supersetChrome(supersetPosition),
283
+ }
314
284
 
315
- return (
316
- <Pressable onPress={onToggle} accessibilityRole="button">
317
- <View
318
- style={{
319
- opacity: 0.6,
320
- padding: 12,
321
- paddingHorizontal: 14,
322
- ...borderRadius,
323
- ...supersetMargin,
324
- }}
325
- accessibilityLabel={formatAccessibilityLabel(name, undefined, prescription)}
326
- testID="exercise-card"
327
- >
328
- <View className="flex-row items-center">
329
- {/* Name never truncates (no numberOfLines); prescription + previousBest ellipsize first. */}
330
- <Text
331
- className="text-text-primary"
332
- style={{
333
- flexShrink: 0,
334
- fontSize: 14,
335
- fontFamily: '"Space Grotesk", sans-serif',
336
- fontWeight: '700',
337
- }}
338
- testID="exercise-card-name"
339
- >
340
- {name}
341
- </Text>
342
- {prescription && (
343
- <Text
344
- className="text-text-secondary"
345
- numberOfLines={1}
346
- style={{
347
- flexShrink: 1,
348
- fontSize: 12,
349
- fontFamily: 'Inter, sans-serif',
350
- marginLeft: 8,
351
- }}
352
- testID="exercise-card-prescription"
353
- >
354
- {prescription}
355
- </Text>
356
- )}
357
- <View className="flex-1" style={{ minWidth: 8 }} />
358
- {previousBest && (
359
- <Text
360
- className="text-text-tertiary"
361
- numberOfLines={1}
362
- style={{
363
- flexShrink: 1,
364
- fontSize: 11,
365
- fontFamily: 'Inter, sans-serif',
366
- }}
367
- testID="exercise-card-previous-best"
368
- >
369
- {previousBest}
370
- </Text>
371
- )}
372
- </View>
373
- </View>
374
- </Pressable>
285
+ return prescription !== undefined ? (
286
+ <ExerciseCardHeading {...row} prescription={prescription} />
287
+ ) : (
288
+ <ExerciseCardHeading {...row} />
375
289
  )
376
290
  }
377
291
 
378
292
  /**
379
- * The data-contract exercise card, in three representations:
380
- * - `upcoming` a dimmed, not-yet-reached row (prescription + previous best).
381
- * - collapsed — a glance: name + summary + per-set velocity strips.
382
- * - expandedthe unified card: the real {@link ExerciseCardHeading} header over
383
- * the SET · REPS · LBS · RPE body (one {@link SetRow} per set).
293
+ * The data-contract exercise card, in three representations — all three now ONE
294
+ * {@link ExerciseCardHeading}, selected by its `density`, rather than three hand-rolled
295
+ * heads:
296
+ * - `upcoming`a dimmed, not-yet-reached row (`density="upcoming"`).
297
+ * - collapsed a glance: name + prescription + the per-set strip (`density="compact"`).
298
+ * - expanded — the `rail` heading over the SET · REPS · LBS · RPE body (one
299
+ * {@link SetRow} per set).
384
300
  *
385
301
  * Expand is controlled (`expanded` + `onExpandedChange`) or uncontrolled
386
302
  * (`defaultExpanded`, internal state). `upcoming` overrides expand.
@@ -1,31 +1,38 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
- import { View } from 'react-native'
3
2
  import { ExerciseCardHeading } from './ExerciseCardHeading'
4
3
  import type { SetStripSet } from './SetStrip'
4
+ import { Surface } from '../../ui/surface'
5
5
 
6
6
  /**
7
- * `ExerciseCardHeading` — the complete, standalone session-rail heading: an
8
- * `ExerciseHeading` info block over its per-set `SetStrip`. This is the unit the rail
9
- * lists; it is independent of `ExerciseCard` (whose `state="rail"` delegates here).
10
- * An empty `setStates` renders no strip.
7
+ * `ExerciseCardHeading` — THE exercise row. One component for the three densities the
8
+ * workout surfaces list an exercise in: the session-rail heading (`rail`), the collapsed
9
+ * card row (`compact`) and the not-yet-reached row (`upcoming`). An `ExerciseHeading` info
10
+ * block over its per-set `SetStrip`; an empty `setStates` renders no strip.
11
11
  */
12
12
  const meta: Meta<typeof ExerciseCardHeading> = {
13
- title: 'Workout/ExerciseCardHeading',
13
+ title: 'Custom/Workout/ExerciseCardHeading',
14
14
  component: ExerciseCardHeading,
15
15
  tags: ['autodocs'],
16
16
  parameters: {
17
17
  docs: {
18
18
  description: {
19
19
  component:
20
- '**Molecule.** The complete standalone session-rail heading. Composes ' +
21
- '[ExerciseHeading](?path=/docs/workout-exerciseheading--docs) + ' +
22
- '[SetStrip](?path=/docs/workout-setstrip--docs). ' +
20
+ '**Molecule.** The exercise row, in three densities driven by props rather than by ' +
21
+ 'three call sites (TD-03.56). Composes ' +
22
+ '[ExerciseHeading](?path=/docs/custom-workout-exerciseheading--docs) + ' +
23
+ '[SetStrip](?path=/docs/custom-workout-setstrip--docs). ' +
24
+ 'Interaction states (TD-03.55) are `interactive-*` token washes — press > selection > ' +
25
+ 'hover — plus an `isLive` name tone. All static: this row renders on the wall during a ' +
26
+ 'set, so nothing here animates. Hover is web-only; on a touch surface the row simply ' +
27
+ 'has no hover state and selection carries the same meaning. ' +
23
28
  'Used-by ↑ [SessionRail](?path=/docs/shell-sessionrail--docs); ' +
24
- '[ExerciseCard](?path=/docs/workout-exercisecard--docs) `state="rail"` delegates here.',
29
+ '[ExerciseCard](?path=/docs/custom-workout-exercisecard--docs) delegates all three of its ' +
30
+ 'representations here.',
25
31
  },
26
32
  },
27
33
  },
28
34
  argTypes: {
35
+ density: { control: 'inline-radio', options: ['rail', 'compact', 'upcoming'] },
29
36
  unit: { control: 'select', options: ['lbs', 'kg'] },
30
37
  indicator: {
31
38
  control: 'select',
@@ -33,13 +40,15 @@ const meta: Meta<typeof ExerciseCardHeading> = {
33
40
  },
34
41
  stripHeight: { control: { type: 'range', min: 2, max: 16, step: 1 } },
35
42
  dimmed: { control: 'boolean' },
43
+ isSelected: { control: 'boolean' },
44
+ isLive: { control: 'boolean' },
36
45
  onPress: { action: 'press' },
37
46
  },
38
47
  decorators: [
39
48
  (Story) => (
40
- <View style={{ width: 246, backgroundColor: '#131313' }}>
49
+ <Surface level="base" style={{ width: 246 }}>
41
50
  <Story />
42
- </View>
51
+ </Surface>
43
52
  ),
44
53
  ],
45
54
  }
@@ -68,6 +77,80 @@ export const Default: Story = {
68
77
  setStates: inProgress,
69
78
  stripHeight: 8,
70
79
  },
80
+ parameters: {
81
+ docs: {
82
+ description: {
83
+ story: 'The `rail` density: two lines (name, then prescription beside the tempo).',
84
+ },
85
+ },
86
+ },
87
+ }
88
+
89
+ export const Compact: Story = {
90
+ args: {
91
+ ...Default.args,
92
+ density: 'compact',
93
+ },
94
+ parameters: {
95
+ docs: {
96
+ description: {
97
+ story:
98
+ 'The collapsed card row: name and prescription on ONE line, strip below. The tempo is ' +
99
+ 'dropped — a single line has no room for a second metric lockup.',
100
+ },
101
+ },
102
+ },
103
+ }
104
+
105
+ export const Upcoming: Story = {
106
+ args: {
107
+ name: 'Overhead Press',
108
+ density: 'upcoming',
109
+ prescription: '3×8-12 @ RPE 8',
110
+ previousBest: '135 lbs × 10',
111
+ },
112
+ parameters: {
113
+ docs: {
114
+ description: {
115
+ story:
116
+ 'A not-yet-reached exercise: `compact`, dimmed by its own density, with the previous ' +
117
+ 'best pinned right. `prescription` is the free-text alternative to the structured ' +
118
+ '`sets` / `reps` / `load` triple, for an exercise whose numbers are not loaded yet.',
119
+ },
120
+ },
121
+ },
122
+ }
123
+
124
+ export const Live: Story = {
125
+ args: {
126
+ ...Default.args,
127
+ isLive: true,
128
+ },
129
+ parameters: {
130
+ docs: {
131
+ description: {
132
+ story:
133
+ 'The exercise being performed right now — the name takes `status-live`. A tone change, ' +
134
+ 'never motion: this row is on a wall display during a set.',
135
+ },
136
+ },
137
+ },
138
+ }
139
+
140
+ export const Selected: Story = {
141
+ args: {
142
+ ...Default.args,
143
+ isSelected: true,
144
+ },
145
+ parameters: {
146
+ docs: {
147
+ description: {
148
+ story:
149
+ 'The row the user has chosen: a persistent `interactive-selected` wash. Hover the row ' +
150
+ 'to see the lighter `interactive-hover` wash it outranks.',
151
+ },
152
+ },
153
+ },
71
154
  }
72
155
 
73
156
  export const Dimmed: Story = {