@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.
- package/README.md +60 -47
- package/dist/bodymap.js +856 -736
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +858 -738
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +1431 -237
- package/dist/index.d.ts +1431 -237
- package/dist/index.js +6506 -4781
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6280 -4642
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-B1hqnGIC.d.ts → pages-CsjsUn0G.d.ts} +51 -16
- package/dist/{pages-CvsFqNtx.d.mts → pages-ZbT_pD_U.d.mts} +51 -16
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +1919 -1937
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +1927 -1945
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +225 -140
- package/dist/theme/index.d.ts +225 -140
- package/dist/theme/index.js +298 -319
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +283 -315
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +145 -24
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +145 -24
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/dist/theme/tokens.d.mts +128 -11
- package/dist/theme/tokens.d.ts +128 -11
- package/dist/theme/tokens.js +126 -26
- package/dist/theme/tokens.js.map +1 -1
- package/dist/theme/tokens.mjs +124 -27
- package/dist/theme/tokens.mjs.map +1 -1
- package/docs/audits/2026-09-08-critique-addenda.md +51 -0
- package/docs/audits/2026-09-08-library-critique.md +212 -0
- package/docs/library-roadmap.md +99 -0
- package/docs/render-testing-pattern.md +130 -0
- package/package.json +1 -1
- package/src/arch/Architecture.stories.tsx +3 -1
- package/src/arch/arch-graph.freshness.test.ts +52 -0
- package/src/arch/arch-graph.json +4989 -1583
- package/src/components/custom/ActiveWork/CoChangeChip.stories.tsx +44 -0
- package/src/components/custom/ActiveWork/CoChangeChip.test.tsx +29 -0
- package/src/components/custom/ActiveWork/CoChangeChip.tsx +48 -0
- package/src/components/custom/ActiveWork/Eyebrow.stories.tsx +25 -0
- package/src/components/custom/ActiveWork/Eyebrow.test.tsx +16 -0
- package/src/components/custom/ActiveWork/Eyebrow.tsx +26 -0
- package/src/components/custom/ActiveWork/FileActivityDetail.stories.tsx +50 -0
- package/src/components/custom/ActiveWork/FileActivityDetail.test.tsx +66 -0
- package/src/components/custom/ActiveWork/FileActivityDetail.tsx +202 -0
- package/src/components/custom/ActiveWork/FileActivityRow.stories.tsx +70 -0
- package/src/components/custom/ActiveWork/FileActivityRow.test.tsx +67 -0
- package/src/components/custom/ActiveWork/FileActivityRow.tsx +130 -0
- package/src/components/custom/ActiveWork/FileHistoryExplorer.stories.tsx +84 -0
- package/src/components/custom/ActiveWork/FileHistoryExplorer.test.tsx +99 -0
- package/src/components/custom/ActiveWork/FileHistoryExplorer.tsx +138 -0
- package/src/components/custom/ActiveWork/FilePathLabel.stories.tsx +39 -0
- package/src/components/custom/ActiveWork/FilePathLabel.test.tsx +53 -0
- package/src/components/custom/ActiveWork/FilePathLabel.tsx +63 -0
- package/src/components/custom/ActiveWork/InitiativeBrief.stories.tsx +50 -0
- package/src/components/custom/ActiveWork/InitiativeBrief.test.tsx +62 -0
- package/src/components/custom/ActiveWork/InitiativeBrief.tsx +182 -0
- package/src/components/custom/ActiveWork/InitiativeCard.stories.tsx +73 -0
- package/src/components/custom/ActiveWork/InitiativeCard.test.tsx +68 -0
- package/src/components/custom/ActiveWork/InitiativeCard.tsx +136 -0
- package/src/components/custom/ActiveWork/InitiativeHeader.stories.tsx +59 -0
- package/src/components/custom/ActiveWork/InitiativeHeader.test.tsx +40 -0
- package/src/components/custom/ActiveWork/InitiativeHeader.tsx +73 -0
- package/src/components/custom/ActiveWork/InitiativeReader.stories.tsx +145 -0
- package/src/components/custom/ActiveWork/OpenLoops.stories.tsx +58 -0
- package/src/components/custom/ActiveWork/OpenLoops.test.tsx +61 -0
- package/src/components/custom/ActiveWork/OpenLoops.tsx +107 -0
- package/src/components/custom/ActiveWork/PortfolioOverview.stories.tsx +101 -0
- package/src/components/custom/ActiveWork/PortfolioOverview.test.tsx +81 -0
- package/src/components/custom/ActiveWork/PortfolioOverview.tsx +83 -0
- package/src/components/custom/ActiveWork/README.md +305 -0
- package/src/components/custom/ActiveWork/SessionDetail.stories.tsx +71 -0
- package/src/components/custom/ActiveWork/SessionDetail.test.tsx +84 -0
- package/src/components/custom/ActiveWork/SessionDetail.tsx +180 -0
- package/src/components/custom/ActiveWork/SessionList.stories.tsx +69 -0
- package/src/components/custom/ActiveWork/SessionList.test.tsx +78 -0
- package/src/components/custom/ActiveWork/SessionList.tsx +103 -0
- package/src/components/custom/ActiveWork/SessionListItem.stories.tsx +64 -0
- package/src/components/custom/ActiveWork/SessionListItem.test.tsx +62 -0
- package/src/components/custom/ActiveWork/SessionListItem.tsx +152 -0
- package/src/components/custom/ActiveWork/SessionReader.stories.tsx +113 -0
- package/src/components/custom/ActiveWork/SeverityLabel.stories.tsx +51 -0
- package/src/components/custom/ActiveWork/SeverityLabel.test.tsx +68 -0
- package/src/components/custom/ActiveWork/SeverityLabel.tsx +97 -0
- package/src/components/custom/ActiveWork/TaskRow.stories.tsx +63 -0
- package/src/components/custom/ActiveWork/TaskRow.test.tsx +112 -0
- package/src/components/custom/ActiveWork/TaskRow.tsx +176 -0
- package/src/components/custom/ActiveWork/TaskTable.stories.tsx +123 -0
- package/src/components/custom/ActiveWork/TaskTable.test.tsx +233 -0
- package/src/components/custom/ActiveWork/TaskTable.tsx +321 -0
- package/src/components/custom/ActiveWork/active-work-depth.test.tsx +102 -0
- package/src/components/custom/ActiveWork/file-history-fixture.ts +91 -0
- package/src/components/custom/ActiveWork/format-time.test.ts +23 -0
- package/src/components/custom/ActiveWork/format-time.ts +32 -0
- package/src/components/custom/ActiveWork/index.ts +19 -0
- package/src/components/custom/ActiveWork/initiative-fixture.ts +104 -0
- package/src/components/custom/ActiveWork/session-fixture.ts +267 -0
- package/src/components/custom/ActiveWork/session-linkers.ts +56 -0
- package/src/components/custom/ActiveWork/task-list-fixture.ts +97 -0
- package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +12 -6
- package/src/components/custom/DateTime/DateTime.stories.tsx +2 -1
- package/src/components/custom/DateTime/DateTime.test.tsx +3 -1
- package/src/components/custom/EmptyState/EmptyState.stories.tsx +22 -9
- package/src/components/custom/EmptyState/EmptyState.test.tsx +7 -13
- package/src/components/custom/EmptyState/EmptyState.tsx +4 -18
- package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +4 -3
- package/src/components/custom/Fatigue/FatigueLights.stories.tsx +2 -2
- package/src/components/custom/Fatigue/GhostSpark.stories.tsx +4 -3
- package/src/components/custom/Fatigue/LiveFatigueCard.stories.tsx +2 -2
- package/src/components/custom/Fatigue/LiveFatigueCard.tsx +8 -4
- package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +116 -6
- package/src/components/custom/Fatigue/LiveFatiguePanel.test.tsx +134 -0
- package/src/components/custom/Fatigue/LiveFatiguePanel.tsx +54 -83
- package/src/components/custom/Fatigue/README.md +40 -3
- package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +2 -2
- package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +2 -1
- package/src/components/custom/Fatigue/VelocityHero.stories.tsx +4 -3
- package/src/components/custom/Fatigue/VerdictHero.stories.tsx +2 -2
- package/src/components/custom/Fatigue/fatigue-mock.test.ts +0 -5
- package/src/components/custom/Fatigue/fatigue-mock.ts +2 -10
- package/src/components/custom/Fatigue/fatigue-tokens.ts +12 -0
- package/src/components/custom/Fatigue/index.ts +17 -5
- package/src/components/custom/Fatigue/panel-layout.test.ts +149 -0
- package/src/components/custom/Fatigue/panel-layout.ts +185 -0
- package/src/components/custom/Gauge/Gauge.stories.tsx +6 -3
- package/src/components/custom/Gauge/Gauge.test.tsx +1 -1
- package/src/components/custom/Gauge/Gauge.tsx +7 -2
- package/src/components/custom/Metric/Metric.stories.tsx +10 -1
- package/src/components/custom/Metric/Metric.test.tsx +1 -3
- package/src/components/custom/Metric/Metric.tsx +5 -20
- package/src/components/custom/Prose/MarkdownProse.stories.tsx +88 -0
- package/src/components/custom/Prose/MarkdownProse.test.tsx +87 -0
- package/src/components/custom/Prose/MarkdownProse.tsx +201 -0
- package/src/components/custom/Prose/README.md +58 -0
- package/src/components/custom/Prose/index.ts +1 -0
- package/src/components/custom/Scatter/Scatter.stories.tsx +55 -14
- package/src/components/custom/Scatter/Scatter.test.tsx +2 -2
- package/src/components/custom/Scatter/Scatter.tsx +122 -18
- package/src/components/custom/Sidebar/Sidebar.stories.tsx +41 -8
- package/src/components/custom/Sidebar/Sidebar.test.tsx +5 -1
- package/src/components/custom/Sidebar/Sidebar.tsx +16 -33
- package/src/components/custom/Table/Table.stories.tsx +99 -16
- package/src/components/custom/Table/Table.test.tsx +273 -86
- package/src/components/custom/Table/Table.tsx +159 -67
- package/src/components/custom/Table/column-fit.test.ts +75 -0
- package/src/components/custom/Table/column-fit.ts +116 -0
- package/src/components/custom/Table/index.ts +9 -0
- package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +8 -6
- package/src/components/custom/Treemap/Treemap.stories.tsx +15 -12
- package/src/components/custom/Treemap/Treemap.test.tsx +1 -1
- package/src/components/custom/Treemap/Treemap.tsx +11 -3
- package/src/components/custom/Typography/Typography.stories.tsx +28 -1
- package/src/components/custom/Typography/Typography.test.tsx +34 -0
- package/src/components/custom/Typography/Typography.tsx +6 -0
- package/src/components/custom/Typography/index.ts +1 -8
- package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +11 -2
- package/src/components/custom/Workout/BaseBadge.stories.tsx +27 -11
- package/src/components/custom/Workout/BaseBadge.test.tsx +31 -11
- package/src/components/custom/Workout/BaseBadge.tsx +6 -2
- package/src/components/custom/Workout/BodyMap.stories.tsx +4 -1
- package/src/components/custom/Workout/BodyMap.tsx +8 -4
- package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +28 -6
- package/src/components/custom/Workout/BodyMapDetailPanel.test.tsx +11 -15
- package/src/components/custom/Workout/BodyMapDetailPanel.tsx +50 -12
- package/src/components/custom/Workout/CapacityBandChart.stories.tsx +12 -9
- package/src/components/custom/Workout/CapacityBandChart.test.tsx +24 -51
- package/src/components/custom/Workout/CapacityBandChart.tsx +144 -111
- package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
- package/src/components/custom/Workout/DeviationBar.test.tsx +10 -4
- package/src/components/custom/Workout/DeviationBar.tsx +24 -16
- package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +183 -0
- package/src/components/custom/Workout/DualVelocityStrip.test.tsx +32 -18
- package/src/components/custom/Workout/ExerciseCard.stories.tsx +12 -11
- package/src/components/custom/Workout/ExerciseCard.test.tsx +52 -22
- package/src/components/custom/Workout/ExerciseCard.tsx +99 -183
- package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +95 -12
- package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +189 -1
- package/src/components/custom/Workout/ExerciseCardHeading.tsx +167 -18
- package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +9 -1
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +3 -2
- package/src/components/custom/Workout/ExerciseHeading.stories.tsx +8 -8
- package/src/components/custom/Workout/ExerciseHeading.tsx +194 -46
- package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +12 -9
- package/src/components/custom/Workout/ExerciseIndicator.test.tsx +27 -11
- package/src/components/custom/Workout/ExerciseIndicator.tsx +14 -15
- package/src/components/custom/Workout/FatigueMeter.stories.tsx +5 -5
- package/src/components/custom/Workout/InputBar.stories.tsx +4 -5
- package/src/components/custom/Workout/InputBar.tsx +4 -2
- package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
- package/src/components/custom/Workout/IntensityBar.test.tsx +5 -1
- package/src/components/custom/Workout/IntensityBar.tsx +8 -3
- package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +5 -4
- package/src/components/custom/Workout/MesoCard.stories.tsx +4 -1
- package/src/components/custom/Workout/MesoCard.test.tsx +7 -18
- package/src/components/custom/Workout/MesoCard.tsx +14 -10
- package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
- package/src/components/custom/Workout/MesoProgressBar.test.tsx +18 -11
- package/src/components/custom/Workout/MesoProgressBar.tsx +15 -13
- package/src/components/custom/Workout/MesoStatusCard.stories.tsx +2 -1
- package/src/components/custom/Workout/MesoStatusCard.test.tsx +14 -44
- package/src/components/custom/Workout/MesoStatusCard.tsx +20 -33
- package/src/components/custom/Workout/MetricTiles.stories.tsx +3 -2
- package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +2 -8
- package/src/components/custom/Workout/MuscleGroupChip.test.tsx +23 -31
- package/src/components/custom/Workout/MuscleGroupChip.tsx +28 -67
- package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +9 -4
- package/src/components/custom/Workout/PlaceholderStrip.test.tsx +17 -10
- package/src/components/custom/Workout/PlaceholderStrip.tsx +9 -8
- package/src/components/custom/Workout/PrBadge.stories.tsx +2 -1
- package/src/components/custom/Workout/PrBadge.test.tsx +3 -9
- package/src/components/custom/Workout/PrBadge.tsx +13 -17
- package/src/components/custom/Workout/PrHistoryModal.stories.tsx +16 -3
- package/src/components/custom/Workout/PrHistoryModal.test.tsx +13 -36
- package/src/components/custom/Workout/PrHistoryModal.tsx +8 -4
- package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +6 -1
- package/src/components/custom/Workout/README.md +68 -17
- package/src/components/custom/Workout/ReadinessCheck.stories.tsx +3 -2
- package/src/components/custom/Workout/ReadinessCheck.test.tsx +12 -18
- package/src/components/custom/Workout/ReadinessCheck.tsx +22 -4
- package/src/components/custom/Workout/RestTimer.stories.tsx +16 -4
- package/src/components/custom/Workout/RestTimer.tsx +4 -2
- package/src/components/custom/Workout/ScheduleTiles.stories.tsx +3 -2
- package/src/components/custom/Workout/ScheduleTiles.test.tsx +3 -4
- package/src/components/custom/Workout/ScheduleTiles.tsx +4 -7
- package/src/components/custom/Workout/SegmentedBar.stories.tsx +21 -17
- package/src/components/custom/Workout/SegmentedBar.tsx +10 -5
- package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +5 -5
- package/src/components/custom/Workout/SessionHeader.stories.tsx +6 -6
- package/src/components/custom/Workout/SessionHeader.tsx +12 -12
- package/src/components/custom/Workout/SessionRail.stories.tsx +12 -11
- package/src/components/custom/Workout/SessionRail.tsx +7 -9
- package/src/components/custom/Workout/SetBar.stories.tsx +10 -6
- package/src/components/custom/Workout/SetBar.test.tsx +49 -4
- package/src/components/custom/Workout/SetBar.tsx +38 -12
- package/src/components/custom/Workout/SetRow.stories.tsx +3 -2
- package/src/components/custom/Workout/SetRow.test.tsx +16 -2
- package/src/components/custom/Workout/SetRow.tsx +61 -23
- package/src/components/custom/Workout/SetStrip.stories.tsx +63 -7
- package/src/components/custom/Workout/SetStrip.test.tsx +25 -0
- package/src/components/custom/Workout/SetStrip.tsx +24 -4
- package/src/components/custom/Workout/SetTableHeader.stories.tsx +6 -6
- package/src/components/custom/Workout/SetTableHeader.tsx +9 -12
- package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +7 -7
- package/src/components/custom/Workout/SetsRepsLoad.tsx +24 -12
- package/src/components/custom/Workout/Sparkline.stories.tsx +14 -11
- package/src/components/custom/Workout/Sparkline.test.tsx +37 -19
- package/src/components/custom/Workout/Sparkline.tsx +14 -18
- package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusDot.test.tsx +44 -5
- package/src/components/custom/Workout/StatusDot.tsx +93 -63
- package/src/components/custom/Workout/StatusPill.stories.tsx +1 -1
- package/src/components/custom/Workout/StatusPill.test.tsx +6 -15
- package/src/components/custom/Workout/StatusPill.tsx +27 -38
- package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +11 -4
- package/src/components/custom/Workout/StrengthTrendChart.test.tsx +15 -37
- package/src/components/custom/Workout/StrengthTrendChart.tsx +39 -36
- package/src/components/custom/Workout/SupersetWrapper.stories.tsx +27 -64
- package/src/components/custom/Workout/SupersetWrapper.test.tsx +16 -14
- package/src/components/custom/Workout/SupersetWrapper.tsx +17 -21
- package/src/components/custom/Workout/TempoDisplay.stories.tsx +26 -10
- package/src/components/custom/Workout/TempoDisplay.test.tsx +18 -4
- package/src/components/custom/Workout/TempoDisplay.tsx +2 -0
- package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +9 -1
- package/src/components/custom/Workout/VelocityStrip.compact.stories.tsx +4 -1
- package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +10 -6
- package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +5 -2
- package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +44 -1
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +12 -6
- package/src/components/custom/Workout/VelocityStrip.test.tsx +48 -6
- package/src/components/custom/Workout/VelocityStrip.tsx +26 -14
- package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +6 -7
- package/src/components/custom/Workout/VolumeLandmarkBar.tsx +15 -6
- package/src/components/custom/Workout/WeekRow.stories.tsx +4 -1
- package/src/components/custom/Workout/WeekRow.test.tsx +18 -15
- package/src/components/custom/Workout/WeekRow.tsx +23 -16
- package/src/components/custom/Workout/WeightBadge.stories.tsx +2 -1
- package/src/components/custom/Workout/WeightBadge.test.tsx +50 -19
- package/src/components/custom/Workout/WeightBadge.tsx +28 -27
- package/src/components/custom/Workout/WorkoutCard.stories.tsx +6 -1
- package/src/components/custom/Workout/WorkoutCard.test.tsx +18 -0
- package/src/components/custom/Workout/WorkoutCard.tsx +35 -57
- package/src/components/custom/Workout/WorkoutPill.stories.tsx +5 -2
- package/src/components/custom/Workout/WorkoutPill.test.tsx +39 -7
- package/src/components/custom/Workout/WorkoutPill.tsx +88 -57
- package/src/components/custom/Workout/ZoneTrack.stories.tsx +10 -7
- package/src/components/custom/Workout/ZoneTrack.tsx +10 -9
- package/src/components/custom/Workout/exerciseRowState.ts +29 -0
- package/src/components/custom/Workout/index.ts +17 -2
- package/src/components/custom/Workout/metricText.stories.tsx +15 -13
- package/src/components/custom/Workout/metricText.tsx +8 -2
- package/src/components/custom/Workout/setHeadingKit.tsx +219 -36
- package/src/components/custom/Workout/velocity-story-kit.tsx +2 -2
- package/src/components/custom/charts/README.md +66 -0
- package/src/components/custom/charts/SetBarChart.test.tsx +46 -1
- package/src/components/custom/charts/SetBarChart.tsx +56 -9
- package/src/components/custom/charts/SparkBars.stories.tsx +55 -0
- package/src/components/custom/charts/SparkBars.test.tsx +69 -0
- package/src/components/custom/charts/SparkBars.tsx +85 -0
- package/src/components/custom/charts/flatBarGeometry.test.ts +41 -0
- package/src/components/custom/charts/flatBarGeometry.ts +32 -0
- package/src/components/custom/charts/index.ts +3 -0
- package/src/components/custom/index.ts +3 -0
- package/src/components/custom/stepper/Stepper.stories.tsx +13 -10
- package/src/components/custom/stepper/Stepper.test.tsx +1 -7
- package/src/components/custom/stepper/Stepper.tsx +21 -34
- package/src/components/icons/icons.stories.tsx +1 -1
- package/src/components/icons/icons.tsx +51 -0
- package/src/components/shell/AppShell.stories.tsx +150 -0
- package/src/components/shell/AppShell.test.tsx +70 -0
- package/src/components/shell/AppShell.tsx +90 -0
- package/src/components/shell/BrandLockup.stories.tsx +66 -5
- package/src/components/shell/BrandLockup.test.tsx +34 -1
- package/src/components/shell/BrandLockup.tsx +27 -11
- package/src/components/shell/NavItem.stories.tsx +4 -4
- package/src/components/shell/NavItem.test.tsx +17 -0
- package/src/components/shell/NavItem.tsx +17 -5
- package/src/components/shell/README.md +89 -26
- package/src/components/shell/SideNav.stories.tsx +38 -7
- package/src/components/shell/SideNav.test.tsx +16 -8
- package/src/components/shell/SideNav.tsx +17 -17
- package/src/components/shell/TopBar.stories.tsx +44 -19
- package/src/components/shell/TopBar.test.tsx +34 -12
- package/src/components/shell/TopBar.tsx +61 -40
- package/src/components/shell/brands.tsx +66 -0
- package/src/components/shell/index.ts +9 -8
- package/src/components/shell/{DeviceIndicator.stories.tsx → workout/DeviceIndicator.stories.tsx} +2 -2
- package/src/components/shell/{DeviceIndicator.tsx → workout/DeviceIndicator.tsx} +2 -2
- package/src/components/shell/{DeviceMenu.stories.tsx → workout/DeviceMenu.stories.tsx} +2 -2
- package/src/components/shell/{DeviceMenu.tsx → workout/DeviceMenu.tsx} +3 -3
- package/src/components/shell/{DeviceRow.stories.tsx → workout/DeviceRow.stories.tsx} +2 -2
- package/src/components/shell/{DeviceRow.tsx → workout/DeviceRow.tsx} +3 -3
- package/src/components/shell/{SessionStatePill.stories.tsx → workout/SessionStatePill.stories.tsx} +2 -2
- package/src/components/shell/{SessionStatePill.tsx → workout/SessionStatePill.tsx} +5 -3
- package/src/components/shell/workout/WorkoutShell.stories.tsx +71 -0
- package/src/components/shell/{DashboardShell.test.tsx → workout/WorkoutShell.test.tsx} +19 -8
- package/src/components/shell/workout/WorkoutShell.tsx +77 -0
- package/src/components/shell/workout/WorkoutTopBar.stories.tsx +58 -0
- package/src/components/shell/workout/WorkoutTopBar.test.tsx +39 -0
- package/src/components/shell/workout/WorkoutTopBar.tsx +52 -0
- package/src/components/shell/workout/index.ts +9 -0
- package/src/components/shell/workout/workoutNavItems.tsx +16 -0
- package/src/components/ui/README.md +110 -0
- package/src/components/ui/alert/Alert.stories.tsx +15 -5
- package/src/components/ui/alert/Alert.tsx +24 -3
- package/src/components/ui/autocomplete/Autocomplete.stories.tsx +11 -13
- package/src/components/ui/autocomplete/Autocomplete.test.tsx +7 -25
- package/src/components/ui/autocomplete/Autocomplete.tsx +21 -28
- package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
- package/src/components/ui/avatar/Avatar.tsx +2 -10
- package/src/components/ui/badge/Badge.stories.tsx +83 -28
- package/src/components/ui/badge/Badge.test.tsx +19 -3
- package/src/components/ui/badge/Badge.tsx +48 -55
- package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
- package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +4 -16
- package/src/components/ui/button/Button.stories.tsx +46 -12
- package/src/components/ui/button/Button.tsx +44 -37
- package/src/components/ui/card/Card.stories.tsx +90 -94
- package/src/components/ui/card/Card.test.tsx +80 -6
- package/src/components/ui/card/Card.tsx +150 -203
- package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
- package/src/components/ui/checkbox/Checkbox.tsx +7 -27
- package/src/components/ui/chip/Chip.stories.tsx +37 -13
- package/src/components/ui/chip/Chip.test.tsx +10 -6
- package/src/components/ui/chip/Chip.tsx +76 -91
- package/src/components/ui/collapse/Collapse.stories.tsx +23 -23
- package/src/components/ui/collapse/Collapse.tsx +6 -11
- package/src/components/ui/data-row/DataRow.stories.tsx +14 -3
- package/src/components/ui/data-row/DataRow.test.tsx +5 -15
- package/src/components/ui/data-row/DataRow.tsx +18 -7
- package/src/components/ui/divider/Divider.stories.tsx +1 -1
- package/src/components/ui/divider/Divider.tsx +1 -5
- package/src/components/ui/drawer/Drawer.stories.tsx +5 -16
- package/src/components/ui/drawer/Drawer.tsx +13 -27
- package/src/components/ui/floating-overlay-depth.test.tsx +143 -0
- package/src/components/ui/form-field/FormField.stories.tsx +6 -20
- package/src/components/ui/form-field/FormField.test.tsx +1 -5
- package/src/components/ui/form-field/FormField.tsx +10 -46
- package/src/components/ui/help-tip/HelpTip.stories.tsx +8 -19
- package/src/components/ui/help-tip/HelpTip.test.tsx +1 -3
- package/src/components/ui/help-tip/HelpTip.tsx +21 -22
- package/src/components/ui/help-tip/index.ts +7 -1
- package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
- package/src/components/ui/icon-box/IconBox.test.tsx +1 -7
- package/src/components/ui/icon-box/IconBox.tsx +10 -18
- package/src/components/ui/indicator/Indicator.stories.tsx +5 -4
- package/src/components/ui/indicator/Indicator.tsx +20 -14
- package/src/components/ui/input/Input.stories.tsx +3 -11
- package/src/components/ui/input/Input.test.tsx +2 -10
- package/src/components/ui/input/Input.tsx +10 -25
- package/src/components/ui/input/index.ts +7 -1
- package/src/components/ui/link/Link.stories.tsx +19 -7
- package/src/components/ui/list-item/ListItem.stories.tsx +17 -5
- package/src/components/ui/list-item/ListItem.tsx +2 -9
- package/src/components/ui/menu/Menu.stories.tsx +21 -15
- package/src/components/ui/menu/Menu.test.tsx +2 -11
- package/src/components/ui/menu/Menu.tsx +12 -14
- package/src/components/ui/menu/index.ts +8 -1
- package/src/components/ui/modal/Modal.stories.tsx +2 -2
- package/src/components/ui/modal/Modal.tsx +31 -19
- package/src/components/ui/pill/Pill.stories.tsx +111 -23
- package/src/components/ui/pill/Pill.test.tsx +101 -3
- package/src/components/ui/pill/Pill.tsx +168 -49
- package/src/components/ui/pill/index.ts +1 -1
- package/src/components/ui/popover/Popover.stories.tsx +6 -8
- package/src/components/ui/popover/Popover.test.tsx +1 -6
- package/src/components/ui/popover/Popover.tsx +20 -15
- package/src/components/ui/popover/index.ts +7 -1
- package/src/components/ui/progress/Progress.stories.tsx +3 -12
- package/src/components/ui/progress/Progress.test.tsx +1 -5
- package/src/components/ui/progress/Progress.tsx +9 -6
- package/src/components/ui/radio/Radio.stories.tsx +42 -21
- package/src/components/ui/radio/Radio.test.tsx +6 -2
- package/src/components/ui/radio/Radio.tsx +23 -22
- package/src/components/ui/section/Section.stories.tsx +12 -21
- package/src/components/ui/section/Section.test.tsx +1 -6
- package/src/components/ui/section/Section.tsx +1 -3
- package/src/components/ui/section/index.ts +1 -5
- package/src/components/ui/select/Select.stories.tsx +3 -13
- package/src/components/ui/select/Select.test.tsx +2 -11
- package/src/components/ui/select/Select.tsx +16 -15
- package/src/components/ui/skeleton/Skeleton.stories.tsx +3 -11
- package/src/components/ui/skeleton/Skeleton.tsx +3 -14
- package/src/components/ui/skeleton/index.ts +1 -7
- package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
- package/src/components/ui/spinner/Spinner.tsx +4 -5
- package/src/components/ui/stack/Stack.stories.tsx +8 -8
- package/src/components/ui/stack/Stack.tsx +1 -1
- package/src/components/ui/surface/Surface.stories.tsx +92 -33
- package/src/components/ui/surface/Surface.test.tsx +88 -28
- package/src/components/ui/surface/Surface.tsx +37 -87
- package/src/components/ui/surface/SurfaceContext.ts +22 -62
- package/src/components/ui/surface/index.ts +8 -0
- package/src/components/ui/surface/resolveSurface.ts +98 -0
- package/src/components/ui/surface/surface.contract.test.ts +6 -1
- package/src/components/ui/switch/Switch.stories.tsx +1 -1
- package/src/components/ui/switch/Switch.tsx +7 -6
- package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
- package/src/components/ui/tabs/Tabs.tsx +8 -21
- package/src/components/ui/tabs/index.ts +1 -7
- package/src/components/ui/tile/Tile.stories.tsx +1 -1
- package/src/components/ui/tile/Tile.tsx +3 -0
- package/src/components/ui/toast/Toast.stories.tsx +1 -1
- package/src/components/ui/toast/Toast.test.tsx +2 -8
- package/src/components/ui/toast/Toast.tsx +49 -57
- package/src/components/ui/toast/index.ts +0 -1
- package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +92 -71
- package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +3 -18
- package/src/components/ui/toolbar-button/ToolbarButton.tsx +38 -49
- package/src/components/ui/tooltip/Tooltip.stories.tsx +12 -3
- package/src/components/ui/tooltip/Tooltip.test.tsx +21 -3
- package/src/components/ui/tooltip/Tooltip.tsx +55 -36
- package/src/components/ui/tooltip/index.ts +1 -1
- package/src/components/ui/trigger/TriggerSurface.test.tsx +215 -0
- package/src/components/ui/trigger/TriggerSurface.tsx +81 -0
- package/src/components/ui/trigger/index.ts +2 -0
- package/src/stories/PresetShowcase.stories.tsx +101 -48
- package/src/test/no-device-internals.test.ts +3 -3
- package/src/test/no-raw-color.test.ts +39 -0
- package/src/theme/Alpha.stories.tsx +103 -0
- package/src/theme/ChoosingTokens.stories.tsx +48 -0
- package/src/theme/ColorPalettes.stories.tsx +62 -45
- package/src/theme/ColorPrimitives.stories.tsx +35 -35
- package/src/theme/Depth.stories.tsx +132 -65
- package/src/theme/DepthCalibration.stories.tsx +23 -21
- package/src/theme/ProposedTokensVW82.stories.tsx +414 -0
- package/src/theme/ThemeProvider.test.tsx +4 -4
- package/src/theme/barrel.ts +7 -6
- package/src/theme/color-stories.coverage.test.ts +1 -1
- package/src/theme/color-utils.ts +41 -29
- package/src/theme/config.completeness.test.ts +1 -1
- package/src/theme/config.ts +40 -0
- package/src/theme/elevation.test.ts +82 -56
- package/src/theme/elevation.ts +77 -561
- package/src/theme/global.css +45 -5
- package/src/theme/grey-ramp.test.ts +24 -8
- package/src/theme/lift.test.ts +39 -0
- package/src/theme/lift.ts +102 -0
- package/src/theme/materials.test.ts +5 -2
- package/src/theme/materials.ts +38 -4
- package/src/theme/native-theming-guard.test.ts +5 -5
- package/src/theme/presets/apply.ts +4 -0
- package/src/theme/presets/audiobook.ts +5 -2
- package/src/theme/presets/default.ts +4 -0
- package/src/theme/presets/index.ts +14 -1
- package/src/theme/presets/types.ts +1 -1
- package/src/theme/semantic-contrast.test.ts +13 -4
- package/src/theme/status-distinctness.test.ts +66 -0
- package/src/theme/subtle-tone-weight.test.ts +163 -0
- package/src/theme/surface-planes.test.ts +37 -0
- package/src/theme/surface-planes.ts +68 -0
- package/src/theme/tailwind-var-opacity.test.ts +163 -0
- package/src/theme/token-export-guard.test.ts +1 -3
- package/src/theme/tokens/primitives.test.ts +15 -7
- package/src/theme/tokens/primitives.ts +98 -33
- package/src/theme/tokens/semantic-hex-guard.test.ts +322 -0
- package/src/theme/tokens/semantic.ts +124 -54
- package/src/theme/wash-ladder.test.ts +58 -0
- package/src/theme/workout-tokens.ts +1 -7
- package/src/types/jest-axe.d.ts +2 -6
- package/src/types/raw-md.d.ts +5 -0
- package/src/utils/colors.alpha-adoption.test.ts +408 -0
- package/src/utils/colors.ts +4 -4
- package/src/utils/form.ts +1 -3
- package/src/utils/index.ts +2 -7
- package/src/utils/number-format.ts +17 -0
- package/src/utils/workout-format.ts +15 -6
- package/src/web-jsx/jsx-runtime.ts +1 -5
- package/tailwind.config.js +55 -9
- package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +0 -151
- package/src/components/custom/Workout/RowInventory.stories.tsx +0 -408
- package/src/components/custom/Workout/S3FullRail.stories.tsx +0 -46
- package/src/components/custom/Workout/S3SessionPace.stories.tsx +0 -95
- package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +0 -61
- package/src/components/custom/Workout/S3SetTypes.stories.tsx +0 -176
- package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +0 -240
- package/src/components/shell/DashboardShell.stories.tsx +0 -65
- package/src/components/shell/DashboardShell.tsx +0 -75
- package/src/components/shell/SessionRailSpecimen.stories.tsx +0 -753
- package/src/theme/elevation.stories.tsx +0 -346
- package/src/utils/useTheme.ts +0 -72
- /package/src/components/shell/{DeviceIndicator.test.tsx → workout/DeviceIndicator.test.tsx} +0 -0
- /package/src/components/shell/{DeviceMenu.test.tsx → workout/DeviceMenu.test.tsx} +0 -0
- /package/src/components/shell/{DeviceRow.test.tsx → workout/DeviceRow.test.tsx} +0 -0
- /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: '
|
|
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: '
|
|
20
|
-
{ id: 'domain', x: 0.22, y: 0.7, r: 8, color: '
|
|
21
|
-
{ id: 'services', x: 0.45, y: 0.4, r: 12, color: '
|
|
22
|
-
{ id: 'api', x: 0.6, y: 0.35, r: 7, color: '
|
|
23
|
-
{ id: 'cli', x: 0.9, y: 0.08, r: 9, color: '
|
|
24
|
-
{ id: 'utils', x: 0.85, y: 0.75, r: 6, color: '
|
|
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: {
|
|
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: {
|
|
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: {
|
|
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: [
|
|
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: {
|
|
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: {
|
|
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 =
|
|
53
|
-
const AXIS_LINE =
|
|
54
|
-
const DIAGONAL_LINE =
|
|
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,
|
|
90
|
-
|
|
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(
|
|
136
|
-
|
|
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={{
|
|
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
|
|
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={{
|
|
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
|
|
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
|
|
193
|
-
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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={{
|
|
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>
|