feat: added density conversion

This commit is contained in:
Leyla Becker 2026-02-22 17:50:06 -06:00
parent bac7a14f95
commit df26f0243b
5 changed files with 415 additions and 12 deletions

View file

@ -8,6 +8,7 @@
*/
const { findAllMeasurements, unitType } = require('./matcher');
const { matchDensity } = require('./densities');
// ─── Number Formatting ──────────────────────────────────
@ -463,6 +464,116 @@ function formatMeasurementText(amount, unit, approximate) {
return prefix + formatValueUnit(amount.value, unit);
}
// ─── Density-Based Auto Hybrid Generation ───────────────
function generateDensityVolumeData(measurement, density) {
const { amount, unit, approximate } = measurement;
const prefix = approximate ? '~' : '';
if (amount.min !== undefined) {
const mlMin = toBaseGrams(amount.min.value, unit) / density;
const mlMax = toBaseGrams(amount.max.value, unit) / density;
const mMin = smartMetricVolume(mlMin);
const mMax = smartMetricVolume(mlMax);
const iMin = smartImperialVolume(mlMin);
const iMax = smartImperialVolume(mlMax);
const rPrefix = (approximate || amount.min.approximate) ? '~' : '';
let volumeDefault, volumeAlt;
if (mMin.unit === mMax.unit) {
const space = NO_SPACE_UNITS.has(mMin.unit) ? '' : ' ';
volumeDefault = rPrefix + formatNumber(mMin.value) + '-' + formatNumber(mMax.value) + space + unitLabel(mMin.unit, mMax.value !== 1);
} else {
volumeDefault = rPrefix + formatValueUnit(mMin.value, mMin.unit) + '-' + formatValueUnit(mMax.value, mMax.unit);
}
if (iMin.unit === iMax.unit) {
const space = NO_SPACE_UNITS.has(iMin.unit) ? '' : ' ';
volumeAlt = rPrefix + formatNumber(iMin.value) + '-' + formatNumber(iMax.value) + space + unitLabel(iMin.unit, iMax.value !== 1);
} else {
volumeAlt = rPrefix + formatValueUnit(iMin.value, iMin.unit) + '-' + formatValueUnit(iMax.value, iMax.unit);
}
return {
default: volumeDefault,
alt: volumeAlt,
scalable: {
base: [mlMin, mlMax],
type: 'volume',
approx: approximate || amount.min.approximate || false,
},
};
}
const grams = toBaseGrams(amount.value, unit);
const ml = grams / density;
const metric = smartMetricVolume(ml);
const imperial = smartImperialVolume(ml);
return {
default: prefix + formatValueUnit(metric.value, metric.unit),
alt: prefix + formatValueUnit(imperial.value, imperial.unit),
scalable: {
base: ml,
type: 'volume',
approx: approximate || false,
},
};
}
function generateDensityWeightData(measurement, density) {
const { amount, unit, approximate } = measurement;
const prefix = approximate ? '~' : '';
if (amount.min !== undefined) {
const gMin = toBaseMl(amount.min.value, unit) * density;
const gMax = toBaseMl(amount.max.value, unit) * density;
const mMin = smartMetricWeight(gMin);
const mMax = smartMetricWeight(gMax);
const iMin = smartImperialWeight(gMin / 28.3495);
const iMax = smartImperialWeight(gMax / 28.3495);
const rPrefix = (approximate || amount.min.approximate) ? '~' : '';
let weightDefault, weightAlt;
if (mMin.unit === mMax.unit) {
const space = NO_SPACE_UNITS.has(mMin.unit) ? '' : ' ';
weightDefault = rPrefix + formatNumber(mMin.value) + '-' + formatNumber(mMax.value) + space + unitLabel(mMin.unit, mMax.value !== 1);
} else {
weightDefault = rPrefix + formatValueUnit(mMin.value, mMin.unit) + '-' + formatValueUnit(mMax.value, mMax.unit);
}
if (iMin.unit === iMax.unit) {
const space = NO_SPACE_UNITS.has(iMin.unit) ? '' : ' ';
weightAlt = rPrefix + formatNumber(iMin.value) + '-' + formatNumber(iMax.value) + space + unitLabel(iMin.unit, iMax.value !== 1);
} else {
weightAlt = rPrefix + formatValueUnit(iMin.value, iMin.unit) + '-' + formatValueUnit(iMax.value, iMax.unit);
}
return {
default: weightDefault,
alt: weightAlt,
scalable: {
base: [gMin, gMax],
type: 'weight',
approx: approximate || amount.min.approximate || false,
},
};
}
const ml = toBaseMl(amount.value, unit);
const grams = ml * density;
const metric = smartMetricWeight(grams);
const imperial = smartImperialWeight(grams / 28.3495);
return {
default: prefix + formatValueUnit(metric.value, metric.unit),
alt: prefix + formatValueUnit(imperial.value, imperial.unit),
scalable: {
base: grams,
type: 'weight',
approx: approximate || false,
},
};
}
// ─── Serving Vessel Detection ───────────────────────────
// Serving vessel keywords — items in the Tools section containing these words
@ -534,6 +645,26 @@ function replaceMeasurementsInToken(token, Token, inToolsSection) {
}
}
// Density-based auto hybrid: generate the OTHER type's data from ingredient density
if (!hybridData && (m.type === 'weight' || m.type === 'volume')) {
const textBefore = text.slice(0, m.index);
const density = matchDensity(textBefore);
if (density) {
open.attrSet('data-hybrid', 'true');
if (m.type === 'weight') {
const volData = generateDensityVolumeData(m, density);
open.attrSet('data-volume-default', volData.default);
open.attrSet('data-volume-alt', volData.alt);
open.attrSet('data-volume-scalable', JSON.stringify(volData.scalable));
} else {
const wtData = generateDensityWeightData(m, density);
open.attrSet('data-weight-default', wtData.default);
open.attrSet('data-weight-alt', wtData.alt);
open.attrSet('data-weight-scalable', JSON.stringify(wtData.scalable));
}
}
}
newTokens.push(open);
// Display metric-normalized text by default
@ -620,4 +751,6 @@ module.exports.generateTexts = generateTexts;
module.exports.collapsedTime = collapsedTime;
module.exports.expandedTime = expandedTime;
module.exports.getHybridVolumeData = getHybridVolumeData;
module.exports.generateHybridVolumeTexts = generateHybridVolumeTexts;
module.exports.generateHybridVolumeTexts = generateHybridVolumeTexts;
module.exports.generateDensityVolumeData = generateDensityVolumeData;
module.exports.generateDensityWeightData = generateDensityWeightData;