@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,201 @@
1
+ // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
+ import { useMemo, type ReactNode } from 'react'
3
+ import { Text, View } from 'react-native'
4
+ import { cn } from '../../../utils/cn'
5
+ import { Typography } from '../Typography'
6
+
7
+ /** How a linked reference reads: brand for the domain's own ids, link for cross-references, muted for asides. */
8
+ export type ProseLinkTone = 'brand' | 'link' | 'muted'
9
+
10
+ /**
11
+ * A pattern the prose auto-links. Patterns must not carry the `g` flag or
12
+ * capture groups: the renderer combines them into one tokenizer.
13
+ */
14
+ export interface ProseLinker {
15
+ /** Stable id, used in keys and test ids. */
16
+ id: string
17
+ pattern: RegExp
18
+ tone?: ProseLinkTone
19
+ /** Text to show for a match. Defaults to the match itself. */
20
+ label?: (ref: string) => string
21
+ /** When set the reference is pressable and exposes a link role. */
22
+ onPress?: (ref: string) => void
23
+ }
24
+
25
+ export type ProseBlockType = 'h1' | 'h2' | 'h3' | 'li' | 'p'
26
+
27
+ export interface ProseBlock {
28
+ type: ProseBlockType
29
+ text: string
30
+ }
31
+
32
+ export interface MarkdownProseProps {
33
+ /** Markdown source. Headings, bullet lists, paragraphs, bold and code are understood. */
34
+ body: string
35
+ /** Reference patterns to auto-link, tried in order. */
36
+ linkers?: ProseLinker[]
37
+ className?: string
38
+ testID?: string
39
+ }
40
+
41
+ const BOLD = /\*\*[^*]+\*\*/
42
+ const CODE = /`[^`]+`/
43
+
44
+ /**
45
+ * Splits markdown into the block kinds this renderer understands. Consecutive
46
+ * text lines join into one paragraph; a blank line ends it. An indented line
47
+ * straight after a bullet continues that bullet. Deeper headings flatten to
48
+ * h3, since session prose never needs more than three levels.
49
+ */
50
+ export function parseProseBlocks(body: string): ProseBlock[] {
51
+ const blocks: ProseBlock[] = []
52
+ let paragraph: string[] = []
53
+ const flush = () => {
54
+ if (paragraph.length) blocks.push({ type: 'p', text: paragraph.join(' ') })
55
+ paragraph = []
56
+ }
57
+ for (const raw of body.split('\n')) {
58
+ const line = raw.trim()
59
+ if (!line) {
60
+ flush()
61
+ continue
62
+ }
63
+ const heading = line.match(/^(#{1,6})\s+(.*)/)
64
+ if (heading) {
65
+ flush()
66
+ const level = Math.min(3, heading[1]!.length)
67
+ blocks.push({ type: `h${level}` as ProseBlockType, text: heading[2]! })
68
+ continue
69
+ }
70
+ const item = line.match(/^[-*]\s+(.*)/)
71
+ if (item) {
72
+ flush()
73
+ blocks.push({ type: 'li', text: item[1]! })
74
+ continue
75
+ }
76
+ // Lazy continuation: an indented line straight after a bullet belongs to it.
77
+ const last = blocks[blocks.length - 1]
78
+ if (last?.type === 'li' && paragraph.length === 0 && /^\s/.test(raw)) {
79
+ last.text = `${last.text} ${line}`
80
+ continue
81
+ }
82
+ paragraph.push(line)
83
+ }
84
+ flush()
85
+ return blocks
86
+ }
87
+
88
+ // One step denser than body2, with a 20px line so long logs stay readable at that size.
89
+ const BODY_TEXT = 'text-xs leading-5 text-text-secondary'
90
+
91
+ const TONE_CLASS: Record<ProseLinkTone, string> = {
92
+ brand: 'font-semibold text-brand-primary',
93
+ link: 'font-medium text-text-link',
94
+ muted: 'text-text-tertiary',
95
+ }
96
+
97
+ function anchored(pattern: RegExp): RegExp {
98
+ return new RegExp(`^(?:${pattern.source})$`)
99
+ }
100
+
101
+ /** One tokenizer for bold, code and every linker, so a span is classified exactly once. */
102
+ function buildTokenizer(linkers: ProseLinker[]): RegExp {
103
+ const parts = [BOLD.source, CODE.source, ...linkers.map((l) => l.pattern.source)]
104
+ return new RegExp(`(${parts.join('|')})`, 'g')
105
+ }
106
+
107
+ function LinkedRef({ linker, value }: { linker: ProseLinker; value: string }) {
108
+ const label = linker.label ? linker.label(value) : value
109
+ const pressable = !!linker.onPress
110
+ return (
111
+ <Text
112
+ className={TONE_CLASS[linker.tone ?? 'link']}
113
+ onPress={pressable ? () => linker.onPress?.(value) : undefined}
114
+ accessibilityRole={pressable ? 'link' : undefined}
115
+ testID={`prose-ref-${linker.id}`}
116
+ >
117
+ {label}
118
+ </Text>
119
+ )
120
+ }
121
+
122
+ function renderInline(text: string, linkers: ProseLinker[], tokenizer: RegExp): ReactNode[] {
123
+ const anchoredLinkers = linkers.map((l) => ({ linker: l, test: anchored(l.pattern) }))
124
+ return text
125
+ .split(tokenizer)
126
+ .filter((piece) => piece !== '' && piece !== undefined)
127
+ .map((piece, i) => {
128
+ if (anchored(BOLD).test(piece)) {
129
+ return (
130
+ <Text key={i} className="font-semibold text-text-primary">
131
+ {piece.slice(2, -2)}
132
+ </Text>
133
+ )
134
+ }
135
+ if (anchored(CODE).test(piece)) {
136
+ return (
137
+ <Text key={i} className="font-mono text-text-primary">
138
+ {piece.slice(1, -1)}
139
+ </Text>
140
+ )
141
+ }
142
+ const hit = anchoredLinkers.find((l) => l.test.test(piece))
143
+ if (hit) return <LinkedRef key={i} linker={hit.linker} value={piece} />
144
+ return <Text key={i}>{piece}</Text>
145
+ })
146
+ }
147
+
148
+ function Block({ block, inline }: { block: ProseBlock; inline: (text: string) => ReactNode[] }) {
149
+ if (block.type === 'h1' || block.type === 'h2') {
150
+ return (
151
+ <Typography variant={block.type === 'h1' ? 'h5' : 'h6'} className="mt-1.5 text-text-primary">
152
+ {block.text}
153
+ </Typography>
154
+ )
155
+ }
156
+ if (block.type === 'h3') {
157
+ return (
158
+ <Typography variant="subtitle2" className="mt-1 font-bold text-text-primary">
159
+ {inline(block.text)}
160
+ </Typography>
161
+ )
162
+ }
163
+ if (block.type === 'li') {
164
+ return (
165
+ <View className="flex-row gap-2 pl-1">
166
+ <Text className="text-xs leading-5 text-brand-primary">•</Text>
167
+ <Typography variant="body2" className={cn('flex-1', BODY_TEXT)}>
168
+ {inline(block.text)}
169
+ </Typography>
170
+ </View>
171
+ )
172
+ }
173
+ return (
174
+ <Typography variant="body2" className={BODY_TEXT}>
175
+ {inline(block.text)}
176
+ </Typography>
177
+ )
178
+ }
179
+
180
+ /**
181
+ * MarkdownProse — renders a small, predictable markdown subset as themed prose
182
+ * and auto-links references the caller describes.
183
+ *
184
+ * It is deliberately not a full markdown engine: headings, bullet lists,
185
+ * paragraphs, bold and code cover the notes, briefs and session logs this
186
+ * system reads, and anything richer would need a design pass of its own.
187
+ * Composes {@link Typography}. Used by `SessionDetail` (session logs) and the
188
+ * initiative reader (brief and handoff prose).
189
+ */
190
+ export function MarkdownProse({ body, linkers = [], className, testID }: MarkdownProseProps) {
191
+ const blocks = useMemo(() => parseProseBlocks(body), [body])
192
+ const tokenizer = useMemo(() => buildTokenizer(linkers), [linkers])
193
+ const inline = (text: string) => renderInline(text, linkers, tokenizer)
194
+ return (
195
+ <View className={cn('gap-2', className)} testID={testID}>
196
+ {blocks.map((block, i) => (
197
+ <Block key={i} block={block} inline={inline} />
198
+ ))}
199
+ </View>
200
+ )
201
+ }
@@ -0,0 +1,58 @@
1
+ # `custom/Prose` — long-form text rendering
2
+
3
+ One component. It exists because the ActiveWork readers display markdown written
4
+ by humans and agents — briefs, session notes, open loops — and a `<Text>` per
5
+ paragraph loses headings, bullets, emphasis and, critically, the domain
6
+ references (`AW-124`, `#172`, a file path) that make that prose navigable.
7
+
8
+ This README is the **index**: **composes ↓** and **used-by ↑**, so the tree
9
+ navigates both ways. Counts come from
10
+ [`src/arch/arch-graph.json`](../../../arch/arch-graph.json).
11
+
12
+ ## Dependency map
13
+
14
+ | Member | Kind | Composes ↓ | Used-by ↑ |
15
+ | ------------------------------- | -------- | ------------------ | ---------------------------------------------------------- |
16
+ | `MarkdownProse` | molecule | `Typography`, `cn` | InitiativeBrief, OpenLoops, SessionDetail (all ActiveWork) |
17
+ | `parseProseBlocks` | pure fn | — | `MarkdownProse`, and its own tests |
18
+ | `ProseLinker` / `ProseLinkTone` | types | — | every consumer that supplies reference patterns |
19
+
20
+ Four in-repo consumers, zero external — `keep-internal` in the arch graph. The
21
+ parser is exported separately on purpose: block-splitting is the part worth
22
+ testing directly, and it is testable without rendering.
23
+
24
+ ## The two halves
25
+
26
+ **`parseProseBlocks(body)`** splits markdown into the five block kinds this
27
+ renderer understands (`h1` `h2` `h3` `li` `p`). Consecutive text lines join into
28
+ one paragraph and a blank line ends it; an indented line straight after a bullet
29
+ continues that bullet; deeper headings flatten to `h3`, because session prose
30
+ never needs more than three levels. It is pure, so it is unit-tested on its own.
31
+
32
+ **`MarkdownProse`** renders those blocks and inline-links references. `linkers`
33
+ is an ordered list of `{ id, pattern, tone, label, onPress }`; the renderer
34
+ combines the patterns into one tokenizer, which is why **a `ProseLinker.pattern`
35
+ must carry neither the `g` flag nor capture groups**. `tone` chooses how a
36
+ reference reads: `brand` for the domain's own ids, `link` for cross-references,
37
+ `muted` for asides. A linker with `onPress` becomes pressable and takes a link
38
+ role; without one it is styled text, not an interactive element.
39
+
40
+ ## Reuse audit
41
+
42
+ | Leaf | Should compose | Status |
43
+ | ---------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
44
+ | Headings and body copy | `Typography` | **Closed.** `h1`/`h2` → `h5`/`h6`, `h3` → `subtitle2`, paragraphs and bullets → `body2`. |
45
+ | Bold and code spans | `Typography` | **Open, deliberately.** Inline spans are raw `<Text>` with `font-semibold` / `font-mono`, because they nest inside a parent `<Text>` run where a `Typography` block would break the flow. |
46
+ | Bullet glyph | a shared list mark | **Open.** The `•` is drawn inline. One consumer family, so no primitive is justified yet — revisit at the second. |
47
+ | Colour | semantic tokens | **Closed.** `text-text-primary` / `text-brand-primary` throughout; the family is in the token-pure eslint error block. |
48
+ | Class merging | `cn()` | **Closed.** |
49
+
50
+ ## Watch list
51
+
52
+ - **The tokenizer contract is unenforced.** A `linkers` entry with a `g` flag or a
53
+ capture group breaks reference splitting at runtime with no type error. If a
54
+ third consumer arrives, validate in `MarkdownProse` rather than in each caller.
55
+ - **Markdown coverage is intentionally partial** — no tables, links, images,
56
+ blockquotes or nested lists. Widen it only against a real document that needs
57
+ it; the parser's value is that it is small enough to read.
58
+ - **Zero external consumers.** Prop changes are cheap while that holds.
@@ -0,0 +1 @@
1
+ export * from './MarkdownProse'
@@ -1,10 +1,13 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import { Scatter, type ScatterDatum } from './Scatter'
3
+ import { getSemanticColors } from '../../../theme/tokens/semantic'
4
+
5
+ const t = getSemanticColors('dark')
3
6
 
4
7
  const meta: Meta<typeof Scatter> = {
5
- title: 'Components/DataViz/Scatter',
8
+ title: 'Custom/Charts/Scatter',
6
9
  component: Scatter,
7
- tags: ['autodocs'],
10
+ tags: ['autodocs', 'status:candidate', '!status:review'],
8
11
  argTypes: {
9
12
  width: { control: { type: 'range', min: 240, max: 800, step: 20 } },
10
13
  height: { control: { type: 'range', min: 180, max: 600, step: 20 } },
@@ -16,12 +19,12 @@ type Story = StoryObj<typeof Scatter>
16
19
 
17
20
  /** Modules on the instability × abstractness "main sequence". */
18
21
  const mainSequence: ScatterDatum[] = [
19
- { id: 'core', x: 0.08, y: 0.85, r: 10, color: '#14B8A6', label: 'core' },
20
- { id: 'domain', x: 0.22, y: 0.7, r: 8, color: '#14B8A6', label: 'domain' },
21
- { id: 'services', x: 0.45, y: 0.4, r: 12, color: '#5B9BD5', label: 'services' },
22
- { id: 'api', x: 0.6, y: 0.35, r: 7, color: '#5B9BD5', label: 'api' },
23
- { id: 'cli', x: 0.9, y: 0.08, r: 9, color: '#F4A736', label: 'cli' },
24
- { id: 'utils', x: 0.85, y: 0.75, r: 6, color: '#F83030', label: 'utils (pain?)' },
22
+ { id: 'core', x: 0.08, y: 0.85, r: 10, color: t['data-1'], label: 'core' },
23
+ { id: 'domain', x: 0.22, y: 0.7, r: 8, color: t['data-1'], label: 'domain' },
24
+ { id: 'services', x: 0.45, y: 0.4, r: 12, color: t['data-7'], label: 'services' },
25
+ { id: 'api', x: 0.6, y: 0.35, r: 7, color: t['data-7'], label: 'api' },
26
+ { id: 'cli', x: 0.9, y: 0.08, r: 9, color: t['data-6'], label: 'cli' },
27
+ { id: 'utils', x: 0.85, y: 0.75, r: 6, color: t['data-4'], label: 'utils (pain?)' },
25
28
  ]
26
29
 
27
30
  export const Default: Story = {
@@ -30,7 +33,14 @@ export const Default: Story = {
30
33
  width: 480,
31
34
  height: 340,
32
35
  diagonal: true,
33
- axis: { xLabel: 'Instability (I)', yLabel: 'Abstractness (A)', xMin: 0, xMax: 1, yMin: 0, yMax: 1 },
36
+ axis: {
37
+ xLabel: 'Instability (I)',
38
+ yLabel: 'Abstractness (A)',
39
+ xMin: 0,
40
+ xMax: 1,
41
+ yMin: 0,
42
+ yMax: 1,
43
+ },
34
44
  },
35
45
  }
36
46
 
@@ -39,7 +49,14 @@ export const NoDiagonal: Story = {
39
49
  data: mainSequence,
40
50
  width: 480,
41
51
  height: 340,
42
- axis: { xLabel: 'Instability (I)', yLabel: 'Abstractness (A)', xMin: 0, xMax: 1, yMin: 0, yMax: 1 },
52
+ axis: {
53
+ xLabel: 'Instability (I)',
54
+ yLabel: 'Abstractness (A)',
55
+ xMin: 0,
56
+ xMax: 1,
57
+ yMin: 0,
58
+ yMax: 1,
59
+ },
43
60
  },
44
61
  }
45
62
 
@@ -50,7 +67,14 @@ export const Selected: Story = {
50
67
  height: 340,
51
68
  diagonal: true,
52
69
  selectedId: 'utils',
53
- axis: { xLabel: 'Instability (I)', yLabel: 'Abstractness (A)', xMin: 0, xMax: 1, yMin: 0, yMax: 1 },
70
+ axis: {
71
+ xLabel: 'Instability (I)',
72
+ yLabel: 'Abstractness (A)',
73
+ xMin: 0,
74
+ xMax: 1,
75
+ yMin: 0,
76
+ yMax: 1,
77
+ },
54
78
  },
55
79
  }
