{"version":3,"file":"CBbLIJmf.mjs","sources":["../../../../../../src/lib/calculator/conversions.ts"],"sourcesContent":["/**\n * Pure-math unit-conversion helpers — ported verbatim from\n * `D:\\testgpt\\lce_ui_reference\\lib\\src\\elevator_conversions.dart`.\n *\n * **Why this is its own module.** The Flutter reference\n * app co-locates field-conversion constants (fpm↔m/s,\n * in↔mm, lbs↔kg, m↔ft, mps²↔fpm/s, …) in\n * `elevator_conversions.dart`. Phase 17 (elevator-mechanical\n * calculators) and phase 16 (electrical calculators)\n * both need the same conversions; rather than duplicate\n * the factor table we put them here in one place\n * calculators from both phases can re-export.\n *\n * **Why SI internally, conversion at the boundary.** The\n * math layer (`$lib/calculator/formulas.ts`,\n * `$lib/calculator/electrical-calculations.ts`) operates\n * in SI and converts to imperial at the presentation\n * layer. Keeping these helpers here means the UI layer\n * reads the active unit set, picks the matching helper\n * (`fpmToMps` vs `mpsToFpm`), and the math API never\n * sees a mixed-unit input.\n *\n * **Why constants are pinned to the Flutter source.**\n * The brief asks for \"the SAME numbers the Flutter\n * reference produces\" — `MPS_PER_FPM = 0.00508`\n * exactly, `KG_PER_POUND = 0.45359237` exactly,\n * `METERS_PER_FOOT = 0.3048` exactly. Round-tripping a\n * pair of values through `aToB(bToA(x))` lands on the\n * original to floating-point precision; a test in\n * `conversions.test.ts` locks each constant with an\n * exact-value assertion so a future refactor cannot\n * silently change a constant.\n *\n * **No external IO.** This module is pure JS; no\n * `node:fs` or DOM imports — bundles cleanly into\n * the SSR loader and the test worker without shims.\n */\n\n// ---------------------------------------------------------------------------\n// Constants — exact values from `elevator_conversions.dart::ElevatorConversionConstants`.\n// Each constant is the A→B factor; `B→A = value / factor`.\n// ---------------------------------------------------------------------------\n\n/** 1 fpm = 0.00508 m/s exactly. Matches `ElevatorConversionConstants.fpmToMps`. */\nexport const MPS_PER_FPM = 0.00508;\n\n/** 1 lb = 0.45359237 kg exactly. Matches `ElevatorConversionConstants.lbToKg`. */\nexport const KG_PER_POUND = 0.45359237;\n\n/** 1 in = 25.4 mm exactly (international inch, 1959). Matches `ElevatorConversionConstants.inchToMm`. */\nexport const MM_PER_INCH = 25.4;\n\n/**\n * Deprecated alias for `MM_PER_INCH` — kept for back-compat with any\n * downstream code that imported `MILLIMETERS_PER_INCH` from the\n * Flutter reference's `elevator_conversions.dart` (where the\n * constant is named `inchToMm`). New code should use `MM_PER_INCH`.\n */\nexport const MILLIMETERS_PER_INCH = MM_PER_INCH;\n\n/** 1 ft = 0.3048 m exactly. Matches `ElevatorConversionConstants.footToMeter`. */\nexport const METERS_PER_FOOT = 0.3048;\n\n/** 1 HP = 0.745699872 kW exactly. Matches `ElevatorConversionConstants.horsepowerToKw`. */\nexport const KW_PER_HP = 0.745699872;\n\n/** 1 psi = 6.894757293168 kPa exactly. Matches `ElevatorConversionConstants.psiToKpa`. */\nexport const KPA_PER_PSI = 6.894757293168;\n\n/** 1 psi = 0.06894757293168 bar exactly. Matches `ElevatorConversionConstants.psiToBar`. */\nexport const BAR_PER_PSI = 0.06894757293168;\n\n/** 1 lbf = 4.4482216152605 N exactly. Matches `ElevatorConversionConstants.poundForceToNewton`. */\nexport const NEWTONS_PER_POUND_FORCE = 4.4482216152605;\n\n// ---------------------------------------------------------------------------\n// Inverse constants — derived (not stored in the Dart source) so consumers\n// can compare A→B and B→A without recomputing `1/x` at the call site.\n// ---------------------------------------------------------------------------\n\n/** 1 m/s = 196.85039... fpm. */\nexport const FPM_PER_MPS = 1 / MPS_PER_FPM;\n\n/** 1 kg = 2.2046226... lb. */\nexport const POUNDS_PER_KILOGRAM = 1 / KG_PER_POUND;\n\n/** 1 mm = 1/25.4 in. */\nexport const INCHES_PER_MM = 1 / MM_PER_INCH;\n\n/** 1 m = 3.2808399... ft. */\nexport const FEET_PER_METER = 1 / METERS_PER_FOOT;\n\n/** 1 kW = 1/0.745699872 HP. */\nexport const HP_PER_KW = 1 / KW_PER_HP;\n\n/** 1 kPa = 1/6.894757293168 psi. */\nexport const PSI_PER_KPA = 1 / KPA_PER_PSI;\n\n/** 1 bar = 1/0.06894757293168 psi. */\nexport const PSI_PER_BAR = 1 / BAR_PER_PSI;\n\n/** 1 N = 1/4.4482216152605 lbf. */\nexport const POUND_FORCE_PER_NEWTON = 1 / NEWTONS_PER_POUND_FORCE;\n\n/**\n * 1 m/s² = 60 / 0.3048 fpm/s.\n *\n * The Dart source does not pin an acceleration constant — the brief\n * asks for `mps²↔fpm/s` as a derived pair. The factor arises from\n * 1 m/s² = (1 m/s²) × (1 ft / 0.3048 m) × (60 s / 1 min) = 60/0.3048\n * fpm/s ≈ 196.85039 fpm/s.\n *\n * Verified against the Flutter reference:\n *   1 m/s² = 196.8503937007874 fpm/s\n *   1 fpm/s = 0.00508 m/s²   (which equals MPS_PER_FPM by construction —\n *   converting fpm/s → m/s² only re-scales the time unit from s to min/60,\n *   cancelling out, so the inverse is the same factor as fpm↔m/s)\n *\n * In one line: `METERS_PER_SECOND_SQUARED_PER_FPM_PER_SECOND`\n *   = METERS_PER_FOOT / 60 = 0.00508 (= MPS_PER_FPM).\n */\nexport const FPM_PER_SECOND_PER_MPS_PER_SECOND = 60 / METERS_PER_FOOT;\n\n/** 1 fpm/s = 0.00508 m/s² exactly. Same factor as MPS_PER_FPM — see the note above. */\nexport const MPS_PER_SECOND_SQUARED_PER_FPM_PER_SECOND = 1 / FPM_PER_SECOND_PER_MPS_PER_SECOND;\n\n// ---------------------------------------------------------------------------\n// Conversions — fpm ↔ m/s\n// ---------------------------------------------------------------------------\n\n/**\n * Convert feet per minute to meters per second.\n *\n * @param fpm Speed in fpm\n * @returns Speed in m/s\n */\nexport function fpmToMps(fpm: number): number {\n\treturn fpm * MPS_PER_FPM;\n}\n\n/**\n * Convert meters per second to feet per minute.\n *\n * @param mps Speed in m/s\n * @returns Speed in fpm\n */\nexport function mpsToFpm(mps: number): number {\n\treturn mps * FPM_PER_MPS;\n}\n\n// ---------------------------------------------------------------------------\n// Conversions — lbs ↔ kg\n// ---------------------------------------------------------------------------\n\n/**\n * Convert pounds to kilograms.\n *\n * @param pounds Mass in lb\n * @returns Mass in kg\n */\nexport function poundsToKilograms(pounds: number): number {\n\treturn pounds * KG_PER_POUND;\n}\n\n/**\n * Convert kilograms to pounds.\n *\n * @param kilograms Mass in kg\n * @returns Mass in lb\n */\nexport function kilogramsToPounds(kilograms: number): number {\n\treturn kilograms * POUNDS_PER_KILOGRAM;\n}\n\n// ---------------------------------------------------------------------------\n// Conversions — inches ↔ millimeters\n// ---------------------------------------------------------------------------\n\n/**\n * Convert inches to millimeters.\n *\n * @param inches Length in in\n * @returns Length in mm\n */\nexport function inchesToMillimeters(inches: number): number {\n\treturn inches * MM_PER_INCH;\n}\n\n/**\n * Convert millimeters to inches.\n *\n * @param millimeters Length in mm\n * @returns Length in in\n */\nexport function millimetersToInches(millimeters: number): number {\n\treturn millimeters * INCHES_PER_MM;\n}\n\n// ---------------------------------------------------------------------------\n// Conversions — feet ↔ meters\n// ---------------------------------------------------------------------------\n\n/**\n * Convert feet to meters.\n *\n * @param feet Length in ft\n * @returns Length in m\n */\nexport function feetToMeters(feet: number): number {\n\treturn feet * METERS_PER_FOOT;\n}\n\n/**\n * Convert meters to feet.\n *\n * @param meters Length in m\n * @returns Length in ft\n */\nexport function metersToFeet(meters: number): number {\n\treturn meters * FEET_PER_METER;\n}\n\n// ---------------------------------------------------------------------------\n// Conversions — acceleration (m/s² ↔ fpm/s)\n//\n// Unit analysis: m/s² is meters-per-second-squared. fpm/s is\n// feet-per-minute per second = ft/min·s — i.e. ft per minute per second.\n// Factor: 1 m/s² × (1 ft / 0.3048 m) × (60 s / 1 min) = 60/0.3048 fpm/s.\n// ---------------------------------------------------------------------------\n\n/**\n * Convert m/s² to fpm/s (feet per minute per second).\n *\n * @param mps2 Acceleration in m/s²\n * @returns Acceleration in fpm/s\n */\nexport function mpsSquaredToFpmPerSecond(mps2: number): number {\n\treturn mps2 * FPM_PER_SECOND_PER_MPS_PER_SECOND;\n}\n\n/**\n * Convert fpm/s (feet per minute per second) to m/s².\n *\n * @param fpmPerSecond Acceleration in fpm/s\n * @returns Acceleration in m/s²\n */\nexport function fpmPerSecondToMpsSquared(fpmPerSecond: number): number {\n\treturn fpmPerSecond * MPS_PER_SECOND_SQUARED_PER_FPM_PER_SECOND;\n}\n\n// ---------------------------------------------------------------------------\n// Conversions — HP ↔ kW (kept here so the unit table stays co-located, even\n// though no current calculator uses it directly).\n// ---------------------------------------------------------------------------\n\n/**\n * Convert horsepower to kilowatts.\n *\n * @param horsepower Power in HP\n * @returns Power in kW\n */\nexport function horsepowerToKw(horsepower: number): number {\n\treturn horsepower * KW_PER_HP;\n}\n\n/**\n * Convert kilowatts to horsepower.\n *\n * @param kilowatts Power in kW\n * @returns Power in HP\n */\nexport function kilowattsToHorsepower(kilowatts: number): number {\n\treturn kilowatts * HP_PER_KW;\n}\n\n// ---------------------------------------------------------------------------\n// Conversions — psi ↔ kPa / bar\n// ---------------------------------------------------------------------------\n\n/**\n * Convert psi to kilopascals.\n *\n * @param psi Pressure in psi\n * @returns Pressure in kPa\n */\nexport function psiToKpa(psi: number): number {\n\treturn psi * KPA_PER_PSI;\n}\n\n/**\n * Convert kilopascals to psi.\n *\n * @param kpa Pressure in kPa\n * @returns Pressure in psi\n */\nexport function kpaToPsi(kpa: number): number {\n\treturn kpa * PSI_PER_KPA;\n}\n\n/**\n * Convert psi to bar.\n *\n * @param psi Pressure in psi\n * @returns Pressure in bar\n */\nexport function psiToBar(psi: number): number {\n\treturn psi * BAR_PER_PSI;\n}\n\n/**\n * Convert bar to psi.\n *\n * @param bar Pressure in bar\n * @returns Pressure in psi\n */\nexport function barToPsi(bar: number): number {\n\treturn bar * PSI_PER_BAR;\n}\n\n// ---------------------------------------------------------------------------\n// Conversions — pound-force ↔ newton\n// ---------------------------------------------------------------------------\n\n/**\n * Convert pound-force to newtons.\n *\n * @param poundForce Force in lbf\n * @returns Force in N\n */\nexport function poundForceToNewtons(poundForce: number): number {\n\treturn poundForce * NEWTONS_PER_POUND_FORCE;\n}\n\n/**\n * Convert newtons to pound-force.\n *\n * @param newtons Force in N\n * @returns Force in lbf\n */\nexport function newtonsToPoundForce(newtons: number): number {\n\treturn newtons * POUND_FORCE_PER_NEWTON;\n}\n\n// ---------------------------------------------------------------------------\n// Input parsers — ported verbatim from `elevator_conversions.dart`\n// (`parseConversionInput`, `parseOptionalPositive`, `parsePositive`).\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a user-entered string as a finite number.\n *\n * Returns `null` for empty strings, whitespace-only, unparsable,\n * NaN, or ±Infinity — never throws. Mirrors Dart's\n * `parseConversionInput` from `elevator_conversions.dart:171`.\n *\n * Note: `Number(\"\")` returns `0` (not `NaN`), so we explicitly\n * bail out on the empty-string path the way Dart's\n * `double.tryParse(\"\")` → `null` does.\n *\n * @param value Raw input from a form field\n * @returns Parsed finite number or `null`\n */\nexport function parseConversionInput(value: string): number | null {\n\tconst trimmed = value.trim();\n\tif (trimmed.length === 0) {\n\t\treturn null;\n\t}\n\tconst parsed = Number(trimmed);\n\tif (Number.isNaN(parsed) || !Number.isFinite(parsed)) {\n\t\treturn null;\n\t}\n\treturn parsed;\n}\n\n/**\n * Parse a positive (>0) number. Empty / non-numeric / ≤0 → `null`.\n *\n * Mirrors `parseOptionalPositive` from `electrical_calculations.dart:878`\n * + the same name in `elevator_conversions.dart` (the Dart source\n * has both — identical bodies).\n *\n * @param value Raw input from a form field\n * @returns Parsed positive number or `null`\n */\nexport function parseOptionalPositive(value: string): number | null {\n\tconst trimmed = value.trim();\n\tif (trimmed.length === 0) {\n\t\treturn null;\n\t}\n\tconst parsed = Number(trimmed);\n\tif (Number.isNaN(parsed) || parsed <= 0) {\n\t\treturn null;\n\t}\n\treturn parsed;\n}\n\n/**\n * Parse a strictly-positive (>0) number required field — empty\n * input throws with a useful message (mirrors Dart's\n * `parsePositive` from `elevrical_calculations.dart` which is the\n * `double.parse` + validation path used by the UI's required\n * input handling).\n *\n * The Dart source treats `parsePositive` as a \"throw-on-bad-input\"\n * helper at the UI boundary. We replicate the throw here.\n *\n * @param value Raw input from a form field (must be non-empty + finite + >0)\n * @returns Parsed positive number\n * @throws Error if the input is empty, non-numeric, or ≤0\n */\nexport function parsePositive(value: string): number {\n\tconst parsed = parseOptionalPositive(value);\n\tif (parsed === null) {\n\t\tthrow new Error(\n\t\t\t`parsePositive: expected a positive number, got \"${value}\"`\n\t\t);\n\t}\n\treturn parsed;\n}\n\n/**\n * Format a number for display in a converter tile. Trailing zeros\n * trimmed; outputs exponential for very small / very large values.\n *\n * Port of `formatConversionResult` from\n * `elevator_conversions.dart:179`. Decimals are bumped by 2 when\n * the absolute value is below 1 (the Flutter UI renders small\n * numbers with more fractional precision).\n *\n * @param value Finite number to format\n * @param decimals Base decimal count (overridden if value is < 1)\n * @returns The trimmed string, or \"—\" for non-finite values\n */\nexport function formatConversionResult(\n\tvalue: number,\n\tdecimals = 4\n): string {\n\tif (!Number.isFinite(value)) {\n\t\treturn \"—\";\n\t}\n\tconst abs = Math.abs(value);\n\tconst resolvedDecimals =\n\t\tabs >= 1000 ? 2 : abs >= 1 ? decimals : decimals + 2;\n\tlet text = value.toFixed(resolvedDecimals);\n\tif (text.includes(\".\")) {\n\t\t// Trim trailing zeros after the decimal point, then trim a\n\t\t// trailing \".\" if it remains. Mirrors the Dart regex chain:\n\t\t//   `0+$` then `\\.0+$` then `\\.$`.\n\t\ttext = text.replace(/0+$/, \"\").replace(/\\.$/, \"\");\n\t}\n\treturn text;\n}\n\n// ---------------------------------------------------------------------------\n// Pair descriptor + category grouping — mirrors `ConversionPair` +\n// `ConversionCategory` in Dart. The presentation layer\n// (`/tools/converters/+page.svelte`) reads `CONVERSION_CATEGORIES`\n// to render the sectioned tile grid; calculator UIs import the raw\n// helpers above.\n// ---------------------------------------------------------------------------\n\n/**\n * A pair of units with a fixed A→B factor. Renders both directions via\n * `convertAtoB` / `convertBtoA`.\n */\nexport interface ConversionPair {\n\treadonly id: string;\n\treadonly title: string;\n\treadonly unitA: string;\n\treadonly unitB: string;\n\treadonly factorAToB: number;\n\treadonly note?: string;\n\treadonly decimals: number;\n\tconvertAtoB(value: number): number;\n\tconvertBtoA(value: number): number;\n}\n\n/**\n * A group of conversion pairs sharing a theme (Speed / Mass /\n * Length / Pressure / Power / Force). Mirrors Flutter's\n * `ConversionCategory` from `elevator_conversions.dart:28`.\n */\nexport interface ConversionCategory {\n\treadonly eyebrow: string;\n\treadonly title: string;\n\treadonly description: string;\n\treadonly pairs: ReadonlyArray<ConversionPair>;\n}\n\n/**\n * Build a typed `ConversionPair` value from the helper layer.\n * Kept as a builder (not a class) so the result is JSON-serializable\n * and round-trips through `devalue` cleanly.\n */\nexport function makeConversionPair(spec: {\n\tid: string;\n\ttitle: string;\n\tunitA: string;\n\tunitB: string;\n\tfactorAToB: number;\n\tnote?: string;\n\tdecimals?: number;\n\taToB: (v: number) => number;\n\tbToA: (v: number) => number;\n}): ConversionPair {\n\treturn {\n\t\tid: spec.id,\n\t\ttitle: spec.title,\n\t\tunitA: spec.unitA,\n\t\tunitB: spec.unitB,\n\t\tfactorAToB: spec.factorAToB,\n\t\tnote: spec.note,\n\t\tdecimals: spec.decimals ?? 4,\n\t\tconvertAtoB: spec.aToB,\n\t\tconvertBtoA: spec.bToA\n\t};\n}\n\n/**\n * The conversion pair catalogue — mirrors\n * `conversionCategories` from `elevator_conversions.dart:54`.\n * Add a new pair by appending an entry below; the calculator\n * UI layer iterates this array to render the tile grid.\n */\nexport const CONVERSION_PAIRS: ReadonlyArray<ConversionPair> = [\n\tmakeConversionPair({\n\t\tid: \"fpm_mps\",\n\t\ttitle: \"Feet per minute ↔ meters per second\",\n\t\tunitA: \"fpm\",\n\t\tunitB: \"m/s\",\n\t\tfactorAToB: MPS_PER_FPM,\n\t\tnote: \"× 0.00508 (fpm → m/s)\",\n\t\taToB: fpmToMps,\n\t\tbToA: mpsToFpm\n\t}),\n\tmakeConversionPair({\n\t\tid: \"lb_kg\",\n\t\ttitle: \"Pounds ↔ kilograms\",\n\t\tunitA: \"lb\",\n\t\tunitB: \"kg\",\n\t\tfactorAToB: KG_PER_POUND,\n\t\tnote: \"× 0.45359237 (lb → kg)\",\n\t\taToB: poundsToKilograms,\n\t\tbToA: kilogramsToPounds\n\t}),\n\tmakeConversionPair({\n\t\tid: \"in_mm\",\n\t\ttitle: \"Inches ↔ millimeters\",\n\t\tunitA: \"in\",\n\t\tunitB: \"mm\",\n\t\tfactorAToB: MM_PER_INCH,\n\t\tnote: \"× 25.4 (in → mm)\",\n\t\taToB: inchesToMillimeters,\n\t\tbToA: millimetersToInches\n\t}),\n\tmakeConversionPair({\n\t\tid: \"ft_m\",\n\t\ttitle: \"Feet ↔ meters\",\n\t\tunitA: \"ft\",\n\t\tunitB: \"m\",\n\t\tfactorAToB: METERS_PER_FOOT,\n\t\tnote: \"× 0.3048 (ft → m)\",\n\t\taToB: feetToMeters,\n\t\tbToA: metersToFeet\n\t}),\n\tmakeConversionPair({\n\t\tid: \"mps2_fpmps\",\n\t\ttitle: \"Meters per second² ↔ ft/min per second\",\n\t\tunitA: \"m/s²\",\n\t\tunitB: \"fpm/s\",\n\t\tfactorAToB: FPM_PER_SECOND_PER_MPS_PER_SECOND,\n\t\tnote: \"× 196.85039 (m/s² → fpm/s)\",\n\t\taToB: mpsSquaredToFpmPerSecond,\n\t\tbToA: fpmPerSecondToMpsSquared\n\t}),\n\tmakeConversionPair({\n\t\tid: \"psi_kpa\",\n\t\ttitle: \"PSI ↔ kilopascals\",\n\t\tunitA: \"psi\",\n\t\tunitB: \"kPa\",\n\t\tfactorAToB: KPA_PER_PSI,\n\t\tdecimals: 3,\n\t\tnote: \"× 6.894757… (psi → kPa)\",\n\t\taToB: psiToKpa,\n\t\tbToA: kpaToPsi\n\t}),\n\tmakeConversionPair({\n\t\tid: \"psi_bar\",\n\t\ttitle: \"PSI ↔ bar\",\n\t\tunitA: \"psi\",\n\t\tunitB: \"bar\",\n\t\tfactorAToB: BAR_PER_PSI,\n\t\tnote: \"× 0.06894757… (psi → bar)\",\n\t\taToB: psiToBar,\n\t\tbToA: barToPsi\n\t}),\n\tmakeConversionPair({\n\t\tid: \"hp_kw\",\n\t\ttitle: \"Horsepower ↔ kilowatts\",\n\t\tunitA: \"HP\",\n\t\tunitB: \"kW\",\n\t\tfactorAToB: KW_PER_HP,\n\t\tnote: \"× 0.745699872 (HP → kW)\",\n\t\taToB: horsepowerToKw,\n\t\tbToA: kilowattsToHorsepower\n\t}),\n\tmakeConversionPair({\n\t\tid: \"lbf_n\",\n\t\ttitle: \"Pound-force ↔ newtons\",\n\t\tunitA: \"lbf\",\n\t\tunitB: \"N\",\n\t\tfactorAToB: NEWTONS_PER_POUND_FORCE,\n\t\tdecimals: 3,\n\t\tnote: \"× 4.448221615… (lbf → N)\",\n\t\taToB: poundForceToNewtons,\n\t\tbToA: newtonsToPoundForce\n\t})\n];\n\n// ---------------------------------------------------------------------------\n// Converter categories — mirrors `conversionCategories` from\n// `elevator_conversions.dart:54`. The Flutter reference groups pairs\n// under six themes (Speed / Mass / Length / Pressure / Power /\n// Force); the page renders these as the top-level section headings.\n// ---------------------------------------------------------------------------\n\nexport const CONVERSION_CATEGORIES: ReadonlyArray<ConversionCategory> = [\n\t{\n\t\teyebrow: \"Speed\",\n\t\ttitle: \"Rated & inspection speed\",\n\t\tdescription:\n\t\t\t\"fpm ↔ m/s (rated travel) and m/s² ↔ fpm/s (acceleration during run-up).\",\n\t\tpairs: [\n\t\t\tmakeConversionPair({\n\t\t\t\tid: \"fpm_mps\",\n\t\t\t\ttitle: \"Feet per minute ↔ meters per second\",\n\t\t\t\tunitA: \"fpm\",\n\t\t\t\tunitB: \"m/s\",\n\t\t\t\tfactorAToB: MPS_PER_FPM,\n\t\t\t\tnote: \"× 0.00508 (fpm → m/s)\",\n\t\t\t\taToB: fpmToMps,\n\t\t\t\tbToA: mpsToFpm\n\t\t\t}),\n\t\t\tmakeConversionPair({\n\t\t\t\tid: \"mps2_fpmps\",\n\t\t\t\ttitle: \"Meters per second² ↔ ft/min per second\",\n\t\t\t\tunitA: \"m/s²\",\n\t\t\t\tunitB: \"fpm/s\",\n\t\t\t\tfactorAToB: FPM_PER_SECOND_PER_MPS_PER_SECOND,\n\t\t\t\tnote: \"× 196.85039 (m/s² → fpm/s)\",\n\t\t\t\taToB: mpsSquaredToFpmPerSecond,\n\t\t\t\tbToA: fpmPerSecondToMpsSquared\n\t\t\t})\n\t\t]\n\t},\n\t{\n\t\teyebrow: \"Mass\",\n\t\ttitle: \"Load & counterweight mass\",\n\t\tdescription:\n\t\t\t\"Pounds to kilograms, used for the load and counterweight on hydraulic / traction jobs.\",\n\t\tpairs: [\n\t\t\tmakeConversionPair({\n\t\t\t\tid: \"lb_kg\",\n\t\t\t\ttitle: \"Pounds ↔ kilograms\",\n\t\t\t\tunitA: \"lb\",\n\t\t\t\tunitB: \"kg\",\n\t\t\t\tfactorAToB: KG_PER_POUND,\n\t\t\t\tnote: \"× 0.45359237 (lb → kg)\",\n\t\t\t\taToB: poundsToKilograms,\n\t\t\t\tbToA: kilogramsToPounds\n\t\t\t})\n\t\t]\n\t},\n\t{\n\t\teyebrow: \"Length\",\n\t\ttitle: \"Travel, door, and clearance dimensions\",\n\t\tdescription:\n\t\t\t\"Hoistway, entrance, buffer stroke, and landing dimensions.\",\n\t\tpairs: [\n\t\t\tmakeConversionPair({\n\t\t\t\tid: \"in_mm\",\n\t\t\t\ttitle: \"Inches ↔ millimeters\",\n\t\t\t\tunitA: \"in\",\n\t\t\t\tunitB: \"mm\",\n\t\t\t\tfactorAToB: MM_PER_INCH,\n\t\t\t\tnote: \"× 25.4 (in → mm)\",\n\t\t\t\taToB: inchesToMillimeters,\n\t\t\t\tbToA: millimetersToInches\n\t\t\t}),\n\t\t\tmakeConversionPair({\n\t\t\t\tid: \"ft_m\",\n\t\t\t\ttitle: \"Feet ↔ meters\",\n\t\t\t\tunitA: \"ft\",\n\t\t\t\tunitB: \"m\",\n\t\t\t\tfactorAToB: METERS_PER_FOOT,\n\t\t\t\tnote: \"× 0.3048 (ft → m)\",\n\t\t\t\taToB: feetToMeters,\n\t\t\t\tbToA: metersToFeet\n\t\t\t})\n\t\t]\n\t},\n\t{\n\t\teyebrow: \"Pressure\",\n\t\ttitle: \"Hydraulic & safety valve settings\",\n\t\tdescription: \"Line and relief pressures on hydraulic elevators.\",\n\t\tpairs: [\n\t\t\tmakeConversionPair({\n\t\t\t\tid: \"psi_kpa\",\n\t\t\t\ttitle: \"PSI ↔ kilopascals\",\n\t\t\t\tunitA: \"psi\",\n\t\t\t\tunitB: \"kPa\",\n\t\t\t\tfactorAToB: KPA_PER_PSI,\n\t\t\t\tdecimals: 3,\n\t\t\t\tnote: \"× 6.894757… (psi → kPa)\",\n\t\t\t\taToB: psiToKpa,\n\t\t\t\tbToA: kpaToPsi\n\t\t\t}),\n\t\t\tmakeConversionPair({\n\t\t\t\tid: \"psi_bar\",\n\t\t\t\ttitle: \"PSI ↔ bar\",\n\t\t\t\tunitA: \"psi\",\n\t\t\t\tunitB: \"bar\",\n\t\t\t\tfactorAToB: BAR_PER_PSI,\n\t\t\t\tnote: \"× 0.06894757… (psi → bar)\",\n\t\t\t\taToB: psiToBar,\n\t\t\t\tbToA: barToPsi\n\t\t\t})\n\t\t]\n\t},\n\t{\n\t\teyebrow: \"Power\",\n\t\ttitle: \"Machine nameplate power\",\n\t\tdescription: \"Motor horsepower versus kilowatts.\",\n\t\tpairs: [\n\t\t\tmakeConversionPair({\n\t\t\t\tid: \"hp_kw\",\n\t\t\t\ttitle: \"Horsepower ↔ kilowatts\",\n\t\t\t\tunitA: \"HP\",\n\t\t\t\tunitB: \"kW\",\n\t\t\t\tfactorAToB: KW_PER_HP,\n\t\t\t\tnote: \"× 0.745699872 (HP → kW)\",\n\t\t\t\taToB: horsepowerToKw,\n\t\t\t\tbToA: kilowattsToHorsepower\n\t\t\t})\n\t\t]\n\t},\n\t{\n\t\teyebrow: \"Force\",\n\t\ttitle: \"Rope tension & load force\",\n\t\tdescription:\n\t\t\t\"Static rope and compensation forces (earth gravity, 1 g).\",\n\t\tpairs: [\n\t\t\tmakeConversionPair({\n\t\t\t\tid: \"lbf_n\",\n\t\t\t\ttitle: \"Pound-force ↔ newtons\",\n\t\t\t\tunitA: \"lbf\",\n\t\t\t\tunitB: \"N\",\n\t\t\t\tfactorAToB: NEWTONS_PER_POUND_FORCE,\n\t\t\t\tdecimals: 3,\n\t\t\t\tnote: \"× 4.448221615… (lbf → N)\",\n\t\t\t\taToB: poundForceToNewtons,\n\t\t\t\tbToA: newtonsToPoundForce\n\t\t\t})\n\t\t]\n\t}\n];\n\n// ---------------------------------------------------------------------------\n// Result + compute helper — mirrors Dart's `computeConversion` +\n// `ConversionResult` from `elevator_conversions.dart:198`. The page\n// renders `result.display` in the gradient result box.\n// ---------------------------------------------------------------------------\n\nexport interface ConversionResult {\n\treadonly input: number;\n\treadonly output: number;\n\treadonly fromLabel: string;\n\treadonly toLabel: string;\n\treadonly display: string;\n}\n\n/**\n * Run a conversion pair against a parsed input. Returns `null` when\n * `input` is empty / non-numeric (the Flutter UI shows a placeholder\n * line in that case).\n *\n * The Flutter reference picks `_fromUnitA = true` by default and\n * toggles via `SegmentedButton`; we mirror the same default and let\n * the caller flip the direction.\n */\nexport function computeConversion(opts: {\n\tpair: ConversionPair;\n\tinput: number | null;\n\tfromUnitA: boolean;\n}): ConversionResult | null {\n\tif (opts.input === null) {\n\t\treturn null;\n\t}\n\tconst output = opts.fromUnitA\n\t\t? opts.pair.convertAtoB(opts.input)\n\t\t: opts.pair.convertBtoA(opts.input);\n\tconst fromLabel = opts.fromUnitA ? opts.pair.unitA : opts.pair.unitB;\n\tconst toLabel = opts.fromUnitA ? opts.pair.unitB : opts.pair.unitA;\n\tconst decimals = opts.pair.decimals;\n\treturn {\n\t\tinput: opts.input,\n\t\toutput,\n\t\tfromLabel,\n\t\ttoLabel,\n\t\tdisplay: `${formatConversionResult(opts.input, decimals)} ${fromLabel} → ${formatConversionResult(output, decimals)} ${toLabel}`\n\t};\n}\n"],"names":["MPS_PER_FPM","KG_PER_POUND","MM_PER_INCH","METERS_PER_FOOT","KW_PER_HP","KPA_PER_PSI","BAR_PER_PSI","NEWTONS_PER_POUND_FORCE","FPM_PER_MPS","POUNDS_PER_KILOGRAM","INCHES_PER_MM","FEET_PER_METER","HP_PER_KW","PSI_PER_KPA","PSI_PER_BAR","POUND_FORCE_PER_NEWTON","FPM_PER_SECOND_PER_MPS_PER_SECOND","MPS_PER_SECOND_SQUARED_PER_FPM_PER_SECOND","fpmToMps","fpm","mpsToFpm","mps","poundsToKilograms","pounds","kilogramsToPounds","kilograms","inchesToMillimeters","inches","millimetersToInches","millimeters","feetToMeters","feet","metersToFeet","meters","mpsSquaredToFpmPerSecond","mps2","fpmPerSecondToMpsSquared","fpmPerSecond","horsepowerToKw","horsepower","kilowattsToHorsepower","kilowatts","psiToKpa","psi","kpaToPsi","kpa","psiToBar","barToPsi","bar","poundForceToNewtons","poundForce","newtonsToPoundForce","newtons","parseConversionInput","value","trimmed","parsed","formatConversionResult","decimals","abs","resolvedDecimals","text","makeConversionPair","spec","CONVERSION_CATEGORIES","computeConversion","opts","output","fromLabel","toLabel"],"mappings":"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"}