Add contentFormatter for tooltips and error/warning bubbles, format i18n texts, add missing high-dose warning
This commit is contained in:
@@ -19,6 +19,7 @@ import { IconButtonWithTooltip } from './ui/icon-button-with-tooltip';
|
||||
import CollapsibleCardHeader from './ui/collapsible-card-header';
|
||||
import { Plus, Copy, Trash2, ArrowDownAZ, TrendingUp, TrendingDown, Utensils } from 'lucide-react';
|
||||
import type { DayGroup } from '../constants/defaults';
|
||||
import { formatText } from '../utils/contentFormatter';
|
||||
|
||||
interface DayScheduleProps {
|
||||
days: DayGroup[];
|
||||
@@ -243,6 +244,8 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
|
||||
|
||||
// Check for zero dose
|
||||
const isZeroDose = dose.ldx === '0' || dose.ldx === '0.0';
|
||||
// Check for dose > 70 mg
|
||||
const isHighDose = parseFloat(dose.ldx) > 70;
|
||||
|
||||
return (
|
||||
<div key={dose.id} className="space-y-2">
|
||||
@@ -252,8 +255,8 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
|
||||
onChange={(value) => onUpdateDose(day.id, dose.id, 'time', value)}
|
||||
required={true}
|
||||
warning={hasDuplicateTime}
|
||||
errorMessage={t('errorTimeRequired')}
|
||||
warningMessage={t('warningDuplicateTime')}
|
||||
errorMessage={formatText(t('errorTimeRequired'))}
|
||||
warningMessage={formatText(t('warningDuplicateTime'))}
|
||||
/>
|
||||
<FormNumericInput
|
||||
value={dose.ldx}
|
||||
@@ -263,9 +266,13 @@ const DaySchedule: React.FC<DayScheduleProps> = ({
|
||||
max={200}
|
||||
//unit="mg"
|
||||
required={true}
|
||||
warning={isZeroDose}
|
||||
errorMessage={t('errorNumberRequired')}
|
||||
warningMessage={t('warningZeroDose')}
|
||||
warning={isZeroDose || isHighDose}
|
||||
errorMessage={formatText(t('errorNumberRequired'))}
|
||||
warningMessage={
|
||||
isZeroDose ? formatText(t('warningZeroDose'))
|
||||
: isHighDose ? formatText(t('warningDoseAbove70mg'))
|
||||
: undefined // should not happen since warning is false
|
||||
}
|
||||
inputWidth="w-[72px]"
|
||||
/>
|
||||
<div className="flex gap-2 sm:contents">
|
||||
|
||||
@@ -22,6 +22,7 @@ import { FormSelect } from './ui/form-select';
|
||||
import CollapsibleCardHeader from './ui/collapsible-card-header';
|
||||
import { Info } from 'lucide-react';
|
||||
import { getDefaultState } from '../constants/defaults';
|
||||
import { formatContent, formatText } from '../utils/contentFormatter';
|
||||
|
||||
/**
|
||||
* Helper function to create translation interpolation values for defaults.
|
||||
@@ -76,45 +77,6 @@ const tWithDefaults = (translationFn: any, key: string, defaults: Record<string,
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to render tooltip content with inline source links.
|
||||
* Parses [link text](url) markdown-style syntax and renders as clickable links.
|
||||
* @example "See [this study](https://example.com)" → clickable link within tooltip
|
||||
*/
|
||||
const renderTooltipWithLinks = (text: string): React.ReactNode => {
|
||||
const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
|
||||
const parts: React.ReactNode[] = [];
|
||||
let lastIndex = 0;
|
||||
let match;
|
||||
|
||||
while ((match = linkRegex.exec(text)) !== null) {
|
||||
// Add text before link
|
||||
if (match.index > lastIndex) {
|
||||
parts.push(text.substring(lastIndex, match.index));
|
||||
}
|
||||
// Add link
|
||||
parts.push(
|
||||
<a
|
||||
key={`link-${match.index}`}
|
||||
href={match[2]}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline italic text-yellow-300 hover:text-yellow-200 cursor-pointer"
|
||||
>
|
||||
{match[1]}
|
||||
</a>
|
||||
);
|
||||
lastIndex = linkRegex.lastIndex;
|
||||
}
|
||||
|
||||
// Add remaining text
|
||||
if (lastIndex < text.length) {
|
||||
parts.push(text.substring(lastIndex));
|
||||
}
|
||||
|
||||
return parts.length > 0 ? parts : text;
|
||||
};
|
||||
|
||||
const Settings = ({
|
||||
pkParams,
|
||||
therapeuticRange,
|
||||
@@ -319,7 +281,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{tWithDefaults(t, 'showTemplateDayTooltip', defaultsForT)}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'showTemplateDayTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -348,7 +310,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{tWithDefaults(t, 'showDayReferenceLinesTooltip', defaultsForT)}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'showDayReferenceLinesTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -377,7 +339,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{tWithDefaults(t, 'showTherapeuticRangeLinesTooltip', defaultsForT)}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'showTherapeuticRangeLinesTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -400,7 +362,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{tWithDefaults(t, 'therapeuticRangeTooltip', defaultsForT)}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'therapeuticRangeTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -414,7 +376,7 @@ const Settings = ({
|
||||
placeholder={t('min')}
|
||||
required={true}
|
||||
error={!!therapeuticRangeError || !therapeuticRange.min}
|
||||
errorMessage={therapeuticRangeError || t('errorTherapeuticRangeMinRequired') || 'Minimum therapeutic range is required'}
|
||||
errorMessage={formatText(therapeuticRangeError || t('errorTherapeuticRangeMinRequired') || 'Minimum therapeutic range is required')}
|
||||
showResetButton={true}
|
||||
defaultValue={defaultsForT.therapeuticRangeMin}
|
||||
/>
|
||||
@@ -429,7 +391,7 @@ const Settings = ({
|
||||
unit="ng/ml"
|
||||
required={true}
|
||||
error={!!therapeuticRangeError || !therapeuticRange.max}
|
||||
errorMessage={therapeuticRangeError || t('errorTherapeuticRangeMaxRequired') || 'Maximum therapeutic range is required'}
|
||||
errorMessage={formatText(therapeuticRangeError || t('errorTherapeuticRangeMaxRequired') || 'Maximum therapeutic range is required')}
|
||||
showResetButton={true}
|
||||
defaultValue={defaultsForT.therapeuticRangeMax}
|
||||
/>
|
||||
@@ -456,7 +418,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{tWithDefaults(t, 'displayedDaysTooltip', defaultsForT)}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'displayedDaysTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -468,7 +430,7 @@ const Settings = ({
|
||||
max={parseInt(simulationDays, 10) || 3}
|
||||
unit={t('unitDays')}
|
||||
required={true}
|
||||
errorMessage={t('errorNumberRequired')}
|
||||
errorMessage={formatText(t('errorNumberRequired'))}
|
||||
showResetButton={true}
|
||||
defaultValue={defaultsForT.displayedDays}
|
||||
/>
|
||||
@@ -490,7 +452,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{tWithDefaults(t, 'yAxisRangeTooltip', defaultsForT)}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'yAxisRangeTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -506,7 +468,7 @@ const Settings = ({
|
||||
showResetButton={true}
|
||||
defaultValue={defaultsForT.yAxisMin}
|
||||
warning={!!yAxisRangeError}
|
||||
warningMessage={yAxisRangeError}
|
||||
warningMessage={formatText(yAxisRangeError)}
|
||||
/>
|
||||
<span className="text-muted-foreground">-</span>
|
||||
<FormNumericInput
|
||||
@@ -521,7 +483,7 @@ const Settings = ({
|
||||
showResetButton={true}
|
||||
defaultValue={defaultsForT.yAxisMax}
|
||||
warning={!!yAxisRangeError}
|
||||
warningMessage={yAxisRangeError}
|
||||
warningMessage={formatText(yAxisRangeError)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -600,7 +562,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{tWithDefaults(t, 'simulationDurationTooltip', defaultsForT)}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'simulationDurationTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -612,7 +574,7 @@ const Settings = ({
|
||||
max={7}
|
||||
unit={t('unitDays')}
|
||||
required={true}
|
||||
errorMessage={t('errorNumberRequired')}
|
||||
errorMessage={formatText(t('errorNumberRequired'))}
|
||||
showResetButton={true}
|
||||
defaultValue={defaultsForT.simulationDays}
|
||||
/>
|
||||
@@ -652,7 +614,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{tWithDefaults(t, 'steadyStateDaysTooltip', defaultsForT)}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'steadyStateDaysTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -702,7 +664,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'halfLifeTooltip', defaultsForT))}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'halfLifeTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -716,8 +678,8 @@ const Settings = ({
|
||||
required={true}
|
||||
warning={eliminationWarning && !eliminationExtreme}
|
||||
error={eliminationExtreme}
|
||||
warningMessage={t('warningEliminationOutOfRange')}
|
||||
errorMessage={t('errorEliminationHalfLifeRequired')}
|
||||
warningMessage={formatText(t('warningEliminationOutOfRange'))}
|
||||
errorMessage={formatText(t('errorEliminationHalfLifeRequired'))}
|
||||
showResetButton={true}
|
||||
defaultValue={defaultsForT.damphHalfLife}
|
||||
/>
|
||||
@@ -742,7 +704,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{tWithDefaults(t, 'conversionHalfLifeTooltip', defaultsForT)}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'conversionHalfLifeTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -755,8 +717,8 @@ const Settings = ({
|
||||
unit="h"
|
||||
required={true}
|
||||
warning={conversionWarning}
|
||||
warningMessage={t('warningConversionOutOfRange')}
|
||||
errorMessage={t('errorConversionHalfLifeRequired')}
|
||||
warningMessage={formatText(t('warningConversionOutOfRange'))}
|
||||
errorMessage={formatText(t('errorConversionHalfLifeRequired'))}
|
||||
showResetButton={true}
|
||||
defaultValue={defaultsForT.ldxHalfLife}
|
||||
/>
|
||||
@@ -778,7 +740,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{tWithDefaults(t, 'absorptionHalfLifeTooltip', defaultsForT)}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'absorptionHalfLifeTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -791,8 +753,8 @@ const Settings = ({
|
||||
unit="h"
|
||||
required={true}
|
||||
warning={absorptionWarning}
|
||||
warningMessage={t('warningAbsorptionOutOfRange')}
|
||||
errorMessage={t('errorAbsorptionRateRequired')}
|
||||
warningMessage={formatText(t('warningAbsorptionOutOfRange'))}
|
||||
errorMessage={formatText(t('errorAbsorptionRateRequired'))}
|
||||
showResetButton={true}
|
||||
defaultValue={defaultsForT.ldxAbsorptionHalfLife}
|
||||
/>
|
||||
@@ -831,7 +793,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'standardVdTooltip', {
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'standardVdTooltip', {
|
||||
...defaultsForT,
|
||||
standardVdValue: pkParams.advanced.standardVd?.preset === 'adult' ? '377' : pkParams.advanced.standardVd?.preset === 'child' ? '175' : pkParams.advanced.standardVd?.customValue || '377',
|
||||
standardVdPreset: t(`standardVdPreset${pkParams.advanced.standardVd?.preset?.charAt(0).toUpperCase()}${pkParams.advanced.standardVd?.preset?.slice(1)}` || 'standardVdPresetAdult')
|
||||
@@ -891,7 +853,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'bodyWeightTooltip', defaultsForT))}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'bodyWeightTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -929,7 +891,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'tmaxDelayTooltip', defaultsForT))}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'tmaxDelayTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -967,7 +929,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{tWithDefaults(t, 'urinePHTooltip', defaultsForT)}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'urinePHTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -1009,7 +971,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'ageGroupTooltip', defaultsForT))}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'ageGroupTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -1061,7 +1023,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'renalFunctionTooltip', defaultsForT))}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'renalFunctionTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -1111,7 +1073,7 @@ const Settings = ({
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={tooltipSide}>
|
||||
<p className="text-xs max-w-xs">{renderTooltipWithLinks(tWithDefaults(t, 'oralBioavailabilityTooltip', defaultsForT))}</p>
|
||||
<p className="text-xs max-w-xs">{formatContent(tWithDefaults(t, 'oralBioavailabilityTooltip', defaultsForT))}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
@@ -30,8 +30,8 @@ interface NumericInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElem
|
||||
error?: boolean
|
||||
warning?: boolean
|
||||
required?: boolean
|
||||
errorMessage?: string
|
||||
warningMessage?: string
|
||||
errorMessage?: React.ReactNode
|
||||
warningMessage?: React.ReactNode
|
||||
inputWidth?: string // Custom width for the input field (e.g., 'w-16', 'w-20')
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ interface TimeInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement
|
||||
error?: boolean
|
||||
warning?: boolean
|
||||
required?: boolean
|
||||
errorMessage?: string
|
||||
warningMessage?: string
|
||||
errorMessage?: React.ReactNode
|
||||
warningMessage?: React.ReactNode
|
||||
}
|
||||
|
||||
const FormTimeInput = React.forwardRef<HTMLInputElement, TimeInputProps>(
|
||||
|
||||
@@ -67,7 +67,7 @@ export const de = {
|
||||
refLineMax: "Max",
|
||||
pinChart: "Diagramm oben fixieren",
|
||||
unpinChart: "Diagramm freigeben",
|
||||
stickyChartTooltip: "Diagramm beim Scrollen durch die Einstellungen sichtbar halten, um Änderungen in Echtzeit zu sehen. Standard: aus.",
|
||||
stickyChartTooltip: "Diagramm beim Scrollen durch die Einstellungen sichtbar halten, um Änderungen in Echtzeit zu sehen.\\n\\n__Standard:__ **aus**",
|
||||
chartViewDamphTooltip: "Nur den aktiven Metaboliten (d-Amphetamin) im Konzentrationsverlauf anzeigen",
|
||||
chartViewLdxTooltip: "Nur das Prodrug (Lisdexamfetamin) im Konzentrationsverlauf anzeigen",
|
||||
chartViewBothTooltip: "Sowohl d-Amphetamin als auch Lisdexamfetamin gemeinsam anzeigen",
|
||||
@@ -79,13 +79,13 @@ export const de = {
|
||||
advancedSettings: "Erweiterte Einstellungen",
|
||||
advancedSettingsWarning: "⚠️ Diese Parameter beeinflussen die Simulationsgenauigkeit und können von Bevölkerungsdurchschnitten abweichen. Nur anpassen, wenn spezifische klinische Daten oder Forschungsreferenzen vorliegen.",
|
||||
standardVolumeOfDistribution: "Verteilungsvolumen (Vd)",
|
||||
standardVdTooltip: "Definiert wie sich der Wirkstoff im Körper verteilt. Erwachsene: 377L (Roberts 2015), Kinder: ~150-200L. Gewichtsbasierte Skalierung: ~5,4 L/kg (für Erwachsene >18 Jahre basierend auf [Populations-Pharmakokinetik](https://pmc.ncbi.nlm.nih.gov/articles/PMC5572767/)). Beeinflusst alle Konzentrationsberechnungen. Nur für pädiatrische oder spezialisierte Simulationen ändern. Standard: {{standardVdValue}}L ({{standardVdPreset}}).",
|
||||
standardVdTooltip: "Definiert wie sich der Wirkstoff im Körper verteilt.\\n\\n__Voreinstellungen:__\\n• Erwachsene: 377L (Roberts 2015)\\n• Kinder: ~150-200L\\n• Gewichtsbasiert: ~5,4 L/kg (für Erwachsene >18 Jahre basierend auf [Populations-Pharmakokinetik](https://pmc.ncbi.nlm.nih.gov/articles/PMC5572767/))\\n\\nBeeinflusst alle Konzentrationsberechnungen. Nur für pädiatrische oder spezialisierte Simulationen ändern.\\n\\n__Standard:__ **{{standardVdValue}}L** ({{standardVdPreset}})",
|
||||
standardVdPresetAdult: "Erwachsene (377L)",
|
||||
standardVdPresetChild: "Kinder (175L)",
|
||||
standardVdPresetCustom: "Benutzerdefiniert",
|
||||
standardVdPresetWeightBased: "Gewichtsbasiert (~5,4 L/kg)",
|
||||
customVdValue: "Benutzerdefiniertes Vd (L)",
|
||||
weightBasedVdInfo: "Gewichtsbasiertes Vd passt Plasmakonzentrationen basierend auf Körpergewicht an (~5,4 L/kg). Leichtere Personen → höhere Spitzen, schwerere → niedrigere Spitzen. Diese Option ist für Erwachsene (>18 Jahre) basierend auf der Populations-PK-Studie vorgesehen. Für pädiatrische Patienten verwenden Sie die Voreinstellung 'Kinder'.",
|
||||
weightBasedVdInfo: "Gewichtsbasiertes Vd passt Plasmakonzentrationen basierend auf Körpergewicht an (~5,4 L/kg). Leichtere Personen → höhere Spitzen, schwerere → niedrigere Spitzen.\\n\\nDiese Option ist für Erwachsene (>18 Jahre) basierend auf der Populations-PK-Studie vorgesehen.\\n\\nFür pädiatrische Patienten verwenden Sie die Voreinstellung 'Kinder'.",
|
||||
xAxisTimeFormat: "Zeitformat",
|
||||
xAxisFormatContinuous: "Fortlaufend",
|
||||
xAxisFormatContinuousDesc: "Endlose Sequenz (0h, 6h, 12h...)",
|
||||
@@ -94,75 +94,75 @@ export const de = {
|
||||
xAxisFormat12h: "Tageszeit (12h AM/PM)",
|
||||
xAxisFormat12hDesc: "Wiederholend 12h Zyklus im AM/PM Format",
|
||||
showTemplateDayInChart: "Regulären Plan kontinuierlich anzeigen",
|
||||
showTemplateDayTooltip: "Medikationsplan als Referenz-Overlay jederzeit anzeigen (Standard: aktiviert).",
|
||||
showTemplateDayTooltip: "Medikationsplan als Referenz-Overlay jederzeit anzeigen.\\n\\n__Standard:__ **aktiviert**",
|
||||
simulationSettings: "Simulations-Einstellungen",
|
||||
|
||||
showDayReferenceLines: "Tagestrenner anzeigen",
|
||||
showDayReferenceLinesTooltip: "Vertikale Linien und Statusanzeigen zwischen Tagen anzeigen (Standard: aktiviert).",
|
||||
showDayReferenceLinesTooltip: "Vertikale Linien und Statusanzeigen zwischen Tagen anzeigen.\\n\\n__Standard:__ **aktiviert**",
|
||||
showTherapeuticRangeLines: "Therapeutischen Bereich anzeigen ",
|
||||
showTherapeuticRangeLinesTooltip: "Horizontale Referenzlinien für therapeutisches Min/Max anzeigen (Standard: aktiviert).",
|
||||
showTherapeuticRangeLinesTooltip: "Horizontale Referenzlinien für therapeutisches Min/Max anzeigen.\\n\\n__Standard:__ **aktiviert**",
|
||||
simulationDuration: "Simulationsdauer",
|
||||
simulationDurationTooltip: "Anzahl der zu simulierenden Tage. Längere Zeiträume zeigen Steady-State. Standard: {{simulationDays}} Tage.",
|
||||
simulationDurationTooltip: "Anzahl der zu simulierenden Tage. Längere Zeiträume zeigen Steady-State.\\n\\n__Standard:__ **{{simulationDays}} Tage**",
|
||||
displayedDays: "Sichtbare Tage (im Fokus)",
|
||||
displayedDaysTooltip: "Wie viele Tage auf einmal angezeigt werden. Kleinere Werte zoomen in Details. Standard: {{displayedDays}} Tag(e).",
|
||||
displayedDaysTooltip: "Wie viele Tage auf einmal angezeigt werden. Kleinere Werte zoomen in Details.\\n\\n__Standard:__ **{{displayedDays}} Tag(e)**",
|
||||
yAxisRange: "Y-Achsen-Bereich (Konzentrations-Zoom)",
|
||||
yAxisRangeTooltip: "Vertikale Achse manuell festlegen (Konzentrationsskala). Leer lassen für automatische Anpassung. Standard: auto.",
|
||||
yAxisRangeTooltip: "Vertikale Achse manuell festlegen (Konzentrationsskala). Leer lassen für automatische Anpassung.\\n\\n__Standard:__ **auto**",
|
||||
yAxisRangeAutoButton: "A",
|
||||
yAxisRangeAutoButtonTitle: "Bereich automatisch anhand des Datenbereichs bestimmen",
|
||||
auto: "Auto",
|
||||
therapeuticRange: "Therapeutischer Bereich (d-Amphetamin)",
|
||||
therapeuticRangeTooltip: "Personalisierte Konzentrationsziele basierend auf DEINER individuellen Reaktion. Setze diese nachdem du beobachtet hast, welche Werte Symptomkontrolle vs. Nebenwirkungen bieten. Referenzbereiche (stark variabel): Erwachsene ~10-80 ng/mL, Kinder ~20-120 ng/mL (aufgrund geringeren Körpergewichts/Vd). Leer lassen wenn unsicher. Konsultiere deinen Arzt.",
|
||||
therapeuticRangeTooltip: "Personalisierte Konzentrationsziele basierend auf DEINER individuellen Reaktion. Setze diese nachdem du beobachtet hast, welche Werte Symptomkontrolle vs. Nebenwirkungen bieten.\\n\\n**Referenzbereiche** (stark variabel):\\n• __Erwachsene:__ **~10-80 ng/mL**\\n• __Kinder:__ **~20-120 ng/mL** (aufgrund geringeren Körpergewichts/Vd)\\n\\nLeer lassen wenn unsicher.\\n\\n***Konsultiere deinen Arzt.***",
|
||||
dAmphetamineParameters: "d-Amphetamin Parameter",
|
||||
halfLife: "Eliminations-Halbwertszeit",
|
||||
halfLifeTooltip: "Zeit bis der Körper die Hälfte des d-Amphetamins aus dem Blut ausscheidet. Beeinflusst durch Urin-pH: sauer (<6) → 7-9h, neutral (6-7,5) → 10-12h, alkalisch (>7,5) → 13-15h. Siehe [therapeutische Referenzbereiche](https://www.thieme-connect.com/products/ejournals/pdf/10.1055/a-2689-4911.pdf). Standard: {{damphHalfLife}}h.",
|
||||
halfLifeTooltip: "Zeit bis der Körper die Hälfte des d-Amphetamins aus dem Blut ausscheidet.\\n\\n__Beeinflusst durch Urin-pH:__\\n• __Sauer (<6):__ **7-9h**\\n• __Neutral (6-7,5)__ → **10-12h**\\n• __Alkalisch (>7,5)__ → **13-15h**\\n\\nSiehe [therapeutische Referenzbereiche](https://www.thieme-connect.com/products/ejournals/pdf/10.1055/a-2689-4911.pdf).\\n\\n__Standard:__ **{{damphHalfLife}}h**",
|
||||
lisdexamfetamineParameters: "Lisdexamfetamin (LDX) Parameter",
|
||||
conversionHalfLife: "LDX→d-Amph Umwandlungs-Halbwertszeit",
|
||||
conversionHalfLifeTooltip: "Zeit bis rote Blutkörperchen die Hälfte des inaktiven LDX-Prodrugs in aktives d-Amphetamin umwandeln. Typisch: 0,7-1,2h. Standard: {{ldxHalfLife}}h.",
|
||||
conversionHalfLifeTooltip: "Zeit bis rote Blutkörperchen die Hälfte des inaktiven LDX-Prodrugs in aktives d-Amphetamin umwandeln.\\n\\nTypischer Bereich: **0,7-1,2h**.\\n\\n__Standard:__ **{{ldxHalfLife}}h**",
|
||||
absorptionHalfLife: "Absorptions-Halbwertszeit",
|
||||
absorptionHalfLifeTooltip: "Zeit bis der Darm die Hälfte des LDX vom Magen ins Blut aufnimmt. Durch Nahrung verzögert (~1h Verschiebung). Typisch: 0,7-1,2h. Standard: {{ldxAbsorptionHalfLife}}h.",
|
||||
absorptionHalfLifeTooltip: "Zeit bis der Darm die Hälfte des LDX vom Magen ins Blut aufnimmt.\\n\\nDurch Nahrung verzögert (**~1h Verschiebung**).\\n\\nTypischer Bereich: **0,7-1,2h**.\\n\\n__Standard:__ **{{ldxAbsorptionHalfLife}}h**",
|
||||
faster: "(schneller >)",
|
||||
|
||||
// Advanced Settings
|
||||
weightBasedVdScaling: "Gewichtsbasiertes Verteilungsvolumen",
|
||||
weightBasedVdTooltip: "Passt Plasmakonzentrationen basierend auf Körpergewicht an (proportional zu ~5,4 L/kg). Leichtere → höhere Spitzen, schwerere → niedrigere. Bei Deaktivierung: 70 kg Erwachsener.",
|
||||
weightBasedVdTooltip: "Passt Plasmakonzentrationen basierend auf Körpergewicht an (proportional zu **~5,4 L/kg**).\\n\\n__Effekte:__\\n• Leichtere Personen → ***höhere*** Konzentrationsspitzen\\n• __Schwerere Personen__ → ***niedrigere*** Konzentrationsspitzen\\n\\n__Bei Deaktivierung:__ **70 kg Erwachsene Person**",
|
||||
bodyWeight: "Körpergewicht",
|
||||
bodyWeightTooltip: "Dein Körpergewicht für Konzentrationsanpassung. Verwendet zur Berechnung des Verteilungsvolumens (Vd = Gewicht × 5,4). Siehe [Populations-Pharmakokinetik](https://pmc.ncbi.nlm.nih.gov/articles/PMC5572767/). Standard: {{bodyWeight}} kg.",
|
||||
bodyWeightTooltip: "Dein Körpergewicht für Konzentrationsanpassung. Verwendet zur Berechnung des Verteilungsvolumens:\\n\\n**Vd = Gewicht × 5,4**\\n\\nSiehe [Populations-Pharmakokinetik](https://pmc.ncbi.nlm.nih.gov/articles/PMC5572767/).\\n\\n__Standard:__ **{{bodyWeight}} kg**",
|
||||
bodyWeightUnit: "kg",
|
||||
|
||||
foodEffectEnabled: "Mit Mahlzeit eingenommen",
|
||||
foodEffectDelay: "Nahrungseffekt-Verzögerung",
|
||||
foodEffectTooltip: "Fettreiche Mahlzeiten verzögern die Absorption ohne die Gesamtaufnahme zu ändern. Verlangsamt Wirkungseintritt (~1h Verzögerung). Hinweis: Die in dieser Studie verwendete fettreiche Mahlzeit bestand aus 1 englischem Muffin mit Butter, 1 Spiegelei, 1 Scheibe amerikanischem Käse, 1 Scheibe kanadischem Speck, 57 g Bratkartoffeln und 240 ml Vollmilch. Deaktiviert nimmt nüchternen Zustand an.",
|
||||
foodEffectTooltip: "Fettreiche Mahlzeiten verzögern die Absorption **ohne die Gesamtaufnahme zu ändern**.\\n\\nVerlangsamt Wirkungseintritt um **~1 Stunde**.\\n\\nDeaktiviert nimmt nüchternen Zustand an.",
|
||||
tmaxDelay: "Absorptions-Verzögerung",
|
||||
tmaxDelayTooltip: "Zeitverzögerung bei Einnahme mit fettreicher Mahlzeit. Wird durch Einzel-Dosis Nahrungsschalter (🍴 Symbol) im Zeitplan angewendet. Forschung zeigt ~1h Verzögerung ohne Spitzenreduktion. Siehe [Studie](https://pmc.ncbi.nlm.nih.gov/articles/PMC4823324/). Standard: {{tmaxDelay}}h.",
|
||||
tmaxDelayTooltip: "Zeitverzögerung bei Einnahme mit **fettreicher Mahlzeit**. Wird durch Einzel-Dosis Nahrungsschalter (🍴 Symbol) im Zeitplan angewendet.\\n\\nForschung zeigt ~1h Verzögerung ohne Spitzenreduktion. Siehe [Studie](https://pmc.ncbi.nlm.nih.gov/articles/PMC4823324/).\\n\\n__Hinweis:__ Die in dieser Studie verwendete fettreiche Mahlzeit bestand aus 1 englischem Muffin mit Butter, 1 Spiegelei, 1 Scheibe amerikanischem Käse, 1 Scheibe kanadischem Speck, 57 g Bratkartoffeln und 240 ml Vollmilch.\\n\\n__Standard:__ **{{tmaxDelay}}h**",
|
||||
tmaxDelayUnit: "h",
|
||||
|
||||
urinePHTendency: "Urin-pH-Effekte",
|
||||
urinePHTooltip: "Urin-pH beeinflusst Nierenrückresorption von Amphetamin. Saurer Urin (<6) erhöht Elimination (schnellere Ausscheidung, t½ ~7-9h). Normaler pH (6-7,5) hält Basis-Elimination (~11h). Alkalischer Urin (>7,5) reduziert Elimination (langsamere Ausscheidung, t½ ~13-15h). Typischer Bereich: 5,5-8,0. Standard: Normaler pH (6-7,5).",
|
||||
urinePHTooltip: "Urin-pH beeinflusst Nierenrückresorption von Amphetamin.\\n\\n__Effekte auf die Elimination:__\\n• __Sauer__ (<6): ***Erhöhte*** Elimination (***schnellere*** Ausscheidung), **t½ ~7-9h**\\n• __Normal__ (6-7,5): ***Basis***-Elimination (**t½ ~11h**)\\n• __Alkalisch__ (>7,5) → ***Reduzierte*** Elimination (***langsamere*** Ausscheidung), **t½ ~13-15h**\\n\\nTypischer Bereich: 5,5-8,0.\\n\\n__Standard:__ **Normaler pH** (6-7,5)",
|
||||
urinePHMode: "pH-Effekt",
|
||||
urinePHModeNormal: "Normal (pH 6-7,5, t½ 11h)",
|
||||
urinePHModeAcidic: "Sauer (pH <6, schnellere Elimination)",
|
||||
urinePHModeAlkaline: "Alkalisch (pH >7,5, langsamere Elimination)",
|
||||
urinePHValue: "pH-Wert",
|
||||
urinePHValueTooltip: "Dein typischer Urin-pH (sauer=schnellere Ausscheidung, alkalisch=langsamer). Standard: {{phTendency}}. Bereich: 5,5-8,0.",
|
||||
urinePHValueTooltip: "Dein typischer Urin-pH (sauer=schnellere Ausscheidung, alkalisch=langsamer).\\n\\nBereich: **5,5-8,0**.\\n\\n__Standard:__ **{{phTendency}}**",
|
||||
phValue: "pH-Wert",
|
||||
phUnit: "(5,5-8,0)",
|
||||
|
||||
oralBioavailability: "Orale Bioverfügbarkeit",
|
||||
oralBioavailabilityTooltip: "Anteil der LDX-Dosis, der ins Blut gelangt. Siehe [Bioverfügbarkeitsstudie](https://www.frontiersin.org/journals/pharmacology/articles/10.3389/fphar.2022.881198/full) (FDA-Label: 96,4%). Selten Anpassung nötig, außer bei dokumentierten Absorptionsproblemen. Standard: {{fOral}} ({{fOralPercent}}%).",
|
||||
oralBioavailabilityTooltip: "Anteil der LDX-Dosis, der ins Blut gelangt.\\n\\nSiehe [Bioverfügbarkeitsstudie](https://www.frontiersin.org/journals/pharmacology/articles/10.3389/fphar.2022.881198/full) — **FDA-Label: 96,4%**.\\n\\nSelten Anpassung nötig, außer bei dokumentierten Absorptionsproblemen.\\n\\n__Standard:__ **{{fOral}} ({{fOralPercent}}%)**",
|
||||
|
||||
steadyStateDays: "Medikationshistorie",
|
||||
steadyStateDaysTooltip: "Anzahl vorheriger Tage stabiler Medikamentendosis zur Simulation der Akkumulation/Steady-State. 0 setzen für \"erster Tag ohne Vorgeschichte.\" Standard: {{steadyStateDays}} Tage. Max: 7.",
|
||||
steadyStateDaysTooltip: "Anzahl vorheriger Tage stabiler Medikamentendosis zur Simulation der Akkumulation/Steady-State.\\n\\nWird diese Option ausgeschaltet, beginnt die Simulation an Tag eins ohne vorherige Medikationshistorie. Dasselbe gilt für den Wert **0**.\\n\\nMax: **7 Tage**.\\n\\n__Standard:__ **{{steadyStateDays}} Tage**",
|
||||
|
||||
// Age-specific pharmacokinetics
|
||||
ageGroup: "Altersgruppe",
|
||||
ageGroupTooltip: "Pädiatrische Personen (6-12 J.) zeigen schnellere d-Amphetamin-Elimination (t½ ~9h) verglichen mit Erwachsenen (~11h) aufgrund höherer gewichtsnormalisierter Stoffwechselrate. Siehe [Forschungsdokument](https://git.11001001.org/cbaoth/med-plan-assistant/src/branch/main/docs/2026-01-17_AI-Reseach_SimulatingLDXandD-AmphetaminePlasmaLevels.md#52-pediatric-vs-adult-modeling) Abschnitt 5.2. 'Benutzerdefiniert' wählen, um manuell konfigurierte Halbwertszeit zu verwenden. Standard: Erwachsener.",
|
||||
ageGroupTooltip: "Pädiatrische Personen (6-12 J.) zeigen **schnellere d-Amphetamin-Elimination** (t½ ~9h) verglichen mit Erwachsenen (~11h) aufgrund höherer gewichtsnormalisierter Stoffwechselrate.\\n\\nSiehe [Forschungsdokument](https://git.11001001.org/cbaoth/med-plan-assistant/src/branch/main/docs/2026-01-17_AI-Reseach_SimulatingLDXandD-AmphetaminePlasmaLevels.md#52-pediatric-vs-adult-modeling) Abschnitt 5.2.\\n\\n'Benutzerdefiniert' wählen, um manuell konfigurierte Halbwertszeit zu verwenden.\\n\\n__Standard:__ **Erwachsener**",
|
||||
ageGroupAdult: "Erwachsener (t½ 11h)",
|
||||
ageGroupChild: "Kind 6-12 J. (t½ 9h)",
|
||||
ageGroupCustom: "Benutzerdefiniert (manuelle t½)",
|
||||
|
||||
// Renal function effects
|
||||
renalFunction: "Niereninsuffizienz",
|
||||
renalFunctionTooltip: "Schwere Niereninsuffizienz verlängert d-Amphetamin-Halbwertszeit um ~50% (von 11h auf 16,5h). FDA-Label empfiehlt Dosierungsobergrenzen: 50mg bei schwerer Insuffizienz, 30mg bei Nierenversagen (ESRD). Siehe [FDA-Label Abschnitt 8.6](https://www.accessdata.fda.gov/drugsatfda_docs/label/2017/021977s049lbl.pdf) und [Forschungsdokument](https://git.11001001.org/cbaoth/med-plan-assistant/src/branch/main/docs/2026-01-17_AI-Reseach_SimulatingLDXandD-AmphetaminePlasmaLevels.md#82-renal-function) Abschnitt 8.2. Standard: deaktiviert.",
|
||||
renalFunctionTooltip: "Schwere Niereninsuffizienz verlängert d-Amphetamin-Halbwertszeit um **~50%** (von 11h auf 16,5h).\\n\\n__FDA-Label Dosierungsobergrenzen:__\\n• __Schwere Insuffizienz:__ **50mg**\\n• __Nierenversagen (ESRD):__ **30mg**\\n\\nSiehe [FDA-Label Abschnitt 8.6](https://www.accessdata.fda.gov/drugsatfda_docs/label/2017/021977s049lbl.pdf) und [Forschungsdokument](https://git.11001001.org/cbaoth/med-plan-assistant/src/branch/main/docs/2026-01-17_AI-Reseach_SimulatingLDXandD-AmphetaminePlasmaLevels.md#82-renal-function) Abschnitt 8.2.\\n\\n__Standard:__ **deaktiviert**",
|
||||
renalFunctionSeverity: "Schweregrad der Insuffizienz",
|
||||
renalFunctionNormal: "Normal (keine Anpassung)",
|
||||
renalFunctionMild: "Leicht (keine Anpassung)",
|
||||
|
||||
@@ -67,7 +67,7 @@ export const en = {
|
||||
refLineMax: "Max",
|
||||
pinChart: "Pin chart to top",
|
||||
unpinChart: "Unpin chart",
|
||||
stickyChartTooltip: "Keep chart visible while scrolling through settings for real-time feedback. Default: off.",
|
||||
stickyChartTooltip: "Keep chart visible while scrolling through settings for real-time feedback.\\n\\n__Default:__ **off**",
|
||||
chartViewDamphTooltip: "Show only the active metabolite (d-Amphetamine) concentration profile",
|
||||
chartViewLdxTooltip: "Show only the prodrug (Lisdexamfetamine) concentration profile",
|
||||
chartViewBothTooltip: "Show both d-Amphetamine and Lisdexamfetamine profiles together",
|
||||
@@ -78,13 +78,13 @@ export const en = {
|
||||
advancedSettings: "Advanced Settings",
|
||||
advancedSettingsWarning: "⚠️ These parameters affect simulation accuracy and may deviate from population averages. Adjust only if you have specific clinical data or research references.",
|
||||
standardVolumeOfDistribution: "Volume of Distribution (Vd)",
|
||||
standardVdTooltip: "Defines how drug disperses in body. Adult: 377L (Roberts 2015), Child: ~150-200L. Weight-based scaling: ~5.4 L/kg (intended for adults >18 years based on [population PK analysis](https://pmc.ncbi.nlm.nih.gov/articles/PMC5572767/)). Affects all concentration calculations. Change only for pediatric or specialized simulations. Default: {{standardVdValue}}L ({{standardVdPreset}}).",
|
||||
standardVdTooltip: "Defines how drug disperses in body.\\n\\n__Presets:__\\n• __Adult:__ **377L** (Roberts 2015)\\n• __Child:__ **~150-200L**\\n• __Weight-based:__ **~5.4 L/kg** (intended for adults >18 years based on [population PK analysis](https://pmc.ncbi.nlm.nih.gov/articles/PMC5572767/))\\n\\nAffects all concentration calculations. Change only for pediatric or specialized simulations.\\n\\n__Default:__ **{{standardVdValue}}L** ({{standardVdPreset}})",
|
||||
standardVdPresetAdult: "Adult (377L)",
|
||||
standardVdPresetChild: "Child (175L)",
|
||||
standardVdPresetCustom: "Custom",
|
||||
standardVdPresetWeightBased: "Weight-Based (~5.4 L/kg)",
|
||||
customVdValue: "Custom Vd (L)",
|
||||
weightBasedVdInfo: "Weight-based Vd adjusts plasma concentrations based on body weight (~5.4 L/kg). Lighter persons → higher peaks, heavier → lower peaks. This option is intended for adults (>18 years) based on the population PK study. For pediatric patients, use the 'Child' preset.",
|
||||
weightBasedVdInfo: "Weight-based Vd adjusts plasma concentrations based on body weight (~5.4 L/kg).\\n\\nLighter persons → higher peaks, heavier → lower peaks.\\n\\nThis option is intended for adults (>18 years) based on the population PK study. For pediatric patients, use the 'Child' preset.",
|
||||
xAxisTimeFormat: "Time Format",
|
||||
xAxisFormatContinuous: "Continuous",
|
||||
xAxisFormatContinuousDesc: "Endless sequence (0h, 6h, 12h...)",
|
||||
@@ -93,74 +93,74 @@ export const en = {
|
||||
xAxisFormat12h: "Time of Day (12h AM/PM)",
|
||||
xAxisFormat12hDesc: "Repeating 12h cycle in AM/PM format",
|
||||
showTemplateDayInChart: "Continuously Show Regular Plan",
|
||||
showTemplateDayTooltip: "Display the regular medication plan as reference overlay at all times (default: enabled).",
|
||||
showTemplateDayTooltip: "Display the regular medication plan as reference overlay at all times.\\n\\n__Default:__ **enabled**",
|
||||
simulationSettings: "Simulation Settings",
|
||||
showDayReferenceLines: "Show Day Separators",
|
||||
showDayReferenceLinesTooltip: "Display vertical lines and status indicators separating days (default: enabled).",
|
||||
showDayReferenceLinesTooltip: "Display vertical lines and status indicators separating days.\\n\\n__Default:__ **enabled**",
|
||||
showTherapeuticRangeLines: "Show Therapeutic Range",
|
||||
showTherapeuticRangeLinesTooltip: "Display horizontal reference lines for therapeutic min/max concentrations (default: enabled).",
|
||||
showTherapeuticRangeLinesTooltip: "Display horizontal reference lines for therapeutic min/max concentrations.\\n\\n__Default:__ **enabled**",
|
||||
simulationDuration: "Simulation Duration",
|
||||
simulationDurationTooltip: "Number of days to simulate. Longer periods allow steady-state observation. Default: {{simulationDays}} days.",
|
||||
simulationDurationTooltip: "Number of days to simulate. Longer periods allow steady-state observation.\\n\\n__Default:__ **{{simulationDays}} days**",
|
||||
displayedDays: "Visible Days (in Focus)",
|
||||
displayedDaysTooltip: "How many days to display on screen at once. Smaller values zoom in on details. Default: {{displayedDays}} day(s).",
|
||||
displayedDaysTooltip: "How many days to display on screen at once. Smaller values zoom in on details.\\n\\n__Default:__ **{{displayedDays}} day(s)**",
|
||||
yAxisRange: "Y-Axis Range (Concentration Zoom)",
|
||||
yAxisRangeTooltip: "Manually set vertical axis limits (concentration scale). Leave empty for automatic scaling based on data. Default: auto.",
|
||||
yAxisRangeTooltip: "Manually set vertical axis limits (concentration scale). Leave empty for automatic scaling based on data.\\n\\n__Default:__ **auto**",
|
||||
yAxisRangeAutoButton: "A",
|
||||
yAxisRangeAutoButtonTitle: "Determine range automatically based on data range",
|
||||
auto: "Auto",
|
||||
therapeuticRange: "Therapeutic Range (d-Amphetamine)",
|
||||
therapeuticRangeTooltip: "Personalized concentration targets based on YOUR individual response. Set these after observing which levels provide symptom control vs. side effects. Reference ranges (highly variable): Adults ~10-80 ng/mL, Children ~20-120 ng/mL (due to lower body weight/Vd). Leave empty if unsure. Consult your physician.",
|
||||
therapeuticRangeTooltip: "Personalized concentration targets based on **YOUR individual response**.\\n\\nSet these after observing which levels provide symptom control vs. side effects.\\n\\n**Reference ranges** (highly variable):\\n• __Adults:__ **~10-80 ng/mL**\\n• __Children:__ **~20-120 ng/mL** (due to lower body weight/Vd)\\n\\nLeave empty if unsure. ***Consult your physician.***",
|
||||
dAmphetamineParameters: "d-Amphetamine Parameters",
|
||||
halfLife: "Elimination Half-life",
|
||||
halfLifeTooltip: "Time for body to clear half the d-amphetamine from blood. Affected by urine pH: acidic (<6) → 7-9h, neutral (6-7.5) → 10-12h, alkaline (>7.5) → 13-15h. See [therapeutic reference ranges](https://www.thieme-connect.com/products/ejournals/pdf/10.1055/a-2689-4911.pdf). Default: {{damphHalfLife}}h.",
|
||||
halfLifeTooltip: "Time for body to clear half the d-amphetamine from blood.\\n\\n__Affected by urine pH:__\\n• __Acidic__ (<6) → **7-9h**\\n• __Neutral__ (6-7.5) → **10-12h**\\n• __Alkaline__ (>7.5) → **13-15h**\\n\\n*See* [therapeutic reference ranges](https://www.thieme-connect.com/products/ejournals/pdf/10.1055/a-2689-4911.pdf).\\n\\n__Default:__ **{{damphHalfLife}}h**",
|
||||
lisdexamfetamineParameters: "Lisdexamfetamine (LDX) Parameters",
|
||||
conversionHalfLife: "LDX→d-Amph Conversion Half-life",
|
||||
conversionHalfLifeTooltip: "Time for red blood cells to convert half the inactive LDX prodrug into active d-amphetamine. Typical: 0.7-1.2h. Default: {{ldxHalfLife}}h.",
|
||||
conversionHalfLifeTooltip: "Time for red blood cells to convert half the inactive LDX prodrug into active d-amphetamine.\\n\\n__Typical range:__ **0.7-1.2h**.\\n__Default:__ **{{ldxHalfLife}}h**",
|
||||
absorptionHalfLife: "Absorption Half-life",
|
||||
absorptionHalfLifeTooltip: "Time for intestines to absorb half the LDX from stomach to blood. Delayed by food (~1h shift). Typical: 0.7-1.2h. Default: {{ldxAbsorptionHalfLife}}h.",
|
||||
absorptionHalfLifeTooltip: "Time for intestines to absorb half the LDX from stomach to blood.\\n\\nDelayed by food (**~1h shift**).\\n\\n__Typical range:__ **0.7-1.2h**.\\n__Default:__ **{{ldxAbsorptionHalfLife}}h**",
|
||||
faster: "(faster >)",
|
||||
|
||||
// Advanced Settings
|
||||
weightBasedVdScaling: "Weight-Based Volume of Distribution",
|
||||
weightBasedVdTooltip: "Adjusts plasma concentrations based on body weight (proportional to ~5.4 L/kg). Lighter persons → higher peaks, heavier → lower peaks. When disabled, assumes 70 kg adult.",
|
||||
weightBasedVdTooltip: "Adjusts plasma concentrations based on body weight (proportional to **~5.4 L/kg**).\\n\\n__Effects:__\\n• __Lighter persons__ → ***higher*** concentration peaks\\n• __Heavier persons__ → ***lower*** concentration peaks\\n\\n__When disabled:__ assumes **70 kg adult**",
|
||||
bodyWeight: "Body Weight",
|
||||
bodyWeightTooltip: "Your body weight for concentration scaling. Used to calculate volume of distribution (Vd = weight × 5.4). See [population PK analysis](https://pmc.ncbi.nlm.nih.gov/articles/PMC5572767/). Default: {{bodyWeight}} kg.",
|
||||
bodyWeightTooltip: "Your body weight for concentration scaling.\\n\\nUsed to calculate volume of distribution:\\n**Vd = weight × 5.4**\\n\\nSee [population PK analysis](https://pmc.ncbi.nlm.nih.gov/articles/PMC5572767/).\\n\\n__Default:__ **{{bodyWeight}} kg**",
|
||||
bodyWeightUnit: "kg",
|
||||
|
||||
foodEffectEnabled: "Taken With Meal",
|
||||
foodEffectDelay: "Food Effect Delay",
|
||||
foodEffectTooltip: "High-fat meals delay absorption without changing total exposure. Slows onset of effects (~1h delay). When disabled, assumes fasted state.",
|
||||
foodEffectTooltip: "High-fat meals delay absorption **without changing total exposure**.\\n\\nSlows onset of effects by **~1 hour**.\\n\\nWhen disabled, assumes fasted state.",
|
||||
tmaxDelay: "Absorption Delay",
|
||||
tmaxDelayTooltip: "Time delay when dose is taken with high-fat meal. Applied using per-dose food toggles (🍴 icon) in schedule. Research shows ~1h delay without peak reduction. See [study](https://pmc.ncbi.nlm.nih.gov/articles/PMC4823324/). Note: The high-fat meal used in this study consisted of 1 English muffin with butter, 1 fried egg, 1 slice of American cheese, 1 slice of Canadian bacon, 2 oz (57 g) of hash brown potatoes, and 8 fl oz (240 mL) of whole milk. Default: {{tmaxDelay}}h.",
|
||||
tmaxDelayTooltip: "Time delay when dose is taken with **high-fat meal**. Applied using per-dose food toggles (🍴 icon) in schedule.\\n\\nResearch shows ~1h delay without peak reduction. *See* [study](https://pmc.ncbi.nlm.nih.gov/articles/PMC4823324/).\\n\\n__Note:__ The high-fat meal used in this study consisted of 1 English muffin with butter, 1 fried egg, 1 slice of American cheese, 1 slice of Canadian bacon, 2 oz (57 g) of hash brown potatoes, and 8 fl oz (240 mL) of whole milk.\\n\\n__Default:__ **{{tmaxDelay}}h**",
|
||||
tmaxDelayUnit: "h",
|
||||
|
||||
urinePHTendency: "Urine pH Effects",
|
||||
urinePHTooltip: "Urine pH affects kidney reabsorption of amphetamine. Acidic urine (<6) increases elimination (faster clearance, t½ ~7-9h). Normal pH (6-7.5) maintains baseline elimination (~11h). Alkaline urine (>7.5) reduces elimination (slower clearance, t½ ~13-15h). Typical range: 5.5-8.0. Default: Normal pH (6-7.5).",
|
||||
urinePHTooltip: "Urine pH affects kidney reabsorption of amphetamine.\\n\\n__Effects on elimination:__\\n• __Acidic__ (<6) → ***Faster*** clearance, **t½ ~7-9h**\\n• __Normal__ (6-7.5) → ***Baseline*** elimination **~11h**\\n• __Alkaline__ (>7.5) → ***Slower*** clearance, **t½ ~13-15h**\\n\\n__Typical range:__ **5.5-8.0**\\n\\n__Default:__ **Normal pH** (6-7.5)",
|
||||
urinePHMode: "pH Effect",
|
||||
urinePHModeNormal: "Normal (pH 6-7.5, t½ 11h)",
|
||||
urinePHModeAcidic: "Acidic (pH <6, faster elimination)",
|
||||
urinePHModeAlkaline: "Alkaline (pH >7.5, slower elimination)",
|
||||
urinePHValue: "pH Value",
|
||||
urinePHValueTooltip: "Your typical urine pH (acidic=faster clearance, alkaline=slower). Default: {{phTendency}}. Range: 5.5-8.0.",
|
||||
urinePHValueTooltip: "Your typical urine pH (acidic=faster clearance, alkaline=slower).\\n\\nRange: **5.5-8.0**.\\n\\n__Default:__ **{{phTendency}}**",
|
||||
phValue: "pH Value",
|
||||
phUnit: "(5.5-8.0)",
|
||||
|
||||
oralBioavailability: "Oral Bioavailability",
|
||||
oralBioavailabilityTooltip: "Fraction of LDX dose that reaches bloodstream. See [bioavailability study](https://www.frontiersin.org/journals/pharmacology/articles/10.3389/fphar.2022.881198/full) (FDA label: 96.4%). Rarely needs adjustment unless you have documented absorption issues. Default: {{fOral}} ({{fOralPercent}}%).",
|
||||
oralBioavailabilityTooltip: "Fraction of LDX dose that reaches bloodstream.\\n\\n*See* [bioavailability study](https://www.frontiersin.org/journals/pharmacology/articles/10.3389/fphar.2022.881198/full) — **FDA label: 96.4%**.\\n\\nRarely needs adjustment unless you have documented absorption issues.\\n\\n__Default:__ **{{fOral}} ({{fOralPercent}}%)**",
|
||||
|
||||
steadyStateDays: "Medication History",
|
||||
steadyStateDaysTooltip: "Number of prior days on stable medication dose to simulate accumulation/steady-state. Set 0 for \"first day from scratch.\" Default: {{steadyStateDays}} days. Max: 7.",
|
||||
steadyStateDaysTooltip: "Number of prior days on stable medication dose to simulate accumulation/steady-state.\\n\\If this option is disabled, the simulation will begin from day one with no prior medication history. The same applies for the value is **0**.\\n\\nMax: **7 days**.\\n\\n__Default:__ **{{steadyStateDays}} days**.",
|
||||
|
||||
// Age-specific pharmacokinetics
|
||||
ageGroup: "Age Group",
|
||||
ageGroupTooltip: "Pediatric subjects (6-12y) exhibit faster d-amphetamine elimination (t½ ~9h) compared to adults (~11h) due to higher weight-normalized metabolic rate. See [research document](https://git.11001001.org/cbaoth/med-plan-assistant/src/branch/main/docs/2026-01-17_AI-Reseach_SimulatingLDXandD-AmphetaminePlasmaLevels.md#52-pediatric-vs-adult-modeling) Section 5.2. Select 'custom' to use your manually configured half-life. Default: adult.",
|
||||
ageGroupTooltip: "Pediatric subjects (6-12y) exhibit **faster d-amphetamine elimination** (t½ ~9h) compared to adults (~11h) due to higher weight-normalized metabolic rate.\\n\\n*See* [research document](https://git.11001001.org/cbaoth/med-plan-assistant/src/branch/main/docs/2026-01-17_AI-Reseach_SimulatingLDXandD-AmphetaminePlasmaLevels.md#52-pediatric-vs-adult-modeling) *Section 5.2.*\\n\\nSelect 'custom' to use your manually configured half-life.\\n\\n__Default:__ **adult**.",
|
||||
ageGroupAdult: "Adult (t½ 11h)",
|
||||
ageGroupChild: "Child 6-12y (t½ 9h)",
|
||||
ageGroupCustom: "Custom (use manual t½)",
|
||||
|
||||
// Renal function effects
|
||||
renalFunction: "Renal Impairment",
|
||||
renalFunctionTooltip: "Severe renal impairment extends d-amphetamine half-life by ~50% (from 11h to 16.5h). FDA label recommends dose caps: 50mg for severe impairment, 30mg for ESRD. See [FDA Label Section 8.6](https://www.accessdata.fda.gov/drugsatfda_docs/label/2017/021977s049lbl.pdf) and [research document](https://git.11001001.org/cbaoth/med-plan-assistant/src/branch/main/docs/2026-01-17_AI-Reseach_SimulatingLDXandD-AmphetaminePlasmaLevels.md#82-renal-function) Section 8.2. Default: disabled.",
|
||||
renalFunctionTooltip: "Severe renal impairment extends d-amphetamine half-life by **~50%** (from 11h to 16.5h).\\n\\n__FDA label dose caps:__\\n• __Severe impairment__: **50mg**\\n• __ESRD__: **30mg**\\n*See* [FDA Label Section 8.6](https://www.accessdata.fda.gov/drugsatfda_docs/label/2017/021977s049lbl.pdf) *and* [research document](https://git.11001001.org/cbaoth/med-plan-assistant/src/branch/main/docs/2026-01-17_AI-Reseach_SimulatingLDXandD-AmphetaminePlasmaLevels.md#82-renal-function) *Section 8.2.*\\n\\n__Default:__ **disabled**.",
|
||||
renalFunctionSeverity: "Impairment Severity",
|
||||
renalFunctionNormal: "Normal (no adjustment)",
|
||||
renalFunctionMild: "Mild (no adjustment)",
|
||||
@@ -292,10 +292,10 @@ export const en = {
|
||||
// Field validation - Warnings
|
||||
warningDuplicateTime: "⚠️ Multiple doses at same time.",
|
||||
warningZeroDose: "⚠️ Zero dose has no effect on simulation.",
|
||||
warningAbsorptionOutOfRange: "⚠️ Typical range: 0.7-1.2h. Current value may be outside clinical norms.",
|
||||
warningConversionOutOfRange: "⚠️ Typical range: 0.7-1.2h. Current value may be outside clinical norms.",
|
||||
warningEliminationOutOfRange: "⚠️ Typical range: 9-12h (normal pH). Extended range 7-15h (pH effects). Current value is unusual.",
|
||||
warningDoseAbove70mg: "⚠️ FDA-approved maximum: 70 mg. Higher doses lack safety data and increase cardiovascular risk.",
|
||||
warningAbsorptionOutOfRange: "⚠️ Current value may be outside clinical norms.\\n\\n__Typical range:__ **0.7-1.2h**.",
|
||||
warningConversionOutOfRange: "⚠️ Current value may be outside clinical norms.\\n\\n__Typical range:__ **0.7-1.2h**.",
|
||||
warningEliminationOutOfRange: "⚠️ Current value may be outside clinical norms.\\n\\n__Typical range:__ **9-12h** (normal pH).\\nExtended range 7-15h (pH effects).",
|
||||
warningDoseAbove70mg: "⚠️ Higher doses lack safety data and increase cardiovascular risk.\\n\\n__FDA-approved maximum:__ **70 mg**.\\n\\nConsult your physician before exceeding this dose.",
|
||||
|
||||
// Time picker
|
||||
timePickerHour: "Hour",
|
||||
|
||||
230
src/utils/contentFormatter.tsx
Normal file
230
src/utils/contentFormatter.tsx
Normal file
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* Content Formatting Utilities
|
||||
*
|
||||
* Provides markdown-style formatting capabilities for various UI content including:
|
||||
* - Tooltips
|
||||
* - Error/warning messages
|
||||
* - Info boxes
|
||||
* - Help text
|
||||
*
|
||||
* Supported formatting (processed in this order):
|
||||
* 1. Links: [text](url)
|
||||
* 2. Bold+Italic: ***text***
|
||||
* 3. Bold: **text**
|
||||
* 4. Italic: *text*
|
||||
* 5. Underline: __text__
|
||||
* 6. Line breaks: \n
|
||||
*
|
||||
* @author Andreas Weyer
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
/**
|
||||
* Renders formatted formatContent with markdown-style formatting support.
|
||||
* Can be used for tooltips, error messages, info boxes, and other UI text.
|
||||
*
|
||||
* Processing order: Links → Bold+Italic (***) → Bold (**) → Italic (*) → Underline (__) → Line breaks (\n)
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // In tooltip
|
||||
* formatContent("See [study](https://example.com)\\n__Important:__ **Take with food**.")
|
||||
*
|
||||
* // In error message
|
||||
* formatContent("**Error:** Value must be between *5* and *50*.")
|
||||
*
|
||||
* // In info box
|
||||
* formatContent("***Note:*** This setting affects accuracy.\\n\\nSee [docs](https://example.com).")
|
||||
* ```
|
||||
*
|
||||
* @param text - The text to format with markdown-style syntax
|
||||
* @returns Formatted React nodes ready for rendering
|
||||
*/
|
||||
export const formatContent = (text: string): React.ReactNode => {
|
||||
// Helper to process text segments with bold/italic/underline formatting
|
||||
const processFormatting = (segment: string, keyPrefix: string): React.ReactNode[] => {
|
||||
const parts: React.ReactNode[] = [];
|
||||
let remaining = segment;
|
||||
let partIndex = 0;
|
||||
|
||||
// Process bold+italic first (***text***)
|
||||
const boldItalicRegex = /\*\*\*([^*]+)\*\*\*/g;
|
||||
let lastIdx = 0;
|
||||
let boldItalicMatch;
|
||||
|
||||
while ((boldItalicMatch = boldItalicRegex.exec(remaining)) !== null) {
|
||||
// Add text before bold+italic
|
||||
if (boldItalicMatch.index > lastIdx) {
|
||||
const beforeBoldItalic = remaining.substring(lastIdx, boldItalicMatch.index);
|
||||
parts.push(...processBoldItalicAndUnderline(beforeBoldItalic, `${keyPrefix}-bi${partIndex++}`));
|
||||
}
|
||||
// Add bold+italic text
|
||||
parts.push(
|
||||
<strong key={`${keyPrefix}-bolditalic-${partIndex++}`} className="font-semibold italic">
|
||||
{boldItalicMatch[1]}
|
||||
</strong>
|
||||
);
|
||||
lastIdx = boldItalicRegex.lastIndex;
|
||||
}
|
||||
|
||||
// Add remaining text with bold/italic/underline processing
|
||||
if (lastIdx < remaining.length) {
|
||||
parts.push(...processBoldItalicAndUnderline(remaining.substring(lastIdx), `${keyPrefix}-bi${partIndex++}`));
|
||||
}
|
||||
|
||||
return parts.length > 0 ? parts : [remaining];
|
||||
};
|
||||
|
||||
// Helper to process bold/italic/underline (after bold+italic ***)
|
||||
const processBoldItalicAndUnderline = (segment: string, keyPrefix: string): React.ReactNode[] => {
|
||||
const parts: React.ReactNode[] = [];
|
||||
const boldRegex = /\*\*([^*]+)\*\*/g;
|
||||
let lastIdx = 0;
|
||||
let boldMatch;
|
||||
|
||||
while ((boldMatch = boldRegex.exec(segment)) !== null) {
|
||||
// Add text before bold
|
||||
if (boldMatch.index > lastIdx) {
|
||||
const beforeBold = segment.substring(lastIdx, boldMatch.index);
|
||||
parts.push(...processItalicAndUnderline(beforeBold, `${keyPrefix}-b${lastIdx}`));
|
||||
}
|
||||
// Add bold text
|
||||
parts.push(
|
||||
<strong key={`${keyPrefix}-bold-${boldMatch.index}`} className="font-semibold">
|
||||
{boldMatch[1]}
|
||||
</strong>
|
||||
);
|
||||
lastIdx = boldRegex.lastIndex;
|
||||
}
|
||||
|
||||
// Add remaining text with italic/underline processing
|
||||
if (lastIdx < segment.length) {
|
||||
parts.push(...processItalicAndUnderline(segment.substring(lastIdx), `${keyPrefix}-b${lastIdx}`));
|
||||
}
|
||||
|
||||
return parts.length > 0 ? parts : [segment];
|
||||
};
|
||||
|
||||
// Helper to process italic and underline (*text* and __text__)
|
||||
const processItalicAndUnderline = (segment: string, keyPrefix: string): React.ReactNode[] => {
|
||||
const parts: React.ReactNode[] = [];
|
||||
// Match single * that's not part of ** or inside links
|
||||
const italicRegex = /(?<!\*)\*(?!\*)([^*]+)\*(?!\*)/g;
|
||||
let lastIdx = 0;
|
||||
let italicMatch;
|
||||
|
||||
while ((italicMatch = italicRegex.exec(segment)) !== null) {
|
||||
// Add text before italic (process underline in it)
|
||||
if (italicMatch.index > lastIdx) {
|
||||
const beforeItalic = segment.substring(lastIdx, italicMatch.index);
|
||||
parts.push(...processUnderline(beforeItalic, `${keyPrefix}-i${lastIdx}`));
|
||||
}
|
||||
// Add italic text
|
||||
parts.push(
|
||||
<em key={`${keyPrefix}-italic-${italicMatch.index}`} className="italic">
|
||||
{italicMatch[1]}
|
||||
</em>
|
||||
);
|
||||
lastIdx = italicRegex.lastIndex;
|
||||
}
|
||||
|
||||
// Add remaining text with underline processing
|
||||
if (lastIdx < segment.length) {
|
||||
parts.push(...processUnderline(segment.substring(lastIdx), `${keyPrefix}-i${lastIdx}`));
|
||||
}
|
||||
|
||||
return parts.length > 0 ? parts : [segment];
|
||||
};
|
||||
|
||||
// Helper to process underline (__text__) - final level of formatting
|
||||
const processUnderline = (segment: string, keyPrefix: string): React.ReactNode[] => {
|
||||
const parts: React.ReactNode[] = [];
|
||||
const underlineRegex = /__([^_]+)__/g;
|
||||
let lastIdx = 0;
|
||||
let underlineMatch;
|
||||
|
||||
while ((underlineMatch = underlineRegex.exec(segment)) !== null) {
|
||||
// Add text before underline (plain text)
|
||||
if (underlineMatch.index > lastIdx) {
|
||||
parts.push(segment.substring(lastIdx, underlineMatch.index));
|
||||
}
|
||||
// Add underlined text
|
||||
parts.push(
|
||||
<span key={`${keyPrefix}-underline-${underlineMatch.index}`} className="underline">
|
||||
{underlineMatch[1]}
|
||||
</span>
|
||||
);
|
||||
lastIdx = underlineRegex.lastIndex;
|
||||
}
|
||||
|
||||
// Add remaining plain text
|
||||
if (lastIdx < segment.length) {
|
||||
parts.push(segment.substring(lastIdx));
|
||||
}
|
||||
|
||||
return parts.length > 0 ? parts : [segment];
|
||||
};
|
||||
|
||||
// Split by line breaks first
|
||||
const lines = text.split('\\n');
|
||||
const result: React.ReactNode[] = [];
|
||||
|
||||
lines.forEach((line, lineIndex) => {
|
||||
const lineParts: React.ReactNode[] = [];
|
||||
const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
|
||||
let lastIndex = 0;
|
||||
let match;
|
||||
|
||||
while ((match = linkRegex.exec(line)) !== null) {
|
||||
// Add text before link with formatting
|
||||
if (match.index > lastIndex) {
|
||||
const beforeLink = line.substring(lastIndex, match.index);
|
||||
lineParts.push(...processFormatting(beforeLink, `line${lineIndex}-seg${lastIndex}`));
|
||||
}
|
||||
// Add link
|
||||
lineParts.push(
|
||||
<a
|
||||
key={`line${lineIndex}-link-${match.index}`}
|
||||
href={match[2]}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline italic text-yellow-300 hover:text-yellow-200 cursor-pointer"
|
||||
>
|
||||
{match[1]}
|
||||
</a>
|
||||
);
|
||||
lastIndex = linkRegex.lastIndex;
|
||||
}
|
||||
|
||||
// Add remaining text with formatting
|
||||
if (lastIndex < line.length) {
|
||||
const remaining = line.substring(lastIndex);
|
||||
lineParts.push(...processFormatting(remaining, `line${lineIndex}-seg${lastIndex}`));
|
||||
}
|
||||
|
||||
// Add line content
|
||||
if (lineParts.length > 0) {
|
||||
result.push(...lineParts);
|
||||
} else {
|
||||
result.push(line);
|
||||
}
|
||||
|
||||
// Add line break if not the last line
|
||||
if (lineIndex < lines.length - 1) {
|
||||
result.push(<br key={`br-${lineIndex}`} />);
|
||||
}
|
||||
});
|
||||
|
||||
return result.length > 0 ? result : text;
|
||||
};
|
||||
|
||||
/**
|
||||
* Alias for renderContent for use in non-tooltip contexts (error messages, info boxes, etc.).
|
||||
* Provides the same markdown-style formatting capabilities.
|
||||
*
|
||||
* @param text - The text to format with markdown-style syntax
|
||||
* @returns Formatted React nodes ready for rendering
|
||||
*/
|
||||
export const formatText = formatContent; // Alias for non-tooltip contexts
|
||||
Reference in New Issue
Block a user