56
80
 
@@ -67,11 +91,21 @@ export const SinglePoint: Story = {
67
91
  /** An outlier far outside the main cluster, kept legible by an explicit domain. */
68
92
  export const Outlier: Story = {
69
93
  args: {
70
- data: [...mainSequence, { id: 'legacy', x: 0.98, y: 0.98, r: 16, color: '#F83030', label: 'legacy.ts' }],
94
+ data: [
95
+ ...mainSequence,
96
+ { id: 'legacy', x: 0.98, y: 0.98, r: 16, color: t['data-4'], label: 'legacy.ts' },
97
+ ],
71
98
  width: 480,
72
99
  height: 340,
73
100
  diagonal: true,
74
- axis: { xLabel: 'Instability (I)', yLabel: 'Abstractness (A)', xMin: 0, xMax: 1, yMin: 0, yMax: 1 },
101
+ axis: {
102
+ xLabel: 'Instability (I)',
103
+ yLabel: 'Abstractness (A)',
104
+ xMin: 0,
105
+ xMax: 1,
106
+ yMin: 0,
107
+ yMax: 1,
108
+ },
75
109
  },
76
110
  }
77
111
 
@@ -80,6 +114,13 @@ export const Empty: Story = {
80
114
  data: [],
81
115
  width: 400,
82
116
  height: 300,
83
- axis: { xLabel: 'Instability (I)', yLabel: 'Abstractness (A)', xMin: 0, xMax: 1, yMin: 0, yMax: 1 },
117
+ axis: {
118
+ xLabel: 'Instability (I)',
119
+ yLabel: 'Abstractness (A)',
120
+ xMin: 0,
121
+ xMax: 1,
122
+ yMin: 0,
123
+ yMax: 1,
124
+ },
84
125
  },
85
126
  }
