Skip to content

fix(editor): unit-aware sidebar controls + preserve user-typed precision#238

Open
b9llach wants to merge 1 commit intopascalorg:mainfrom
b9llach:billy/unit-aware-sidebar
Open

fix(editor): unit-aware sidebar controls + preserve user-typed precision#238
b9llach wants to merge 1 commit intopascalorg:mainfrom
b9llach:billy/unit-aware-sidebar

Conversation

@b9llach
Copy link
Copy Markdown

@b9llach b9llach commented Apr 15, 2026

What does this PR do?

Two related fixes to the sidebar's length inputs.

1. SliderControl is unit-aware

useViewer.unit ('metric' | 'imperial') already existed and was toggled by the toolbar m/ft button, but the only places that actually respected it were wall-measurement-label, site-edge-labels, metric-control, and the 2D floorplan-panel. Every sidebar slider hard-coded unit="m" as its suffix and showed raw metres regardless of the user's preference. This PR teaches SliderControl to read useViewer.unit and, when the caller passes unit="m", convert the value for display + editing while keeping scene data in metres (conversion is display-only). About 70 slider instances across every panel pick up imperial support with zero call-site changes.

Sliders with non-"m" unit strings (e.g. "%", "°") are unchanged.

2. Parent-side Math.round(...) clipping removed from panels

Every panel had a value={Math.round(node.X * 100) / 100} pattern around its sliders. It was there to defend against floating-point display noise (2.10000000000000001"2.10"), but SliderControl's own .toFixed(precision) already does that — so the parent rounds were redundant even in metric mode.

With the imperial path landed, the parent rounds became actively harmful. Tracing the door-height precision={2} case:

  1. User sees "8.01 ft" (whatever was stored), types 8.00, Enter
  2. submitValue converts: 8 / 3.28084 ≈ 2.43840 m
  3. onChange(2.43840) — handler calls updateNode({ height: 2.43840 })
  4. React re-renders; door-panel.tsx passes value={Math.round(node.height * 100) / 100} = 2.44
  5. SliderControl gets 2.44, displays toDisplay(2.44).toFixed(2) = (2.44 × 3.28084).toFixed(2) = "8.01"

So typing 8.00 bounced back to 8.01. Removing the Math.round in the parent lets the full-precision metres value survive the round trip, and the display cleanly shows "8.00" again.

The submitValue path also now rounds in the display unit before converting back to metres, so user-typed values preserve their exact precision regardless of which unit is stored underneath.

New shared helper

packages/editor/src/lib/units.tsformatLength, formatArea, metersToFeet, feetToMeters, METERS_TO_FEET. The slab + ceiling Info sections now use formatArea(area, unit) instead of a hardcoded "m²" suffix. Follow-up opportunity (out of scope for this PR): consolidate the four existing ad-hoc copies in wall-measurement-label.tsx, site-edge-labels.tsx, metric-control.tsx, and floorplan-panel.tsx onto this new module.

How to test

  1. bun dev
  2. Click the m/ft toggle in the viewer toolbar.
  3. Click a door: every slider suffix should flip from m to ft, and the numbers should reflect the conversion (2.1 m height shows as 6.89 ft).
  4. Type 8.00 into any length slider while in imperial mode, press Enter: value should stay at 8.00 ft (on main it snaps to 8.01 ft).
  5. Flip back to metric: everything returns to metres, no precision loss.
  6. Click a slab, check the Info section: area should read "12.50 m²" in metric and "134.55 ft²" in imperial.

Screenshots / screen recording

N/A — unit-toggle behaviour, hard to screenshot without video. Will record a short clip if reviewers want one.

Checklist

  • I've tested this locally with bun dev
  • My code follows the existing code style (bun check passes on the touched files — verified via biome check at @biomejs/biome@^2.4.6, which is the version pinned in root package.json)
  • I've updated relevant documentation (N/A — no docs affected)
  • This PR targets the main branch

Two related fixes to the sidebar's length inputs:

1. `SliderControl` now reads `useViewer.unit` and, when the caller
   passes `unit="m"`, displays / edits / round-trips length values in
   feet when the user has toggled imperial. Scene data stays in
   metres — conversion is display-only. Roughly 70 slider instances
   across every panel pick up imperial support for free with no
   call-site changes. Fixes a pre-existing half-wired feature: the
   `useViewer.unit` toggle in the toolbar was only respected by
   `wall-measurement-label`, `site-edge-labels`, `metric-control`,
   and `floorplan-panel`; every panel slider hardcoded `"m"` as its
   suffix and showed raw metres regardless.

2. Removed parent-side `Math.round(node.X * 100) / 100` clipping
   from every panel that passed it as the `value` prop to a slider.
   These defensive rounds were there to avoid floating-point display
   noise but actively broke the new imperial round-trip: typing
   `8.00 ft` into a door-height slider with `precision={2}` would
   round the converted metres value to `2.44 m` (losing 4 mm), then
   the re-display `2.44 × 3.28084 ≈ 8.0052` rounds to `"8.01"`.
   SliderControl's own `.toFixed(precision)` already handles the
   display cleanliness the parents were trying to achieve, so the
   parent-side rounds were redundant even in metric mode.

   The `submitValue` path also now rounds in the DISPLAY unit before
   converting back to metres, so user-typed values preserve their
   exact precision regardless of the stored unit.

New: `packages/editor/src/lib/units.ts` — shared `formatLength`,
`formatArea`, `metersToFeet`, `feetToMeters` helpers that replace
the four ad-hoc copies scattered around the editor. The slab and
ceiling panel Info sections now use `formatArea(area, unit)`
instead of a hardcoded `"m²"` suffix.

Caveats:
- Sliders with non-`"m"` units (percent, angle degrees, etc.) are
  unchanged — only `unit="m"` triggers the imperial conversion.
- Follow-up opportunity: the four existing `formatMeasurement` /
  `formatArea` duplicates in `wall-measurement-label.tsx`,
  `site-edge-labels.tsx`, `metric-control.tsx`, and `floorplan-
  panel.tsx` could be consolidated onto the new `lib/units.ts`
  helpers in a separate refactor PR.
@b9llach b9llach marked this pull request as ready for review April 15, 2026 07:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant