5.5 KiB
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:
-
Shift+click coupling bug:
handleSetInputin+page.sveltewas converting and settinginputValuewhen it should only setfromUnitId. A new Ctrl+click gesture is the intended "adopt value" action. -
Highlight layout jump bug: The
.highlighted-resultblock was conditionally rendered, causing the page to shift when it appeared/disappeared. Fixed: always rendered withmin-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: 8removervisibility: hidden— simpler, no extra wrapper div needed - ctrlKey checked before shiftKey in handleClick — gives Ctrl priority if both held; more specific action wins
onctrlclickin 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.svelteto match sibling component pattern - Merged identical
:hoverand.highlightedCSS rules; removed no-opbackground-colorresets
Verification
- ISC-1/2:
handleSetInputconfirmed as single linefromUnitId = idonly — no convertById call, no inputValue mutation - ISC-3/4:
handleCtrlClick(id, convertedValue: Big)setsinputValue = parseFloat(convertedValue.toFixed(FRACTIONAL_DIGITS))thenfromUnitId = id - ISC-5/6:
onctrlclick: () => voidpresent in ConversionResult props type - ISC-7/8/9: handleClick branches:
e.ctrlKey → onctrlclick(),e.shiftKey → onshiftclick(), elseonclick() - 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: 8remadded to.highlighted-resultrule in app.css - ISC-16/17/18:
.highlighted-resultdiv 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