@titan-design/react-ui 0.12.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (509) hide show
  1. package/README.md +46 -47
  2. package/dist/bodymap.js +768 -701
  3. package/dist/bodymap.js.map +1 -1
  4. package/dist/bodymap.mjs +770 -703
  5. package/dist/bodymap.mjs.map +1 -1
  6. package/dist/index.d.mts +1319 -216
  7. package/dist/index.d.ts +1319 -216
  8. package/dist/index.js +5996 -4498
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +5773 -4357
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/{pages-CvsFqNtx.d.mts → pages-CpW3OB3g.d.mts} +21 -3
  13. package/dist/{pages-B1hqnGIC.d.ts → pages-_3Ajpiqu.d.ts} +21 -3
  14. package/dist/pages.d.mts +1 -1
  15. package/dist/pages.d.ts +1 -1
  16. package/dist/pages.js +1435 -1428
  17. package/dist/pages.js.map +1 -1
  18. package/dist/pages.mjs +1443 -1436
  19. package/dist/pages.mjs.map +1 -1
  20. package/dist/theme/index.d.mts +195 -134
  21. package/dist/theme/index.d.ts +195 -134
  22. package/dist/theme/index.js +215 -300
  23. package/dist/theme/index.js.map +1 -1
  24. package/dist/theme/index.mjs +202 -296
  25. package/dist/theme/index.mjs.map +1 -1
  26. package/dist/theme/tokens-css.js +62 -5
  27. package/dist/theme/tokens-css.js.map +1 -1
  28. package/dist/theme/tokens-css.mjs +62 -5
  29. package/dist/theme/tokens-css.mjs.map +1 -1
  30. package/dist/theme/tokens.d.mts +73 -5
  31. package/dist/theme/tokens.d.ts +73 -5
  32. package/dist/theme/tokens.js +55 -7
  33. package/dist/theme/tokens.js.map +1 -1
  34. package/dist/theme/tokens.mjs +55 -8
  35. package/dist/theme/tokens.mjs.map +1 -1
  36. package/docs/audits/2026-09-08-critique-addenda.md +51 -0
  37. package/docs/audits/2026-09-08-library-critique.md +212 -0
  38. package/docs/library-roadmap.md +99 -0
  39. package/package.json +1 -1
  40. package/src/arch/arch-graph.freshness.test.ts +52 -0
  41. package/src/arch/arch-graph.json +5021 -1617
  42. package/src/components/custom/ActiveWork/CoChangeChip.stories.tsx +44 -0
  43. package/src/components/custom/ActiveWork/CoChangeChip.test.tsx +29 -0
  44. package/src/components/custom/ActiveWork/CoChangeChip.tsx +48 -0
  45. package/src/components/custom/ActiveWork/Eyebrow.stories.tsx +25 -0
  46. package/src/components/custom/ActiveWork/Eyebrow.test.tsx +16 -0
  47. package/src/components/custom/ActiveWork/Eyebrow.tsx +26 -0
  48. package/src/components/custom/ActiveWork/FileActivityDetail.stories.tsx +50 -0
  49. package/src/components/custom/ActiveWork/FileActivityDetail.test.tsx +66 -0
  50. package/src/components/custom/ActiveWork/FileActivityDetail.tsx +202 -0
  51. package/src/components/custom/ActiveWork/FileActivityRow.stories.tsx +70 -0
  52. package/src/components/custom/ActiveWork/FileActivityRow.test.tsx +67 -0
  53. package/src/components/custom/ActiveWork/FileActivityRow.tsx +130 -0
  54. package/src/components/custom/ActiveWork/FileHistoryExplorer.stories.tsx +84 -0
  55. package/src/components/custom/ActiveWork/FileHistoryExplorer.test.tsx +99 -0
  56. package/src/components/custom/ActiveWork/FileHistoryExplorer.tsx +138 -0
  57. package/src/components/custom/ActiveWork/FilePathLabel.stories.tsx +39 -0
  58. package/src/components/custom/ActiveWork/FilePathLabel.test.tsx +53 -0
  59. package/src/components/custom/ActiveWork/FilePathLabel.tsx +63 -0
  60. package/src/components/custom/ActiveWork/InitiativeBrief.stories.tsx +50 -0
  61. package/src/components/custom/ActiveWork/InitiativeBrief.test.tsx +62 -0
  62. package/src/components/custom/ActiveWork/InitiativeBrief.tsx +181 -0
  63. package/src/components/custom/ActiveWork/InitiativeCard.stories.tsx +73 -0
  64. package/src/components/custom/ActiveWork/InitiativeCard.test.tsx +68 -0
  65. package/src/components/custom/ActiveWork/InitiativeCard.tsx +136 -0
  66. package/src/components/custom/ActiveWork/InitiativeHeader.stories.tsx +59 -0
  67. package/src/components/custom/ActiveWork/InitiativeHeader.test.tsx +40 -0
  68. package/src/components/custom/ActiveWork/InitiativeHeader.tsx +73 -0
  69. package/src/components/custom/ActiveWork/InitiativeReader.stories.tsx +145 -0
  70. package/src/components/custom/ActiveWork/OpenLoops.stories.tsx +58 -0
  71. package/src/components/custom/ActiveWork/OpenLoops.test.tsx +61 -0
  72. package/src/components/custom/ActiveWork/OpenLoops.tsx +107 -0
  73. package/src/components/custom/ActiveWork/PortfolioOverview.stories.tsx +101 -0
  74. package/src/components/custom/ActiveWork/PortfolioOverview.test.tsx +81 -0
  75. package/src/components/custom/ActiveWork/PortfolioOverview.tsx +83 -0
  76. package/src/components/custom/ActiveWork/README.md +305 -0
  77. package/src/components/custom/ActiveWork/SessionDetail.stories.tsx +71 -0
  78. package/src/components/custom/ActiveWork/SessionDetail.test.tsx +84 -0
  79. package/src/components/custom/ActiveWork/SessionDetail.tsx +180 -0
  80. package/src/components/custom/ActiveWork/SessionList.stories.tsx +69 -0
  81. package/src/components/custom/ActiveWork/SessionList.test.tsx +78 -0
  82. package/src/components/custom/ActiveWork/SessionList.tsx +103 -0
  83. package/src/components/custom/ActiveWork/SessionListItem.stories.tsx +64 -0
  84. package/src/components/custom/ActiveWork/SessionListItem.test.tsx +62 -0
  85. package/src/components/custom/ActiveWork/SessionListItem.tsx +152 -0
  86. package/src/components/custom/ActiveWork/SessionReader.stories.tsx +113 -0
  87. package/src/components/custom/ActiveWork/SeverityLabel.stories.tsx +51 -0
  88. package/src/components/custom/ActiveWork/SeverityLabel.test.tsx +68 -0
  89. package/src/components/custom/ActiveWork/SeverityLabel.tsx +97 -0
  90. package/src/components/custom/ActiveWork/TaskRow.stories.tsx +63 -0
  91. package/src/components/custom/ActiveWork/TaskRow.test.tsx +112 -0
  92. package/src/components/custom/ActiveWork/TaskRow.tsx +176 -0
  93. package/src/components/custom/ActiveWork/TaskTable.stories.tsx +123 -0
  94. package/src/components/custom/ActiveWork/TaskTable.test.tsx +233 -0
  95. package/src/components/custom/ActiveWork/TaskTable.tsx +321 -0
  96. package/src/components/custom/ActiveWork/active-work-depth.test.tsx +102 -0
  97. package/src/components/custom/ActiveWork/file-history-fixture.ts +91 -0
  98. package/src/components/custom/ActiveWork/format-time.test.ts +23 -0
  99. package/src/components/custom/ActiveWork/format-time.ts +32 -0
  100. package/src/components/custom/ActiveWork/index.ts +19 -0
  101. package/src/components/custom/ActiveWork/initiative-fixture.ts +104 -0
  102. package/src/components/custom/ActiveWork/session-fixture.ts +267 -0
  103. package/src/components/custom/ActiveWork/session-linkers.ts +56 -0
  104. package/src/components/custom/ActiveWork/task-list-fixture.ts +97 -0
  105. package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +4 -3
  106. package/src/components/custom/DateTime/DateTime.stories.tsx +2 -1
  107. package/src/components/custom/DateTime/DateTime.test.tsx +3 -1
  108. package/src/components/custom/EmptyState/EmptyState.stories.tsx +22 -9
  109. package/src/components/custom/EmptyState/EmptyState.test.tsx +7 -13
  110. package/src/components/custom/EmptyState/EmptyState.tsx +4 -18
  111. package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +4 -3
  112. package/src/components/custom/Fatigue/FatigueLights.stories.tsx +2 -2
  113. package/src/components/custom/Fatigue/GhostSpark.stories.tsx +4 -3
  114. package/src/components/custom/Fatigue/LiveFatigueCard.stories.tsx +2 -2
  115. package/src/components/custom/Fatigue/LiveFatigueCard.tsx +8 -4
  116. package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +116 -6
  117. package/src/components/custom/Fatigue/LiveFatiguePanel.test.tsx +134 -0
  118. package/src/components/custom/Fatigue/LiveFatiguePanel.tsx +54 -83
  119. package/src/components/custom/Fatigue/README.md +40 -3
  120. package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +2 -2
  121. package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +2 -1
  122. package/src/components/custom/Fatigue/VelocityHero.stories.tsx +4 -3
  123. package/src/components/custom/Fatigue/VerdictHero.stories.tsx +2 -2
  124. package/src/components/custom/Fatigue/fatigue-mock.test.ts +0 -5
  125. package/src/components/custom/Fatigue/fatigue-mock.ts +2 -10
  126. package/src/components/custom/Fatigue/fatigue-tokens.ts +12 -0
  127. package/src/components/custom/Fatigue/index.ts +17 -5
  128. package/src/components/custom/Fatigue/panel-layout.test.ts +149 -0
  129. package/src/components/custom/Fatigue/panel-layout.ts +185 -0
  130. package/src/components/custom/Gauge/Gauge.stories.tsx +2 -2
  131. package/src/components/custom/Gauge/Gauge.test.tsx +1 -1
  132. package/src/components/custom/Gauge/Gauge.tsx +4 -1
  133. package/src/components/custom/Metric/Metric.stories.tsx +10 -1
  134. package/src/components/custom/Metric/Metric.test.tsx +1 -3
  135. package/src/components/custom/Metric/Metric.tsx +5 -20
  136. package/src/components/custom/Prose/MarkdownProse.stories.tsx +88 -0
  137. package/src/components/custom/Prose/MarkdownProse.test.tsx +87 -0
  138. package/src/components/custom/Prose/MarkdownProse.tsx +201 -0
  139. package/src/components/custom/Prose/README.md +58 -0
  140. package/src/components/custom/Prose/index.ts +1 -0
  141. package/src/components/custom/Scatter/Scatter.stories.tsx +46 -8
  142. package/src/components/custom/Scatter/Scatter.test.tsx +2 -2
  143. package/src/components/custom/Scatter/Scatter.tsx +116 -14
  144. package/src/components/custom/Sidebar/Sidebar.stories.tsx +40 -7
  145. package/src/components/custom/Sidebar/Sidebar.test.tsx +5 -1
  146. package/src/components/custom/Sidebar/Sidebar.tsx +16 -33
  147. package/src/components/custom/Table/Table.stories.tsx +98 -15
  148. package/src/components/custom/Table/Table.test.tsx +273 -86
  149. package/src/components/custom/Table/Table.tsx +159 -67
  150. package/src/components/custom/Table/column-fit.test.ts +75 -0
  151. package/src/components/custom/Table/column-fit.ts +116 -0
  152. package/src/components/custom/Table/index.ts +9 -0
  153. package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +8 -6
  154. package/src/components/custom/Treemap/Treemap.stories.tsx +2 -2
  155. package/src/components/custom/Treemap/Treemap.test.tsx +1 -1
  156. package/src/components/custom/Treemap/Treemap.tsx +7 -2
  157. package/src/components/custom/Typography/Typography.stories.tsx +28 -1
  158. package/src/components/custom/Typography/Typography.test.tsx +34 -0
  159. package/src/components/custom/Typography/Typography.tsx +6 -0
  160. package/src/components/custom/Typography/index.ts +1 -8
  161. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +9 -1
  162. package/src/components/custom/Workout/BaseBadge.stories.tsx +19 -7
  163. package/src/components/custom/Workout/BaseBadge.test.tsx +31 -11
  164. package/src/components/custom/Workout/BaseBadge.tsx +3 -0
  165. package/src/components/custom/Workout/BodyMap.stories.tsx +4 -1
  166. package/src/components/custom/Workout/BodyMap.tsx +3 -1
  167. package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +16 -4
  168. package/src/components/custom/Workout/BodyMapDetailPanel.test.tsx +11 -15
  169. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +45 -9
  170. package/src/components/custom/Workout/CapacityBandChart.stories.tsx +10 -8
  171. package/src/components/custom/Workout/CapacityBandChart.test.tsx +24 -51
  172. package/src/components/custom/Workout/CapacityBandChart.tsx +139 -108
  173. package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
  174. package/src/components/custom/Workout/DeviationBar.test.tsx +5 -2
  175. package/src/components/custom/Workout/DeviationBar.tsx +21 -16
  176. package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +182 -0
  177. package/src/components/custom/Workout/DualVelocityStrip.test.tsx +3 -2
  178. package/src/components/custom/Workout/ExerciseCard.stories.tsx +8 -7
  179. package/src/components/custom/Workout/ExerciseCard.test.tsx +13 -3
  180. package/src/components/custom/Workout/ExerciseCard.tsx +42 -51
  181. package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +7 -7
  182. package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +9 -1
  183. package/src/components/custom/Workout/ExerciseHeading.stories.tsx +8 -8
  184. package/src/components/custom/Workout/ExerciseHeading.tsx +8 -5
  185. package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +6 -4
  186. package/src/components/custom/Workout/ExerciseIndicator.test.tsx +27 -11
  187. package/src/components/custom/Workout/ExerciseIndicator.tsx +14 -15
  188. package/src/components/custom/Workout/FatigueMeter.stories.tsx +5 -5
  189. package/src/components/custom/Workout/InputBar.stories.tsx +4 -5
  190. package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
  191. package/src/components/custom/Workout/IntensityBar.test.tsx +5 -1
  192. package/src/components/custom/Workout/IntensityBar.tsx +4 -2
  193. package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +5 -4
  194. package/src/components/custom/Workout/MesoCard.stories.tsx +4 -1
  195. package/src/components/custom/Workout/MesoCard.test.tsx +7 -18
  196. package/src/components/custom/Workout/MesoCard.tsx +12 -9
  197. package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
  198. package/src/components/custom/Workout/MesoProgressBar.test.tsx +18 -11
  199. package/src/components/custom/Workout/MesoProgressBar.tsx +15 -13
  200. package/src/components/custom/Workout/MesoStatusCard.stories.tsx +2 -1
  201. package/src/components/custom/Workout/MesoStatusCard.test.tsx +14 -44
  202. package/src/components/custom/Workout/MesoStatusCard.tsx +9 -23
  203. package/src/components/custom/Workout/MetricTiles.stories.tsx +3 -2
  204. package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +2 -8
  205. package/src/components/custom/Workout/MuscleGroupChip.test.tsx +23 -31
  206. package/src/components/custom/Workout/MuscleGroupChip.tsx +28 -67
  207. package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +1 -1
  208. package/src/components/custom/Workout/PlaceholderStrip.test.tsx +17 -10
  209. package/src/components/custom/Workout/PlaceholderStrip.tsx +9 -8
  210. package/src/components/custom/Workout/PrBadge.stories.tsx +2 -1
  211. package/src/components/custom/Workout/PrBadge.test.tsx +3 -9
  212. package/src/components/custom/Workout/PrBadge.tsx +13 -17
  213. package/src/components/custom/Workout/PrHistoryModal.stories.tsx +4 -1
  214. package/src/components/custom/Workout/PrHistoryModal.test.tsx +13 -36
  215. package/src/components/custom/Workout/PrHistoryModal.tsx +8 -4
  216. package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +6 -1
  217. package/src/components/custom/Workout/README.md +11 -6
  218. package/src/components/custom/Workout/ReadinessCheck.stories.tsx +3 -2
  219. package/src/components/custom/Workout/ReadinessCheck.test.tsx +12 -18
  220. package/src/components/custom/Workout/ReadinessCheck.tsx +19 -3
  221. package/src/components/custom/Workout/RestTimer.stories.tsx +3 -2
  222. package/src/components/custom/Workout/ScheduleTiles.stories.tsx +3 -2
  223. package/src/components/custom/Workout/ScheduleTiles.test.tsx +3 -4
  224. package/src/components/custom/Workout/ScheduleTiles.tsx +4 -7
  225. package/src/components/custom/Workout/SegmentedBar.stories.tsx +5 -5
  226. package/src/components/custom/Workout/SegmentedBar.tsx +6 -2
  227. package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +5 -5
  228. package/src/components/custom/Workout/SessionHeader.stories.tsx +6 -6
  229. package/src/components/custom/Workout/SessionHeader.tsx +12 -12
  230. package/src/components/custom/Workout/SessionRail.stories.tsx +12 -11
  231. package/src/components/custom/Workout/SessionRail.tsx +7 -9
  232. package/src/components/custom/Workout/SetBar.stories.tsx +10 -6
  233. package/src/components/custom/Workout/SetBar.test.tsx +49 -4
  234. package/src/components/custom/Workout/SetBar.tsx +38 -12
  235. package/src/components/custom/Workout/SetRow.stories.tsx +3 -2
  236. package/src/components/custom/Workout/SetRow.test.tsx +16 -2
  237. package/src/components/custom/Workout/SetRow.tsx +61 -23
  238. package/src/components/custom/Workout/SetStrip.stories.tsx +60 -5
  239. package/src/components/custom/Workout/SetStrip.test.tsx +25 -0
  240. package/src/components/custom/Workout/SetStrip.tsx +24 -4
  241. package/src/components/custom/Workout/SetTableHeader.stories.tsx +6 -6
  242. package/src/components/custom/Workout/SetTableHeader.tsx +9 -12
  243. package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +7 -7
  244. package/src/components/custom/Workout/SetsRepsLoad.tsx +3 -5
  245. package/src/components/custom/Workout/Sparkline.stories.tsx +1 -1
  246. package/src/components/custom/Workout/Sparkline.test.tsx +37 -19
  247. package/src/components/custom/Workout/Sparkline.tsx +14 -18
  248. package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
  249. package/src/components/custom/Workout/StatusDot.test.tsx +44 -5
  250. package/src/components/custom/Workout/StatusDot.tsx +93 -63
  251. package/src/components/custom/Workout/StatusPill.stories.tsx +1 -1
  252. package/src/components/custom/Workout/StatusPill.test.tsx +6 -15
  253. package/src/components/custom/Workout/StatusPill.tsx +27 -38
  254. package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +9 -3
  255. package/src/components/custom/Workout/StrengthTrendChart.test.tsx +15 -37
  256. package/src/components/custom/Workout/StrengthTrendChart.tsx +31 -30
  257. package/src/components/custom/Workout/SupersetWrapper.stories.tsx +27 -64
  258. package/src/components/custom/Workout/SupersetWrapper.test.tsx +16 -14
  259. package/src/components/custom/Workout/SupersetWrapper.tsx +17 -21
  260. package/src/components/custom/Workout/TempoDisplay.stories.tsx +22 -7
  261. package/src/components/custom/Workout/TempoDisplay.test.tsx +18 -4
  262. package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +9 -1
  263. package/src/components/custom/Workout/VelocityStrip.compact.stories.tsx +4 -1
  264. package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +7 -4
  265. package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +4 -1
  266. package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +4 -1
  267. package/src/components/custom/Workout/VelocityStrip.stories.tsx +12 -6
  268. package/src/components/custom/Workout/VelocityStrip.tsx +3 -0
  269. package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +6 -7
  270. package/src/components/custom/Workout/VolumeLandmarkBar.tsx +15 -6
  271. package/src/components/custom/Workout/WeekRow.stories.tsx +4 -1
  272. package/src/components/custom/Workout/WeekRow.test.tsx +18 -15
  273. package/src/components/custom/Workout/WeekRow.tsx +23 -16
  274. package/src/components/custom/Workout/WeightBadge.stories.tsx +2 -1
  275. package/src/components/custom/Workout/WeightBadge.test.tsx +50 -19
  276. package/src/components/custom/Workout/WeightBadge.tsx +28 -27
  277. package/src/components/custom/Workout/WorkoutCard.stories.tsx +6 -1
  278. package/src/components/custom/Workout/WorkoutCard.test.tsx +18 -0
  279. package/src/components/custom/Workout/WorkoutCard.tsx +35 -57
  280. package/src/components/custom/Workout/WorkoutPill.stories.tsx +5 -2
  281. package/src/components/custom/Workout/WorkoutPill.test.tsx +39 -7
  282. package/src/components/custom/Workout/WorkoutPill.tsx +88 -57
  283. package/src/components/custom/Workout/ZoneTrack.stories.tsx +7 -6
  284. package/src/components/custom/Workout/ZoneTrack.tsx +10 -9
  285. package/src/components/custom/Workout/index.ts +5 -0
  286. package/src/components/custom/Workout/metricText.stories.tsx +6 -5
  287. package/src/components/custom/Workout/metricText.tsx +4 -0
  288. package/src/components/custom/Workout/setHeadingKit.tsx +207 -26
  289. package/src/components/custom/charts/README.md +61 -0
  290. package/src/components/custom/charts/SetBarChart.test.tsx +30 -1
  291. package/src/components/custom/charts/SetBarChart.tsx +42 -4
  292. package/src/components/custom/charts/SparkBars.stories.tsx +55 -0
  293. package/src/components/custom/charts/SparkBars.test.tsx +69 -0
  294. package/src/components/custom/charts/SparkBars.tsx +85 -0
  295. package/src/components/custom/charts/index.ts +3 -0
  296. package/src/components/custom/index.ts +3 -0
  297. package/src/components/custom/stepper/Stepper.stories.tsx +13 -10
  298. package/src/components/custom/stepper/Stepper.test.tsx +1 -7
  299. package/src/components/custom/stepper/Stepper.tsx +13 -28
  300. package/src/components/icons/icons.stories.tsx +1 -1
  301. package/src/components/icons/icons.tsx +51 -0
  302. package/src/components/shell/AppShell.stories.tsx +150 -0
  303. package/src/components/shell/AppShell.test.tsx +70 -0
  304. package/src/components/shell/AppShell.tsx +90 -0
  305. package/src/components/shell/BrandLockup.stories.tsx +66 -5
  306. package/src/components/shell/BrandLockup.test.tsx +34 -1
  307. package/src/components/shell/BrandLockup.tsx +27 -11
  308. package/src/components/shell/NavItem.stories.tsx +4 -4
  309. package/src/components/shell/NavItem.test.tsx +17 -0
  310. package/src/components/shell/NavItem.tsx +17 -5
  311. package/src/components/shell/README.md +89 -26
  312. package/src/components/shell/SideNav.stories.tsx +38 -7
  313. package/src/components/shell/SideNav.test.tsx +16 -8
  314. package/src/components/shell/SideNav.tsx +17 -17
  315. package/src/components/shell/TopBar.stories.tsx +44 -19
  316. package/src/components/shell/TopBar.test.tsx +34 -12
  317. package/src/components/shell/TopBar.tsx +61 -40
  318. package/src/components/shell/brands.tsx +66 -0
  319. package/src/components/shell/index.ts +9 -8
  320. package/src/components/shell/{DeviceIndicator.stories.tsx → workout/DeviceIndicator.stories.tsx} +2 -2
  321. package/src/components/shell/{DeviceIndicator.tsx → workout/DeviceIndicator.tsx} +2 -2
  322. package/src/components/shell/{DeviceMenu.stories.tsx → workout/DeviceMenu.stories.tsx} +2 -2
  323. package/src/components/shell/{DeviceMenu.tsx → workout/DeviceMenu.tsx} +3 -3
  324. package/src/components/shell/{DeviceRow.stories.tsx → workout/DeviceRow.stories.tsx} +2 -2
  325. package/src/components/shell/{DeviceRow.tsx → workout/DeviceRow.tsx} +3 -3
  326. package/src/components/shell/{SessionStatePill.stories.tsx → workout/SessionStatePill.stories.tsx} +2 -2
  327. package/src/components/shell/{SessionStatePill.tsx → workout/SessionStatePill.tsx} +5 -3
  328. package/src/components/shell/workout/WorkoutShell.stories.tsx +71 -0
  329. package/src/components/shell/{DashboardShell.test.tsx → workout/WorkoutShell.test.tsx} +19 -8
  330. package/src/components/shell/workout/WorkoutShell.tsx +77 -0
  331. package/src/components/shell/workout/WorkoutTopBar.stories.tsx +58 -0
  332. package/src/components/shell/workout/WorkoutTopBar.test.tsx +39 -0
  333. package/src/components/shell/workout/WorkoutTopBar.tsx +52 -0
  334. package/src/components/shell/workout/index.ts +9 -0
  335. package/src/components/shell/workout/workoutNavItems.tsx +16 -0
  336. package/src/components/ui/README.md +110 -0
  337. package/src/components/ui/alert/Alert.stories.tsx +1 -1
  338. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +11 -13
  339. package/src/components/ui/autocomplete/Autocomplete.test.tsx +7 -25
  340. package/src/components/ui/autocomplete/Autocomplete.tsx +21 -28
  341. package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
  342. package/src/components/ui/avatar/Avatar.tsx +2 -10
  343. package/src/components/ui/badge/Badge.stories.tsx +83 -28
  344. package/src/components/ui/badge/Badge.test.tsx +19 -3
  345. package/src/components/ui/badge/Badge.tsx +48 -55
  346. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  347. package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +4 -16
  348. package/src/components/ui/button/Button.stories.tsx +46 -12
  349. package/src/components/ui/button/Button.tsx +32 -25
  350. package/src/components/ui/card/Card.stories.tsx +90 -94
  351. package/src/components/ui/card/Card.test.tsx +80 -6
  352. package/src/components/ui/card/Card.tsx +150 -203
  353. package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
  354. package/src/components/ui/checkbox/Checkbox.tsx +6 -26
  355. package/src/components/ui/chip/Chip.stories.tsx +37 -13
  356. package/src/components/ui/chip/Chip.test.tsx +10 -6
  357. package/src/components/ui/chip/Chip.tsx +76 -91
  358. package/src/components/ui/collapse/Collapse.stories.tsx +23 -23
  359. package/src/components/ui/collapse/Collapse.tsx +6 -11
  360. package/src/components/ui/data-row/DataRow.stories.tsx +9 -2
  361. package/src/components/ui/data-row/DataRow.test.tsx +5 -15
  362. package/src/components/ui/data-row/DataRow.tsx +18 -7
  363. package/src/components/ui/divider/Divider.stories.tsx +1 -1
  364. package/src/components/ui/divider/Divider.tsx +1 -5
  365. package/src/components/ui/drawer/Drawer.stories.tsx +5 -16
  366. package/src/components/ui/drawer/Drawer.tsx +13 -30
  367. package/src/components/ui/floating-overlay-depth.test.tsx +143 -0
  368. package/src/components/ui/form-field/FormField.stories.tsx +6 -20
  369. package/src/components/ui/form-field/FormField.test.tsx +1 -5
  370. package/src/components/ui/form-field/FormField.tsx +10 -46
  371. package/src/components/ui/help-tip/HelpTip.stories.tsx +8 -19
  372. package/src/components/ui/help-tip/HelpTip.test.tsx +1 -3
  373. package/src/components/ui/help-tip/HelpTip.tsx +21 -22
  374. package/src/components/ui/help-tip/index.ts +7 -1
  375. package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
  376. package/src/components/ui/icon-box/IconBox.test.tsx +1 -7
  377. package/src/components/ui/icon-box/IconBox.tsx +2 -12
  378. package/src/components/ui/indicator/Indicator.stories.tsx +1 -1
  379. package/src/components/ui/indicator/Indicator.tsx +20 -14
  380. package/src/components/ui/input/Input.stories.tsx +3 -11
  381. package/src/components/ui/input/Input.test.tsx +2 -10
  382. package/src/components/ui/input/Input.tsx +10 -25
  383. package/src/components/ui/input/index.ts +7 -1
  384. package/src/components/ui/link/Link.stories.tsx +19 -7
  385. package/src/components/ui/list-item/ListItem.stories.tsx +11 -2
  386. package/src/components/ui/list-item/ListItem.tsx +2 -9
  387. package/src/components/ui/menu/Menu.stories.tsx +21 -15
  388. package/src/components/ui/menu/Menu.test.tsx +2 -11
  389. package/src/components/ui/menu/Menu.tsx +12 -14
  390. package/src/components/ui/menu/index.ts +8 -1
  391. package/src/components/ui/modal/Modal.stories.tsx +2 -2
  392. package/src/components/ui/modal/Modal.tsx +29 -19
  393. package/src/components/ui/pill/Pill.stories.tsx +58 -23
  394. package/src/components/ui/pill/Pill.test.tsx +101 -3
  395. package/src/components/ui/pill/Pill.tsx +164 -49
  396. package/src/components/ui/pill/index.ts +1 -1
  397. package/src/components/ui/popover/Popover.stories.tsx +5 -7
  398. package/src/components/ui/popover/Popover.test.tsx +1 -6
  399. package/src/components/ui/popover/Popover.tsx +20 -15
  400. package/src/components/ui/popover/index.ts +7 -1
  401. package/src/components/ui/progress/Progress.stories.tsx +3 -12
  402. package/src/components/ui/progress/Progress.test.tsx +1 -5
  403. package/src/components/ui/radio/Radio.stories.tsx +42 -21
  404. package/src/components/ui/radio/Radio.test.tsx +6 -2
  405. package/src/components/ui/radio/Radio.tsx +23 -22
  406. package/src/components/ui/section/Section.stories.tsx +5 -14
  407. package/src/components/ui/section/Section.test.tsx +1 -6
  408. package/src/components/ui/section/Section.tsx +1 -3
  409. package/src/components/ui/section/index.ts +1 -5
  410. package/src/components/ui/select/Select.stories.tsx +3 -13
  411. package/src/components/ui/select/Select.test.tsx +2 -11
  412. package/src/components/ui/select/Select.tsx +14 -14
  413. package/src/components/ui/skeleton/Skeleton.stories.tsx +3 -11
  414. package/src/components/ui/skeleton/Skeleton.tsx +3 -14
  415. package/src/components/ui/skeleton/index.ts +1 -7
  416. package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
  417. package/src/components/ui/spinner/Spinner.tsx +1 -4
  418. package/src/components/ui/stack/Stack.stories.tsx +1 -1
  419. package/src/components/ui/stack/Stack.tsx +1 -1
  420. package/src/components/ui/surface/Surface.stories.tsx +17 -5
  421. package/src/components/ui/surface/Surface.test.tsx +88 -28
  422. package/src/components/ui/surface/Surface.tsx +37 -87
  423. package/src/components/ui/surface/SurfaceContext.ts +22 -62
  424. package/src/components/ui/surface/index.ts +8 -0
  425. package/src/components/ui/surface/resolveSurface.ts +98 -0
  426. package/src/components/ui/surface/surface.contract.test.ts +6 -1
  427. package/src/components/ui/switch/Switch.stories.tsx +1 -1
  428. package/src/components/ui/switch/Switch.tsx +7 -6
  429. package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
  430. package/src/components/ui/tabs/Tabs.tsx +7 -22
  431. package/src/components/ui/tabs/index.ts +1 -7
  432. package/src/components/ui/tile/Tile.stories.tsx +1 -1
  433. package/src/components/ui/tile/Tile.tsx +3 -0
  434. package/src/components/ui/toast/Toast.stories.tsx +1 -1
  435. package/src/components/ui/toast/Toast.test.tsx +2 -8
  436. package/src/components/ui/toast/Toast.tsx +43 -53
  437. package/src/components/ui/toast/index.ts +0 -1
  438. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +92 -71
  439. package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +3 -18
  440. package/src/components/ui/toolbar-button/ToolbarButton.tsx +25 -43
  441. package/src/components/ui/tooltip/Tooltip.stories.tsx +12 -3
  442. package/src/components/ui/tooltip/Tooltip.test.tsx +21 -3
  443. package/src/components/ui/tooltip/Tooltip.tsx +55 -36
  444. package/src/components/ui/tooltip/index.ts +1 -1
  445. package/src/components/ui/trigger/TriggerSurface.test.tsx +215 -0
  446. package/src/components/ui/trigger/TriggerSurface.tsx +81 -0
  447. package/src/components/ui/trigger/index.ts +2 -0
  448. package/src/stories/PresetShowcase.stories.tsx +100 -47
  449. package/src/test/no-device-internals.test.ts +3 -3
  450. package/src/theme/ChoosingTokens.stories.tsx +48 -0
  451. package/src/theme/ColorPalettes.stories.tsx +62 -45
  452. package/src/theme/ColorPrimitives.stories.tsx +35 -35
  453. package/src/theme/Depth.stories.tsx +132 -65
  454. package/src/theme/DepthCalibration.stories.tsx +23 -21
  455. package/src/theme/ThemeProvider.test.tsx +4 -4
  456. package/src/theme/barrel.ts +7 -6
  457. package/src/theme/color-stories.coverage.test.ts +1 -1
  458. package/src/theme/color-utils.ts +41 -29
  459. package/src/theme/config.completeness.test.ts +1 -1
  460. package/src/theme/config.ts +28 -0
  461. package/src/theme/elevation.test.ts +82 -56
  462. package/src/theme/elevation.ts +77 -561
  463. package/src/theme/global.css +30 -2
  464. package/src/theme/grey-ramp.test.ts +24 -8
  465. package/src/theme/lift.test.ts +39 -0
  466. package/src/theme/lift.ts +102 -0
  467. package/src/theme/materials.test.ts +5 -2
  468. package/src/theme/materials.ts +38 -4
  469. package/src/theme/native-theming-guard.test.ts +5 -5
  470. package/src/theme/presets/apply.ts +4 -0
  471. package/src/theme/presets/audiobook.ts +5 -2
  472. package/src/theme/presets/default.ts +4 -0
  473. package/src/theme/presets/index.ts +14 -1
  474. package/src/theme/presets/types.ts +1 -1
  475. package/src/theme/semantic-contrast.test.ts +13 -4
  476. package/src/theme/status-distinctness.test.ts +66 -0
  477. package/src/theme/surface-planes.test.ts +37 -0
  478. package/src/theme/surface-planes.ts +68 -0
  479. package/src/theme/token-export-guard.test.ts +1 -3
  480. package/src/theme/tokens/primitives.test.ts +15 -7
  481. package/src/theme/tokens/primitives.ts +65 -33
  482. package/src/theme/tokens/semantic.ts +71 -37
  483. package/src/theme/wash-ladder.test.ts +58 -0
  484. package/src/theme/workout-tokens.ts +1 -7
  485. package/src/types/jest-axe.d.ts +2 -6
  486. package/src/types/raw-md.d.ts +5 -0
  487. package/src/utils/colors.ts +4 -4
  488. package/src/utils/form.ts +1 -3
  489. package/src/utils/index.ts +2 -7
  490. package/src/utils/number-format.ts +17 -0
  491. package/src/utils/workout-format.ts +15 -6
  492. package/src/web-jsx/jsx-runtime.ts +1 -5
  493. package/tailwind.config.js +5 -0
  494. package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +0 -151
  495. package/src/components/custom/Workout/RowInventory.stories.tsx +0 -408
  496. package/src/components/custom/Workout/S3FullRail.stories.tsx +0 -46
  497. package/src/components/custom/Workout/S3SessionPace.stories.tsx +0 -95
  498. package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +0 -61
  499. package/src/components/custom/Workout/S3SetTypes.stories.tsx +0 -176
  500. package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +0 -240
  501. package/src/components/shell/DashboardShell.stories.tsx +0 -65
  502. package/src/components/shell/DashboardShell.tsx +0 -75
  503. package/src/components/shell/SessionRailSpecimen.stories.tsx +0 -753
  504. package/src/theme/elevation.stories.tsx +0 -346
  505. package/src/utils/useTheme.ts +0 -72
  506. /package/src/components/shell/{DeviceIndicator.test.tsx → workout/DeviceIndicator.test.tsx} +0 -0
  507. /package/src/components/shell/{DeviceMenu.test.tsx → workout/DeviceMenu.test.tsx} +0 -0
  508. /package/src/components/shell/{DeviceRow.test.tsx → workout/DeviceRow.test.tsx} +0 -0
  509. /package/src/components/shell/{SessionStatePill.test.tsx → workout/SessionStatePill.test.tsx} +0 -0
@@ -0,0 +1,44 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { Surface } from '../../ui/surface'
3
+ import { CoChangeChip } from './CoChangeChip'
4
+
5
+ const meta: Meta<typeof CoChangeChip> = {
6
+ title: 'Custom/ActiveWork/CoChangeChip',
7
+ component: CoChangeChip,
8
+ tags: ['autodocs', 'status:candidate', '!status:review'],
9
+ decorators: [
10
+ (Story) => (
11
+ <Surface level="base" className="min-h-screen p-6" testID="page-surface">
12
+ <Story />
13
+ </Surface>
14
+ ),
15
+ ],
16
+ parameters: {
17
+ layout: 'fullscreen',
18
+ docs: {
19
+ description: {
20
+ component:
21
+ '**Molecule.** One symmetric "these two files change together" pair. Only basenames are ' +
22
+ 'shown — at chip size the directory is noise. Composes ' +
23
+ '[Card](?path=/docs/components-molecules-card--docs), ' +
24
+ '[Pill](?path=/docs/components-atoms-pill--docs) and ' +
25
+ '[FilePathLabel](?path=/docs/custom-activework-filepathlabel--docs). ' +
26
+ 'Used by [FileHistoryExplorer](?path=/docs/custom-activework-filehistoryexplorer--docs).',
27
+ },
28
+ },
29
+ },
30
+ args: {
31
+ a: 'src/commands/open.ts',
32
+ b: 'src/commands/_open-helpers.ts',
33
+ count: 19,
34
+ },
35
+ }
36
+ export default meta
37
+ type Story = StoryObj<typeof CoChangeChip>
38
+
39
+ export const Default: Story = {}
40
+
41
+ /** A weak pair — the count pill is the only thing distinguishing it. */
42
+ export const WeakPair: Story = {
43
+ args: { a: 'src/utils/fs-atomic.ts', b: 'src/utils/flock.ts', count: 2 },
44
+ }
@@ -0,0 +1,29 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { render, screen } from '@testing-library/react'
3
+ import { axe } from 'jest-axe'
4
+ import { CoChangeChip } from './CoChangeChip'
5
+
6
+ describe('CoChangeChip', () => {
7
+ it('renders both basenames and the count', () => {
8
+ render(<CoChangeChip a="src/commands/open.ts" b="src/registry/index.ts" count={19} />)
9
+ expect(screen.getByText('open.ts')).toBeInTheDocument()
10
+ expect(screen.getByText('index.ts')).toBeInTheDocument()
11
+ expect(screen.getByText('19×')).toBeInTheDocument()
12
+ })
13
+
14
+ it('hides the directory prefixes', () => {
15
+ render(<CoChangeChip a="src/commands/open.ts" b="src/registry/index.ts" count={19} />)
16
+ expect(screen.queryByText('src/commands/')).not.toBeInTheDocument()
17
+ expect(screen.queryByText('src/registry/')).not.toBeInTheDocument()
18
+ })
19
+
20
+ it('describes the pair for screen readers', () => {
21
+ render(<CoChangeChip a="a.ts" b="b.ts" count={3} />)
22
+ expect(screen.getByLabelText('a.ts and b.ts changed together 3 times')).toBeInTheDocument()
23
+ })
24
+
25
+ it('has no a11y violations', async () => {
26
+ const { container } = render(<CoChangeChip a="a.ts" b="b.ts" count={3} />)
27
+ expect(await axe(container)).toHaveNoViolations()
28
+ })
29
+ })
@@ -0,0 +1,48 @@
1
+ // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
+ import { View } from 'react-native'
3
+ import { Card } from '../../ui/card'
4
+ import { Pill } from '../../ui/pill'
5
+ import { Typography } from '../Typography'
6
+ import { FilePathLabel } from './FilePathLabel'
7
+
8
+ export interface CoChangeChipProps {
9
+ /** One side of the pair. */
10
+ a: string
11
+ /** The other side. Order is not meaningful — co-change is symmetric. */
12
+ b: string
13
+ /** How many sessions changed both files. */
14
+ count: number
15
+ className?: string
16
+ }
17
+
18
+ /**
19
+ * CoChangeChip — one symmetric "these two files change together" pair.
20
+ *
21
+ * Only basenames are shown: at chip size the directory is noise, and the pair
22
+ * is a pointer into {@link FileActivityDetail} rather than a full identifier.
23
+ * Used by {@link FileHistoryExplorer}'s repo-wide co-change strip.
24
+ *
25
+ * @deprecated Use `<Pill tone="brand" size="xs">` — removed after AW-127 consumer migration.
26
+ */
27
+ export function CoChangeChip({ a, b, count, className }: CoChangeChipProps) {
28
+ return (
29
+ <Card
30
+ variant="filled"
31
+ className={`gap-1 p-2.5 ${className ?? ''}`}
32
+ accessibilityRole="text"
33
+ accessibilityLabel={`${a} and ${b} changed together ${count} times`}
34
+ testID="co-change-chip"
35
+ >
36
+ <View className="flex-row items-center gap-2">
37
+ <FilePathLabel path={a} size="sm" baseOnly />
38
+ <Typography variant="caption" className="text-text-tertiary">
39
+
40
+ </Typography>
41
+ <FilePathLabel path={b} size="sm" baseOnly />
42
+ <Pill variant="subtle" color="primary" size="xs">
43
+ {`${count}×`}
44
+ </Pill>
45
+ </View>
46
+ </Card>
47
+ )
48
+ }
@@ -0,0 +1,25 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { Eyebrow } from './Eyebrow'
3
+
4
+ const meta: Meta<typeof Eyebrow> = {
5
+ title: 'Components/Molecules/Eyebrow',
6
+ component: Eyebrow,
7
+ tags: ['autodocs', 'status:candidate', '!status:review'],
8
+ args: { children: 'Focused · by rank' },
9
+ argTypes: {
10
+ children: { control: 'text' },
11
+ },
12
+ parameters: {
13
+ docs: {
14
+ description: {
15
+ component:
16
+ '**Molecule.** An uppercase micro-label used above a value or a section of ' +
17
+ "content. Composes [Typography](?path=/docs/foundations-typography--docs)'s `overline` variant.",
18
+ },
19
+ },
20
+ },
21
+ }
22
+ export default meta
23
+ type Story = StoryObj<typeof Eyebrow>
24
+
25
+ export const Default: Story = {}
@@ -0,0 +1,16 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { render, screen } from '@testing-library/react'
3
+ import { axe } from 'jest-axe'
4
+ import { Eyebrow } from './Eyebrow'
5
+
6
+ describe('Eyebrow', () => {
7
+ it('renders its children', () => {
8
+ render(<Eyebrow>Focused · by rank</Eyebrow>)
9
+ expect(screen.getByText('Focused · by rank')).toBeInTheDocument()
10
+ })
11
+
12
+ it('has no accessibility violations', async () => {
13
+ const { container } = render(<Eyebrow>Backburner</Eyebrow>)
14
+ expect(await axe(container)).toHaveNoViolations()
15
+ })
16
+ })
@@ -0,0 +1,26 @@
1
+ // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
+ import type { ReactNode } from 'react'
3
+ import { cn } from '../../../utils/cn'
4
+ import { Typography } from '../Typography'
5
+
6
+ export interface EyebrowProps {
7
+ children: ReactNode
8
+ className?: string
9
+ }
10
+
11
+ /**
12
+ * Eyebrow — an uppercase micro-label used above a value or a section of
13
+ * content (e.g. "Focused · by rank", a stat tile's caption). Composes
14
+ * {@link Typography}'s `overline` variant; never hand-roll the letter-spacing.
15
+ */
16
+ export function Eyebrow({ children, className }: EyebrowProps) {
17
+ return (
18
+ <Typography
19
+ variant="overline"
20
+ color="inherit"
21
+ className={cn('text-xs font-semibold uppercase tracking-wider text-text-tertiary', className)}
22
+ >
23
+ {children}
24
+ </Typography>
25
+ )
26
+ }
@@ -0,0 +1,50 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { Surface } from '../../ui/surface'
3
+ import { FileActivityDetail } from './FileActivityDetail'
4
+ import { FILE_HISTORY_FILES } from './file-history-fixture'
5
+
6
+ const meta: Meta<typeof FileActivityDetail> = {
7
+ title: 'Custom/ActiveWork/FileActivityDetail',
8
+ component: FileActivityDetail,
9
+ tags: ['autodocs', 'status:candidate', '!status:review'],
10
+ decorators: [
11
+ (Story) => (
12
+ <Surface level="base" className="min-h-screen w-full max-w-[560px] p-6" testID="page-surface">
13
+ <Story />
14
+ </Surface>
15
+ ),
16
+ ],
17
+ parameters: {
18
+ layout: 'fullscreen',
19
+ docs: {
20
+ description: {
21
+ component:
22
+ "**Card.** One file's full mined history: activity split, net char growth over " +
23
+ 'sessions, and the files it changes together with. Composes ' +
24
+ '[Card](?path=/docs/components-molecules-card--docs), ' +
25
+ '[Tile](?path=/docs/components-atoms-tile--docs), ' +
26
+ '[Pill](?path=/docs/components-atoms-pill--docs), ' +
27
+ '[DataRow](?path=/docs/components-molecules-datarow--docs), ' +
28
+ '[DateTime](?path=/docs/custom-datetime--docs), ' +
29
+ '[SparkBars](?path=/docs/custom-charts-sparkbars--docs), ' +
30
+ '[FilePathLabel](?path=/docs/custom-activework-filepathlabel--docs) and ' +
31
+ '[Eyebrow](?path=/docs/components-molecules-eyebrow--docs).',
32
+ },
33
+ },
34
+ },
35
+ args: { file: FILE_HISTORY_FILES[0] },
36
+ }
37
+ export default meta
38
+ type Story = StoryObj<typeof FileActivityDetail>
39
+
40
+ export const Default: Story = {}
41
+
42
+ /** Net-negative growth: the readout and the sparkline both flip to the error token. */
43
+ export const NetShrink: Story = {
44
+ args: { file: FILE_HISTORY_FILES[2] },
45
+ }
46
+
47
+ /** No co-changes mined — the section falls back to an explicit empty line. */
48
+ export const NoCoChanges: Story = {
49
+ args: { file: FILE_HISTORY_FILES[3] },
50
+ }
@@ -0,0 +1,66 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { render, screen } from '@testing-library/react'
3
+ import { axe } from 'jest-axe'
4
+ import { FileActivityDetail } from './FileActivityDetail'
5
+ import { FILE_HISTORY_FILES } from './file-history-fixture'
6
+
7
+ const grew = FILE_HISTORY_FILES[0]
8
+ const shrank = FILE_HISTORY_FILES[2]
9
+ const noCoChange = FILE_HISTORY_FILES[3]
10
+
11
+ describe('FileActivityDetail', () => {
12
+ it('renders the split path header and session summary', () => {
13
+ render(<FileActivityDetail file={grew} />)
14
+ // The header dir and a co-change entry share this prefix, hence getAllByText.
15
+ expect(screen.getAllByText('src/commands/').length).toBeGreaterThan(0)
16
+ expect(screen.getByText('open.ts')).toBeInTheDocument()
17
+ expect(screen.getByText(/23 sessions/)).toBeInTheDocument()
18
+ })
19
+
20
+ it('renders a tile per activity kind', () => {
21
+ render(<FileActivityDetail file={grew} />)
22
+ for (const label of ['Reads', 'Writes', 'Edits', 'Touches']) {
23
+ expect(screen.getByText(label)).toBeInTheDocument()
24
+ }
25
+ expect(screen.getByText('157')).toBeInTheDocument()
26
+ })
27
+
28
+ it('compacts the growth figures', () => {
29
+ render(<FileActivityDetail file={grew} />)
30
+ expect(screen.getByText('+31.6k ch')).toBeInTheDocument()
31
+ expect(screen.getByText('+41.3k')).toBeInTheDocument()
32
+ expect(screen.getByText('-9.6k')).toBeInTheDocument()
33
+ })
34
+
35
+ it('signs a net-negative growth readout', () => {
36
+ render(<FileActivityDetail file={shrank} />)
37
+ expect(screen.getByText('-3.4k ch')).toBeInTheDocument()
38
+ })
39
+
40
+ it('lists co-changed files with their counts', () => {
41
+ render(<FileActivityDetail file={grew} />)
42
+ expect(screen.getByText('_open-helpers.ts')).toBeInTheDocument()
43
+ expect(screen.getByText('19×')).toBeInTheDocument()
44
+ expect(screen.getByText('11×')).toBeInTheDocument()
45
+ })
46
+
47
+ it('falls back to an explicit line when nothing co-changed', () => {
48
+ render(<FileActivityDetail file={noCoChange} />)
49
+ expect(screen.getByText('no co-changes recorded')).toBeInTheDocument()
50
+ })
51
+
52
+ it('falls back to a dash for a missing touch date', () => {
53
+ render(<FileActivityDetail file={{ ...grew, firstTouched: null, lastTouched: null }} />)
54
+ expect(screen.getAllByText('—')).toHaveLength(2)
55
+ })
56
+
57
+ it('renders "./" for a file at the repo root', () => {
58
+ render(<FileActivityDetail file={noCoChange} />)
59
+ expect(screen.getByText('./')).toBeInTheDocument()
60
+ })
61
+
62
+ it('has no a11y violations', async () => {
63
+ const { container } = render(<FileActivityDetail file={grew} />)
64
+ expect(await axe(container)).toHaveNoViolations()
65
+ })
66
+ })
@@ -0,0 +1,202 @@
1
+ // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
+ import { View } from 'react-native'
3
+ // Deep import: `CardInset` is not on the card barrel yet (Lab/Depth reaches it the same way).
4
+ import { Card, CardInset } from '../../ui/card/Card'
5
+ import { Pill } from '../../ui/pill'
6
+ import { Tile } from '../../ui/tile'
7
+ import { DataRow } from '../../ui/data-row'
8
+ import { SparkBars } from '../charts'
9
+ import { DateTime } from '../DateTime'
10
+ import { Typography } from '../Typography'
11
+ import { formatCompact, formatSignedCompact } from '../../../utils/number-format'
12
+ import { resolveColor } from '../../../theme/resolve-color'
13
+ import { Eyebrow } from './Eyebrow'
14
+ import { FilePathLabel, splitPath } from './FilePathLabel'
15
+ import { FILE_EVENT_COLOR, type FileEventColors, type FileActivity } from './FileActivityRow'
16
+
17
+ /**
18
+ * Growth stats are *char deltas* — a measurement moving up or down — so they take
19
+ * the `result-*` family, not `status-*` and not {@link FILE_EVENT_COLOR}. A file
20
+ * that net-shrank was refactored, not broken. See TOKENS.md §1.
21
+ */
22
+ const GROWTH_COLOR = {
23
+ added: resolveColor('result-improve'),
24
+ removed: resolveColor('result-degrade'),
25
+ neutral: resolveColor('result-neutral'),
26
+ } as const
27
+
28
+ /**
29
+ * `Tile` paints `surface-raised` itself, which IS this card's plane. Inside the
30
+ * lifted pane it has to read one plane up to separate at all. Overriding the
31
+ * fill at the call site keeps Tile's own default right for a page-level strip;
32
+ * teaching Tile to resolve its plane from the surface context is its own epic.
33
+ */
34
+ const TILE_PLANE = 'bg-surface-overlay'
35
+
36
+ /** Another file that tends to change in the same session as this one. */
37
+ export interface FileCoChange {
38
+ path: string
39
+ count: number
40
+ }
41
+
42
+ /** {@link FileActivity} plus the history only the detail pane shows. */
43
+ export interface FileActivityDetailData extends FileActivity {
44
+ /** Distinct sessions that touched this file. */
45
+ sessions: number
46
+ charsAdded: number
47
+ charsRemoved: number
48
+ /** `charsAdded - charsRemoved`; negative means the file net-shrank. */
49
+ netGrowth: number
50
+ firstTouched?: string | null
51
+ lastTouched?: string | null
52
+ coChange: FileCoChange[]
53
+ }
54
+
55
+ export interface FileActivityDetailProps {
56
+ file: FileActivityDetailData
57
+ /** Override the read/write/edit fills. Defaults to {@link FILE_EVENT_COLOR}. */
58
+ eventColors?: FileEventColors
59
+ className?: string
60
+ }
61
+
62
+ function GrowthStat({ label, value, color }: { label: string; value: string; color: string }) {
63
+ return (
64
+ <View className="flex-1 gap-0.5">
65
+ <Eyebrow>{label}</Eyebrow>
66
+ <Typography variant="mono" className="text-lg font-bold" style={{ color }}>
67
+ {value}
68
+ </Typography>
69
+ </View>
70
+ )
71
+ }
72
+
73
+ /**
74
+ * FileActivityDetail — the right-hand pane of {@link FileHistoryExplorer}: one
75
+ * file's full mined history. Activity split, net char growth over sessions, and
76
+ * the files it changes together with — the co-change list being the part a
77
+ * plain file tree cannot show.
78
+ *
79
+ * Composes Card / CardInset / Tile / Pill / DataRow / DateTime plus
80
+ * {@link SparkBars}, {@link FilePathLabel} and {@link Eyebrow}.
81
+ *
82
+ * One lift only: the pane itself. Inside it the activity split reads as filled
83
+ * tiles a plane up and the growth block as a `CardInset` well a plane down.
84
+ */
85
+ export function FileActivityDetail({
86
+ file,
87
+ eventColors = FILE_EVENT_COLOR,
88
+ className,
89
+ }: FileActivityDetailProps) {
90
+ const { dir, base } = splitPath(file.path)
91
+ const grew = file.netGrowth >= 0
92
+
93
+ return (
94
+ <Card className={`flex-1 gap-4 p-4 ${className ?? ''}`} testID="file-activity-detail">
95
+ <View className="gap-1">
96
+ <Typography variant="mono" className="text-xs text-text-tertiary">
97
+ {dir || './'}
98
+ </Typography>
99
+ <Typography variant="mono" className="text-base font-bold text-text-primary">
100
+ {base}
101
+ </Typography>
102
+ <View className="flex-row items-center gap-1">
103
+ <Typography variant="caption" className="text-xs text-text-tertiary">
104
+ {`${file.sessions} sessions · last touched `}
105
+ </Typography>
106
+ <DateTime
107
+ value={file.lastTouched}
108
+ format="short"
109
+ fallback="—"
110
+ className="text-xs text-text-tertiary"
111
+ />
112
+ <Typography variant="caption" className="text-xs text-text-tertiary">
113
+ {' · first '}
114
+ </Typography>
115
+ <DateTime
116
+ value={file.firstTouched}
117
+ format="short"
118
+ fallback="—"
119
+ className="text-xs text-text-tertiary"
120
+ />
121
+ </View>
122
+ </View>
123
+
124
+ <View className="flex-row gap-2">
125
+ {/* Tile's own fill is the card plane, which is this card; step it one up. */}
126
+ <Tile
127
+ label="Reads"
128
+ value={String(file.reads)}
129
+ valueColor={eventColors.reads}
130
+ className={TILE_PLANE}
131
+ />
132
+ <Tile
133
+ label="Writes"
134
+ value={String(file.writes)}
135
+ valueColor={eventColors.writes}
136
+ className={TILE_PLANE}
137
+ />
138
+ <Tile
139
+ label="Edits"
140
+ value={String(file.edits)}
141
+ valueColor={eventColors.edits}
142
+ className={TILE_PLANE}
143
+ />
144
+ <Tile label="Touches" value={String(file.touches)} className={TILE_PLANE} />
145
+ </View>
146
+
147
+ <CardInset className="gap-2 rounded-lg p-3" testID="growth-well">
148
+ <View className="flex-row items-center justify-between">
149
+ <Eyebrow>Net change over sessions</Eyebrow>
150
+ <Typography
151
+ variant="mono"
152
+ className="text-sm font-bold"
153
+ style={{ color: grew ? GROWTH_COLOR.added : GROWTH_COLOR.removed }}
154
+ >
155
+ {`${formatSignedCompact(file.netGrowth)} ch`}
156
+ </Typography>
157
+ </View>
158
+ <SparkBars
159
+ values={file.timeline}
160
+ height={34}
161
+ label={`Per-session net char change for ${file.path}`}
162
+ />
163
+ <View className="flex-row gap-4">
164
+ <GrowthStat
165
+ label="Added"
166
+ value={`+${formatCompact(file.charsAdded)}`}
167
+ color={GROWTH_COLOR.added}
168
+ />
169
+ <GrowthStat
170
+ label="Removed"
171
+ value={`-${formatCompact(file.charsRemoved)}`}
172
+ color={GROWTH_COLOR.removed}
173
+ />
174
+ <GrowthStat label="Sessions" value={String(file.sessions)} color={GROWTH_COLOR.neutral} />
175
+ </View>
176
+ </CardInset>
177
+
178
+ <View className="gap-0.5">
179
+ <Eyebrow>Changes together with</Eyebrow>
180
+ {file.coChange.length ? (
181
+ file.coChange.map((c) => (
182
+ <DataRow
183
+ key={c.path}
184
+ className="py-1"
185
+ labelClassName="shrink"
186
+ label={<FilePathLabel path={c.path} size="sm" />}
187
+ value={
188
+ <Pill variant="subtle" color="primary" size="xs">
189
+ {`${c.count}×`}
190
+ </Pill>
191
+ }
192
+ />
193
+ ))
194
+ ) : (
195
+ <Typography variant="caption" className="text-xs text-text-tertiary">
196
+ no co-changes recorded
197
+ </Typography>
198
+ )}
199
+ </View>
200
+ </Card>
201
+ )
202
+ }
@@ -0,0 +1,70 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import {
3
+ FileActivityRow,
4
+ FILE_EVENT_COLOR_CATEGORICAL,
5
+ FILE_EVENT_COLOR_SEMANTIC,
6
+ } from './FileActivityRow'
7
+ import { Surface } from '../../ui/surface'
8
+ import { FILE_HISTORY_FILES } from './file-history-fixture'
9
+
10
+ const meta: Meta<typeof FileActivityRow> = {
11
+ title: 'Custom/ActiveWork/FileActivityRow',
12
+ component: FileActivityRow,
13
+ tags: ['autodocs', 'status:candidate', '!status:review'],
14
+ parameters: {
15
+ docs: {
16
+ description: {
17
+ component:
18
+ '**Row.** One file in the ranked "hottest files" list: path, total touches, the ' +
19
+ 'read/write/edit split, and a sparkline of per-session growth. Composes ' +
20
+ '[FilePathLabel](?path=/docs/custom-activework-filepathlabel--docs) and ' +
21
+ '[SparkBars](?path=/docs/custom-charts-sparkbars--docs). ' +
22
+ 'Used by [FileHistoryExplorer](?path=/docs/custom-activework-filehistoryexplorer--docs).',
23
+ },
24
+ },
25
+ },
26
+ args: { file: FILE_HISTORY_FILES[0], selected: false },
27
+ // The row is a listbox `option`, which is only valid inside a listbox.
28
+ decorators: [
29
+ (Story) => (
30
+ <Surface level="base" className="min-h-screen p-6" testID="page-surface">
31
+ <div role="listbox" aria-label="Hottest files" style={{ width: 404 }}>
32
+ <Story />
33
+ </div>
34
+ </Surface>
35
+ ),
36
+ ],
37
+ parameters: { layout: 'fullscreen' },
38
+ }
39
+ export default meta
40
+ type Story = StoryObj<typeof FileActivityRow>
41
+
42
+ export const Default: Story = {}
43
+
44
+ export const Selected: Story = {
45
+ args: { selected: true },
46
+ }
47
+
48
+ /** A net-shrinking file — negative sparkline bars take `result-degrade`. */
49
+ export const NetShrink: Story = {
50
+ args: { file: FILE_HISTORY_FILES[2] },
51
+ }
52
+
53
+ /**
54
+ * Palette option A (current default) — reads/writes/edits coloured by
55
+ * *importance*: quiet tertiary, brand for the consequential writes, info blue
56
+ * between. Borrows a status token for non-status data.
57
+ */
58
+ export const PaletteSemantic: Story = {
59
+ args: { eventColors: FILE_EVENT_COLOR_SEMANTIC },
60
+ }
61
+
62
+ /**
63
+ * Palette option B — the same three as *peer categories*, taken in order from the
64
+ * canonical CVD-safe palette (TOKENS.md §2). Systematic and colourblind-safe, but
65
+ * it drops the importance ordering, and its red sits next to `result-degrade` red
66
+ * in the same row.
67
+ */
68
+ export const PaletteCategorical: Story = {
69
+ args: { eventColors: FILE_EVENT_COLOR_CATEGORICAL },
70
+ }
@@ -0,0 +1,67 @@
1
+ import { describe, it, expect, vi } from 'vitest'
2
+ import { render, screen, fireEvent } from '@testing-library/react'
3
+ import { axe } from 'jest-axe'
4
+ import { FileActivityRow, type FileActivity } from './FileActivityRow'
5
+
6
+ const file: FileActivity = {
7
+ path: 'src/commands/open.ts',
8
+ reads: 84,
9
+ writes: 12,
10
+ edits: 61,
11
+ touches: 157,
12
+ timeline: [120, 340, -80, 910],
13
+ }
14
+
15
+ describe('FileActivityRow', () => {
16
+ it('renders the path, touch count, and the read/write/edit split', () => {
17
+ render(<FileActivityRow file={file} />)
18
+ expect(screen.getByText('src/commands/')).toBeInTheDocument()
19
+ expect(screen.getByText('open.ts')).toBeInTheDocument()
20
+ expect(screen.getByText('157')).toBeInTheDocument()
21
+ expect(screen.getByText('84r')).toBeInTheDocument()
22
+ expect(screen.getByText('12w')).toBeInTheDocument()
23
+ expect(screen.getByText('61e')).toBeInTheDocument()
24
+ })
25
+
26
+ it('renders a sparkline of the timeline', () => {
27
+ render(<FileActivityRow file={file} />)
28
+ expect(screen.getAllByTestId(/^spark-bars-bar-/)).toHaveLength(4)
29
+ })
30
+
31
+ it('shows the selection accent only when selected', () => {
32
+ const { rerender } = render(<FileActivityRow file={file} />)
33
+ expect(screen.queryByTestId('file-activity-row-accent')).not.toBeInTheDocument()
34
+ rerender(<FileActivityRow file={file} selected />)
35
+ expect(screen.getByTestId('file-activity-row-accent')).toBeInTheDocument()
36
+ })
37
+
38
+ it('reports selection state to assistive tech as a listbox option', () => {
39
+ const { rerender } = render(<FileActivityRow file={file} selected />)
40
+ expect(screen.getByRole('option', { selected: true })).toBeInTheDocument()
41
+ rerender(<FileActivityRow file={file} />)
42
+ expect(screen.getByRole('option', { selected: false })).toBeInTheDocument()
43
+ })
44
+
45
+ it('calls onSelect when pressed', () => {
46
+ const onSelect = vi.fn()
47
+ render(<FileActivityRow file={file} onSelect={onSelect} />)
48
+ fireEvent.click(screen.getByRole('option'))
49
+ expect(onSelect).toHaveBeenCalledOnce()
50
+ })
51
+
52
+ it('does not throw when pressed without an onSelect handler', () => {
53
+ render(<FileActivityRow file={file} />)
54
+ expect(() => fireEvent.click(screen.getByRole('option'))).not.toThrow()
55
+ })
56
+
57
+ it('has no a11y violations inside its listbox', async () => {
58
+ // `role="option"` is only valid within a listbox — which is how
59
+ // FileHistoryExplorer renders it, so the a11y check mirrors that context.
60
+ const { container } = render(
61
+ <div role="listbox" aria-label="Hottest files">
62
+ <FileActivityRow file={file} />
63
+ </div>
64
+ )
65
+ expect(await axe(container)).toHaveNoViolations()
66
+ })
67
+ })