diff --git a/hdyc-svelte/src/lib/components/QuickConversionTable.svelte b/hdyc-svelte/src/lib/components/QuickConversionTable.svelte
index eac68d7..2ef5f9d 100644
--- a/hdyc-svelte/src/lib/components/QuickConversionTable.svelte
+++ b/hdyc-svelte/src/lib/components/QuickConversionTable.svelte
@@ -8,8 +8,8 @@
type Row = { input: number; output: string };
- const buildRow = (value: number): Row => {
- const formatted = solve(config, 1, value.toString(), '', '');
+ const buildRow = (value: number, c: CalculatorDef): Row => {
+ const formatted = solve(c, 1, value.toString(), '', '');
return {
input: value,
output: formatted.val2 || '—',
@@ -22,8 +22,8 @@
let outputLabel = 'target units';
$: supportsTable = ['standard', 'inverse'].includes(config.type);
- $: rows = supportsTable
- ? numericSamples.map(buildRow)
+ $: rows = (config && supportsTable)
+ ? numericSamples.map(v => buildRow(v, config))
: [];
$: inputLabel = config.labels?.in1 ?? 'source units';
$: outputLabel = config.labels?.in2 ?? 'target units';
diff --git a/hdyc-svelte/src/routes/[slug]/+page.svelte b/hdyc-svelte/src/routes/[slug]/+page.svelte
index ad15cd0..15d837c 100644
--- a/hdyc-svelte/src/routes/[slug]/+page.svelte
+++ b/hdyc-svelte/src/routes/[slug]/+page.svelte
@@ -82,7 +82,9 @@
{calc.name}
-
+{#key calc.slug}
+
+{/key}
{#if calc.descriptionHTML}
diff --git a/tests/test_consistency.py b/tests/test_consistency.py
new file mode 100644
index 0000000..178547b
--- /dev/null
+++ b/tests/test_consistency.py
@@ -0,0 +1,106 @@
+
+import math
+import re
+import unittest
+from pathlib import Path
+
+ROOT = Path(__file__).resolve().parents[1]
+CALCULATORS_TS = ROOT / "hdyc-svelte" / "src" / "lib" / "data" / "calculators.ts"
+
+def _js_fmt(n: float) -> str:
+ """Mimics the fmt() function in engine.ts"""
+ if not math.isfinite(n):
+ return str(n)
+ if n == 0:
+ return "0"
+ if abs(n) < 1e-6:
+ return f"{n:.6e}".replace("e-0", "e-").replace("e+0", "e+")
+
+ # engine.ts uses parseFloat(n.toFixed(6)).toString()
+ rounded = round(n, 6)
+ if rounded == 0: # Handle -0.0
+ return "0"
+ if rounded == int(rounded):
+ return str(int(rounded))
+ return str(rounded)
+
+def _js_fmt_precise(n: float) -> str:
+ """Mimics formatExampleValue in QuickConversionExample.svelte"""
+ if n is None or math.isnan(n):
+ return "—"
+ if not math.isfinite(n):
+ return str(n)
+ if n == 0:
+ return "0"
+
+ rounded = round(n, 6)
+ if rounded != 0:
+ if rounded == int(rounded):
+ return str(int(rounded))
+ return str(rounded)
+
+ # Precise version for very small numbers
+ precise = f"{n:.12f}".rstrip('0').rstrip('.')
+ return precise if precise else "0"
+
+class TestCalculatorsConsistency(unittest.TestCase):
+ def test_standard_calculators_consistency(self):
+ text = CALCULATORS_TS.read_text(encoding="utf-8")
+
+ # Extract the calculators array content
+ match = re.search(r"export const calculators: CalculatorDef\[\] = \[(.*?)\];", text, re.S)
+ self.assertTrue(match, "Could not find calculators array in calculators.ts")
+
+ body = match.group(1)
+ # Split by '{"slug":' to avoid splitting on nested braces
+ raw_entries = body.split('{"slug":')
+
+ errors = []
+ for raw_entry in raw_entries:
+ if not raw_entry.strip():
+ continue
+ entry = '{"slug":' + raw_entry
+ slug_match = re.search(r'"slug": "(.*?)"', entry)
+ if not slug_match:
+ continue
+ slug = slug_match.group(1)
+
+ type_match = re.search(r'"type": "(.*?)"', entry)
+ if not type_match or type_match.group(1) != "standard":
+ continue
+
+ # Use non-greedy search for factor/offset and handle potential whitespace
+ factor_match = re.search(r'"factor":\s*([0-9.eE+-]+)', entry)
+ offset_match = re.search(r'"offset":\s*([0-9.eE+-]+)', entry)
+
+ factor = float(factor_match.group(1)) if factor_match else 1.0
+ offset = float(offset_match.group(1)) if offset_match else 0.0
+
+ # 1. Formula Hint vs Chart Row 1 Consistency
+ # Logic: solve(config, 1, "1") -> val2 = fmt(1 * factor + offset)
+ row_one_output = _js_fmt(1.0 * factor + offset)
+
+ # 2. How to convert Examples Consistency (Inverse)
+ if factor != 0:
+ reverse_val = (1.0 - offset) / factor
+ formatted_reverse = _js_fmt_precise(reverse_val)
+
+ # Specific check for Réaumur to Kelvin (User request)
+ if slug == "reaumur-to-kelvin":
+ if formatted_reverse != "-217.72":
+ errors.append(f"[{slug}] Reverse example mismatch: expected -217.72, got {formatted_reverse}")
+ if row_one_output != "274.4":
+ errors.append(f"[{slug}] Chart row 1 mismatch: expected 274.4, got {row_one_output}")
+
+ # Specific check for Feet per minute to Knots (Previous bug fix)
+ if slug == "feet-per-minute-to-knots":
+ if row_one_output != "0.009875":
+ errors.append(f"[{slug}] Chart row 1 mismatch: expected 0.009875, got {row_one_output}")
+ if formatted_reverse != "101.268504":
+ errors.append(f"[{slug}] Reverse example mismatch: expected 101.268504, got {formatted_reverse}")
+
+ if errors:
+ self.fail("\n" + "\n".join(errors))
+
+if __name__ == "__main__":
+ unittest.main()