Skip to content

Story count limit ignored when filters are enabled #589

Description

@matt-tingen

Describe the bug

The "Number of stories shown" setting is ignored after a page reload when any content filters are enabled.

To Reproduce
Steps to reproduce the behavior:

  1. Open news.kagi.com
  2. Settings → Filters: enable any content filter
  3. Settings → Stories: set "Number of stories shown" to 3
  4. Reload the page

Expected behavior

3 stories are displayed

Screenshots
N/A

Environment (please complete the following information):

  • Browser: Android Firefox 154; also reproduced in desktop Firefox and Chromium. Logged-out, no sync.

Additional context

The filter does not need to match or hide any stories; merely being enabled triggers it.

Workaround: Move the "Number of stories shown" slider to any value and back restores the limit until the next reload.

LLM findings Two code paths meet here:

1. src/lib/data/setting.svelte.ts (this repo) — number-typed settings round-trip as strings

save() stores numbers via String(this.currentValue) (lines 96–102), and load() only JSON-parses stored values starting with {/[ or equal to true/false (lines 58–68) — everything else is kept as a string. So after every reload, displaySettings.storyCount is the string "3", not the number 3. The slider's setter writes real numbers (Math.max(3, Math.min(12, value))), which is why touching the slider repairs the session.

2. Deployed build — strict limit guard in the filter-aware limiter

In the deployed bundle, when the content filter is active, the story limit is passed into a filter-aware filterStories(stories, keywords, scope, mode, limit) whose guard is:

const l = (i !== null && Number.isFinite(i)) ? i : null;

Number.isFinite("3") is false (it never coerces), so the limit is treated as absent and the full list renders. When no filter is active, the limit flows into stories.slice(0, limit) instead, and slice coerces the string — which is why the bug only manifests with filters on. Note the public repo's filterStories (src/lib/utils/contentFilter.ts) takes no limit parameter and StoryList.svelte slices before filtering, so the guard only exists in the deployed variant.

Other observations consistent with this mechanism:

  • SSR renders the correct count (the kn_prefs cookie stores a real number), so a brief flash of the correct count is visible before hydration replaces the list
  • The limit drop is silent — no console output marks it
  • Reproduces in a private window with just the filter enabled; clearing site data doesn't help (re-saving the setting re-creates the string)
  • The nudge workaround works because the slider setter writes a real number, which passes the guard

Suggested fixes

  1. This repo (fixes the class): make Setting round-trip types correctly — save(): use JSON.stringify for non-string primitives; load(): parse JSON-representable values (or at minimum, parse numeric strings for number-typed settings). Every number-typed setting currently comes back as a string after reload.
  2. Deployed code (belt and braces): coerce before the guard — Number.isFinite(Number(i)) — so a string limit can't silently disable the cap.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions