Files
2026-03-17 13:22:32 -04:00

5.5 KiB

task, slug, effort, phase, progress, mode, started, updated
task slug effort phase progress mode started updated
Fix shift+click input coupling and highlight layout jump 20260316-120000_fix-shift-click-highlight-layout standard complete 24/24 interactive 2026-03-16T12:00:00Z 2026-03-16T12:08:00Z

Context

Humor-units is a Svelte unit converter. Two bugs fixed:

  1. Shift+click coupling bug: handleSetInput in +page.svelte was converting and setting inputValue when it should only set fromUnitId. A new Ctrl+click gesture is the intended "adopt value" action.

  2. Highlight layout jump bug: The .highlighted-result block was conditionally rendered, causing the page to shift when it appeared/disappeared. Fixed: always rendered with min-height: 8rem, conditional content inside.

Files changed: src/routes/+page.svelte, src/components/ConverterCard.svelte, src/components/ConversionResult.svelte, src/app.css.

Risks

  • Adding ctrlclick event must not interfere with shift+click detection — resolved by checking ctrlKey before shiftKey
  • min-height value must match actual content height — 8rem confirmed sufficient (2.5rem value + 1.25rem label + symbol + gaps ≈ 7-8rem)
  • visibility:hidden vs min-height choice — min-height chosen as simpler, no inner wrapper needed

Criteria

Issue 1 — Shift+click / Ctrl+click refactor

  • ISC-1: handleSetInput in +page.svelte sets fromUnitId only, no inputValue change
  • ISC-2: handleSetInput no longer calls convertById to compute a new inputValue
  • ISC-3: handleCtrlClick handler exists in +page.svelte and sets both fromUnitId and inputValue
  • ISC-4: handleCtrlClick sets inputValue to the converted value of the clicked tile (not raw input)
  • ISC-5: ConversionResult.svelte has onctrlclick prop in the props destructure
  • ISC-6: ConversionResult.svelte onctrlclick typed as () => void (value captured via closure in ConverterCard)
  • ISC-7: ConversionResult.svelte handleClick calls onctrlclick when e.ctrlKey is true
  • ISC-8: ConversionResult.svelte shift+click path still calls onshiftclick (unchanged behavior)
  • ISC-9: ConversionResult.svelte regular click (no modifier) still calls onclick (unchanged)
  • ISC-10: ConverterCard.svelte receives onctrlclick prop and types it correctly
  • ISC-11: ConverterCard.svelte passes onctrlclick={() => onctrlclick(item.unit.id, item.convertedValue)} to each ConversionResult
  • ISC-12: ConverterCard.svelte onctrlclick prop typed as (id: string, value: Big) => void
  • ISC-13: +page.svelte passes onctrlclick={handleCtrlClick} to ConverterCard
  • ISC-14: Hotkey hint in ConverterCard.svelte updated to include Ctrl+click description

Issue 2 — Highlight layout stability

  • ISC-15: .highlighted-result in app.css has min-height: 8rem
  • ISC-16: +page.svelte always renders the .highlighted-result div (no {#if} wrapper hiding it entirely)
  • ISC-17: When highlightedUnitId is null, .highlighted-result renders as empty/placeholder (no content visible)
  • ISC-18: When highlightedUnitId is not null, full content (value, label, symbol, dismiss button) renders inside
  • ISC-19: Page below the highlighted-result block does not shift when highlight is toggled

Build & verification

  • ISC-20: npm run build exits with zero errors
  • ISC-21: No TypeScript type errors introduced
  • ISC-22: Shift+click on a tile only changes fromUnitId — inputValue stays unchanged
  • ISC-23: Ctrl+click on a tile changes fromUnitId AND sets inputValue to that tile's converted value
  • ISC-24: Regular click on a tile still toggles highlight with no other state change

Decisions

  • Used min-height: 8rem over visibility: hidden — simpler, no extra wrapper div needed
  • ctrlKey checked before shiftKey in handleClick — gives Ctrl priority if both held; more specific action wins
  • onctrlclick in ConversionResult is () => void (value captured in ConverterCard closure), not (v: Big) => void — keeps the leaf component unaware of value semantics
  • Promoted import type { Big } to top-level in +page.svelte to match sibling component pattern
  • Merged identical :hover and .highlighted CSS rules; removed no-op background-color resets

Verification

  • ISC-1/2: handleSetInput confirmed as single line fromUnitId = id only — no convertById call, no inputValue mutation
  • ISC-3/4: handleCtrlClick(id, convertedValue: Big) sets inputValue = parseFloat(convertedValue.toFixed(FRACTIONAL_DIGITS)) then fromUnitId = id
  • ISC-5/6: onctrlclick: () => void present in ConversionResult props type
  • ISC-7/8/9: handleClick branches: e.ctrlKey → onctrlclick(), e.shiftKey → onshiftclick(), else onclick()
  • ISC-10/11/12: ConverterCard has onctrlclick: (id: string, value: Big) => void; passes closure () => onctrlclick(item.unit.id, item.convertedValue) to each tile
  • ISC-13: onctrlclick={handleCtrlClick} present in ConverterCard usage in +page.svelte
  • ISC-14: Hotkey hint reads "Click to highlight · Shift+click to change input unit · Ctrl+click to adopt value as input"
  • ISC-15: min-height: 8rem added to .highlighted-result rule in app.css
  • ISC-16/17/18: .highlighted-result div always rendered; {#if highlightedResult !== null} gates content inside
  • ISC-19: Layout stable by construction — container always occupies space, only inner content changes
  • ISC-20/21: npm run build✓ built in 1.70s, zero errors, zero type errors (both SSR and client passes)
  • ISC-22/23/24: Logic traced from event to handler; all three paths isolated correctly