@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
@@ -0,0 +1,185 @@
1
+ /**
2
+ * The {@link LiveFatiguePanel}'s responsive layout, as one pure function (TD-03.58).
3
+ *
4
+ * The panel is container-responsive, not prop-sized (SIZE-D01): it measures its own width
5
+ * in `onLayout` and feeds it here. Everything the two columns need — whether they stack,
6
+ * the padding, the gap, the card's width, and the split of the ONE body height — comes out
7
+ * of this one call, so the hero and the card cannot disagree about any of it.
8
+ *
9
+ * Pure, with no `react-native` import, so the breakpoint behaviour is unit-testable
10
+ * without `onLayout` (which never fires under jsdom).
11
+ *
12
+ * SOURCED vs CHOSEN. The breakpoint edges are titan's own `primitiveBreakpoints` — not
13
+ * invented here. The per-tier padding/gap/card numbers below the `md` edge, the `xl`
14
+ * card-width ratio and its cap, and the stacked height share ARE chosen; each is a single
15
+ * named constant and each is called out in the PR body.
16
+ */
17
+ import { primitiveBreakpoints } from '../../../theme/tokens/primitives'
18
+
19
+ /** titan's breakpoint scale, re-exported as the panel's tier edges. Sourced, not chosen. */
20
+ export const PANEL_BREAKPOINTS = primitiveBreakpoints
21
+
22
+ export type PanelTier = keyof typeof PANEL_BREAKPOINTS
23
+
24
+ /**
25
+ * The `md` edge (1000) is where the two columns fit side by side: the card's own default
26
+ * 318 plus the hero's usable minimum plus the padding and gap. Below it they stack.
27
+ */
28
+ const ROW_TIER_MIN = PANEL_BREAKPOINTS.md
29
+
30
+ /**
31
+ * The tier for a measured container width. An unmeasured container (0, before the first
32
+ * `onLayout`) resolves to `md` — today's side-by-side geometry — so the first paint matches
33
+ * what the panel has always rendered rather than flashing a stacked layout.
34
+ */
35
+ export function panelTier(width: number): PanelTier {
36
+ if (width <= 0) return 'md'
37
+ if (width >= PANEL_BREAKPOINTS.xl) return 'xl'
38
+ if (width >= PANEL_BREAKPOINTS.lg) return 'lg'
39
+ if (width >= PANEL_BREAKPOINTS.md) return 'md'
40
+ if (width >= PANEL_BREAKPOINTS.sm) return 'sm'
41
+ return 'xs'
42
+ }
43
+
44
+ /** titan's long-standing card column width, and the floor every wider tier clamps to. */
45
+ export const CARD_WIDTH_BASE = 318
46
+
47
+ /**
48
+ * CHOSEN. At wall width a 318px card is ~17% of the content row against ~34% at the `md`
49
+ * edge, so it reads as a sliver beside the hero. 0.22 pulls it back toward the `md`
50
+ * proportion without letting the hero lose its lead.
51
+ */
52
+ export const CARD_WIDTH_XL_RATIO = 0.22
53
+
54
+ /**
55
+ * CHOSEN. The cap. The card's internal charts (`RomProgressionChart`, `GhostSpark`) were
56
+ * drawn around ~318 and go sparse rather than richer past roughly 1.5x that, so the
57
+ * expansion stops at 460 (reached at 2091px) instead of tracking the container forever.
58
+ */
59
+ export const CARD_WIDTH_MAX = 460
60
+
61
+ /**
62
+ * CHOSEN. Stacked, the CARD takes the larger share of the one body height. Its sections
63
+ * have hard floors — the verdict hero, the three lights, the ROM chart and the ghost spark —
64
+ * while the velocity hero is a bar plot that scales continuously, so squeezing the hero
65
+ * degrades gracefully and squeezing the card overflows it.
66
+ */
67
+ export const CARD_HEIGHT_SHARE_STACKED = 0.55
68
+
69
+ /** What the hero reserves above its plot for the `VELOCITY · this set` eyebrow, px. */
70
+ export const HERO_EYEBROW_ALLOWANCE = 26
71
+
72
+ // --- the card's own height arithmetic ------------------------------------------------
73
+ // It lives here rather than inside LiveFatigueCard so the panel can reason about the card's
74
+ // floor without importing a component (this module stays react-native-free and testable).
75
+
76
+ /** Floor for the ghost-spark plot inside the card, px. */
77
+ export const CARD_MIN_CHART_HEIGHT = 168
78
+ /** Ceiling for the ghost-spark plot inside the card, px. */
79
+ export const CARD_MAX_CHART_HEIGHT = 240
80
+ /** Share of a pinned card height the ghost-spark plot takes, before the clamp. */
81
+ export const CARD_CHART_HEIGHT_RATIO = 0.4
82
+ /** What an unpinned card gives the ghost-spark plot, px. */
83
+ export const CARD_NATURAL_CHART_HEIGHT = 172
84
+
85
+ /** The ghost-spark plot height for a card height, or the natural height when unpinned. */
86
+ export function cardChartHeight(cardHeight?: number): number {
87
+ if (cardHeight == null) return CARD_NATURAL_CHART_HEIGHT
88
+ const scaled = cardHeight * CARD_CHART_HEIGHT_RATIO
89
+ return Math.round(Math.min(CARD_MAX_CHART_HEIGHT, Math.max(CARD_MIN_CHART_HEIGHT, scaled)))
90
+ }
91
+
92
+ /**
93
+ * MEASURED, not chosen: everything in the card that is not the ghost-spark plot — the 18px
94
+ * padding top and bottom, the verdict hero, the three lights, the two 16px minimum spacers
95
+ * and the ROM chart. Taken from the rendered card in Storybook (`scrollHeight` 384 at the
96
+ * {@link CARD_MIN_CHART_HEIGHT} floor), so a change to those sections needs re-measuring.
97
+ */
98
+ export const CARD_CHROME_HEIGHT = 216
99
+
100
+ /**
101
+ * The shortest a stacked card can be drawn without its content spilling past the rounded
102
+ * edge. Before this floor existed, an `xs` panel at `bodyHeight` 560 gave the card 301px
103
+ * against a 384px content height and the ghost spark rendered outside the card.
104
+ */
105
+ export const CARD_MIN_HEIGHT_STACKED = CARD_CHROME_HEIGHT + CARD_MIN_CHART_HEIGHT
106
+
107
+ /** Padding and column gap per tier. `md` and up hold today's values exactly. */
108
+ const TIER_SPACING: Record<PanelTier, { padding: number; gap: number }> = {
109
+ xs: { padding: 16, gap: 12 },
110
+ sm: { padding: 20, gap: 14 },
111
+ md: { padding: 24, gap: 18 },
112
+ lg: { padding: 24, gap: 18 },
113
+ xl: { padding: 24, gap: 18 },
114
+ }
115
+
116
+ export interface PanelLayout {
117
+ tier: PanelTier
118
+ /** Card below the hero rather than beside it. */
119
+ stacked: boolean
120
+ /** Uniform padding around the body row, px. */
121
+ padding: number
122
+ /** Gap between the hero and the card, px — column gap when row, row gap when stacked. */
123
+ gap: number
124
+ /**
125
+ * Card width, px. Stacked, this is the full content width, so the card takes a real
126
+ * number rather than needing a second `onLayout` of its own to size its charts.
127
+ */
128
+ cardWidth: number
129
+ }
130
+
131
+ /** The card column width for a row tier, before any explicit `cardWidth` override. */
132
+ function rowCardWidth(tier: PanelTier, width: number): number {
133
+ if (tier !== 'xl') return CARD_WIDTH_BASE
134
+ const fluid = Math.round(width * CARD_WIDTH_XL_RATIO)
135
+ return Math.min(CARD_WIDTH_MAX, Math.max(CARD_WIDTH_BASE, fluid))
136
+ }
137
+
138
+ /** The whole layout for a measured container width. */
139
+ export function panelLayout(width: number): PanelLayout {
140
+ const tier = panelTier(width)
141
+ const { padding, gap } = TIER_SPACING[tier]
142
+ const stacked = width > 0 && width < ROW_TIER_MIN
143
+ const cardWidth = stacked
144
+ ? Math.max(0, Math.round(width - padding * 2))
145
+ : rowCardWidth(tier, width)
146
+ return { tier, stacked, padding, gap, cardWidth }
147
+ }
148
+
149
+ export interface PanelBodySplit {
150
+ /** Height handed to the velocity hero's plot, px. */
151
+ heroHeight: number
152
+ /** Height handed to the fatigue card, px. */
153
+ cardHeight: number
154
+ }
155
+
156
+ /**
157
+ * CHOSEN. The shortest the velocity plot is still worth drawing. Below this the bars stop
158
+ * being readable across a room, so the stacked panel grows past `bodyHeight` rather than
159
+ * flattening the hero any further.
160
+ */
161
+ export const HERO_MIN_PLOT_HEIGHT = 140
162
+
163
+ /**
164
+ * The ONE body height split across the hero and the card (TD-03.60). Side by side both
165
+ * take the full height; stacked they share it by {@link CARD_HEIGHT_SHARE_STACKED} with the
166
+ * gap taken out first, and neither drops below its floor. Either way both numbers come from
167
+ * this one call, so changing `bodyHeight` moves both or neither.
168
+ */
169
+ export function panelBodySplit(bodyHeight: number, layout: PanelLayout): PanelBodySplit {
170
+ if (!layout.stacked) {
171
+ return {
172
+ heroHeight: Math.max(0, bodyHeight - HERO_EYEBROW_ALLOWANCE),
173
+ cardHeight: Math.max(0, bodyHeight),
174
+ }
175
+ }
176
+ const usable = Math.max(0, bodyHeight - layout.gap)
177
+ const cardHeight = Math.max(
178
+ CARD_MIN_HEIGHT_STACKED,
179
+ Math.round(usable * CARD_HEIGHT_SHARE_STACKED)
180
+ )
181
+ return {
182
+ heroHeight: Math.max(HERO_MIN_PLOT_HEIGHT, usable - cardHeight - HERO_EYEBROW_ALLOWANCE),
183
+ cardHeight,
184
+ }
185
+ }
@@ -1,10 +1,13 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import { Gauge } from './Gauge'
3
+ import { getSemanticColors } from '../../../theme/tokens/semantic'
4
+
5
+ const t = getSemanticColors('dark')
3
6
 
4
7
  const meta: Meta<typeof Gauge> = {
5
- title: 'Components/DataViz/Gauge',
8
+ title: 'Custom/Charts/Gauge',
6
9
  component: Gauge,
7
- tags: ['autodocs'],
10
+ tags: ['autodocs', 'status:candidate', '!status:review'],
8
11
  argTypes: {
9
12
  value: { control: { type: 'range', min: 0, max: 100, step: 1 } },
10
13
  size: { control: { type: 'range', min: 100, max: 320, step: 10 } },
@@ -32,7 +35,7 @@ export const ArbitraryDomain: Story = {
32
35
 
33
36
  /** Single-color override ignores the threshold bands. */
34
37
  export const OverrideColor: Story = {
35
- args: { value: 55, unit: '%', label: 'Coverage', color: '#5B9BD5', size: 180 },
38
+ args: { value: 55, unit: '%', label: 'Coverage', color: t['data-7'], size: 180 },
36
39
  }
37
40
 
38
41
  /** Value above max is clamped for the fill but shown verbatim. */
@@ -50,7 +50,7 @@ describe('Gauge', () => {
50
50
  { value: 0, color: '#14B8A6' },
51
51
  { value: 50, color: '#D14343' },
52
52
  ]}
53
- />,
53
+ />
54
54
  )
55
55
  expect(screen.getByTestId('gauge-value')).toHaveStyle({ color: '#14B8A6' })
56
56
  })
@@ -1,6 +1,8 @@
1
1
  import { View, Text, type ViewProps } from 'react-native'
2
2
  import { cn } from '../../../utils/cn'
3
3
  import { getSemanticColors } from '../../../theme/tokens/semantic'
4
+ import { primitiveColors } from '../../../theme/tokens/primitives'
5
+ import { alpha } from '../../../utils/colors'
4
6
 
5
7
  const sem = getSemanticColors('dark')
6
8
 
@@ -36,7 +38,7 @@ export interface GaugeProps extends Omit<ViewProps, 'children'> {
36
38
  const STATUS_SUCCESS = sem['status-success']
37
39
  const STATUS_WARNING = sem['status-warning']
38
40
  const STATUS_ERROR = sem['status-error']
39
- const TRACK = 'rgba(255,255,255,0.08)'
41
+ const TRACK = alpha(primitiveColors.white, 0.08)
40
42
 
41
43
  /** Titan status-token bands for a 0–100 score. */
42
44
  const DEFAULT_THRESHOLDS: GaugeThreshold[] = [
@@ -152,7 +154,10 @@ export function Gauge({
152
154
  ))}
153
155
 
154
156
  {/* Center readout — Metric visual language. */}
155
- <View style={{ position: 'absolute', top: 0, left: 0, width: size, height: size }} className="items-center justify-center">
157
+ <View
158
+ style={{ position: 'absolute', top: 0, left: 0, width: size, height: size }}
159
+ className="items-center justify-center"
160
+ >
156
161
  <View className="flex-row items-baseline gap-0.5">
157
162
  <Text testID="gauge-value" className="text-3xl font-bold" style={{ color: displayColor }}>
158
163
  {formatValue(value)}
@@ -5,7 +5,16 @@ import { Metric, MetricGroup } from './Metric'
5
5
  const meta: Meta<typeof Metric> = {
6
6
  title: 'Components/Molecules/Metric',
7
7
  component: Metric,
8
- tags: ['autodocs'],
8
+ tags: ['autodocs', 'status:candidate', '!status:review'],
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component:
13
+ 'Composes RN primitives only. `MetricGroup` arranges several with a divider rule ' +
14
+ 'between them.',
15
+ },
16
+ },
17
+ },
9
18
  argTypes: {
10
19
  value: {
11
20
  control: 'text',
@@ -60,9 +60,7 @@ describe('Metric', () => {
60
60
 
61
61
  describe('accessibility', () => {
62
62
  it('has no accessibility violations', async () => {
63
- const { container } = render(
64
- <Metric value="42" label="Reps" unit="reps" trend="up" />
65
- )
63
+ const { container } = render(<Metric value="42" label="Reps" unit="reps" trend="up" />)
66
64
  const results = await axe(container)
67
65
  expect(results).toHaveNoViolations()
68
66
  })
@@ -46,18 +46,10 @@ export function Metric({
46
46
  <View className={cn('items-center', className)} {...props}>
47
47
  <View className="flex-row items-baseline gap-1">
48
48
  <Text className={cn(styles.value, 'text-text-primary')}>{value}</Text>
49
- {unit && (
50
- <Text className={cn(styles.unit, 'text-text-tertiary')}>{unit}</Text>
51
- )}
52
- {trend && (
53
- <Text className={cn(styles.unit, trendColors[trend])}>
54
- {trendArrows[trend]}
55
- </Text>
56
- )}
49
+ {unit && <Text className={cn(styles.unit, 'text-text-tertiary')}>{unit}</Text>}
50
+ {trend && <Text className={cn(styles.unit, trendColors[trend])}>{trendArrows[trend]}</Text>}
57
51
  </View>
58
- <Text className={cn(styles.label, 'text-text-secondary mt-1')}>
59
- {label}
60
- </Text>
52
+ <Text className={cn(styles.label, 'text-text-secondary mt-1')}>{label}</Text>
61
53
  </View>
62
54
  )
63
55
  }
@@ -67,11 +59,7 @@ export interface MetricGroupProps extends ViewProps {
67
59
  children: React.ReactNode
68
60
  }
69
61
 
70
- export function MetricGroup({
71
- className,
72
- children,
73
- ...props
74
- }: MetricGroupProps) {
62
+ export function MetricGroup({ className, children, ...props }: MetricGroupProps) {
75
63
  const items = React.Children.toArray(children)
76
64
 
77
65
  return (
@@ -80,10 +68,7 @@ export function MetricGroup({
80
68
  <React.Fragment key={i}>
81
69
  <View className="flex-1 items-center">{child}</View>
82
70
  {i < items.length - 1 && (
83
- <View
84
- className="w-px h-8 bg-divider mx-2"
85
- testID="metric-divider"
86
- />
71
+ <View className="w-px h-8 bg-divider mx-2" testID="metric-divider" />
87
72
  )}
88
73
  </React.Fragment>
89
74
  ))}
@@ -0,0 +1,88 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { View } from 'react-native'
3
+ import { MarkdownProse, type ProseLinker } from './MarkdownProse'
4
+
5
+ const SAMPLE = `# Shipped AW-17 and AW-18
6
+
7
+ Resolved the cwd to an initiative via the registered worktree, then folded the
8
+ result into [[bootstrap-prompt]]. See #42 for the file-history explorer.
9
+
10
+ ## What changed
11
+
12
+ - \`active-work open\` now resolves **worktree-registered** cwds first
13
+ - A missing worktree no longer aborts the launch; AW-116 tracks the prune
14
+ - TD-07.14 unified the greys, so the shell planes moved with it
15
+
16
+ ### Follow-ups
17
+
18
+ Nothing blocks AW-22; the reader is next.`
19
+
20
+ const TASK_LINKER: ProseLinker = {
21
+ id: 'task',
22
+ pattern: /\b[A-Z]{2,}-\d+(?:\.\d+)?\b/,
23
+ tone: 'brand',
24
+ }
25
+ const WIKI_LINKER: ProseLinker = {
26
+ id: 'wiki',
27
+ pattern: /\[\[[^\]]+\]\]/,
28
+ tone: 'link',
29
+ label: (ref) => ref.slice(2, -2),
30
+ }
31
+ const PR_LINKER: ProseLinker = { id: 'pr', pattern: /#\d+\b/, tone: 'muted' }
32
+
33
+ /**
34
+ * **MarkdownProse** — a small markdown subset (headings, bullets, paragraphs,
35
+ * bold, code) rendered as themed prose, with caller-described references
36
+ * auto-linked in one of three tones.
37
+ *
38
+ * Composes `Typography`. Used by `SessionDetail` and the initiative reader.
39
+ */
40
+ const meta: Meta<typeof MarkdownProse> = {
41
+ title: 'Custom/Prose/MarkdownProse',
42
+ component: MarkdownProse,
43
+ tags: ['autodocs', 'status:candidate', '!status:review'],
44
+ args: {
45
+ body: SAMPLE,
46
+ linkers: [TASK_LINKER, WIKI_LINKER, PR_LINKER],
47
+ },
48
+ argTypes: {
49
+ linkers: { table: { disable: true } },
50
+ },
51
+ decorators: [
52
+ (Story) => (
53
+ <View className="w-full max-w-[640px] p-4">
54
+ <Story />
55
+ </View>
56
+ ),
57
+ ],
58
+ parameters: {
59
+ docs: {
60
+ description: {
61
+ component:
62
+ 'Composes **Typography**. Linkers are tried in order; a match renders in its tone and, when the linker carries `onPress`, as a pressable link.',
63
+ },
64
+ },
65
+ },
66
+ }
67
+ export default meta
68
+
69
+ type Story = StoryObj<typeof MarkdownProse>
70
+
71
+ /** Task ids in brand, `[[links]]` as links, PR numbers muted. */
72
+ export const Default: Story = {}
73
+
74
+ /** The same body with no linkers: plain prose. */
75
+ export const NoLinkers: Story = {
76
+ args: { linkers: [] },
77
+ }
78
+
79
+ /** Pressable references: each linker carries an `onPress`. */
80
+ export const Pressable: Story = {
81
+ args: {
82
+ linkers: [
83
+ { ...TASK_LINKER, onPress: (id) => console.log('task', id) },
84
+ { ...WIKI_LINKER, onPress: (name) => console.log('link', name) },
85
+ { ...PR_LINKER, onPress: (ref) => console.log('pr', ref) },
86
+ ],
87
+ },
88
+ }
@@ -0,0 +1,87 @@
1
+ import { describe, it, expect, vi } from 'vitest'
2
+ import { fireEvent, render, screen } from '@testing-library/react'
3
+ import { axe } from 'jest-axe'
4
+ import { MarkdownProse, parseProseBlocks, type ProseLinker } from './MarkdownProse'
5
+
6
+ const taskLinker: ProseLinker = { id: 'task', pattern: /\b[A-Z]{2,}-\d+\b/, tone: 'brand' }
7
+ const wikiLinker: ProseLinker = {
8
+ id: 'wiki',
9
+ pattern: /\[\[[^\]]+\]\]/,
10
+ tone: 'link',
11
+ label: (ref) => ref.slice(2, -2),
12
+ }
13
+
14
+ describe('parseProseBlocks', () => {
15
+ it('joins consecutive lines into one paragraph and splits on blank lines', () => {
16
+ const blocks = parseProseBlocks('one\ntwo\n\nthree')
17
+ expect(blocks).toEqual([
18
+ { type: 'p', text: 'one two' },
19
+ { type: 'p', text: 'three' },
20
+ ])
21
+ })
22
+
23
+ it('reads headings and bullets, flattening deep headings to h3', () => {
24
+ const blocks = parseProseBlocks('# Title\n## Section\n#### Deep\n- item\n* other')
25
+ expect(blocks.map((b) => b.type)).toEqual(['h1', 'h2', 'h3', 'li', 'li'])
26
+ expect(blocks[2]!.text).toBe('Deep')
27
+ expect(blocks[4]!.text).toBe('other')
28
+ })
29
+
30
+ it('folds an indented continuation line into the bullet above it', () => {
31
+ const blocks = parseProseBlocks('- first `code\n span` continues\nnot indented')
32
+ expect(blocks).toEqual([
33
+ { type: 'li', text: 'first `code span` continues' },
34
+ { type: 'p', text: 'not indented' },
35
+ ])
36
+ })
37
+
38
+ it('renders nothing for an empty body', () => {
39
+ expect(parseProseBlocks('')).toEqual([])
40
+ })
41
+ })
42
+
43
+ describe('MarkdownProse', () => {
44
+ it('renders bold and code spans without their markers', () => {
45
+ render(<MarkdownProse body="a **bold** and `code` span" />)
46
+ expect(screen.getByText('bold')).toBeInTheDocument()
47
+ expect(screen.getByText('code')).toBeInTheDocument()
48
+ expect(screen.queryByText(/\*\*/)).not.toBeInTheDocument()
49
+ })
50
+
51
+ it('auto-links each linker in its tone and applies the label', () => {
52
+ render(
53
+ <MarkdownProse body="See AW-22 and [[memory-note]]." linkers={[taskLinker, wikiLinker]} />
54
+ )
55
+ expect(screen.getByTestId('prose-ref-task')).toHaveTextContent('AW-22')
56
+ expect(screen.getByTestId('prose-ref-wiki')).toHaveTextContent('memory-note')
57
+ expect(screen.queryByText('[[memory-note]]')).not.toBeInTheDocument()
58
+ })
59
+
60
+ it('makes a reference pressable only when its linker has a handler', () => {
61
+ const onPress = vi.fn()
62
+ render(
63
+ <MarkdownProse body="AW-1 then [[note]]" linkers={[{ ...taskLinker, onPress }, wikiLinker]} />
64
+ )
65
+ fireEvent.click(screen.getByTestId('prose-ref-task'))
66
+ expect(onPress).toHaveBeenCalledWith('AW-1')
67
+ expect(screen.getByTestId('prose-ref-task')).toHaveAttribute('role', 'link')
68
+ expect(screen.getByTestId('prose-ref-wiki')).not.toHaveAttribute('role')
69
+ })
70
+
71
+ it('lets the first matching linker win when two patterns overlap', () => {
72
+ const broad: ProseLinker = { id: 'broad', pattern: /\b[A-Z]+-\d+\b/ }
73
+ render(<MarkdownProse body="AW-9" linkers={[taskLinker, broad]} />)
74
+ expect(screen.getByTestId('prose-ref-task')).toBeInTheDocument()
75
+ expect(screen.queryByTestId('prose-ref-broad')).not.toBeInTheDocument()
76
+ })
77
+
78
+ it('has no a11y violations', async () => {
79
+ const { container } = render(
80
+ <MarkdownProse
81
+ body="# Head\n\nBody with AW-3.\n\n- a bullet"
82
+ linkers={[{ ...taskLinker, onPress: () => {} }]}
83
+ />
84
+ )
85
+ expect(await axe(container)).toHaveNoViolations()
86
+ })
87
+ })