@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.
Files changed (509) hide show
  1. package/README.md +46 -47
  2. package/dist/bodymap.js +768 -701
  3. package/dist/bodymap.js.map +1 -1
  4. package/dist/bodymap.mjs +770 -703
  5. package/dist/bodymap.mjs.map +1 -1
  6. package/dist/index.d.mts +1319 -216
  7. package/dist/index.d.ts +1319 -216
  8. package/dist/index.js +5996 -4498
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +5773 -4357
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/{pages-CvsFqNtx.d.mts → pages-CpW3OB3g.d.mts} +21 -3
  13. package/dist/{pages-B1hqnGIC.d.ts → pages-_3Ajpiqu.d.ts} +21 -3
  14. package/dist/pages.d.mts +1 -1
  15. package/dist/pages.d.ts +1 -1
  16. package/dist/pages.js +1435 -1428
  17. package/dist/pages.js.map +1 -1
  18. package/dist/pages.mjs +1443 -1436
  19. package/dist/pages.mjs.map +1 -1
  20. package/dist/theme/index.d.mts +195 -134
  21. package/dist/theme/index.d.ts +195 -134
  22. package/dist/theme/index.js +215 -300
  23. package/dist/theme/index.js.map +1 -1
  24. package/dist/theme/index.mjs +202 -296
  25. package/dist/theme/index.mjs.map +1 -1
  26. package/dist/theme/tokens-css.js +62 -5
  27. package/dist/theme/tokens-css.js.map +1 -1
  28. package/dist/theme/tokens-css.mjs +62 -5
  29. package/dist/theme/tokens-css.mjs.map +1 -1
  30. package/dist/theme/tokens.d.mts +73 -5
  31. package/dist/theme/tokens.d.ts +73 -5
  32. package/dist/theme/tokens.js +55 -7
  33. package/dist/theme/tokens.js.map +1 -1
  34. package/dist/theme/tokens.mjs +55 -8
  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/package.json +1 -1
  40. package/src/arch/arch-graph.freshness.test.ts +52 -0
  41. package/src/arch/arch-graph.json +5021 -1617
  42. package/src/components/custom/ActiveWork/CoChangeChip.stories.tsx +44 -0
  43. package/src/components/custom/ActiveWork/CoChangeChip.test.tsx +29 -0
  44. package/src/components/custom/ActiveWork/CoChangeChip.tsx +48 -0
  45. package/src/components/custom/ActiveWork/Eyebrow.stories.tsx +25 -0
  46. package/src/components/custom/ActiveWork/Eyebrow.test.tsx +16 -0
  47. package/src/components/custom/ActiveWork/Eyebrow.tsx +26 -0
  48. package/src/components/custom/ActiveWork/FileActivityDetail.stories.tsx +50 -0
  49. package/src/components/custom/ActiveWork/FileActivityDetail.test.tsx +66 -0
  50. package/src/components/custom/ActiveWork/FileActivityDetail.tsx +202 -0
  51. package/src/components/custom/ActiveWork/FileActivityRow.stories.tsx +70 -0
  52. package/src/components/custom/ActiveWork/FileActivityRow.test.tsx +67 -0
  53. package/src/components/custom/ActiveWork/FileActivityRow.tsx +130 -0
  54. package/src/components/custom/ActiveWork/FileHistoryExplorer.stories.tsx +84 -0
  55. package/src/components/custom/ActiveWork/FileHistoryExplorer.test.tsx +99 -0
  56. package/src/components/custom/ActiveWork/FileHistoryExplorer.tsx +138 -0
  57. package/src/components/custom/ActiveWork/FilePathLabel.stories.tsx +39 -0
  58. package/src/components/custom/ActiveWork/FilePathLabel.test.tsx +53 -0
  59. package/src/components/custom/ActiveWork/FilePathLabel.tsx +63 -0
  60. package/src/components/custom/ActiveWork/InitiativeBrief.stories.tsx +50 -0
  61. package/src/components/custom/ActiveWork/InitiativeBrief.test.tsx +62 -0
  62. package/src/components/custom/ActiveWork/InitiativeBrief.tsx +181 -0
  63. package/src/components/custom/ActiveWork/InitiativeCard.stories.tsx +73 -0
  64. package/src/components/custom/ActiveWork/InitiativeCard.test.tsx +68 -0
  65. package/src/components/custom/ActiveWork/InitiativeCard.tsx +136 -0
  66. package/src/components/custom/ActiveWork/InitiativeHeader.stories.tsx +59 -0
  67. package/src/components/custom/ActiveWork/InitiativeHeader.test.tsx +40 -0
  68. package/src/components/custom/ActiveWork/InitiativeHeader.tsx +73 -0
  69. package/src/components/custom/ActiveWork/InitiativeReader.stories.tsx +145 -0
  70. package/src/components/custom/ActiveWork/OpenLoops.stories.tsx +58 -0
  71. package/src/components/custom/ActiveWork/OpenLoops.test.tsx +61 -0
  72. package/src/components/custom/ActiveWork/OpenLoops.tsx +107 -0
  73. package/src/components/custom/ActiveWork/PortfolioOverview.stories.tsx +101 -0
  74. package/src/components/custom/ActiveWork/PortfolioOverview.test.tsx +81 -0
  75. package/src/components/custom/ActiveWork/PortfolioOverview.tsx +83 -0
  76. package/src/components/custom/ActiveWork/README.md +305 -0
  77. package/src/components/custom/ActiveWork/SessionDetail.stories.tsx +71 -0
  78. package/src/components/custom/ActiveWork/SessionDetail.test.tsx +84 -0
  79. package/src/components/custom/ActiveWork/SessionDetail.tsx +180 -0
  80. package/src/components/custom/ActiveWork/SessionList.stories.tsx +69 -0
  81. package/src/components/custom/ActiveWork/SessionList.test.tsx +78 -0
  82. package/src/components/custom/ActiveWork/SessionList.tsx +103 -0
  83. package/src/components/custom/ActiveWork/SessionListItem.stories.tsx +64 -0
  84. package/src/components/custom/ActiveWork/SessionListItem.test.tsx +62 -0
  85. package/src/components/custom/ActiveWork/SessionListItem.tsx +152 -0
  86. package/src/components/custom/ActiveWork/SessionReader.stories.tsx +113 -0
  87. package/src/components/custom/ActiveWork/SeverityLabel.stories.tsx +51 -0
  88. package/src/components/custom/ActiveWork/SeverityLabel.test.tsx +68 -0
  89. package/src/components/custom/ActiveWork/SeverityLabel.tsx +97 -0
  90. package/src/components/custom/ActiveWork/TaskRow.stories.tsx +63 -0
  91. package/src/components/custom/ActiveWork/TaskRow.test.tsx +112 -0
  92. package/src/components/custom/ActiveWork/TaskRow.tsx +176 -0
  93. package/src/components/custom/ActiveWork/TaskTable.stories.tsx +123 -0
  94. package/src/components/custom/ActiveWork/TaskTable.test.tsx +233 -0
  95. package/src/components/custom/ActiveWork/TaskTable.tsx +321 -0
  96. package/src/components/custom/ActiveWork/active-work-depth.test.tsx +102 -0
  97. package/src/components/custom/ActiveWork/file-history-fixture.ts +91 -0
  98. package/src/components/custom/ActiveWork/format-time.test.ts +23 -0
  99. package/src/components/custom/ActiveWork/format-time.ts +32 -0
  100. package/src/components/custom/ActiveWork/index.ts +19 -0
  101. package/src/components/custom/ActiveWork/initiative-fixture.ts +104 -0
  102. package/src/components/custom/ActiveWork/session-fixture.ts +267 -0
  103. package/src/components/custom/ActiveWork/session-linkers.ts +56 -0
  104. package/src/components/custom/ActiveWork/task-list-fixture.ts +97 -0
  105. package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +4 -3
  106. package/src/components/custom/DateTime/DateTime.stories.tsx +2 -1
  107. package/src/components/custom/DateTime/DateTime.test.tsx +3 -1
  108. package/src/components/custom/EmptyState/EmptyState.stories.tsx +22 -9
  109. package/src/components/custom/EmptyState/EmptyState.test.tsx +7 -13
  110. package/src/components/custom/EmptyState/EmptyState.tsx +4 -18
  111. package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +4 -3
  112. package/src/components/custom/Fatigue/FatigueLights.stories.tsx +2 -2
  113. package/src/components/custom/Fatigue/GhostSpark.stories.tsx +4 -3
  114. package/src/components/custom/Fatigue/LiveFatigueCard.stories.tsx +2 -2
  115. package/src/components/custom/Fatigue/LiveFatigueCard.tsx +8 -4
  116. package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +116 -6
  117. package/src/components/custom/Fatigue/LiveFatiguePanel.test.tsx +134 -0
  118. package/src/components/custom/Fatigue/LiveFatiguePanel.tsx +54 -83
  119. package/src/components/custom/Fatigue/README.md +40 -3
  120. package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +2 -2
  121. package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +2 -1
  122. package/src/components/custom/Fatigue/VelocityHero.stories.tsx +4 -3
  123. package/src/components/custom/Fatigue/VerdictHero.stories.tsx +2 -2
  124. package/src/components/custom/Fatigue/fatigue-mock.test.ts +0 -5
  125. package/src/components/custom/Fatigue/fatigue-mock.ts +2 -10
  126. package/src/components/custom/Fatigue/fatigue-tokens.ts +12 -0
  127. package/src/components/custom/Fatigue/index.ts +17 -5
  128. package/src/components/custom/Fatigue/panel-layout.test.ts +149 -0
  129. package/src/components/custom/Fatigue/panel-layout.ts +185 -0
  130. package/src/components/custom/Gauge/Gauge.stories.tsx +2 -2
  131. package/src/components/custom/Gauge/Gauge.test.tsx +1 -1
  132. package/src/components/custom/Gauge/Gauge.tsx +4 -1
  133. package/src/components/custom/Metric/Metric.stories.tsx +10 -1
  134. package/src/components/custom/Metric/Metric.test.tsx +1 -3
  135. package/src/components/custom/Metric/Metric.tsx +5 -20
  136. package/src/components/custom/Prose/MarkdownProse.stories.tsx +88 -0
  137. package/src/components/custom/Prose/MarkdownProse.test.tsx +87 -0
  138. package/src/components/custom/Prose/MarkdownProse.tsx +201 -0
  139. package/src/components/custom/Prose/README.md +58 -0
  140. package/src/components/custom/Prose/index.ts +1 -0
  141. package/src/components/custom/Scatter/Scatter.stories.tsx +46 -8
  142. package/src/components/custom/Scatter/Scatter.test.tsx +2 -2
  143. package/src/components/custom/Scatter/Scatter.tsx +116 -14
  144. package/src/components/custom/Sidebar/Sidebar.stories.tsx +40 -7
  145. package/src/components/custom/Sidebar/Sidebar.test.tsx +5 -1
  146. package/src/components/custom/Sidebar/Sidebar.tsx +16 -33
  147. package/src/components/custom/Table/Table.stories.tsx +98 -15
  148. package/src/components/custom/Table/Table.test.tsx +273 -86
  149. package/src/components/custom/Table/Table.tsx +159 -67
  150. package/src/components/custom/Table/column-fit.test.ts +75 -0
  151. package/src/components/custom/Table/column-fit.ts +116 -0
  152. package/src/components/custom/Table/index.ts +9 -0
  153. package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +8 -6
  154. package/src/components/custom/Treemap/Treemap.stories.tsx +2 -2
  155. package/src/components/custom/Treemap/Treemap.test.tsx +1 -1
  156. package/src/components/custom/Treemap/Treemap.tsx +7 -2
  157. package/src/components/custom/Typography/Typography.stories.tsx +28 -1
  158. package/src/components/custom/Typography/Typography.test.tsx +34 -0
  159. package/src/components/custom/Typography/Typography.tsx +6 -0
  160. package/src/components/custom/Typography/index.ts +1 -8
  161. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +9 -1
  162. package/src/components/custom/Workout/BaseBadge.stories.tsx +19 -7
  163. package/src/components/custom/Workout/BaseBadge.test.tsx +31 -11
  164. package/src/components/custom/Workout/BaseBadge.tsx +3 -0
  165. package/src/components/custom/Workout/BodyMap.stories.tsx +4 -1
  166. package/src/components/custom/Workout/BodyMap.tsx +3 -1
  167. package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +16 -4
  168. package/src/components/custom/Workout/BodyMapDetailPanel.test.tsx +11 -15
  169. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +45 -9
  170. package/src/components/custom/Workout/CapacityBandChart.stories.tsx +10 -8
  171. package/src/components/custom/Workout/CapacityBandChart.test.tsx +24 -51
  172. package/src/components/custom/Workout/CapacityBandChart.tsx +139 -108
  173. package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
  174. package/src/components/custom/Workout/DeviationBar.test.tsx +5 -2
  175. package/src/components/custom/Workout/DeviationBar.tsx +21 -16
  176. package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +182 -0
  177. package/src/components/custom/Workout/DualVelocityStrip.test.tsx +3 -2
  178. package/src/components/custom/Workout/ExerciseCard.stories.tsx +8 -7
  179. package/src/components/custom/Workout/ExerciseCard.test.tsx +13 -3
  180. package/src/components/custom/Workout/ExerciseCard.tsx +42 -51
  181. package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +7 -7
  182. package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +9 -1
  183. package/src/components/custom/Workout/ExerciseHeading.stories.tsx +8 -8
  184. package/src/components/custom/Workout/ExerciseHeading.tsx +8 -5
  185. package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +6 -4
  186. package/src/components/custom/Workout/ExerciseIndicator.test.tsx +27 -11
  187. package/src/components/custom/Workout/ExerciseIndicator.tsx +14 -15
  188. package/src/components/custom/Workout/FatigueMeter.stories.tsx +5 -5
  189. package/src/components/custom/Workout/InputBar.stories.tsx +4 -5
  190. package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
  191. package/src/components/custom/Workout/IntensityBar.test.tsx +5 -1
  192. package/src/components/custom/Workout/IntensityBar.tsx +4 -2
  193. package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +5 -4
  194. package/src/components/custom/Workout/MesoCard.stories.tsx +4 -1
  195. package/src/components/custom/Workout/MesoCard.test.tsx +7 -18
  196. package/src/components/custom/Workout/MesoCard.tsx +12 -9
  197. package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
  198. package/src/components/custom/Workout/MesoProgressBar.test.tsx +18 -11
  199. package/src/components/custom/Workout/MesoProgressBar.tsx +15 -13
  200. package/src/components/custom/Workout/MesoStatusCard.stories.tsx +2 -1
  201. package/src/components/custom/Workout/MesoStatusCard.test.tsx +14 -44
  202. package/src/components/custom/Workout/MesoStatusCard.tsx +9 -23
  203. package/src/components/custom/Workout/MetricTiles.stories.tsx +3 -2
  204. package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +2 -8
  205. package/src/components/custom/Workout/MuscleGroupChip.test.tsx +23 -31
  206. package/src/components/custom/Workout/MuscleGroupChip.tsx +28 -67
  207. package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +1 -1
  208. package/src/components/custom/Workout/PlaceholderStrip.test.tsx +17 -10
  209. package/src/components/custom/Workout/PlaceholderStrip.tsx +9 -8
  210. package/src/components/custom/Workout/PrBadge.stories.tsx +2 -1
  211. package/src/components/custom/Workout/PrBadge.test.tsx +3 -9
  212. package/src/components/custom/Workout/PrBadge.tsx +13 -17
  213. package/src/components/custom/Workout/PrHistoryModal.stories.tsx +4 -1
  214. package/src/components/custom/Workout/PrHistoryModal.test.tsx +13 -36
  215. package/src/components/custom/Workout/PrHistoryModal.tsx +8 -4
  216. package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +6 -1
  217. package/src/components/custom/Workout/README.md +11 -6
  218. package/src/components/custom/Workout/ReadinessCheck.stories.tsx +3 -2
  219. package/src/components/custom/Workout/ReadinessCheck.test.tsx +12 -18
  220. package/src/components/custom/Workout/ReadinessCheck.tsx +19 -3
  221. package/src/components/custom/Workout/RestTimer.stories.tsx +3 -2
  222. package/src/components/custom/Workout/ScheduleTiles.stories.tsx +3 -2
  223. package/src/components/custom/Workout/ScheduleTiles.test.tsx +3 -4
  224. package/src/components/custom/Workout/ScheduleTiles.tsx +4 -7
  225. package/src/components/custom/Workout/SegmentedBar.stories.tsx +5 -5
  226. package/src/components/custom/Workout/SegmentedBar.tsx +6 -2
  227. package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +5 -5
  228. package/src/components/custom/Workout/SessionHeader.stories.tsx +6 -6
  229. package/src/components/custom/Workout/SessionHeader.tsx +12 -12
  230. package/src/components/custom/Workout/SessionRail.stories.tsx +12 -11
  231. package/src/components/custom/Workout/SessionRail.tsx +7 -9
  232. package/src/components/custom/Workout/SetBar.stories.tsx +10 -6
  233. package/src/components/custom/Workout/SetBar.test.tsx +49 -4
  234. package/src/components/custom/Workout/SetBar.tsx +38 -12
  235. package/src/components/custom/Workout/SetRow.stories.tsx +3 -2
  236. package/src/components/custom/Workout/SetRow.test.tsx +16 -2
  237. package/src/components/custom/Workout/SetRow.tsx +61 -23
  238. package/src/components/custom/Workout/SetStrip.stories.tsx +60 -5
  239. package/src/components/custom/Workout/SetStrip.test.tsx +25 -0
  240. package/src/components/custom/Workout/SetStrip.tsx +24 -4
  241. package/src/components/custom/Workout/SetTableHeader.stories.tsx +6 -6
  242. package/src/components/custom/Workout/SetTableHeader.tsx +9 -12
  243. package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +7 -7
  244. package/src/components/custom/Workout/SetsRepsLoad.tsx +3 -5
  245. package/src/components/custom/Workout/Sparkline.stories.tsx +1 -1
  246. package/src/components/custom/Workout/Sparkline.test.tsx +37 -19
  247. package/src/components/custom/Workout/Sparkline.tsx +14 -18
  248. package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
  249. package/src/components/custom/Workout/StatusDot.test.tsx +44 -5
  250. package/src/components/custom/Workout/StatusDot.tsx +93 -63
  251. package/src/components/custom/Workout/StatusPill.stories.tsx +1 -1
  252. package/src/components/custom/Workout/StatusPill.test.tsx +6 -15
  253. package/src/components/custom/Workout/StatusPill.tsx +27 -38
  254. package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +9 -3
  255. package/src/components/custom/Workout/StrengthTrendChart.test.tsx +15 -37
  256. package/src/components/custom/Workout/StrengthTrendChart.tsx +31 -30
  257. package/src/components/custom/Workout/SupersetWrapper.stories.tsx +27 -64
  258. package/src/components/custom/Workout/SupersetWrapper.test.tsx +16 -14
  259. package/src/components/custom/Workout/SupersetWrapper.tsx +17 -21
  260. package/src/components/custom/Workout/TempoDisplay.stories.tsx +22 -7
  261. package/src/components/custom/Workout/TempoDisplay.test.tsx +18 -4
  262. package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +9 -1
  263. package/src/components/custom/Workout/VelocityStrip.compact.stories.tsx +4 -1
  264. package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +7 -4
  265. package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +4 -1
  266. package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +4 -1
  267. package/src/components/custom/Workout/VelocityStrip.stories.tsx +12 -6
  268. package/src/components/custom/Workout/VelocityStrip.tsx +3 -0
  269. package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +6 -7
  270. package/src/components/custom/Workout/VolumeLandmarkBar.tsx +15 -6
  271. package/src/components/custom/Workout/WeekRow.stories.tsx +4 -1
  272. package/src/components/custom/Workout/WeekRow.test.tsx +18 -15
  273. package/src/components/custom/Workout/WeekRow.tsx +23 -16
  274. package/src/components/custom/Workout/WeightBadge.stories.tsx +2 -1
  275. package/src/components/custom/Workout/WeightBadge.test.tsx +50 -19
  276. package/src/components/custom/Workout/WeightBadge.tsx +28 -27
  277. package/src/components/custom/Workout/WorkoutCard.stories.tsx +6 -1
  278. package/src/components/custom/Workout/WorkoutCard.test.tsx +18 -0
  279. package/src/components/custom/Workout/WorkoutCard.tsx +35 -57
  280. package/src/components/custom/Workout/WorkoutPill.stories.tsx +5 -2
  281. package/src/components/custom/Workout/WorkoutPill.test.tsx +39 -7
  282. package/src/components/custom/Workout/WorkoutPill.tsx +88 -57
  283. package/src/components/custom/Workout/ZoneTrack.stories.tsx +7 -6
  284. package/src/components/custom/Workout/ZoneTrack.tsx +10 -9
  285. package/src/components/custom/Workout/index.ts +5 -0
  286. package/src/components/custom/Workout/metricText.stories.tsx +6 -5
  287. package/src/components/custom/Workout/metricText.tsx +4 -0
  288. package/src/components/custom/Workout/setHeadingKit.tsx +207 -26
  289. package/src/components/custom/charts/README.md +61 -0
  290. package/src/components/custom/charts/SetBarChart.test.tsx +30 -1
  291. package/src/components/custom/charts/SetBarChart.tsx +42 -4
  292. package/src/components/custom/charts/SparkBars.stories.tsx +55 -0
  293. package/src/components/custom/charts/SparkBars.test.tsx +69 -0
  294. package/src/components/custom/charts/SparkBars.tsx +85 -0
  295. package/src/components/custom/charts/index.ts +3 -0
  296. package/src/components/custom/index.ts +3 -0
  297. package/src/components/custom/stepper/Stepper.stories.tsx +13 -10
  298. package/src/components/custom/stepper/Stepper.test.tsx +1 -7
  299. package/src/components/custom/stepper/Stepper.tsx +13 -28
  300. package/src/components/icons/icons.stories.tsx +1 -1
  301. package/src/components/icons/icons.tsx +51 -0
  302. package/src/components/shell/AppShell.stories.tsx +150 -0
  303. package/src/components/shell/AppShell.test.tsx +70 -0
  304. package/src/components/shell/AppShell.tsx +90 -0
  305. package/src/components/shell/BrandLockup.stories.tsx +66 -5
  306. package/src/components/shell/BrandLockup.test.tsx +34 -1
  307. package/src/components/shell/BrandLockup.tsx +27 -11
  308. package/src/components/shell/NavItem.stories.tsx +4 -4
  309. package/src/components/shell/NavItem.test.tsx +17 -0
  310. package/src/components/shell/NavItem.tsx +17 -5
  311. package/src/components/shell/README.md +89 -26
  312. package/src/components/shell/SideNav.stories.tsx +38 -7
  313. package/src/components/shell/SideNav.test.tsx +16 -8
  314. package/src/components/shell/SideNav.tsx +17 -17
  315. package/src/components/shell/TopBar.stories.tsx +44 -19
  316. package/src/components/shell/TopBar.test.tsx +34 -12
  317. package/src/components/shell/TopBar.tsx +61 -40
  318. package/src/components/shell/brands.tsx +66 -0
  319. package/src/components/shell/index.ts +9 -8
  320. package/src/components/shell/{DeviceIndicator.stories.tsx → workout/DeviceIndicator.stories.tsx} +2 -2
  321. package/src/components/shell/{DeviceIndicator.tsx → workout/DeviceIndicator.tsx} +2 -2
  322. package/src/components/shell/{DeviceMenu.stories.tsx → workout/DeviceMenu.stories.tsx} +2 -2
  323. package/src/components/shell/{DeviceMenu.tsx → workout/DeviceMenu.tsx} +3 -3
  324. package/src/components/shell/{DeviceRow.stories.tsx → workout/DeviceRow.stories.tsx} +2 -2
  325. package/src/components/shell/{DeviceRow.tsx → workout/DeviceRow.tsx} +3 -3
  326. package/src/components/shell/{SessionStatePill.stories.tsx → workout/SessionStatePill.stories.tsx} +2 -2
  327. package/src/components/shell/{SessionStatePill.tsx → workout/SessionStatePill.tsx} +5 -3
  328. package/src/components/shell/workout/WorkoutShell.stories.tsx +71 -0
  329. package/src/components/shell/{DashboardShell.test.tsx → workout/WorkoutShell.test.tsx} +19 -8
  330. package/src/components/shell/workout/WorkoutShell.tsx +77 -0
  331. package/src/components/shell/workout/WorkoutTopBar.stories.tsx +58 -0
  332. package/src/components/shell/workout/WorkoutTopBar.test.tsx +39 -0
  333. package/src/components/shell/workout/WorkoutTopBar.tsx +52 -0
  334. package/src/components/shell/workout/index.ts +9 -0
  335. package/src/components/shell/workout/workoutNavItems.tsx +16 -0
  336. package/src/components/ui/README.md +110 -0
  337. package/src/components/ui/alert/Alert.stories.tsx +1 -1
  338. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +11 -13
  339. package/src/components/ui/autocomplete/Autocomplete.test.tsx +7 -25
  340. package/src/components/ui/autocomplete/Autocomplete.tsx +21 -28
  341. package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
  342. package/src/components/ui/avatar/Avatar.tsx +2 -10
  343. package/src/components/ui/badge/Badge.stories.tsx +83 -28
  344. package/src/components/ui/badge/Badge.test.tsx +19 -3
  345. package/src/components/ui/badge/Badge.tsx +48 -55
  346. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  347. package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +4 -16
  348. package/src/components/ui/button/Button.stories.tsx +46 -12
  349. package/src/components/ui/button/Button.tsx +32 -25
  350. package/src/components/ui/card/Card.stories.tsx +90 -94
  351. package/src/components/ui/card/Card.test.tsx +80 -6
  352. package/src/components/ui/card/Card.tsx +150 -203
  353. package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
  354. package/src/components/ui/checkbox/Checkbox.tsx +6 -26
  355. package/src/components/ui/chip/Chip.stories.tsx +37 -13
  356. package/src/components/ui/chip/Chip.test.tsx +10 -6
  357. package/src/components/ui/chip/Chip.tsx +76 -91
  358. package/src/components/ui/collapse/Collapse.stories.tsx +23 -23
  359. package/src/components/ui/collapse/Collapse.tsx +6 -11
  360. package/src/components/ui/data-row/DataRow.stories.tsx +9 -2
  361. package/src/components/ui/data-row/DataRow.test.tsx +5 -15
  362. package/src/components/ui/data-row/DataRow.tsx +18 -7
  363. package/src/components/ui/divider/Divider.stories.tsx +1 -1
  364. package/src/components/ui/divider/Divider.tsx +1 -5
  365. package/src/components/ui/drawer/Drawer.stories.tsx +5 -16
  366. package/src/components/ui/drawer/Drawer.tsx +13 -30
  367. package/src/components/ui/floating-overlay-depth.test.tsx +143 -0
  368. package/src/components/ui/form-field/FormField.stories.tsx +6 -20
  369. package/src/components/ui/form-field/FormField.test.tsx +1 -5
  370. package/src/components/ui/form-field/FormField.tsx +10 -46
  371. package/src/components/ui/help-tip/HelpTip.stories.tsx +8 -19
  372. package/src/components/ui/help-tip/HelpTip.test.tsx +1 -3
  373. package/src/components/ui/help-tip/HelpTip.tsx +21 -22
  374. package/src/components/ui/help-tip/index.ts +7 -1
  375. package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
  376. package/src/components/ui/icon-box/IconBox.test.tsx +1 -7
  377. package/src/components/ui/icon-box/IconBox.tsx +2 -12
  378. package/src/components/ui/indicator/Indicator.stories.tsx +1 -1
  379. package/src/components/ui/indicator/Indicator.tsx +20 -14
  380. package/src/components/ui/input/Input.stories.tsx +3 -11
  381. package/src/components/ui/input/Input.test.tsx +2 -10
  382. package/src/components/ui/input/Input.tsx +10 -25
  383. package/src/components/ui/input/index.ts +7 -1
  384. package/src/components/ui/link/Link.stories.tsx +19 -7
  385. package/src/components/ui/list-item/ListItem.stories.tsx +11 -2
  386. package/src/components/ui/list-item/ListItem.tsx +2 -9
  387. package/src/components/ui/menu/Menu.stories.tsx +21 -15
  388. package/src/components/ui/menu/Menu.test.tsx +2 -11
  389. package/src/components/ui/menu/Menu.tsx +12 -14
  390. package/src/components/ui/menu/index.ts +8 -1
  391. package/src/components/ui/modal/Modal.stories.tsx +2 -2
  392. package/src/components/ui/modal/Modal.tsx +29 -19
  393. package/src/components/ui/pill/Pill.stories.tsx +58 -23
  394. package/src/components/ui/pill/Pill.test.tsx +101 -3
  395. package/src/components/ui/pill/Pill.tsx +164 -49
  396. package/src/components/ui/pill/index.ts +1 -1
  397. package/src/components/ui/popover/Popover.stories.tsx +5 -7
  398. package/src/components/ui/popover/Popover.test.tsx +1 -6
  399. package/src/components/ui/popover/Popover.tsx +20 -15
  400. package/src/components/ui/popover/index.ts +7 -1
  401. package/src/components/ui/progress/Progress.stories.tsx +3 -12
  402. package/src/components/ui/progress/Progress.test.tsx +1 -5
  403. package/src/components/ui/radio/Radio.stories.tsx +42 -21
  404. package/src/components/ui/radio/Radio.test.tsx +6 -2
  405. package/src/components/ui/radio/Radio.tsx +23 -22
  406. package/src/components/ui/section/Section.stories.tsx +5 -14
  407. package/src/components/ui/section/Section.test.tsx +1 -6
  408. package/src/components/ui/section/Section.tsx +1 -3
  409. package/src/components/ui/section/index.ts +1 -5
  410. package/src/components/ui/select/Select.stories.tsx +3 -13
  411. package/src/components/ui/select/Select.test.tsx +2 -11
  412. package/src/components/ui/select/Select.tsx +14 -14
  413. package/src/components/ui/skeleton/Skeleton.stories.tsx +3 -11
  414. package/src/components/ui/skeleton/Skeleton.tsx +3 -14
  415. package/src/components/ui/skeleton/index.ts +1 -7
  416. package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
  417. package/src/components/ui/spinner/Spinner.tsx +1 -4
  418. package/src/components/ui/stack/Stack.stories.tsx +1 -1
  419. package/src/components/ui/stack/Stack.tsx +1 -1
  420. package/src/components/ui/surface/Surface.stories.tsx +17 -5
  421. package/src/components/ui/surface/Surface.test.tsx +88 -28
  422. package/src/components/ui/surface/Surface.tsx +37 -87
  423. package/src/components/ui/surface/SurfaceContext.ts +22 -62
  424. package/src/components/ui/surface/index.ts +8 -0
  425. package/src/components/ui/surface/resolveSurface.ts +98 -0
  426. package/src/components/ui/surface/surface.contract.test.ts +6 -1
  427. package/src/components/ui/switch/Switch.stories.tsx +1 -1
  428. package/src/components/ui/switch/Switch.tsx +7 -6
  429. package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
  430. package/src/components/ui/tabs/Tabs.tsx +7 -22
  431. package/src/components/ui/tabs/index.ts +1 -7
  432. package/src/components/ui/tile/Tile.stories.tsx +1 -1
  433. package/src/components/ui/tile/Tile.tsx +3 -0
  434. package/src/components/ui/toast/Toast.stories.tsx +1 -1
  435. package/src/components/ui/toast/Toast.test.tsx +2 -8
  436. package/src/components/ui/toast/Toast.tsx +43 -53
  437. package/src/components/ui/toast/index.ts +0 -1
  438. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +92 -71
  439. package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +3 -18
  440. package/src/components/ui/toolbar-button/ToolbarButton.tsx +25 -43
  441. package/src/components/ui/tooltip/Tooltip.stories.tsx +12 -3
  442. package/src/components/ui/tooltip/Tooltip.test.tsx +21 -3
  443. package/src/components/ui/tooltip/Tooltip.tsx +55 -36
  444. package/src/components/ui/tooltip/index.ts +1 -1
  445. package/src/components/ui/trigger/TriggerSurface.test.tsx +215 -0
  446. package/src/components/ui/trigger/TriggerSurface.tsx +81 -0
  447. package/src/components/ui/trigger/index.ts +2 -0
  448. package/src/stories/PresetShowcase.stories.tsx +100 -47
  449. package/src/test/no-device-internals.test.ts +3 -3
  450. package/src/theme/ChoosingTokens.stories.tsx +48 -0
  451. package/src/theme/ColorPalettes.stories.tsx +62 -45
  452. package/src/theme/ColorPrimitives.stories.tsx +35 -35
  453. package/src/theme/Depth.stories.tsx +132 -65
  454. package/src/theme/DepthCalibration.stories.tsx +23 -21
  455. package/src/theme/ThemeProvider.test.tsx +4 -4
  456. package/src/theme/barrel.ts +7 -6
  457. package/src/theme/color-stories.coverage.test.ts +1 -1
  458. package/src/theme/color-utils.ts +41 -29
  459. package/src/theme/config.completeness.test.ts +1 -1
  460. package/src/theme/config.ts +28 -0
  461. package/src/theme/elevation.test.ts +82 -56
  462. package/src/theme/elevation.ts +77 -561
  463. package/src/theme/global.css +30 -2
  464. package/src/theme/grey-ramp.test.ts +24 -8
  465. package/src/theme/lift.test.ts +39 -0
  466. package/src/theme/lift.ts +102 -0
  467. package/src/theme/materials.test.ts +5 -2
  468. package/src/theme/materials.ts +38 -4
  469. package/src/theme/native-theming-guard.test.ts +5 -5
  470. package/src/theme/presets/apply.ts +4 -0
  471. package/src/theme/presets/audiobook.ts +5 -2
  472. package/src/theme/presets/default.ts +4 -0
  473. package/src/theme/presets/index.ts +14 -1
  474. package/src/theme/presets/types.ts +1 -1
  475. package/src/theme/semantic-contrast.test.ts +13 -4
  476. package/src/theme/status-distinctness.test.ts +66 -0
  477. package/src/theme/surface-planes.test.ts +37 -0
  478. package/src/theme/surface-planes.ts +68 -0
  479. package/src/theme/token-export-guard.test.ts +1 -3
  480. package/src/theme/tokens/primitives.test.ts +15 -7
  481. package/src/theme/tokens/primitives.ts +65 -33
  482. package/src/theme/tokens/semantic.ts +71 -37
  483. package/src/theme/wash-ladder.test.ts +58 -0
  484. package/src/theme/workout-tokens.ts +1 -7
  485. package/src/types/jest-axe.d.ts +2 -6
  486. package/src/types/raw-md.d.ts +5 -0
  487. package/src/utils/colors.ts +4 -4
  488. package/src/utils/form.ts +1 -3
  489. package/src/utils/index.ts +2 -7
  490. package/src/utils/number-format.ts +17 -0
  491. package/src/utils/workout-format.ts +15 -6
  492. package/src/web-jsx/jsx-runtime.ts +1 -5
  493. package/tailwind.config.js +5 -0
  494. package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +0 -151
  495. package/src/components/custom/Workout/RowInventory.stories.tsx +0 -408
  496. package/src/components/custom/Workout/S3FullRail.stories.tsx +0 -46
  497. package/src/components/custom/Workout/S3SessionPace.stories.tsx +0 -95
  498. package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +0 -61
  499. package/src/components/custom/Workout/S3SetTypes.stories.tsx +0 -176
  500. package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +0 -240
  501. package/src/components/shell/DashboardShell.stories.tsx +0 -65
  502. package/src/components/shell/DashboardShell.tsx +0 -75
  503. package/src/components/shell/SessionRailSpecimen.stories.tsx +0 -753
  504. package/src/theme/elevation.stories.tsx +0 -346
  505. package/src/utils/useTheme.ts +0 -72
  506. /package/src/components/shell/{DeviceIndicator.test.tsx → workout/DeviceIndicator.test.tsx} +0 -0
  507. /package/src/components/shell/{DeviceMenu.test.tsx → workout/DeviceMenu.test.tsx} +0 -0
  508. /package/src/components/shell/{DeviceRow.test.tsx → workout/DeviceRow.test.tsx} +0 -0
  509. /package/src/components/shell/{SessionStatePill.test.tsx → workout/SessionStatePill.test.tsx} +0 -0
@@ -2,11 +2,11 @@ import { describe, it, expect, vi } from 'vitest'
2
2
  import { render, screen, fireEvent } from '@testing-library/react'
3
3
  import { Text } from 'react-native'
4
4
  import { axe } from 'jest-axe'
5
- import { DashboardShell } from './DashboardShell'
5
+ import { WorkoutShell, DashboardShell } from './WorkoutShell'
6
6
 
7
- describe('DashboardShell', () => {
7
+ describe('WorkoutShell', () => {
8
8
  it('composes the SideNav rail and its categories', () => {
9
- render(<DashboardShell activeKey="live" />)
9
+ render(<WorkoutShell activeKey="live" />)
10
10
  expect(screen.getByRole('tablist')).toBeInTheDocument()
11
11
  ;['Live', 'Review', 'Plan', 'Body'].forEach((name) => {
12
12
  expect(screen.getByRole('tab', { name })).toBeInTheDocument()
@@ -14,15 +14,15 @@ describe('DashboardShell', () => {
14
14
  })
15
15
 
16
16
  it('renders the content-slot placeholder when no children are given', () => {
17
- render(<DashboardShell activeKey="live" />)
17
+ render(<WorkoutShell activeKey="live" />)
18
18
  expect(screen.getByText('main content region')).toBeInTheDocument()
19
19
  })
20
20
 
21
21
  it('mounts children in the content slot and drops the placeholder', () => {
22
22
  render(
23
- <DashboardShell activeKey="review">
23
+ <WorkoutShell activeKey="review">
24
24
  <Text>my page body</Text>
25
- </DashboardShell>
25
+ </WorkoutShell>
26
26
  )
27
27
  expect(screen.getByText('my page body')).toBeInTheDocument()
28
28
  expect(screen.queryByText('main content region')).not.toBeInTheDocument()
@@ -30,13 +30,24 @@ describe('DashboardShell', () => {
30
30
 
31
31
  it('forwards nav taps through onNavigate', () => {
32
32
  const onNavigate = vi.fn()
33
- render(<DashboardShell activeKey="live" onNavigate={onNavigate} />)
33
+ render(<WorkoutShell activeKey="live" onNavigate={onNavigate} />)
34
34
  fireEvent.click(screen.getByRole('tab', { name: 'Body' }))
35
35
  expect(onNavigate).toHaveBeenCalledWith('body')
36
36
  })
37
37
 
38
+ it('fills the generic top-bar slot with the workout chrome', () => {
39
+ render(<WorkoutShell activeKey="live" state="rest" />)
40
+ expect(screen.getByText('VOLTRAS')).toBeInTheDocument()
41
+ expect(screen.getByText('REST')).toBeInTheDocument()
42
+ expect(screen.getByRole('button', { name: 'Devices' })).toBeInTheDocument()
43
+ })
44
+
45
+ it('still exports the pre-AW-132 DashboardShell name', () => {
46
+ expect(DashboardShell).toBe(WorkoutShell)
47
+ })
48
+
38
49
  it('has no a11y violations', async () => {
39
- const { container } = render(<DashboardShell activeKey="live" />)
50
+ const { container } = render(<WorkoutShell activeKey="live" />)
40
51
  expect(await axe(container)).toHaveNoViolations()
41
52
  })
42
53
  })
@@ -0,0 +1,77 @@
1
+ import type { ReactNode } from 'react'
2
+ import { AppShell } from '../AppShell'
3
+ import { type SideNavItem } from '../SideNav'
4
+ import { WorkoutTopBar } from './WorkoutTopBar'
5
+ import { workoutNavItems } from './workoutNavItems'
6
+ import { type Device } from './DeviceRow'
7
+ import { type SessionState } from './SessionStatePill'
8
+
9
+ /**
10
+ * `WorkoutShell` — the workout wall-dashboard chrome. Composes the generic
11
+ * {@link AppShell} and fills its top-bar slot with the workout's own
12
+ * {@link WorkoutTopBar} (session state + device menu) and its nav slot with the
13
+ * four workout categories. The generic shell knows none of this; this component
14
+ * is the whole workout-specific surface. Driven entirely by props.
15
+ */
16
+ export interface WorkoutShellProps {
17
+ /** Active nav category key. */
18
+ activeKey?: string
19
+ /** Nav categories, top → bottom. Defaults to the four workout categories. */
20
+ navItems?: SideNavItem[]
21
+ /** A category with off-view live activity → a quiet cue on that item. */
22
+ liveKey?: string | null
23
+ /** Global session state → the TopBar status pill. */
24
+ state?: SessionState
25
+ /** Connected devices → the TopBar connection glyph + dropdown. */
26
+ devices?: Device[]
27
+ /** TopBar brand subtitle. */
28
+ subtitle?: string
29
+ onNavigate?: (key: string) => void
30
+ onSelectDevice?: (device: Device) => void
31
+ /** Main content region. A placeholder renders when omitted. */
32
+ children?: ReactNode
33
+ className?: string
34
+ }
35
+
36
+ const DEMO_DEVICES: Device[] = [
37
+ { id: 'Voltra-A3F2', nickname: 'Left Cable', slot: 'L', state: 'connected' },
38
+ { id: 'Voltra-9B1C', nickname: 'Right Cable', slot: 'R', state: 'connected' },
39
+ ]
40
+
41
+ export function WorkoutShell({
42
+ activeKey = 'live',
43
+ navItems = workoutNavItems,
44
+ liveKey = null,
45
+ state = 'live',
46
+ devices = DEMO_DEVICES,
47
+ subtitle = 'wall dashboard',
48
+ onNavigate,
49
+ onSelectDevice,
50
+ children,
51
+ className,
52
+ }: WorkoutShellProps) {
53
+ return (
54
+ <AppShell
55
+ className={className}
56
+ navItems={navItems}
57
+ activeKey={activeKey}
58
+ liveKey={liveKey}
59
+ onNavigate={onNavigate}
60
+ topBar={
61
+ <WorkoutTopBar
62
+ state={state}
63
+ devices={devices}
64
+ subtitle={subtitle}
65
+ onSelectDevice={onSelectDevice}
66
+ />
67
+ }
68
+ >
69
+ {children}
70
+ </AppShell>
71
+ )
72
+ }
73
+
74
+ /** @deprecated Renamed to `WorkoutShell` — same props (AW-132). Removed after consumer migration. */
75
+ export const DashboardShell = WorkoutShell
76
+ /** @deprecated Renamed to `WorkoutShellProps` (AW-132). */
77
+ export type DashboardShellProps = WorkoutShellProps
@@ -0,0 +1,58 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { WorkoutTopBar } from './WorkoutTopBar'
3
+ import { type Device } from './DeviceRow'
4
+
5
+ const DEVICES: Device[] = [
6
+ { id: 'Voltra-A3F2', nickname: 'Left Cable', slot: 'L', state: 'connected' },
7
+ { id: 'Voltra-9B1C', nickname: 'Right Cable', slot: 'R', state: 'connected' },
8
+ { id: 'Voltra-77E0', nickname: 'Spare', slot: null, state: 'available' },
9
+ ]
10
+
11
+ const meta: Meta<typeof WorkoutTopBar> = {
12
+ title: 'Shell/Workout/WorkoutTopBar',
13
+ component: WorkoutTopBar,
14
+ tags: ['autodocs', 'status:candidate', '!status:review'],
15
+ args: { state: 'live', devices: DEVICES },
16
+ argTypes: {
17
+ state: { control: 'select', options: ['live', 'rest', 'idle'] },
18
+ devices: { control: 'object' },
19
+ subtitle: { control: 'text' },
20
+ showSubtitle: { control: 'boolean' },
21
+ showClock: { control: 'boolean' },
22
+ time: { control: false },
23
+ onSelectDevice: { control: false },
24
+ },
25
+ parameters: {
26
+ layout: 'fullscreen',
27
+ docs: {
28
+ description: {
29
+ component:
30
+ '**Organism (workout app).** The generic ' +
31
+ "[TopBar](?path=/docs/shell-topbar--docs) with the workout's own chrome in its " +
32
+ '`trailing` slot: [SessionStatePill](?path=/docs/shell-workout-sessionstatepill--docs) ' +
33
+ 'then [DeviceMenu](?path=/docs/shell-workout-devicemenu--docs). The bar adds the ' +
34
+ 'dividers and the edge-pinned clock; this component only supplies the items ' +
35
+ '(AW-132).\n\n' +
36
+ '**Try it:** use the **Controls** to change `state`, edit `devices` (set one to ' +
37
+ '`lost` to see the fault), or toggle `showSubtitle` / `showClock`. **Resize the ' +
38
+ 'canvas** to watch the container-responsive collapse (SIZE-D01). Click the device ' +
39
+ 'glyph to open the menu.',
40
+ },
41
+ },
42
+ },
43
+ }
44
+ export default meta
45
+ type Story = StoryObj<typeof WorkoutTopBar>
46
+
47
+ export const Default: Story = {}
48
+
49
+ /** A lost device — the glyph alone carries the fault. */
50
+ export const DeviceLost: Story = {
51
+ args: {
52
+ state: 'rest',
53
+ devices: [
54
+ { id: 'Voltra-A3F2', nickname: 'Left Cable', slot: 'L', state: 'connected' },
55
+ { id: 'Voltra-9B1C', nickname: 'Right Cable', slot: 'R', state: 'lost' },
56
+ ],
57
+ },
58
+ }
@@ -0,0 +1,39 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { render, screen } from '@testing-library/react'
3
+ import { axe } from 'jest-axe'
4
+ import { WorkoutTopBar } from './WorkoutTopBar'
5
+ import { type Device } from './DeviceRow'
6
+
7
+ const devices: Device[] = [
8
+ { id: 'Voltra-A3F2', nickname: 'Left Cable', slot: 'L', state: 'connected' },
9
+ { id: 'Voltra-9B1C', nickname: 'Right Cable', slot: 'R', state: 'connected' },
10
+ ]
11
+
12
+ const AT_1612 = new Date(2024, 0, 1, 16, 12) // local 16:12
13
+
14
+ describe('WorkoutTopBar', () => {
15
+ it('renders brand, session state, the device glyph and a 24h clock', () => {
16
+ render(<WorkoutTopBar state="live" devices={devices} time={AT_1612} showClock />)
17
+ expect(screen.getByText('VOLTRAS')).toBeInTheDocument()
18
+ expect(screen.getByText('LIVE')).toBeInTheDocument()
19
+ expect(screen.getByRole('button', { name: 'Devices' })).toBeInTheDocument()
20
+ expect(screen.getByText('16:12')).toBeInTheDocument()
21
+ })
22
+
23
+ it('hides the clock when showClock is false', () => {
24
+ render(<WorkoutTopBar state="live" devices={devices} time={AT_1612} showClock={false} />)
25
+ expect(screen.queryByText('16:12')).not.toBeInTheDocument()
26
+ })
27
+
28
+ it('hides the subtitle when showSubtitle is false', () => {
29
+ render(<WorkoutTopBar state="rest" devices={devices} showSubtitle={false} />)
30
+ expect(screen.queryByText('/ wall dashboard')).not.toBeInTheDocument()
31
+ })
32
+
33
+ it('has no accessibility violations', async () => {
34
+ const { container } = render(
35
+ <WorkoutTopBar state="live" devices={devices} time={AT_1612} showClock />
36
+ )
37
+ expect(await axe(container)).toHaveNoViolations()
38
+ })
39
+ })
@@ -0,0 +1,52 @@
1
+ import { TopBar } from '../TopBar'
2
+ import { SessionStatePill, type SessionState } from './SessionStatePill'
3
+ import { DeviceMenu } from './DeviceMenu'
4
+ import { type Device } from './DeviceRow'
5
+
6
+ export interface WorkoutTopBarProps {
7
+ /** Global session state → the status pill. */
8
+ state: SessionState
9
+ /** Devices for the connection glyph + dropdown. */
10
+ devices: Device[]
11
+ /** The moment to display in the clock (Date/timestamp). Omit for a live ticking clock. */
12
+ time?: number | Date
13
+ /** Brand subtitle. Default "wall dashboard". */
14
+ subtitle?: string
15
+ /** Force the subtitle on/off; defaults to container-responsive. */
16
+ showSubtitle?: boolean
17
+ /** Force the clock on/off; defaults to container-responsive. */
18
+ showClock?: boolean
19
+ onSelectDevice?: (device: Device) => void
20
+ className?: string
21
+ }
22
+
23
+ /**
24
+ * The workout app's top bar — the generic {@link TopBar} with the workout's own
25
+ * chrome in its `trailing` slot: session state, then the device menu. The bar
26
+ * adds its dividers and the clock; this component only supplies the items.
27
+ */
28
+ export function WorkoutTopBar({
29
+ state,
30
+ devices,
31
+ time,
32
+ subtitle,
33
+ showSubtitle,
34
+ showClock,
35
+ onSelectDevice,
36
+ className,
37
+ }: WorkoutTopBarProps) {
38
+ return (
39
+ <TopBar
40
+ brand="voltras"
41
+ subtitle={subtitle}
42
+ showSubtitle={showSubtitle}
43
+ showClock={showClock}
44
+ time={time}
45
+ className={className}
46
+ trailing={[
47
+ <SessionStatePill key="state" state={state} />,
48
+ <DeviceMenu key="devices" devices={devices} onSelectDevice={onSelectDevice} />,
49
+ ]}
50
+ />
51
+ )
52
+ }
@@ -0,0 +1,9 @@
1
+ // Workout-app shell chrome — composes the generic shell family one directory up.
2
+ // Nothing here is reusable by another app; nothing generic may import from here.
3
+ export * from './SessionStatePill'
4
+ export * from './DeviceIndicator'
5
+ export * from './DeviceRow'
6
+ export * from './DeviceMenu'
7
+ export * from './workoutNavItems'
8
+ export * from './WorkoutTopBar'
9
+ export * from './WorkoutShell'
@@ -0,0 +1,16 @@
1
+ import { ActivityIcon, HistoryIcon, LayersIcon, PersonStandingIcon } from '../../icons'
2
+ import { type SideNavItem } from '../SideNav'
3
+
4
+ /** The four workout-dashboard categories with their locked S2 glyphs (activity · history · layers · figure). */
5
+ export const workoutNavItems: SideNavItem[] = [
6
+ { key: 'live', label: 'Live', icon: <ActivityIcon size={20} color="currentColor" /> },
7
+ { key: 'review', label: 'Review', icon: <HistoryIcon size={20} color="currentColor" /> },
8
+ { key: 'program', label: 'Plan', icon: <LayersIcon size={20} color="currentColor" /> },
9
+ { key: 'body', label: 'Body', icon: <PersonStandingIcon size={20} color="currentColor" /> },
10
+ ]
11
+
12
+ /**
13
+ * @deprecated Use `workoutNavItems` — these are the workout app's categories,
14
+ * not a generic shell default. `SideNav.items` is required now (AW-132).
15
+ */
16
+ export const defaultNavItems = workoutNavItems
@@ -0,0 +1,110 @@
1
+ # `ui/*` — the generic primitive family
2
+
3
+ The domain-free foundation tier: 38 primitives that know nothing about workouts,
4
+ sessions or initiatives. Everything in `custom/*` and `shell/*` is built on this
5
+ layer, and `ui/*` may not import from either of them — the tier order is
6
+ `theme → icons → ui → custom → shell → pages`.
7
+
8
+ This README is the **index**. It maps each primitive's dependencies
9
+ (**composes ↓**) and its consumers (**used-by ↑**) so the tree navigates in both
10
+ directions and a hand-rolled element that should have reached for an existing
11
+ primitive is visible. Storybook mirrors it under
12
+ `Components/Atoms|Molecules|Organisms/…`; each primitive's autodocs page repeats
13
+ its Composes links.
14
+
15
+ Counts below are generated, not hand-kept: they come from
16
+ [`src/arch/arch-graph.json`](../../arch/arch-graph.json) (regenerate with
17
+ `pnpm arch:graph`, which `arch-graph.freshness.test.ts` keeps honest). **In-repo**
18
+ is in-library consumers; **external** is import sites in the three checkouts the
19
+ graph scans (voltras mobile, voltras-mcp, codewatch dashboard). It is a floor,
20
+ not a census: `docs/audits/2026-09-08-critique-addenda.md` §D1 found seven
21
+ downstream checkouts, and audiobook/frontend — the one that tracks HEAD — is not
22
+ among the three.
23
+
24
+ ## The one rule this family exists to enforce
25
+
26
+ A primitive here is the _only_ implementation of its shape. When a family needs
27
+ a rule, a pill, a dot or a plane, it composes `ui/*` — it does not draw one. The
28
+ audit that produced this file found four container primitives competing and
29
+ twelve pill-shaped components; roadmap decisions 1, 3, 4, 5, 10 and 11 collapsed
30
+ them, and the survivors are marked as presets below.
31
+
32
+ ## Dependency map
33
+
34
+ | Component | Tier | Composes ↓ | Used-by ↑ (in-repo) | In-repo | External |
35
+ | ---------------- | -------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -------- |
36
+ | `alert` | atom | — | — (app-facing leaf) | 0 | 6 |
37
+ | `autocomplete` | molecule | Surface | — | 0 | 0 |
38
+ | `avatar` | atom | — | — | 0 | 0 |
39
+ | `badge` | molecule | Pill, Indicator | BodyMapDetailPanel, MesoCard, ReadinessCheck | 3 | 3 |
40
+ | `breadcrumbs` | atom | — | — | 0 | 0 |
41
+ | `button` | atom | — | examples | 1 | 17 |
42
+ | `card` | molecule | Surface | CoChangeChip, FileActivityDetail, FileHistoryExplorer, InitiativeCard, MesoCard, MesoStatusCard, PortfolioOverview, ReadinessCheck, SessionDetail, WorkoutCard | 10 | 74 |
43
+ | `checkbox` | atom | — | examples | 1 | 0 |
44
+ | `chip` | molecule | Pill | — (external-only preset) | 0 | 0 |
45
+ | `collapse` | atom | — | SessionDetail | 1 | 0 |
46
+ | `data-row` | atom | — | FileActivityDetail, VolumeLandmarkBar | 2 | 2 |
47
+ | `divider` | atom | — | FileHistoryExplorer, OpenLoops, SessionDetail, SessionList, TopBar | 5 | 2 |
48
+ | `drawer` | molecule | Surface | PrHistoryModal | 1 | 6 |
49
+ | `form-field` | atom | — | examples | 1 | 0 |
50
+ | `help-tip` | molecule | Surface | — **deprecated**, use Tooltip | 0 | 0 |
51
+ | `icon-box` | atom | — | — | 0 | 0 |
52
+ | `indicator` | atom | — | Badge, DeviceRow, SessionStatePill, SeverityLabel | 6 | 0 |
53
+ | `input` | atom | — | examples | 1 | 1 |
54
+ | `link` | atom | — | — | 0 | 0 |
55
+ | `list-item` | atom | — | — (app-facing leaf) | 0 | 9 |
56
+ | `menu` | molecule | Surface | — | 0 | 0 |
57
+ | `modal` | molecule | Surface | — | 0 | 0 |
58
+ | `pill` | atom | — | Badge, Chip, CoChangeChip, FileActivityDetail, InitiativeCard, InitiativeHeader, MuscleGroupChip, OpenLoops, SessionDetail, StatusPill, TaskRow | 11 | 1 |
59
+ | `popover` | molecule | Surface | DeviceMenu | 1 | 0 |
60
+ | `progress` | atom | — | CircularTimer | 1 | 2 |
61
+ | `radio` | atom | — | examples | 1 | 2 |
62
+ | `section` | atom | — | — (app-facing leaf) | 0 | 10 |
63
+ | `select` | molecule | Surface | examples | 2 | 1 |
64
+ | `skeleton` | atom | — | — | 0 | 0 |
65
+ | `spinner` | atom | — | — (app-facing leaf) | 0 | 2 |
66
+ | `stack` | atom | — | MetricTiles, ScheduleTiles | 2 | 27 |
67
+ | `surface` | atom | — | Autocomplete, Card, DashboardShell, Drawer, HelpTip, LiveFatigueCard, Menu, Modal, Popover, Select, SessionHeader, SessionRail, Toast, ToolbarButton, Tooltip | 15 | 29 |
68
+ | `switch` | atom | — | — | 0 | 0 |
69
+ | `tabs` | atom | — | — | 0 | 0 |
70
+ | `tile` | atom | — | FileActivityDetail, FileHistoryExplorer, MetricTiles, ScheduleTiles — **deprecated**, use a Card stat preset | 4 | 0 |
71
+ | `toast` | molecule | Surface | — | 0 | 0 |
72
+ | `toolbar-button` | molecule | Surface | — | 0 | 0 |
73
+ | `tooltip` | molecule | Surface | FatigueLights, SessionDetail, SessionListItem, Table, TaskRow, ZoneTrack | 8 | 1 |
74
+
75
+ Two roots carry the family: **`surface`** (15 in-repo consumers — every floating
76
+ and raised plane) and **`pill`** (11 — every pill-shaped label since decision 3).
77
+ Nothing else in `ui/*` is composed by more than six.
78
+
79
+ ## Reuse audit — where `ui/*` still hand-rolls
80
+
81
+ | Leaf | Should compose | Status |
82
+ | -------------------------------------------------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
83
+ | Text runs (79 raw `<Text>` in 27 files) | `Typography` | **Open.** `Typography` is in `custom/`, which `ui/*` may not import. Roadmap decision 9 moves it into `ui/`; until then every heading and label here is hand-styled. |
84
+ | Pill-shaped labels | `Pill` | **Closed** (#172). `Badge` and `Chip` now render `<Pill>`; the six folded-in variants are in `DEPRECATIONS.md`. |
85
+ | Status dots | `Indicator` | **Closed** for `ui/*`. `StatusDot` (Workout) is deprecated toward `Indicator` (decision 10). |
86
+ | Raised / floating planes | `Surface` (`raise` / `elevation`) | **Closed** (#166, #173). Eleven molecules compose `Surface` rather than setting `bg-surface-*` themselves. |
87
+ | Rules and separators (20 `border-hairline*` in 12 files) | `Divider` | **Partly open.** Decision 4 allows `border-hairline` only on Card `outline`/`subtle`, Table rules and input borders — `Select`, `Radio`, `Drawer` and `Tabs` still draw their own. |
88
+ | Tooltip-with-a-label lockup | `Tooltip` + your own label | **Closed** by deprecation. `HelpTip` and `LabelWithHelp` are retired (decision 5). |
89
+
90
+ ## Watch list
91
+
92
+ - **`Typography` cannot be composed here.** The tier order forbids the import, so
93
+ the 79 raw `<Text>` runs above cannot be fixed inside this family. Blocked on
94
+ roadmap E3 moving `Typography` and `Eyebrow` into `ui/`.
95
+ - **Fourteen primitives read as dead** — `autocomplete`, `avatar`, `breadcrumbs`,
96
+ `chip`, `help-tip`, `icon-box`, `link`, `menu`, `modal`, `skeleton`, `switch`,
97
+ `tabs`, `toast`, `toolbar-button`. That is not automatically a delete: the graph
98
+ sees three of seven downstream checkouts, and `Chip` in particular has a known
99
+ consumer (audiobook/frontend, §D1) the graph cannot see. Settle per component
100
+ under roadmap E6.4, not by reading this table.
101
+ - **`modal` and `menu` read dead while `drawer` and `popover` do not.** The
102
+ overlay set is mid-migration onto the floating lift (E1); recheck after the
103
+ trigger work lands rather than treating the split as a decision.
104
+
105
+ ## Maturity
106
+
107
+ Status tags on these stories are derived, not chosen — the rule is in
108
+ [`MATURITY.md`](../../../MATURITY.md). Membership in the dependency map above is
109
+ one of its three inputs, so **adding a primitive here without adding its row
110
+ leaves it `status:candidate`**.
@@ -5,7 +5,7 @@ import { Alert, AlertTitle, AlertDescription } from './Alert'
5
5
  const meta: Meta<typeof Alert> = {
6
6
  title: 'Components/Molecules/Alert',
7
7
  component: Alert,
8
- tags: ['autodocs'],
8
+ tags: ['autodocs', 'status:stable', '!status:review'],
9
9
  parameters: {
10
10
  docs: {
11
11
  description: {
@@ -6,7 +6,7 @@ import { Autocomplete, AutocompleteOption } from './Autocomplete'
6
6
  const meta: Meta<typeof Autocomplete> = {
7
7
  title: 'Components/Molecules/Autocomplete',
8
8
  component: Autocomplete,
9
- tags: ['autodocs'],
9
+ tags: ['autodocs', 'status:stable', '!status:review'],
10
10
  argTypes: {
11
11
  isDisabled: {
12
12
  control: 'boolean',
@@ -48,9 +48,7 @@ export const Default: Story = {
48
48
  onChange={setValue}
49
49
  placeholder="Search fruits..."
50
50
  />
51
- <Text className="text-sm text-text-secondary mt-2">
52
- Selected: {value || 'None'}
53
- </Text>
51
+ <Text className="text-sm text-text-secondary mt-2">Selected: {value || 'None'}</Text>
54
52
  </View>
55
53
  )
56
54
  },
@@ -140,12 +138,7 @@ export const Disabled: Story = {
140
138
  render: function Render() {
141
139
  return (
142
140
  <View className="w-64">
143
- <Autocomplete
144
- options={fruitOptions}
145
- value="apple"
146
- label="Disabled field"
147
- isDisabled
148
- />
141
+ <Autocomplete options={fruitOptions} value="apple" label="Disabled field" isDisabled />
149
142
  </View>
150
143
  )
151
144
  },
@@ -175,7 +168,12 @@ export const DisabledOptions: Story = {
175
168
  const optionsWithDisabled: AutocompleteOption[] = [
176
169
  { value: 'free', label: 'Free Plan' },
177
170
  { value: 'pro', label: 'Pro Plan' },
178
- { value: 'enterprise', label: 'Enterprise Plan', isDisabled: true, description: 'Contact sales' },
171
+ {
172
+ value: 'enterprise',
173
+ label: 'Enterprise Plan',
174
+ isDisabled: true,
175
+ description: 'Contact sales',
176
+ },
179
177
  ]
180
178
  return (
181
179
  <View className="w-64">
@@ -289,8 +287,8 @@ export const FormExample: Story = {
289
287
 
290
288
  <View className="pt-2 border-t border-hairline">
291
289
  <Text className="text-sm text-text-secondary">
292
- Selected: {country ? `${countryOptions.find(c => c.value === country)?.label}` : '-'}
293
- {city ? `, ${cityOptions[country!]?.find(c => c.value === city)?.label}` : ''}
290
+ Selected: {country ? `${countryOptions.find((c) => c.value === country)?.label}` : '-'}
291
+ {city ? `, ${cityOptions[country!]?.find((c) => c.value === city)?.label}` : ''}
294
292
  </Text>
295
293
  </View>
296
294
  </View>
@@ -102,26 +102,20 @@ describe('Autocomplete', () => {
102
102
  })
103
103
 
104
104
  it('shows clear button when value is selected', () => {
105
- render(
106
- <Autocomplete options={defaultOptions} value="1" isClearable />
107
- )
105
+ render(<Autocomplete options={defaultOptions} value="1" isClearable />)
108
106
  expect(screen.getByLabelText('Clear selection')).toBeInTheDocument()
109
107
  })
110
108
 
111
109
  it('calls onChange with null when clear is pressed', () => {
112
110
  const onChange = vi.fn()
113
- render(
114
- <Autocomplete options={defaultOptions} value="1" onChange={onChange} isClearable />
115
- )
111
+ render(<Autocomplete options={defaultOptions} value="1" onChange={onChange} isClearable />)
116
112
 
117
113
  fireEvent.click(screen.getByLabelText('Clear selection'))
118
114
  expect(onChange).toHaveBeenCalledWith(null)
119
115
  })
120
116
 
121
117
  it('does not show clear button when isClearable is false', () => {
122
- render(
123
- <Autocomplete options={defaultOptions} value="1" isClearable={false} />
124
- )
118
+ render(<Autocomplete options={defaultOptions} value="1" isClearable={false} />)
125
119
  expect(screen.queryByLabelText('Clear selection')).not.toBeInTheDocument()
126
120
  })
127
121
 
@@ -144,13 +138,7 @@ describe('Autocomplete', () => {
144
138
  })
145
139
 
146
140
  it('shows custom minCharsText', () => {
147
- render(
148
- <Autocomplete
149
- options={defaultOptions}
150
- minChars={3}
151
- minCharsText="Keep typing..."
152
- />
153
- )
141
+ render(<Autocomplete options={defaultOptions} minChars={3} minCharsText="Keep typing..." />)
154
142
  const input = screen.getByPlaceholderText('Search...')
155
143
 
156
144
  typeInInput(input, 'A')
@@ -185,9 +173,7 @@ describe('Autocomplete', () => {
185
173
  })
186
174
 
187
175
  it('does not show clear button when disabled', () => {
188
- render(
189
- <Autocomplete options={defaultOptions} value="1" isDisabled isClearable />
190
- )
176
+ render(<Autocomplete options={defaultOptions} value="1" isDisabled isClearable />)
191
177
  expect(screen.queryByLabelText('Clear selection')).not.toBeInTheDocument()
192
178
  })
193
179
  })
@@ -225,9 +211,7 @@ describe('Autocomplete', () => {
225
211
 
226
212
  describe('accessibility', () => {
227
213
  it('has no accessibility violations', async () => {
228
- const { container } = render(
229
- <Autocomplete options={defaultOptions} label="Search" />
230
- )
214
+ const { container } = render(<Autocomplete options={defaultOptions} label="Search" />)
231
215
  const results = await axe(container)
232
216
  expect(results).toHaveNoViolations()
233
217
  })
@@ -238,9 +222,7 @@ describe('Autocomplete', () => {
238
222
  })
239
223
 
240
224
  it('clear button has accessible label', () => {
241
- render(
242
- <Autocomplete options={defaultOptions} value="1" isClearable />
243
- )
225
+ render(<Autocomplete options={defaultOptions} value="1" isClearable />)
244
226
  expect(screen.getByLabelText('Clear selection')).toBeInTheDocument()
245
227
  })
246
228
  })