feat: added scale buttons
This commit is contained in:
parent
a96734c394
commit
53c25c9da3
4 changed files with 391 additions and 22 deletions
|
|
@ -353,7 +353,7 @@ function findVolumes(text) {
|
|||
const results = [];
|
||||
|
||||
const volumeRe = new RegExp(
|
||||
`(${AMOUNT})\\s+(${VOLUME_UNIT})\\b`,
|
||||
`(${AMOUNT})\\s*(${VOLUME_UNIT})\\b`,
|
||||
'gi'
|
||||
);
|
||||
|
||||
|
|
@ -407,6 +407,38 @@ function findTimes(text) {
|
|||
return results;
|
||||
}
|
||||
|
||||
// ─── Count Matcher ────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Find bare numeric counts in text (no unit attached).
|
||||
* These represent ingredient quantities like "eggs 3" or "biscuits 20-24".
|
||||
* Overlap with unit-bearing matches is resolved by deduplication (longer wins).
|
||||
*/
|
||||
function findCounts(text) {
|
||||
const results = [];
|
||||
|
||||
const countRe = new RegExp(
|
||||
`(${AMOUNT})(?=\\s*$|\\s*,|\\s*\\)|\\s*\\]|\\s*;|\\s+[^\\dxX~])`,
|
||||
'g'
|
||||
);
|
||||
|
||||
let m;
|
||||
while ((m = countRe.exec(text)) !== null) {
|
||||
const amount = parseAmount(m[1]);
|
||||
results.push({
|
||||
match: m[1],
|
||||
index: m.index,
|
||||
type: 'count',
|
||||
amount,
|
||||
unit: null,
|
||||
approximate: typeof amount.approximate === 'boolean' ? amount.approximate : false,
|
||||
alt: null,
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// ─── Main Matcher ─────────────────────────────────────────
|
||||
|
||||
/**
|
||||
|
|
@ -424,6 +456,7 @@ function findAllMeasurements(text) {
|
|||
...findWeights(text),
|
||||
...findVolumes(text),
|
||||
...findTimes(text),
|
||||
...findCounts(text),
|
||||
];
|
||||
|
||||
// Sort by position
|
||||
|
|
@ -466,6 +499,7 @@ module.exports = {
|
|||
findWeights,
|
||||
findVolumes,
|
||||
findTimes,
|
||||
findCounts,
|
||||
|
||||
// Parsing utilities (exported for testing)
|
||||
parseAmount,
|
||||
|
|
|
|||
|
|
@ -136,6 +136,70 @@ function toImperialValue(value, unit) {
|
|||
}
|
||||
}
|
||||
|
||||
// ─── Base Unit Conversion (for scaling) ─────────────────
|
||||
|
||||
function toBaseGrams(value, unit) {
|
||||
switch (unit) {
|
||||
case 'g': return value;
|
||||
case 'kg': return value * 1000;
|
||||
case 'oz': return value * 28.3495;
|
||||
case 'lb': return value * 453.592;
|
||||
default: return value;
|
||||
}
|
||||
}
|
||||
|
||||
function toBaseMl(value, unit) {
|
||||
switch (unit) {
|
||||
case 'ml': return value;
|
||||
case 'L': return value * 1000;
|
||||
case 'cup': return value * 236.588;
|
||||
case 'tablespoon': return value * 14.787;
|
||||
case 'teaspoon': return value * 4.929;
|
||||
case 'quart': return value * 946.353;
|
||||
case 'gallon': return value * 3785.41;
|
||||
case 'pint': return value * 473.176;
|
||||
case 'fl oz': return value * 29.5735;
|
||||
default: return value;
|
||||
}
|
||||
}
|
||||
|
||||
function computeScaleData(measurement) {
|
||||
const { type, amount, unit, approximate } = measurement;
|
||||
if (unit === 'parts by volume' || unit === 'parts by weight') return null;
|
||||
if (type !== 'weight' && type !== 'volume' && type !== 'count') return null;
|
||||
|
||||
if (type === 'count') {
|
||||
if (amount.min !== undefined) {
|
||||
return {
|
||||
base: [amount.min.value, amount.max.value],
|
||||
type: 'count',
|
||||
approx: approximate || amount.min.approximate || false,
|
||||
};
|
||||
}
|
||||
return {
|
||||
base: amount.value,
|
||||
type: 'count',
|
||||
approx: approximate || false,
|
||||
};
|
||||
}
|
||||
|
||||
const toBase = type === 'weight' ? toBaseGrams : toBaseMl;
|
||||
|
||||
if (amount.min !== undefined) {
|
||||
return {
|
||||
base: [toBase(amount.min.value, unit), toBase(amount.max.value, unit)],
|
||||
type: type,
|
||||
approx: approximate || amount.min.approximate || false,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
base: toBase(amount.value, unit),
|
||||
type: type,
|
||||
approx: approximate || false,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Time Formatting ────────────────────────────────────
|
||||
|
||||
function toMinutes(value, unit) {
|
||||
|
|
@ -183,6 +247,12 @@ function generateTexts(measurement) {
|
|||
return { defaultText: text, altText: text };
|
||||
}
|
||||
|
||||
// Bare counts — just display the number, no conversion
|
||||
if (type === 'count') {
|
||||
const text = formatCountText(amount, approximate);
|
||||
return { defaultText: text, altText: text };
|
||||
}
|
||||
|
||||
if (type === 'time') {
|
||||
return generateTimeTexts(amount, unit, approximate);
|
||||
}
|
||||
|
|
@ -190,6 +260,14 @@ function generateTexts(measurement) {
|
|||
return generateUnitTexts(amount, unit, approximate, type);
|
||||
}
|
||||
|
||||
function formatCountText(amount, approximate) {
|
||||
const prefix = approximate ? '~' : '';
|
||||
if (amount.min !== undefined) {
|
||||
return prefix + formatNumber(amount.min.value) + '-' + formatNumber(amount.max.value);
|
||||
}
|
||||
return prefix + formatNumber(amount.value);
|
||||
}
|
||||
|
||||
function generateTimeTexts(amount, unit, approximate) {
|
||||
if (amount.min !== undefined) {
|
||||
const prefix = amount.min.approximate ? '~' : '';
|
||||
|
|
@ -306,6 +384,13 @@ function replaceMeasurementsInToken(token, Token) {
|
|||
open.attrSet('data-default', defaultText);
|
||||
open.attrSet('data-alt', altText);
|
||||
open.attrSet('title', m.match);
|
||||
|
||||
// Add scaling data for weight/volume
|
||||
const scaleData = computeScaleData(m);
|
||||
if (scaleData) {
|
||||
open.attrSet('data-scalable', JSON.stringify(scaleData));
|
||||
}
|
||||
|
||||
newTokens.push(open);
|
||||
|
||||
// Display metric-normalized text by default
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue