diff --git a/lib/measurements/matcher.js b/lib/measurements/matcher.js index 6b99975..bfabb30 100644 --- a/lib/measurements/matcher.js +++ b/lib/measurements/matcher.js @@ -16,8 +16,10 @@ const NUM = '\\d+(?:\\.\\d+)?'; const FRAC = '\\d+\\s*/\\s*\\d+'; const MIXED = '\\d+\\s+\\d+\\s*/\\s*\\d+'; +const UNICODE_FRAC = '[\u00BC-\u00BE\u2150-\u215E]'; +const MIXED_UNI = `\\d+\\s*${UNICODE_FRAC}`; const APPROX_PREFIX = '~\\s*'; -const SINGLE_AMOUNT = `(?:${APPROX_PREFIX})?(?:${MIXED}|${FRAC}|${NUM})`; +const SINGLE_AMOUNT = `(?:${APPROX_PREFIX})?(?:${MIXED}|${FRAC}|${MIXED_UNI}|${UNICODE_FRAC}|${NUM})`; const RANGE_SEP = '\\s*(?:-|to)\\s*'; const AMOUNT = `(?:${SINGLE_AMOUNT}${RANGE_SEP}${SINGLE_AMOUNT}|${SINGLE_AMOUNT})`; @@ -39,6 +41,27 @@ const DIM_UNIT = '(?:inch(?:es)?|in\\.?|cm|mm)'; * @param {string} str — e.g. "200", "1.5", "1/2", "1 1/2", "~250" * @returns {{ value: number, approximate: boolean }} */ +const UNICODE_FRAC_MAP = { + '\u00BC': 0.25, // ¼ + '\u00BD': 0.5, // ½ + '\u00BE': 0.75, // ¾ + '\u2150': 1/7, // ⅐ + '\u2151': 1/9, // ⅑ + '\u2152': 1/10, // ⅒ + '\u2153': 1/3, // ⅓ + '\u2154': 2/3, // ⅔ + '\u2155': 0.2, // ⅕ + '\u2156': 0.4, // ⅖ + '\u2157': 0.6, // ⅗ + '\u2158': 0.8, // ⅘ + '\u2159': 1/6, // ⅙ + '\u215A': 5/6, // ⅚ + '\u215B': 0.125, // ⅛ + '\u215C': 0.375, // ⅜ + '\u215D': 0.625, // ⅝ + '\u215E': 0.875, // ⅞ +}; + function parseSingleAmount(str) { str = str.trim(); const approximate = str.startsWith('~'); @@ -46,6 +69,20 @@ function parseSingleAmount(str) { str = str.replace(/^~\s*/, ''); } + // Unicode mixed number: "1½", "1 ½" + const uniMixedMatch = str.match(/^(\d+)\s*([\u00BC-\u00BE\u2150-\u215E])$/); + if (uniMixedMatch) { + const whole = parseInt(uniMixedMatch[1], 10); + const frac = UNICODE_FRAC_MAP[uniMixedMatch[2]] || 0; + return { value: whole + frac, approximate }; + } + + // Standalone Unicode fraction: "½", "¾" + const uniFracMatch = str.match(/^([\u00BC-\u00BE\u2150-\u215E])$/); + if (uniFracMatch) { + return { value: UNICODE_FRAC_MAP[uniFracMatch[1]] || 0, approximate }; + } + // Mixed number: "1 1/2" const mixedMatch = str.match(/^(\d+)\s+(\d+)\s*\/\s*(\d+)$/); if (mixedMatch) { @@ -299,6 +336,33 @@ function findDimensions(text) { }); } + // Standalone AMOUNT + dimension unit (e.g. "1 inch", "0.5-1cm") + const standaloneDimRe = new RegExp( + `(${AMOUNT})\\s*(${DIM_UNIT})\\b`, + 'gi' + ); + + while ((m = standaloneDimRe.exec(text)) !== null) { + // Skip if overlapping with an NxN match already found + const alreadyMatched = results.some( + r => m.index >= r.index && m.index < r.index + r.match.length + ); + if (alreadyMatched) continue; + + const amount = parseAmount(m[1]); + const unit = normalizeUnit(m[2]); + + results.push({ + match: m[0], + index: m.index, + type: 'dimension', + amount, + unit, + approximate: typeof amount.approximate === 'boolean' ? amount.approximate : false, + alt: null, + }); + } + return results; } diff --git a/recipes/Tater Tot Hotdish.md b/recipes/Tater Tot Hotdish.md index 519f0c5..5ff2b0b 100644 --- a/recipes/Tater Tot Hotdish.md +++ b/recipes/Tater Tot Hotdish.md @@ -38,7 +38,7 @@ makes: 1 9x13 pan or 4 large rectangular pyrex containers ## Tools - pan - mixing bowl -- 9 x 13 inch glass baking dish/4 8x6x2 inch pyrex dishes +- 9 x 13 inch glass baking dish/8x6x2 inch pyrex dishes 4 ## Steps - preheat oven to 350 °F - dice onion