@runaid/lactate-curve 0.1.2

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/components/LactateCurveChart.tsx","../src/lib/generateLactateCurve.ts","../src/lib/chart-defaults.ts","../src/lib/projectMarkerToCurve.ts","../src/lib/resolveZones.ts","../src/lib/utils.ts","../src/components/ui/chart.tsx","../src/examples.tsx"],"sourcesContent":["export { LactateCurveChart } from \"./components/LactateCurveChart\"\nexport { generateLactateCurve } from \"./lib/generateLactateCurve\"\nexport { resolveZones } from \"./lib/resolveZones\"\nexport { projectMarkerToCurve } from \"./lib/projectMarkerToCurve\"\nexport * from \"./types\"\nexport * from \"./examples\"\n","import * as React from \"react\"\nimport {\n CartesianGrid,\n Label,\n Line,\n LineChart,\n ReferenceArea,\n ReferenceDot,\n ReferenceLine,\n XAxis,\n YAxis,\n} from \"recharts\"\n\nimport { generateLactateCurve } from \"../lib/generateLactateCurve\"\nimport { DEFAULT_THEME } from \"../lib/chart-defaults\"\nimport {\n projectIntensityToCurve,\n projectMarkerToCurve,\n} from \"../lib/projectMarkerToCurve\"\nimport { resolveZones } from \"../lib/resolveZones\"\nimport { cn } from \"../lib/utils\"\nimport type {\n LactateCurveChartProps,\n ProjectedRaceMarker,\n ThresholdKey,\n XAxisCompression,\n} from \"../types\"\nimport {\n ChartContainer,\n ChartTooltip,\n ChartTooltipContent,\n type ChartConfig,\n} from \"./ui/chart\"\n\nconst mergeTheme = (theme?: LactateCurveChartProps[\"theme\"]) => ({\n ...DEFAULT_THEME,\n ...theme,\n})\n\nconst formatNumber = (value: number, precision: number) =>\n value.toFixed(Math.max(0, precision))\n\nconst markerLabelStyle = (color: string, fontFamily: string) => ({\n fill: color,\n fontFamily,\n fontSize: 12,\n fontWeight: 600,\n})\n\nconst resolveCompressionEnd = (\n compression: XAxisCompression | undefined,\n values: {\n lt1: number\n lt2: number\n vo2max?: number\n },\n) => {\n if (!compression) return null\n const end = compression.endIntensity\n\n if (typeof end === \"number\") return end\n if (end === \"lt1\") return values.lt1\n if (end === \"lt2\") return values.lt2\n if (end === \"vo2max\") return values.vo2max ?? null\n return null\n}\n\nconst defaultThresholdLabel = (key: ThresholdKey) => {\n switch (key) {\n case \"lt1\":\n return \"LT1\"\n case \"lt2\":\n return \"LT2\"\n case \"vo2max\":\n return \"VO2max\"\n }\n}\n\nexport const LactateCurveChart = ({\n baselineLactate,\n lt1,\n lt2,\n vo2max,\n zones,\n raceMarkers,\n xAxisLabel = \"Intensity\",\n yAxisLabel = \"Blood lactate (mmol/L)\",\n xAxisPrecision = 1,\n yAxisPrecision = 1,\n showXAxisTicks = true,\n xAxisCompression,\n title = \"Approximate lactate curve\",\n description = \"A physiologically plausible lactate curve with thresholds, optional domains, and optional race markers.\",\n showThresholdLabels = true,\n showThresholdGuides = true,\n showThresholdCircles = true,\n showGrid = true,\n showLegend = true,\n thresholdAnnotations,\n theme,\n icon,\n labels,\n minIntensity,\n xAxisMax,\n maxIntensity,\n intensityStep,\n curveSamples = 96,\n height = 420,\n style,\n className,\n}: LactateCurveChartProps) => {\n const resolvedTheme = React.useMemo(() => mergeTheme(theme), [theme])\n const curve = React.useMemo(\n () =>\n generateLactateCurve({\n baselineLactate,\n lt1,\n lt2,\n vo2max,\n minIntensity,\n maxIntensity: xAxisMax ?? maxIntensity,\n intensityStep,\n samples: curveSamples,\n }),\n [\n baselineLactate,\n curveSamples,\n intensityStep,\n lt1,\n lt2,\n maxIntensity,\n minIntensity,\n vo2max,\n xAxisMax,\n ],\n )\n\n const resolvedZones = React.useMemo(\n () => (zones === null ? [] : resolveZones({ zones, curve })),\n [curve, zones],\n )\n\n const compression = React.useMemo(() => {\n const end = resolveCompressionEnd(xAxisCompression, {\n lt1: lt1.intensity,\n lt2: lt2.intensity,\n vo2max: vo2max?.intensity,\n })\n const scale = xAxisCompression?.scale ?? 0.42\n\n if (!end || end <= curve.domain.minIntensity || end >= curve.domain.maxIntensity) {\n return null\n }\n\n if (!(scale > 0 && scale < 1)) {\n return null\n }\n\n const transform = (value: number) => {\n if (value <= end) {\n return curve.domain.minIntensity + (value - curve.domain.minIntensity) * scale\n }\n\n return (\n curve.domain.minIntensity +\n (end - curve.domain.minIntensity) * scale +\n (value - end)\n )\n }\n\n const inverse = (value: number) => {\n const compressedEnd =\n curve.domain.minIntensity + (end - curve.domain.minIntensity) * scale\n\n if (value <= compressedEnd) {\n return curve.domain.minIntensity + (value - curve.domain.minIntensity) / scale\n }\n\n return end + (value - compressedEnd)\n }\n\n return {\n end,\n scale,\n transform,\n inverse,\n }\n }, [\n curve.domain.maxIntensity,\n curve.domain.minIntensity,\n lt1.intensity,\n lt2.intensity,\n vo2max?.intensity,\n xAxisCompression,\n ])\n\n const displayPoints = React.useMemo(\n () =>\n curve.points.map((point) => ({\n ...point,\n displayIntensity: compression?.transform(point.intensity) ?? point.intensity,\n })),\n [compression, curve.points],\n )\n\n const projectedMarkers = React.useMemo<ProjectedRaceMarker[]>(\n () => (raceMarkers ?? []).map((marker) => projectMarkerToCurve(marker, curve.points)),\n [curve.points, raceMarkers],\n )\n\n const chartConfig = React.useMemo<ChartConfig>(\n () => ({\n lactate: {\n label: labels?.curve ?? \"Lactate curve\",\n color: resolvedTheme.curveColor,\n },\n raceMarkers: {\n label: labels?.raceMarkers ?? \"Race intensities\",\n color: resolvedTheme.curveColor,\n },\n }),\n [labels?.curve, labels?.raceMarkers, resolvedTheme.curveColor],\n )\n\n const thresholdEntries = [\n { key: \"lt1\" as const, point: { ...lt1, ...projectIntensityToCurve(lt1.intensity, curve.points) } },\n { key: \"lt2\" as const, point: { ...lt2, ...projectIntensityToCurve(lt2.intensity, curve.points) } },\n vo2max\n ? {\n key: \"vo2max\" as const,\n point: { ...vo2max, ...projectIntensityToCurve(vo2max.intensity, curve.points) },\n }\n : null,\n ].filter(Boolean) as Array<{ key: ThresholdKey; point: { intensity: number; lactate: number } }>\n\n const displayDomain = React.useMemo(\n () => ({\n min: compression?.transform(curve.domain.minIntensity) ?? curve.domain.minIntensity,\n max: compression?.transform(curve.domain.maxIntensity) ?? curve.domain.maxIntensity,\n }),\n [compression, curve.domain.maxIntensity, curve.domain.minIntensity],\n )\n\n const toDisplayIntensity = React.useCallback(\n (value: number) => compression?.transform(value) ?? value,\n [compression],\n )\n\n const iconNode =\n typeof icon === \"function\"\n ? icon({\n width: 18,\n height: 18,\n viewBox: \"0 0 18 18\",\n })\n : icon\n\n return (\n <figure\n className={cn(className)}\n style={{\n margin: 0,\n color: resolvedTheme.labelColor,\n fontFamily: resolvedTheme.fontFamily,\n ...style,\n }}\n >\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n gap: 16,\n marginBottom: 16,\n }}\n >\n <figcaption>\n <div style={{ fontSize: 18, fontWeight: 700 }}>{title}</div>\n <div style={{ fontSize: 14, opacity: 0.78, marginTop: 4 }}>{description}</div>\n </figcaption>\n {iconNode ? <div aria-hidden=\"true\">{iconNode}</div> : null}\n </div>\n\n {showLegend ? (\n <div\n aria-label=\"Chart legend\"\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 14,\n flexWrap: \"wrap\",\n marginBottom: 12,\n fontSize: 12,\n color: resolvedTheme.labelColor,\n }}\n >\n <span style={{ display: \"inline-flex\", alignItems: \"center\", gap: 8 }}>\n <span\n aria-hidden=\"true\"\n style={{\n width: 24,\n height: 0,\n borderTop: `3px solid ${resolvedTheme.curveColor}`,\n borderRadius: 999,\n display: \"inline-block\",\n }}\n />\n {chartConfig.lactate.label}\n </span>\n {projectedMarkers.length ? (\n <span style={{ display: \"inline-flex\", alignItems: \"center\", gap: 8 }}>\n <span\n aria-hidden=\"true\"\n style={{\n width: 10,\n height: 10,\n borderRadius: 999,\n background: resolvedTheme.curveColor,\n display: \"inline-block\",\n }}\n />\n {chartConfig.raceMarkers.label}\n </span>\n ) : null}\n </div>\n ) : null}\n\n <ChartContainer\n config={chartConfig}\n height={height}\n className=\"lactate-curve-chart\"\n role=\"img\"\n aria-label={title}\n style={{\n background: resolvedTheme.backgroundColor,\n border: \"1px solid rgba(148, 163, 184, 0.22)\",\n borderRadius: 20,\n padding: 12,\n }}\n >\n <LineChart\n data={displayPoints}\n margin={{ top: 24, right: 28, bottom: 28, left: 14 }}\n >\n {showGrid ? (\n <CartesianGrid\n stroke={resolvedTheme.gridColor}\n strokeDasharray=\"3 3\"\n vertical={false}\n />\n ) : null}\n\n {resolvedZones.map((zone) => (\n <ReferenceArea\n key={`${zone.label}-${zone.startIntensity}-${zone.endIntensity}`}\n x1={toDisplayIntensity(zone.startIntensity)}\n x2={toDisplayIntensity(zone.endIntensity)}\n fill={zone.color}\n fillOpacity={zone.opacity}\n strokeOpacity={0}\n ifOverflow=\"visible\"\n />\n ))}\n\n <XAxis\n type=\"number\"\n dataKey=\"displayIntensity\"\n domain={[displayDomain.min, displayDomain.max]}\n tick={\n showXAxisTicks\n ? { fill: resolvedTheme.axisColor, fontFamily: resolvedTheme.fontFamily }\n : false\n }\n axisLine={{ stroke: resolvedTheme.axisColor }}\n tickLine={showXAxisTicks ? { stroke: resolvedTheme.axisColor } : false}\n tickFormatter={(value) =>\n formatNumber(\n compression?.inverse(Number(value)) ?? Number(value),\n xAxisPrecision,\n )\n }\n >\n <Label\n value={xAxisLabel}\n position=\"insideBottom\"\n offset={-14}\n style={{\n fill: resolvedTheme.labelColor,\n fontFamily: resolvedTheme.fontFamily,\n }}\n />\n </XAxis>\n\n <YAxis\n type=\"number\"\n domain={[curve.domain.minLactate, curve.domain.maxLactate]}\n tick={{ fill: resolvedTheme.axisColor, fontFamily: resolvedTheme.fontFamily }}\n axisLine={{ stroke: resolvedTheme.axisColor }}\n tickLine={{ stroke: resolvedTheme.axisColor }}\n tickFormatter={(value) => formatNumber(Number(value), yAxisPrecision)}\n width={64}\n >\n <Label\n angle={-90}\n position=\"insideLeft\"\n value={yAxisLabel}\n style={{\n fill: resolvedTheme.labelColor,\n fontFamily: resolvedTheme.fontFamily,\n textAnchor: \"middle\",\n }}\n />\n </YAxis>\n\n <ChartTooltip\n cursor={{ stroke: resolvedTheme.gridColor, strokeDasharray: \"3 3\" }}\n content={\n <ChartTooltipContent\n labelFormatter={(value) =>\n `${xAxisLabel}: ${formatNumber(\n compression?.inverse(Number(value)) ?? Number(value),\n xAxisPrecision,\n )}`\n }\n valueFormatter={(value, name) =>\n name === \"lactate\"\n ? `${formatNumber(Number(value), yAxisPrecision)} mmol/L`\n : `${formatNumber(Number(value), xAxisPrecision)}`\n }\n />\n }\n />\n\n <Line\n type=\"monotone\"\n dataKey=\"lactate\"\n name=\"lactate\"\n stroke={resolvedTheme.curveColor}\n strokeWidth={3}\n dot={false}\n isAnimationActive={false}\n />\n\n {thresholdEntries.map(({ key, point }) => {\n const annotation = thresholdAnnotations?.[key]\n const color = annotation?.color ?? resolvedTheme.thresholdColor\n const showGuide = annotation?.showGuide ?? showThresholdGuides\n const showCircle = annotation?.showCircle ?? showThresholdCircles\n const labelText = annotation?.label ?? labels?.[key] ?? defaultThresholdLabel(key)\n\n return (\n <React.Fragment key={key}>\n {showGuide ? (\n <ReferenceLine\n x={toDisplayIntensity(point.intensity)}\n stroke={color}\n strokeDasharray={annotation?.strokeDasharray ?? \"6 4\"}\n strokeWidth={annotation?.lineWidth ?? 1.5}\n ifOverflow=\"visible\"\n label={\n showThresholdLabels\n ? {\n value: labelText,\n position: \"top\",\n fill: color,\n fontSize: 12,\n fontWeight: 700,\n fontFamily: resolvedTheme.fontFamily,\n }\n : undefined\n }\n />\n ) : null}\n {showCircle ? (\n <ReferenceDot\n x={toDisplayIntensity(point.intensity)}\n y={point.lactate}\n r={annotation?.circleRadius ?? 5}\n fill={color}\n stroke={resolvedTheme.backgroundColor}\n strokeWidth={2}\n ifOverflow=\"visible\"\n />\n ) : null}\n </React.Fragment>\n )\n })}\n\n {projectedMarkers.map((marker) => (\n <ReferenceDot\n key={`${marker.label}-${marker.intensity}`}\n x={toDisplayIntensity(marker.intensity)}\n y={marker.lactate}\n r={marker.radius ?? 5}\n fill={marker.color ?? resolvedTheme.curveColor}\n stroke={resolvedTheme.backgroundColor}\n strokeWidth={2}\n ifOverflow=\"visible\"\n label={\n marker.showLabel === false\n ? undefined\n : {\n value: marker.label,\n position: \"top\",\n offset: 10,\n fill: resolvedTheme.markerLabelColor,\n fontSize: 12,\n fontWeight: 600,\n fontFamily: resolvedTheme.fontFamily,\n }\n }\n />\n ))}\n </LineChart>\n </ChartContainer>\n\n {compression ? (\n <div\n style={{\n marginTop: 10,\n fontSize: 12,\n color: resolvedTheme.labelColor,\n opacity: 0.78,\n }}\n >\n X-axis break: the low-intensity region up to {formatNumber(\n compression.end,\n xAxisPrecision,\n )} is visually compressed.\n </div>\n ) : null}\n\n {resolvedZones.length ? (\n <div\n aria-label={labels?.zones ?? \"Intensity domains\"}\n style={{\n display: \"flex\",\n flexWrap: \"wrap\",\n gap: 10,\n marginTop: 14,\n }}\n >\n {resolvedZones.map((zone) => (\n <span\n key={`${zone.label}-${zone.startIntensity}-${zone.endIntensity}-legend`}\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 8,\n fontSize: 12,\n color: resolvedTheme.labelColor,\n }}\n >\n <span\n aria-hidden=\"true\"\n style={{\n width: 12,\n height: 12,\n borderRadius: 999,\n background: zone.color,\n opacity: Math.max(zone.opacity, 0.35),\n }}\n />\n {zone.label}\n </span>\n ))}\n </div>\n ) : null}\n </figure>\n )\n}\n","import type {\n AnchorPoint,\n GeneratedLactateCurve,\n IntensityAnchor,\n LactateCurvePoint,\n} from \"../types\"\n\ntype CurveInputs = {\n baselineLactate: number\n lt1: AnchorPoint\n lt2: AnchorPoint\n vo2max?: IntensityAnchor\n minIntensity?: number\n maxIntensity?: number\n intensityStep?: number\n samples?: number\n}\n\nconst MAX_LT1_BASELINE_DIP = 0.45\n\nconst clamp = (value: number, min: number, max: number) =>\n Math.min(max, Math.max(min, value))\n\nconst round = (value: number) => Math.round(value * 1000) / 1000\n\nconst assertFinitePositive = (value: number, label: string) => {\n if (!Number.isFinite(value)) {\n throw new Error(`${label} must be a finite number.`)\n }\n}\n\nconst buildModerateDomainFunction = ({\n minIntensity,\n baselineLactate,\n lt1,\n}: {\n minIntensity: number\n baselineLactate: number\n lt1: AnchorPoint\n}) => {\n const span = lt1.intensity - minIntensity\n const delta = lt1.lactate - baselineLactate\n\n if (span <= 0) {\n throw new Error(\"Moderate domain span must be greater than 0.\")\n }\n\n if (Math.abs(delta) < 0.0001) {\n return () => baselineLactate\n }\n\n if (delta > 0) {\n return (intensity: number) => {\n const t = clamp((intensity - minIntensity) / span, 0, 1)\n return baselineLactate + delta * Math.pow(t, 4)\n }\n }\n\n const dip = baselineLactate - lt1.lactate\n const dipAccent = 0.32\n\n return (intensity: number) => {\n const t = clamp((intensity - minIntensity) / span, 0, 1)\n return baselineLactate - dip * (t + dipAccent * 4 * t * (1 - t))\n }\n}\n\nconst buildHeavyDomainFunction = ({\n lt1,\n lt2,\n}: {\n lt1: AnchorPoint\n lt2: AnchorPoint\n}) => {\n const span = lt2.intensity - lt1.intensity\n\n if (span <= 0) {\n throw new Error(\"Heavy domain span must be greater than 0.\")\n }\n\n const slope = (lt2.lactate - lt1.lactate) / span\n\n return {\n slope,\n project: (intensity: number) => lt1.lactate + slope * (intensity - lt1.intensity),\n }\n}\n\nconst buildSevereDomainFunction = ({\n lt2,\n maxIntensity,\n heavySlope,\n}: {\n lt2: AnchorPoint\n maxIntensity: number\n heavySlope: number\n}) => {\n const span = maxIntensity - lt2.intensity\n\n if (span <= 0) {\n throw new Error(\"Severe domain span must be greater than 0.\")\n }\n\n const curvature = clamp(2.4 / span, 0.02, 0.9)\n const initialSevereSlope = heavySlope * 1.4\n\n return {\n curvature,\n initialSevereSlope,\n project: (intensity: number) => {\n const delta = Math.max(0, intensity - lt2.intensity)\n return (\n lt2.lactate +\n (initialSevereSlope / curvature) * (Math.exp(curvature * delta) - 1)\n )\n },\n }\n}\n\nexport const generateLactateCurve = ({\n baselineLactate,\n lt1,\n lt2,\n vo2max,\n minIntensity = 0,\n maxIntensity,\n intensityStep,\n samples = 80,\n}: CurveInputs): GeneratedLactateCurve => {\n assertFinitePositive(baselineLactate, \"baselineLactate\")\n assertFinitePositive(lt1.intensity, \"lt1 intensity\")\n assertFinitePositive(lt1.lactate, \"lt1 lactate\")\n assertFinitePositive(lt2.intensity, \"lt2 intensity\")\n assertFinitePositive(lt2.lactate, \"lt2 lactate\")\n\n if (baselineLactate <= 0) {\n throw new Error(\"baselineLactate must be greater than 0.\")\n }\n\n if (baselineLactate - lt1.lactate > MAX_LT1_BASELINE_DIP) {\n throw new Error(\n `LT1 lactate can sit slightly below baseline, but the dip must be <= ${MAX_LT1_BASELINE_DIP} mmol/L.`,\n )\n }\n\n if (lt1.intensity <= minIntensity) {\n throw new Error(\"LT1 intensity must be above the minimum chart intensity.\")\n }\n\n if (lt1.intensity >= lt2.intensity) {\n throw new Error(\"LT1 intensity must be below LT2 intensity.\")\n }\n\n if (lt1.lactate >= lt2.lactate) {\n throw new Error(\"LT1 lactate must be below LT2 lactate.\")\n }\n\n if (vo2max) {\n assertFinitePositive(vo2max.intensity, \"vo2max intensity\")\n\n if (lt2.intensity >= vo2max.intensity) {\n throw new Error(\"VO2max intensity must be greater than LT2 intensity.\")\n }\n }\n\n const maxX =\n maxIntensity ??\n round(\n vo2max\n ? vo2max.intensity * 1.08\n : lt2.intensity + Math.max((lt2.intensity - lt1.intensity) * 0.6, lt2.intensity * 0.12),\n )\n\n if (maxX <= lt2.intensity) {\n throw new Error(\"maxIntensity must extend beyond LT2.\")\n }\n\n const moderateDomain = buildModerateDomainFunction({\n minIntensity,\n baselineLactate,\n lt1,\n })\n const heavyDomain = buildHeavyDomainFunction({ lt1, lt2 })\n const severeDomain = buildSevereDomainFunction({\n lt2,\n maxIntensity: maxX,\n heavySlope: heavyDomain.slope,\n })\n\n const points: LactateCurvePoint[] = []\n const pointCount = intensityStep\n ? Math.max(2, Math.floor((maxX - minIntensity) / intensityStep) + 1)\n : samples\n\n for (let index = 0; index < pointCount; index += 1) {\n const intensity =\n index === pointCount - 1\n ? maxX\n : intensityStep\n ? minIntensity + intensityStep * index\n : minIntensity + ((maxX - minIntensity) * index) / (pointCount - 1)\n\n let lactate: number\n\n if (intensity <= lt1.intensity) {\n lactate = moderateDomain(intensity)\n } else if (intensity <= lt2.intensity) {\n lactate = heavyDomain.project(intensity)\n } else {\n lactate = severeDomain.project(intensity)\n }\n\n points.push({\n intensity: round(intensity),\n lactate: round(lactate),\n })\n }\n\n const endPoint = points[points.length - 1]\n const minimumModerateEstimate =\n lt1.lactate < baselineLactate\n ? lt1.lactate - (baselineLactate - lt1.lactate) * 0.12\n : Math.min(baselineLactate, lt1.lactate)\n\n return {\n points,\n anchors: {\n baseline: { intensity: minIntensity, lactate: baselineLactate },\n lt1,\n lt2,\n vo2max,\n max: endPoint,\n },\n domain: {\n minIntensity,\n maxIntensity: maxX,\n minLactate: round(Math.max(0, minimumModerateEstimate - 0.12)),\n maxLactate: round(endPoint.lactate * 1.08),\n },\n }\n}\n","import type { LactateCurveTheme, ZoneConfig } from \"../types\"\n\nexport const DEFAULT_THEME: Required<LactateCurveTheme> = {\n curveColor: \"#e11d48\",\n axisColor: \"#334155\",\n gridColor: \"#cbd5e1\",\n labelColor: \"#0f172a\",\n thresholdColor: \"#0f172a\",\n fontFamily: \"ui-sans-serif, system-ui, sans-serif\",\n backgroundColor: \"#ffffff\",\n markerLabelColor: \"#0f172a\",\n}\n\nexport const DEFAULT_ZONE_OPACITY = 0.12\n\nexport const getDefaultZones = (): ZoneConfig[] => [\n {\n startIntensity: \"min\",\n endIntensity: \"lt1\",\n label: \"Moderate domain\",\n color: \"#22c55e\",\n opacity: DEFAULT_ZONE_OPACITY,\n },\n {\n startIntensity: \"lt1\",\n endIntensity: \"lt2\",\n label: \"Heavy domain\",\n color: \"#f59e0b\",\n opacity: DEFAULT_ZONE_OPACITY,\n },\n {\n startIntensity: \"lt2\",\n endIntensity: \"max\",\n label: \"Severe domain\",\n color: \"#ef4444\",\n opacity: DEFAULT_ZONE_OPACITY,\n },\n]\n","import type {\n LactateCurvePoint,\n ProjectedRaceMarker,\n RaceMarker,\n} from \"../types\"\n\nexport const projectIntensityToCurve = (\n intensity: number,\n points: LactateCurvePoint[],\n): { intensity: number; lactate: number } => {\n if (points.length < 2) {\n throw new Error(\"At least two curve points are required to project onto the curve.\")\n }\n\n const clampedIntensity = Math.min(\n points[points.length - 1].intensity,\n Math.max(points[0].intensity, intensity),\n )\n\n for (let index = 0; index < points.length - 1; index += 1) {\n const current = points[index]\n const next = points[index + 1]\n\n if (\n clampedIntensity >= current.intensity &&\n clampedIntensity <= next.intensity\n ) {\n const span = next.intensity - current.intensity || 1\n const ratio = (clampedIntensity - current.intensity) / span\n const lactate = current.lactate + (next.lactate - current.lactate) * ratio\n\n return {\n intensity: clampedIntensity,\n lactate: Math.round(lactate * 1000) / 1000,\n }\n }\n }\n\n return {\n intensity: clampedIntensity,\n lactate: points[points.length - 1].lactate,\n }\n}\n\nexport const projectMarkerToCurve = (\n marker: RaceMarker,\n points: LactateCurvePoint[],\n): ProjectedRaceMarker => ({\n ...marker,\n ...projectIntensityToCurve(marker.intensity, points),\n})\n","import { DEFAULT_ZONE_OPACITY, getDefaultZones } from \"./chart-defaults\"\nimport type {\n AnchorPoint,\n GeneratedLactateCurve,\n IntensityAnchor,\n ResolvedZone,\n ZoneBoundary,\n ZoneConfig,\n} from \"../types\"\n\ntype ResolveZonesInput = {\n zones?: ZoneConfig[]\n curve: GeneratedLactateCurve\n}\n\nconst resolveBoundary = (\n boundary: ZoneBoundary | undefined,\n anchors: {\n baseline: AnchorPoint\n lt1: AnchorPoint\n lt2: AnchorPoint\n vo2max?: IntensityAnchor\n max: AnchorPoint\n },\n fallback: number,\n) => {\n if (boundary === undefined) return fallback\n if (typeof boundary === \"number\") return boundary\n\n switch (boundary) {\n case \"min\":\n case \"baseline\":\n return anchors.baseline.intensity\n case \"lt1\":\n return anchors.lt1.intensity\n case \"lt2\":\n return anchors.lt2.intensity\n case \"vo2max\":\n return anchors.vo2max?.intensity ?? anchors.max.intensity\n case \"max\":\n return anchors.max.intensity\n }\n}\n\nexport const resolveZones = ({\n zones,\n curve,\n}: ResolveZonesInput): ResolvedZone[] => {\n const sourceZones = zones ?? getDefaultZones()\n\n return sourceZones\n .map((zone) => {\n const startIntensity = resolveBoundary(\n zone.startIntensity,\n curve.anchors,\n curve.domain.minIntensity,\n )\n const endIntensity = resolveBoundary(\n zone.endIntensity,\n curve.anchors,\n curve.domain.maxIntensity,\n )\n\n return {\n label: zone.label,\n color: zone.color,\n opacity: zone.opacity ?? DEFAULT_ZONE_OPACITY,\n startIntensity,\n endIntensity,\n }\n })\n .filter((zone) => zone.endIntensity > zone.startIntensity)\n}\n","import { clsx } from \"clsx\"\n\nexport const cn = (...inputs: Array<string | false | null | undefined>) =>\n clsx(inputs)\n","import * as React from \"react\"\nimport { ResponsiveContainer, Tooltip } from \"recharts\"\nimport type {\n NameType,\n ValueType,\n} from \"recharts/types/component/DefaultTooltipContent\"\nimport type { Payload } from \"recharts/types/component/DefaultTooltipContent\"\n\nimport { cn } from \"../../lib/utils\"\n\nexport type ChartConfig = Record<\n string,\n {\n label?: string\n color?: string\n }\n>\n\ntype ChartContextValue = {\n config: ChartConfig\n}\n\nconst ChartContext = React.createContext<ChartContextValue | null>(null)\n\nexport const useChart = () => {\n const context = React.useContext(ChartContext)\n\n if (!context) {\n throw new Error(\"Chart components must be used within a ChartContainer.\")\n }\n\n return context\n}\n\ntype ChartContainerProps = React.HTMLAttributes<HTMLDivElement> & {\n config?: ChartConfig\n height?: number\n children: React.ReactNode\n}\n\nexport const ChartContainer = React.forwardRef<HTMLDivElement, ChartContainerProps>(\n ({ config = {}, className, style, height = 360, children, ...props }, ref) => (\n <ChartContext.Provider value={{ config }}>\n <div\n ref={ref}\n className={cn(\"w-full\", className)}\n style={{ width: \"100%\", height, ...style }}\n {...props}\n >\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n {children}\n </ResponsiveContainer>\n </div>\n </ChartContext.Provider>\n ),\n)\n\nChartContainer.displayName = \"ChartContainer\"\n\nexport const ChartTooltip = Tooltip\n\ntype ChartTooltipContentProps = React.HTMLAttributes<HTMLDivElement> & {\n active?: boolean\n payload?: Payload<ValueType, NameType>[]\n label?: NameType\n indicator?: \"dot\" | \"line\"\n labelFormatter?: (label: NameType) => React.ReactNode\n valueFormatter?: (value: ValueType, name: NameType) => React.ReactNode\n}\n\nexport const ChartTooltipContent = React.forwardRef<\n HTMLDivElement,\n ChartTooltipContentProps\n>(\n (\n {\n active,\n payload,\n label,\n className,\n indicator = \"dot\",\n labelFormatter,\n valueFormatter,\n ...props\n },\n ref,\n ) => {\n const { config } = useChart()\n\n if (!active || !payload?.length) {\n return null\n }\n\n return (\n <div\n ref={ref}\n className={cn(className)}\n style={{\n border: \"1px solid rgba(15, 23, 42, 0.15)\",\n borderRadius: 12,\n background: \"rgba(255,255,255,0.96)\",\n boxShadow: \"0 12px 40px rgba(15, 23, 42, 0.12)\",\n padding: \"0.75rem\",\n minWidth: 180,\n }}\n {...props}\n >\n <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 8 }}>\n {labelFormatter ? labelFormatter(label ?? \"\") : label}\n </div>\n <div style={{ display: \"grid\", gap: 6 }}>\n {payload.map((item) => {\n const key = String(item.dataKey ?? item.name ?? \"\")\n const entry = config[key]\n const color = item.color ?? entry?.color ?? \"#0f172a\"\n\n return (\n <div\n key={key}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n gap: 12,\n fontSize: 12,\n }}\n >\n <span style={{ display: \"inline-flex\", alignItems: \"center\", gap: 8 }}>\n <span\n aria-hidden=\"true\"\n style={{\n width: indicator === \"dot\" ? 8 : 12,\n height: 8,\n borderRadius: indicator === \"dot\" ? 999 : 2,\n background: color,\n display: \"inline-block\",\n }}\n />\n <span>{entry?.label ?? item.name}</span>\n </span>\n <span style={{ fontWeight: 600 }}>\n {valueFormatter\n ? valueFormatter(item.value ?? \"\", item.name ?? \"\")\n : item.value}\n </span>\n </div>\n )\n })}\n </div>\n </div>\n )\n },\n)\n\nChartTooltipContent.displayName = \"ChartTooltipContent\"\n","import * as React from \"react\"\n\nimport { LactateCurveChart } from \"./components/LactateCurveChart\"\n\nconst baseProps = {\n baselineLactate: 1.1,\n lt1: { intensity: 220, lactate: 1.9 },\n lt2: { intensity: 305, lactate: 4.1 },\n}\n\nexport const BasicCurveExample = () => <LactateCurveChart {...baseProps} />\n\nexport const CurveWithVo2maxExample = () => (\n <LactateCurveChart\n {...baseProps}\n vo2max={{ intensity: 360 }}\n description=\"The severe-domain tail is anchored explicitly with a VO2max point.\"\n xAxisCompression={{ endIntensity: \"lt1\", scale: 0.34 }}\n />\n)\n\nexport const DomainsOverlayExample = () => (\n <LactateCurveChart\n {...baseProps}\n vo2max={{ intensity: 355 }}\n xAxisLabel=\"Running power (W)\"\n />\n)\n\nexport const RaceMarkerOverlayExample = () => (\n <LactateCurveChart\n {...baseProps}\n vo2max={{ intensity: 360 }}\n raceMarkers={[\n { intensity: 252, label: \"Marathon\", color: \"#0f766e\" },\n { intensity: 300, label: \"Half\", color: \"#0369a1\" },\n { intensity: 312, label: \"10k\", color: \"#7c3aed\" },\n { intensity: 329, label: \"5k\", color: \"#dc2626\" },\n { intensity: 342, label: \"3k\", color: \"#ea580c\" },\n ]}\n />\n)\n\nconst BrandMark = () => (\n <svg width=\"22\" height=\"22\" viewBox=\"0 0 22 22\" fill=\"none\">\n <circle cx=\"11\" cy=\"11\" r=\"10\" fill=\"#102a43\" />\n <path\n d=\"M6 13.8C8.7 13.8 10.8 12 11.8 8.8C12.7 12.7 14.8 15 17 15\"\n stroke=\"#f0b429\"\n strokeWidth=\"2.2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n)\n\nexport const FullyThemedExample = () => (\n <LactateCurveChart\n {...baseProps}\n vo2max={{ intensity: 362 }}\n title=\"Threshold Map\"\n description=\"A branded version for article embeds and product UI.\"\n xAxisLabel=\"Power (W)\"\n yAxisLabel=\"Lactate (mmol/L)\"\n icon={<BrandMark />}\n labels={{\n curve: \"Athlete curve\",\n zones: \"Training domains\",\n lt1: \"Aerobic threshold\",\n lt2: \"Critical threshold\",\n vo2max: \"VO2 ceiling\",\n }}\n raceMarkers={[\n { intensity: 255, label: \"M\", color: \"#166534\", radius: 6 },\n { intensity: 298, label: \"HM\", color: \"#0f766e\", radius: 6 },\n { intensity: 313, label: \"10k\", color: \"#1d4ed8\", radius: 6 },\n { intensity: 332, label: \"5k\", color: \"#b91c1c\", radius: 6 },\n ]}\n theme={{\n curveColor: \"#b91c1c\",\n thresholdColor: \"#102a43\",\n axisColor: \"#243b53\",\n gridColor: \"#d9e2ec\",\n labelColor: \"#102a43\",\n markerLabelColor: \"#102a43\",\n backgroundColor: \"#f8fbff\",\n fontFamily: \"Avenir Next, ui-sans-serif, system-ui, sans-serif\",\n }}\n zones={[\n {\n startIntensity: \"min\",\n endIntensity: \"lt1\",\n label: \"Easy / moderate\",\n color: \"#a7f3d0\",\n },\n {\n startIntensity: \"lt1\",\n endIntensity: \"lt2\",\n label: \"Threshold / heavy\",\n color: \"#fde68a\",\n },\n {\n startIntensity: \"lt2\",\n endIntensity: \"max\",\n label: \"VO2 / severe\",\n color: \"#fecaca\",\n },\n ]}\n />\n)\n"],"mappings":"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","names":["React","import_recharts","import_jsx_runtime","import_jsx_runtime"]}
@@ -0,0 +1,150 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode, SVGProps, CSSProperties } from 'react';
3
+
4
+ type AnchorPoint = {
5
+ intensity: number;
6
+ lactate: number;
7
+ };
8
+ type IntensityAnchor = {
9
+ intensity: number;
10
+ };
11
+ type XAxisCompression = {
12
+ endIntensity: number | "lt1" | "lt2" | "vo2max";
13
+ scale?: number;
14
+ };
15
+ type ZoneBoundary = number | "min" | "max" | "baseline" | "lt1" | "lt2" | "vo2max";
16
+ type ZoneConfig = {
17
+ startIntensity?: ZoneBoundary;
18
+ endIntensity?: ZoneBoundary;
19
+ label: string;
20
+ color: string;
21
+ opacity?: number;
22
+ };
23
+ type RaceMarker = {
24
+ intensity: number;
25
+ label: string;
26
+ color?: string;
27
+ radius?: number;
28
+ showLabel?: boolean;
29
+ tooltip?: string;
30
+ };
31
+ type ThresholdKey = "lt1" | "lt2" | "vo2max";
32
+ type ThresholdAnnotationConfig = Partial<Record<ThresholdKey, {
33
+ label?: string;
34
+ color?: string;
35
+ strokeDasharray?: string;
36
+ lineWidth?: number;
37
+ showGuide?: boolean;
38
+ showCircle?: boolean;
39
+ circleRadius?: number;
40
+ }>>;
41
+ type LactateCurveTheme = {
42
+ curveColor?: string;
43
+ axisColor?: string;
44
+ gridColor?: string;
45
+ labelColor?: string;
46
+ thresholdColor?: string;
47
+ fontFamily?: string;
48
+ backgroundColor?: string;
49
+ markerLabelColor?: string;
50
+ };
51
+ type LactateCurveChartProps = {
52
+ baselineLactate: number;
53
+ lt1: AnchorPoint;
54
+ lt2: AnchorPoint;
55
+ vo2max?: IntensityAnchor;
56
+ zones?: ZoneConfig[];
57
+ raceMarkers?: RaceMarker[];
58
+ xAxisLabel?: string;
59
+ yAxisLabel?: string;
60
+ xAxisPrecision?: number;
61
+ yAxisPrecision?: number;
62
+ showXAxisTicks?: boolean;
63
+ xAxisCompression?: XAxisCompression;
64
+ title?: string;
65
+ description?: string;
66
+ showThresholdLabels?: boolean;
67
+ showThresholdGuides?: boolean;
68
+ showThresholdCircles?: boolean;
69
+ showGrid?: boolean;
70
+ showLegend?: boolean;
71
+ thresholdAnnotations?: ThresholdAnnotationConfig;
72
+ theme?: LactateCurveTheme;
73
+ icon?: ReactNode | ((props: SVGProps<SVGSVGElement>) => ReactNode);
74
+ labels?: Partial<{
75
+ lt1: string;
76
+ lt2: string;
77
+ vo2max: string;
78
+ curve: string;
79
+ zones: string;
80
+ raceMarkers: string;
81
+ }>;
82
+ minIntensity?: number;
83
+ xAxisMax?: number;
84
+ maxIntensity?: number;
85
+ intensityStep?: number;
86
+ curveSamples?: number;
87
+ height?: number;
88
+ style?: CSSProperties;
89
+ className?: string;
90
+ };
91
+ type LactateCurvePoint = {
92
+ intensity: number;
93
+ lactate: number;
94
+ };
95
+ type GeneratedLactateCurve = {
96
+ points: LactateCurvePoint[];
97
+ anchors: {
98
+ baseline: LactateCurvePoint;
99
+ lt1: AnchorPoint;
100
+ lt2: AnchorPoint;
101
+ vo2max?: IntensityAnchor;
102
+ max: LactateCurvePoint;
103
+ };
104
+ domain: {
105
+ minIntensity: number;
106
+ maxIntensity: number;
107
+ minLactate: number;
108
+ maxLactate: number;
109
+ };
110
+ };
111
+ type ResolvedZone = {
112
+ label: string;
113
+ color: string;
114
+ opacity: number;
115
+ startIntensity: number;
116
+ endIntensity: number;
117
+ };
118
+ type ProjectedRaceMarker = RaceMarker & {
119
+ lactate: number;
120
+ };
121
+
122
+ declare const LactateCurveChart: ({ baselineLactate, lt1, lt2, vo2max, zones, raceMarkers, xAxisLabel, yAxisLabel, xAxisPrecision, yAxisPrecision, showXAxisTicks, xAxisCompression, title, description, showThresholdLabels, showThresholdGuides, showThresholdCircles, showGrid, showLegend, thresholdAnnotations, theme, icon, labels, minIntensity, xAxisMax, maxIntensity, intensityStep, curveSamples, height, style, className, }: LactateCurveChartProps) => react_jsx_runtime.JSX.Element;
123
+
124
+ type CurveInputs = {
125
+ baselineLactate: number;
126
+ lt1: AnchorPoint;
127
+ lt2: AnchorPoint;
128
+ vo2max?: IntensityAnchor;
129
+ minIntensity?: number;
130
+ maxIntensity?: number;
131
+ intensityStep?: number;
132
+ samples?: number;
133
+ };
134
+ declare const generateLactateCurve: ({ baselineLactate, lt1, lt2, vo2max, minIntensity, maxIntensity, intensityStep, samples, }: CurveInputs) => GeneratedLactateCurve;
135
+
136
+ type ResolveZonesInput = {
137
+ zones?: ZoneConfig[];
138
+ curve: GeneratedLactateCurve;
139
+ };
140
+ declare const resolveZones: ({ zones, curve, }: ResolveZonesInput) => ResolvedZone[];
141
+
142
+ declare const projectMarkerToCurve: (marker: RaceMarker, points: LactateCurvePoint[]) => ProjectedRaceMarker;
143
+
144
+ declare const BasicCurveExample: () => react_jsx_runtime.JSX.Element;
145
+ declare const CurveWithVo2maxExample: () => react_jsx_runtime.JSX.Element;
146
+ declare const DomainsOverlayExample: () => react_jsx_runtime.JSX.Element;
147
+ declare const RaceMarkerOverlayExample: () => react_jsx_runtime.JSX.Element;
148
+ declare const FullyThemedExample: () => react_jsx_runtime.JSX.Element;
149
+
150
+ export { type AnchorPoint, BasicCurveExample, CurveWithVo2maxExample, DomainsOverlayExample, FullyThemedExample, type GeneratedLactateCurve, type IntensityAnchor, LactateCurveChart, type LactateCurveChartProps, type LactateCurvePoint, type LactateCurveTheme, type ProjectedRaceMarker, type RaceMarker, RaceMarkerOverlayExample, type ResolvedZone, type ThresholdAnnotationConfig, type ThresholdKey, type XAxisCompression, type ZoneBoundary, type ZoneConfig, generateLactateCurve, projectMarkerToCurve, resolveZones };
@@ -0,0 +1,150 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode, SVGProps, CSSProperties } from 'react';
3
+
4
+ type AnchorPoint = {
5
+ intensity: number;
6
+ lactate: number;
7
+ };
8
+ type IntensityAnchor = {
9
+ intensity: number;
10
+ };
11
+ type XAxisCompression = {
12
+ endIntensity: number | "lt1" | "lt2" | "vo2max";
13
+ scale?: number;
14
+ };
15
+ type ZoneBoundary = number | "min" | "max" | "baseline" | "lt1" | "lt2" | "vo2max";
16
+ type ZoneConfig = {
17
+ startIntensity?: ZoneBoundary;
18
+ endIntensity?: ZoneBoundary;
19
+ label: string;
20
+ color: string;
21
+ opacity?: number;
22
+ };
23
+ type RaceMarker = {
24
+ intensity: number;
25
+ label: string;
26
+ color?: string;
27
+ radius?: number;
28
+ showLabel?: boolean;
29
+ tooltip?: string;
30
+ };
31
+ type ThresholdKey = "lt1" | "lt2" | "vo2max";
32
+ type ThresholdAnnotationConfig = Partial<Record<ThresholdKey, {
33
+ label?: string;
34
+ color?: string;
35
+ strokeDasharray?: string;
36
+ lineWidth?: number;
37
+ showGuide?: boolean;
38
+ showCircle?: boolean;
39
+ circleRadius?: number;
40
+ }>>;
41
+ type LactateCurveTheme = {
42
+ curveColor?: string;
43
+ axisColor?: string;
44
+ gridColor?: string;
45
+ labelColor?: string;
46
+ thresholdColor?: string;
47
+ fontFamily?: string;
48
+ backgroundColor?: string;
49
+ markerLabelColor?: string;
50
+ };
51
+ type LactateCurveChartProps = {
52
+ baselineLactate: number;
53
+ lt1: AnchorPoint;
54
+ lt2: AnchorPoint;
55
+ vo2max?: IntensityAnchor;
56
+ zones?: ZoneConfig[];
57
+ raceMarkers?: RaceMarker[];
58
+ xAxisLabel?: string;
59
+ yAxisLabel?: string;
60
+ xAxisPrecision?: number;
61
+ yAxisPrecision?: number;
62
+ showXAxisTicks?: boolean;
63
+ xAxisCompression?: XAxisCompression;
64
+ title?: string;
65
+ description?: string;
66
+ showThresholdLabels?: boolean;
67
+ showThresholdGuides?: boolean;
68
+ showThresholdCircles?: boolean;
69
+ showGrid?: boolean;
70
+ showLegend?: boolean;
71
+ thresholdAnnotations?: ThresholdAnnotationConfig;
72
+ theme?: LactateCurveTheme;
73
+ icon?: ReactNode | ((props: SVGProps<SVGSVGElement>) => ReactNode);
74
+ labels?: Partial<{
75
+ lt1: string;
76
+ lt2: string;
77
+ vo2max: string;
78
+ curve: string;
79
+ zones: string;
80
+ raceMarkers: string;
81
+ }>;
82
+ minIntensity?: number;
83
+ xAxisMax?: number;
84
+ maxIntensity?: number;
85
+ intensityStep?: number;
86
+ curveSamples?: number;
87
+ height?: number;
88
+ style?: CSSProperties;
89
+ className?: string;
90
+ };
91
+ type LactateCurvePoint = {
92
+ intensity: number;
93
+ lactate: number;
94
+ };
95
+ type GeneratedLactateCurve = {
96
+ points: LactateCurvePoint[];
97
+ anchors: {
98
+ baseline: LactateCurvePoint;
99
+ lt1: AnchorPoint;
100
+ lt2: AnchorPoint;
101
+ vo2max?: IntensityAnchor;
102
+ max: LactateCurvePoint;
103
+ };
104
+ domain: {
105
+ minIntensity: number;
106
+ maxIntensity: number;
107
+ minLactate: number;
108
+ maxLactate: number;
109
+ };
110
+ };
111
+ type ResolvedZone = {
112
+ label: string;
113
+ color: string;
114
+ opacity: number;
115
+ startIntensity: number;
116
+ endIntensity: number;
117
+ };
118
+ type ProjectedRaceMarker = RaceMarker & {
119
+ lactate: number;
120
+ };
121
+
122
+ declare const LactateCurveChart: ({ baselineLactate, lt1, lt2, vo2max, zones, raceMarkers, xAxisLabel, yAxisLabel, xAxisPrecision, yAxisPrecision, showXAxisTicks, xAxisCompression, title, description, showThresholdLabels, showThresholdGuides, showThresholdCircles, showGrid, showLegend, thresholdAnnotations, theme, icon, labels, minIntensity, xAxisMax, maxIntensity, intensityStep, curveSamples, height, style, className, }: LactateCurveChartProps) => react_jsx_runtime.JSX.Element;
123
+
124
+ type CurveInputs = {
125
+ baselineLactate: number;
126
+ lt1: AnchorPoint;
127
+ lt2: AnchorPoint;
128
+ vo2max?: IntensityAnchor;
129
+ minIntensity?: number;
130
+ maxIntensity?: number;
131
+ intensityStep?: number;
132
+ samples?: number;
133
+ };
134
+ declare const generateLactateCurve: ({ baselineLactate, lt1, lt2, vo2max, minIntensity, maxIntensity, intensityStep, samples, }: CurveInputs) => GeneratedLactateCurve;
135
+
136
+ type ResolveZonesInput = {
137
+ zones?: ZoneConfig[];
138
+ curve: GeneratedLactateCurve;
139
+ };
140
+ declare const resolveZones: ({ zones, curve, }: ResolveZonesInput) => ResolvedZone[];
141
+
142
+ declare const projectMarkerToCurve: (marker: RaceMarker, points: LactateCurvePoint[]) => ProjectedRaceMarker;
143
+
144
+ declare const BasicCurveExample: () => react_jsx_runtime.JSX.Element;
145
+ declare const CurveWithVo2maxExample: () => react_jsx_runtime.JSX.Element;
146
+ declare const DomainsOverlayExample: () => react_jsx_runtime.JSX.Element;
147
+ declare const RaceMarkerOverlayExample: () => react_jsx_runtime.JSX.Element;
148
+ declare const FullyThemedExample: () => react_jsx_runtime.JSX.Element;
149
+
150
+ export { type AnchorPoint, BasicCurveExample, CurveWithVo2maxExample, DomainsOverlayExample, FullyThemedExample, type GeneratedLactateCurve, type IntensityAnchor, LactateCurveChart, type LactateCurveChartProps, type LactateCurvePoint, type LactateCurveTheme, type ProjectedRaceMarker, type RaceMarker, RaceMarkerOverlayExample, type ResolvedZone, type ThresholdAnnotationConfig, type ThresholdKey, type XAxisCompression, type ZoneBoundary, type ZoneConfig, generateLactateCurve, projectMarkerToCurve, resolveZones };