Skip to content

Storing an 'off' selection as empty string makes it unrepresentable when the read path uses || default fallback

Symptom

A style/mode picker has a "None"/"Blank" option. Clicking it appears to do nothing: the UI instantly snaps back to the default option. The server write actually SUCCEEDS — the persisted field really is '' — which makes this confusing to debug, because the bug is on the read path, not the write path.

Mechanism

Writer encodes "off" as the empty string:

updated.summary_style = '';   // "blank" selection

Reader treats absent-or-falsy as "never chosen" and falls back:

$: style = extraction?.summary_style || (styles.default ? 'default' : '');

'' is falsy, so an explicit user choice of "off" is indistinguishable from "unset" and the fallback wins every render. The "off" state is literally unrepresentable in the round-trip.

Secondary casualty: any handler keyed by the selection silently drops work in the off state:

function save_edit(text) {
  if (!style) return;          // guard also treats '' as "nothing selected"
  summaries[style] = text;     // edits in blank mode never save
}

Fix

Make "off" a first-class sentinel value ('none') instead of a falsy one. Then:

  • the || fallback naturally passes it through (only genuinely-unset legacy data falls back),
  • guards keyed on truthiness keep working,
  • the off state can own data (e.g. a summaries.none slot the user can edit), which turned "Blank" from a dead toggle into a copy-and-edit flow.

Exclude the sentinel from actions that only make sense for real options (e.g. hide "Regenerate" when style === 'none').

Alternative fix if you must keep '': switch the fallback to nullish coalescing (extraction?.summary_style ?? default), but that still leaves every downstream truthiness guard wrong; a non-falsy sentinel fixes the whole class at once.

Tell

Clicking an option visibly "doesn't take" while the network tab shows a 200 on the save. Check whether the selected value is falsy and whether the display value is derived through ||/truthiness anywhere between store and render.

No signals yet