@@ -68,7 +68,7 @@ describe('Scatter', () => {
68
68
  width={200}
69
69
  height={200}
70
70
  axis={{ xMin: 0, xMax: 1000, yMin: 0, yMax: 1000 }}
71
- />,
71
+ />
72
72
  )
73
73
  expect(screen.getByTestId('scatter-point-big')).toBeInTheDocument()
74
74
  })
@@ -83,7 +83,7 @@ describe('Scatter', () => {
83
83
 
84
84
  it('has no accessibility violations', async () => {
85
85
  const { container } = render(
86
- <Scatter {...base} diagonal axis={{ xLabel: 'I', yLabel: 'A' }} selectedId="b" />,
86
+ <Scatter {...base} diagonal axis={{ xLabel: 'I', yLabel: 'A' }} selectedId="b" />
87
87
  )
88
88
  expect(await axe(container)).toHaveNoViolations()
89
89
  })
@@ -1,6 +1,8 @@
1
1
  import { View, Text, Pressable, type ViewProps } from 'react-native'
2
2
  import { cn } from '../../../utils/cn'
3
3
  import { DATAVIZ_CATEGORICAL_PALETTE } from '../../../theme/extracted-colors-dataviz'
4
+ import { primitiveColors } from '../../../theme/tokens/primitives'
5
+ import { alpha } from '../../../utils/colors'
4
6
 
5
7
  export interface ScatterDatum {
6
8
  /** Stable identity — returned by onPress and used as the React key. */
@@ -49,9 +51,9 @@ export interface ScatterProps extends Omit<ViewProps, 'children'> {
49
51
  /** Titan categorical fallback palette (see extracted-colors-dataviz). */
50
52
  const PALETTE = DATAVIZ_CATEGORICAL_PALETTE
51
53
 
52
- const GRID_LINE = 'rgba(255,255,255,0.07)'
53
- const AXIS_LINE = 'rgba(255,255,255,0.18)'
54
- const DIAGONAL_LINE = 'rgba(255,255,255,0.28)'
54
+ const GRID_LINE = alpha(primitiveColors.white, 0.07)
55
+ const AXIS_LINE = alpha(primitiveColors.white, 0.18)
56
+ const DIAGONAL_LINE = alpha(primitiveColors.white, 0.28)
55
57
  const DEFAULT_R = 6
56
58
 
57
59
  const PLOT_LEFT = 40
@@ -86,8 +88,20 @@ function formatTick(v: number): string {
86
88
 
87
89
  /** A single straight segment drawn as one rotated View (SVG-free). */
88
90
  function LineSegment({
89
- x1, y1, x2, y2, color, testID,
90
- }: { x1: number; y1: number; x2: number; y2: number; color: string; testID: string }) {
91
+ x1,
92
+ y1,
93
+ x2,
94
+ y2,
95
+ color,
96
+ testID,
97
+ }: {
98
+ x1: number
99
+ y1: number
100
+ x2: number
101
+ y2: number
102
+ color: string
103
+ testID: string
104
+ }) {
91
105
  const dx = x2 - x1
92
106
  const dy = y2 - y1
93
107
  const length = Math.sqrt(dx * dx + dy * dy)
@@ -132,8 +146,16 @@ export function Scatter({
132
146
  }: ScatterProps) {
133
147
  const innerW = Math.max(1, width - PLOT_LEFT - PLOT_RIGHT)
134
148
  const innerH = Math.max(1, height - PLOT_TOP - PLOT_BOTTOM)
135
- const xd = domainOf(data.map((d) => d.x), axis.xMin, axis.xMax)
136
- const yd = domainOf(data.map((d) => d.y), axis.yMin, axis.yMax)
149
+ const xd = domainOf(
150
+ data.map((d) => d.x),
151
+ axis.xMin,
152
+ axis.xMax
153
+ )
154
+ const yd = domainOf(
155
+ data.map((d) => d.y),
156
+ axis.yMin,
157
+ axis.yMax
158
+ )
137
159
 
138
160
  const toX = (x: number) => PLOT_LEFT + ((x - xd.min) / (xd.max - xd.min)) * innerW
139
161
  const toY = (y: number) => PLOT_TOP + (1 - (y - yd.min) / (yd.max - yd.min)) * innerH
@@ -164,9 +186,25 @@ export function Scatter({
164
186
  key={`y-${i}`}
165
187
  accessibilityElementsHidden
166
188
  testID="scatter-gridline-y"
167
- style={{ position: 'absolute', left: PLOT_LEFT, width: innerW, top: y, height: 1, backgroundColor: GRID_LINE }}
189
+ style={{
190
+ position: 'absolute',
191
+ left: PLOT_LEFT,
192
+ width: innerW,
193
+ top: y,
194
+ height: 1,
195
+ backgroundColor: GRID_LINE,
196
+ }}
168
197
  >
169
- <Text className="text-[9px] text-text-tertiary" style={{ position: 'absolute', left: -PLOT_LEFT, top: -6, width: PLOT_LEFT - 6, textAlign: 'right' }}>
198
+ <Text
199
+ className="text-[9px] text-text-tertiary"
200
+ style={{
201
+ position: 'absolute',
202
+ left: -PLOT_LEFT,
203
+ top: -6,
204
+ width: PLOT_LEFT - 6,
205
+ textAlign: 'right',
206
+ }}
207
+ >
170
208
  {formatTick(t)}
171
209
  </Text>
172
210
  </View>
@@ -179,9 +217,25 @@ export function Scatter({
179
217
  key={`x-${i}`}
180
218
  accessibilityElementsHidden
181
219
  testID="scatter-gridline-x"
182
- style={{ position: 'absolute', top: PLOT_TOP, height: innerH, left: x, width: 1, backgroundColor: GRID_LINE }}
220
+ style={{
221
+ position: 'absolute',
222
+ top: PLOT_TOP,
223
+ height: innerH,
224
+ left: x,
225
+ width: 1,
226
+ backgroundColor: GRID_LINE,
227
+ }}
183
228
  >
184
- <Text className="text-[9px] text-text-tertiary" style={{ position: 'absolute', top: innerH + 4, left: -16, width: 32, textAlign: 'center' }}>
229
+ <Text
230
+ className="text-[9px] text-text-tertiary"
231
+ style={{
232
+ position: 'absolute',
233
+ top: innerH + 4,
234
+ left: -16,
235
+ width: 32,
236
+ textAlign: 'center',
237
+ }}
238
+ >
185
239
  {formatTick(t)}
186
240
  </Text>
187
241
  </View>
@@ -189,8 +243,28 @@ export function Scatter({
189
243
  })}
190
244
 
191
245
  {/* Axis frame (left + bottom). */}
192
- <View accessibilityElementsHidden style={{ position: 'absolute', left: PLOT_LEFT, top: PLOT_TOP, width: 1, height: innerH, backgroundColor: AXIS_LINE }} />
193
- <View accessibilityElementsHidden style={{ position: 'absolute', left: PLOT_LEFT, top: PLOT_TOP + innerH, width: innerW, height: 1, backgroundColor: AXIS_LINE }} />
246
+ <View
247
+ accessibilityElementsHidden
248
+ style={{
249
+ position: 'absolute',
250
+ left: PLOT_LEFT,
251
+ top: PLOT_TOP,
252
+ width: 1,
253
+ height: innerH,
254
+ backgroundColor: AXIS_LINE,
255
+ }}
256
+ />
257
+ <View
258
+ accessibilityElementsHidden
259
+ style={{
260
+ position: 'absolute',
261
+ left: PLOT_LEFT,
262
+ top: PLOT_TOP + innerH,
263
+ width: innerW,
264
+ height: 1,
265
+ backgroundColor: AXIS_LINE,
266
+ }}
267
+ />
194
268
 
195
269
  {diagonal && (
196
270
  <LineSegment
@@ -204,19 +278,44 @@ export function Scatter({
204
278
  )}
205
279
 
206
280
  {axis.xLabel && (
207
- <Text testID="scatter-x-label" className="text-[10px] font-semibold text-text-secondary" style={{ position: 'absolute', left: PLOT_LEFT, width: innerW, bottom: 2, textAlign: 'center' }}>
281
+ <Text
282
+ testID="scatter-x-label"
283
+ className="text-[10px] font-semibold text-text-secondary"
284
+ style={{
285
+ position: 'absolute',
286
+ left: PLOT_LEFT,
287
+ width: innerW,
288
+ bottom: 2,
289
+ textAlign: 'center',
290
+ }}
291
+ >
208
292
  {axis.xLabel}
209
293
  </Text>
210
294
  )}
211
295
  {axis.yLabel && (
212
- <Text testID="scatter-y-label" className="text-[10px] font-semibold text-text-secondary" style={{ position: 'absolute', left: -innerH / 2 + 6, top: PLOT_TOP + innerH / 2, width: innerH, textAlign: 'center', transformOrigin: '50% 50%', transform: [{ rotate: '-90deg' }] }}>
296
+ <Text
297
+ testID="scatter-y-label"
298
+ className="text-[10px] font-semibold text-text-secondary"
299
+ style={{
300
+ position: 'absolute',
301
+ left: -innerH / 2 + 6,
302
+ top: PLOT_TOP + innerH / 2,
303
+ width: innerH,
304
+ textAlign: 'center',
305
+ transformOrigin: '50% 50%',
306
+ transform: [{ rotate: '-90deg' }],
307
+ }}
308
+ >
213
309
  {axis.yLabel}
214
310
  </Text>
215
311
  )}
216
312
  </View>
217
313
 
218
314
  {/* Interactive point overlay (kept out of the image-role canvas). */}
219
- <View style={{ position: 'absolute', top: 0, left: 0, width, height }} testID="scatter-points">
315
+ <View
316
+ style={{ position: 'absolute', top: 0, left: 0, width, height }}
317
+ testID="scatter-points"
318
+ >
220
319
  {points.map((p) => {
221
320
  const selected = p.datum.id === selectedId
222
321
  const showLabel = p.datum.label && (selected || p.radius >= 10)
@@ -237,14 +336,19 @@ export function Scatter({
237
336
  backgroundColor: p.color,
238
337
  opacity: selected ? 1 : 0.82,
239
338
  borderWidth: selected ? 2 : 0,
240
- borderColor: '#ffffff',
339
+ borderColor: primitiveColors.white,
241
340
  }}
242
341
  >
243
342
  {showLabel && (
244
343
  <Text
245
344
  numberOfLines={1}
246
345
  className="text-[9px] font-semibold text-text-primary"
247
- style={{ position: 'absolute', left: p.radius * 2 + 3, top: p.radius - 6, width: 80 }}
346
+ style={{
347
+ position: 'absolute',
348
+ left: p.radius * 2 + 3,
349
+ top: p.radius - 6,
350
+ width: 80,
351
+ }}
248
352
  >
249
353
  {p.datum.label}
250
354
  </Text>