Skip to content

Component cleanups: dedupe material filtering, shared displayElements, snapshot helper for async calc effects #694

@grzanka

Description

@grzanka

Part of the code-health / complexity review of src/. A bundle of small,
low-risk duplication/robustness cleanups. Each is independently shippable.

Why a bundle

These are individually minor (each <~40 LOC) but share a theme: the same logic is
written twice, or a fragile manual pattern is repeated. Grouped so one focused PR
(or a few commits) can clear them.

Item 1 — Duplicated material filtering (~40 LOC)

entity-selection/material-tab.svelte and entity-selection/picker-sheet.svelte
both implement the same element/compound category filtering
(inElements() / inCompounds() + density filtering).

  • Why: two copies of selection-filtering logic drift apart over time; a fix to
    one (e.g. how externals are categorized) silently misses the other.
  • Fix: extract to src/lib/utils/material-filters.ts (pure functions) and
    call from both; add direct unit tests for the predicates.

Item 2 — displayElements recomputed in mobile sheet

compound-editor/mobile-sheet.svelte (~lines 29–39) re-derives displayElements
(weight→atom conversion) that the parent compound-editor-modal.svelte
(~lines 127–135) already computes.

  • Why: the mobile sheet already receives a shared EditorController from the
    parent; recomputing the same derivation risks the two views disagreeing if the
    conversion logic changes in one place only.
  • Fix: expose displayElements on the controller and consume it in the child
    instead of recomputing.

Item 3 — Manual input-snapshot pattern in async calc effects

multi-program-calc.svelte.ts, multi-entity-calc.svelte.ts, and
inverse-calc.svelte.ts each snapshot all inputs into a local object before a
debounced setTimeout(... , 300) to avoid stale-closure races.

  • Why: correct today, but every new dependency must be hand-copied into the
    snapshot; forgetting one yields stale results with no compile-time error. The
    pattern is duplicated three times.
  • Fix: extract a small shared helper (e.g.
    src/lib/utils/debounced-snapshot.ts) that captures a typed input object and
    runs a debounced async callback with cancellation, so the contract is in one
    place and each call site just declares its input object.

Acceptance criteria

  • Material filtering exists once in a tested util; material-tab and
    picker-sheet both consume it; behaviour unchanged.
  • Mobile compound editor reads displayElements from the controller (no
    independent recomputation); desktop/mobile show identical derived elements.
  • The three async calc effects share one debounced-snapshot helper; race
    behaviour (latest input wins, stale results cancelled) is preserved and
    covered by a unit test.
  • pnpm check, pnpm lint, pnpm test pass.

Out of scope

  • Restructuring the compound editor or entity-selection components beyond these
    extractions.

Files

  • src/lib/components/entity-selection/material-tab.svelte,
    entity-selection/picker-sheet.svelte, new src/lib/utils/material-filters.ts
  • src/lib/components/compound-editor/mobile-sheet.svelte,
    compound-editor-modal.svelte, compound-editor/types.ts (controller type)
  • src/lib/state/multi-program-calc.svelte.ts,
    multi-entity-calc.svelte.ts, inverse-calc.svelte.ts, new helper + test

AI logging

  • CHANGELOG-AI.md entry.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions