# ADR 021: Linked-reference filters are remembered per device, not as a page property

> M7 adds Logseq's linked-references filter (research/13 §4.2 item 5: "Filters for note body", 140 votes, "Sort linked references", 78): on a page's references panel, the set of other pages the…

Source: https://nooklet.danielalder.cz/decisions/021-reference-filters-per-device

Date: 2026-09-12. Status: accepted.

## Context

M7 adds Logseq's linked-references filter (research/13 §4.2 item 5: "Filters for note body", 140
votes, "Sort linked references", 78): on a page's references panel, the set of other pages the
referencing blocks mention, click to include, click again to exclude, and the count on the heading
follows. The filter has to be remembered — a filter that resets every time you open the page is
one you set up again every time — and there are two obvious places to remember it.

Logseq stores it in the page file, as `filters:: {"aurora" true, "done" false}`. research/13's own
shortlist says "stored as a page property so it syncs". That is the alternative this ADR rejects.

## Decision

The filter is per device, in `localStorage`, under one key (`nooklet.referenceFilters`) holding a
map from the page's normalized name (ADR 004) to `{include, exclude}`; empty entries are removed on
save. The sort (most recent / by name) is one preference for the whole panel, not per page.
`apps/web/src/views/referenceFilters.ts` owns both; `ReferencesPanel.tsx` only reads and writes
through it.

## Why

- **A filter is a fact about this screen, not about the notes.** It is the same kind of thing as
  the theme (`app/theme.ts`), the journal title format (ADR 018) and the shelf (`app/shelf.ts`),
  all of which this codebase already keeps per device for the same reason: syncing them would make
  one device's choice fight another's, and none of them is content.
- **A page property turns every click into a graph write.** Each toggle in the popover would mint
  an op, land in the op log, rewrite the page's file in the markdown mirror, appear as a
  `changes.since` event to every agent watching, and be a batch someone could `batch.undo`. Logseq
  users know this as the `filters::` line cluttering their git diffs. ADR 003 wants the mirror to be
  a clean copy of the notes; a reading preference is not a note.
- **It would show in the page's properties block**, next to `tags::` and `icon::`, where it means
  nothing to anyone reading the page and invites "what is this and can I delete it".
- **Refetch churn.** The references panel is a server-computed view stamped on local page writes
  (B-83); writing a page property on every toggle would refetch the very list being filtered,
  once per click.

## What it costs

- The filter does not follow you to another device. Someone who curates a heavy page's references
  on a desktop sees the unfiltered list on their phone. This is the one thing the page-property
  design bought, and it is given up knowingly: the demand behind the feature is "let me hide the
  noise while I read", which is per sitting, not per graph.
- Clearing browser storage clears the filters. Same as the theme; nobody has asked for the theme to
  survive that either.
- Agents cannot see a human's filter. They have no use for it — `page.backlinks` returns
  everything, and an agent that wants a subset filters the result itself.

## Alternatives considered

- **`filters::` page property (Logseq).** Rejected above. If cross-device filters are ever wanted,
  the right shape is a per-user, non-content setting synced outside the page (a settings table
  that does not touch the mirror), not a page property — that keeps the reasons above intact.
- **Server-side per-token preference.** The server has no notion of "a user's settings" yet; adding
  one for a filter would be building the settings table above for one feature. Not now.
- **Remember nothing.** Rejected: the forum thread's complaint is precisely that the filter is
  fiddly to set up; making it ephemeral makes it fiddly every time.

## Consequences

- `docs/spec/`: no API change; `page.backlinks` is unchanged and the filter is computed client-side
  from the block's source page plus the `[[refs]]`/`#tags` in its returned text (first line). Refs
  only on continuation lines are not filter candidates — a known limitation noted in
  `referenceGrouping.ts`; if it matters, `page.backlinks` can grow a `refs` field later.
- Appearance settings (M7 item 6: text size, content width, custom CSS, `data/appearance.ts`)
  follow the same rule for the same reasons and cite this ADR rather than repeating it.
