diff --git a/CLAUDE.md b/CLAUDE.md index 26f8434..a40e5ae 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -201,9 +201,10 @@ event's length. It adds **no schema field**, so the feed contract is untouched. claims. A source quietly moving a date must not erase a fortnight's streak that exists nowhere else. - **Detection is a default, not a verdict.** The reader can mark any event as repeating, or unmark - one detection got wrong (`progress.daily`, resolved by `resolveDaily`). Store an override only - when it *disagrees* with detection — recording agreement would freeze today's guess and stop a - better parser from ever reaching that event. + one detection got wrong (`progress.daily`, resolved by `resolveDaily`), and `prefs.detectDaily` + switches the guessing off entirely. Store an override only when it *disagrees* with detection — + recording agreement would freeze today's guess and stop a better parser from ever reaching that + event. Neither control ever deletes a mark or a logged day, so both are reversible. ## Conventions diff --git a/README.md b/README.md index 4ad71dc..8240642 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,9 @@ whose end was never announced gets a day count rather than a checklist of invent That guess is only a starting point. Open any event and you can say **it repeats daily** — the grind whose page never prints the word still gets a checklist — or dismiss one the wording caught -by mistake. Anything you mark joins today's dailies at the top of the page, so ticking it off is one +by mistake. If you would rather it never guessed, **Spot daily events automatically** in the +settings turns detection off and leaves only what you marked yourself; it discards nothing, so your +ticks and streaks are still there if you turn it back on. Anything you mark joins today's dailies at the top of the page, so ticking it off is one tap rather than a trip back into the event. ## Sorting diff --git a/docs/DATA-MODEL.md b/docs/DATA-MODEL.md index b806d20..1e337ec 100644 --- a/docs/DATA-MODEL.md +++ b/docs/DATA-MODEL.md @@ -199,8 +199,8 @@ Namespaced, versioned, and small. Nothing here ever goes to the server. "gacha-tracker:v1:progress" // { [eventId]: { status?, effort?, note?, at } } "gacha-tracker:v1:daily" // { [id]: { days: ["2026-08-15", ...], at } } "gacha-tracker:v1:ignored" // { [eventId]: { at } } — "stop showing me this" -"gacha-tracker:v1:prefs" // { region, hiddenGames[], sort, showCompleted, showIgnored, - // regionConfirmed, onboarded } +"gacha-tracker:v1:prefs" // { region, hiddenGames[], sort, detectDaily, showCompleted, + // showIgnored, regionConfirmed, onboarded } "gacha-tracker:v1:completions" // SUPERSEDED — read once to migrate, never written ``` @@ -257,8 +257,11 @@ Dailiness is derived from the published event — `type: "login"`, or wording li "check-in", "7-day" in the title or summary — and never from a game's habits or an event's length. It adds no schema field, so nothing about the feed contract or the event ID changes. -**The reader overrules detection.** `progress.daily` records their answer and wins outright -(`resolveDaily`); absent means they have not said, so detection stands. An override that merely +**The reader overrules detection**, per event and globally. `progress.daily` records their answer +for one event and wins outright (`resolveDaily`); absent means they have not said, so detection +stands. `prefs.detectDaily` switches the guessing off altogether, leaving only events they marked +themselves — it silences detection rather than deleting anything, so every mark and every logged day +survives and switching it back on restores exactly what was there. An override that merely agrees with detection is **not stored** (`dailyOverride`) — freezing today's guess into their data would stop a later parser improvement from ever reaching that event. This is the only field in `progress` that changes what the app *shows* rather than recording what the reader did, which is diff --git a/src/client/App.tsx b/src/client/App.tsx index 2ffac8a..13c1eca 100644 --- a/src/client/App.tsx +++ b/src/client/App.tsx @@ -90,7 +90,11 @@ export function App() { * the source's wording; they can overrule it either way. */ const repeatsDaily = (row: RowEvent): boolean => - resolveDaily(row.event, prog.progress[row.event.id]?.daily); + resolveDaily( + row.event, + prog.progress[row.event.id]?.daily, + prefs.detectDaily, + ); /** Today's state for a repeating event, or undefined if it does not repeat. */ const dailyBadge = (row: RowEvent): DailyBadge | undefined => { @@ -395,7 +399,7 @@ export function App() { region={prefs.region} now={now} daily={repeatsDaily(openRow)} - detectedDaily={isDaily(openRow.event)} + detectedDaily={prefs.detectDaily && isDaily(openRow.event)} dailyDays={daily.daysFor(openRow.event.id)} onDaily={prog.setDaily} onToggleDay={daily.toggleDay} diff --git a/src/client/components/Controls.tsx b/src/client/components/Controls.tsx index d40d3c5..43d86eb 100644 --- a/src/client/components/Controls.tsx +++ b/src/client/components/Controls.tsx @@ -86,6 +86,26 @@ export function Controls({ Show events I've finished + {/* Detection reads the source's wording and is wrong in both + directions. Off leaves only the events the reader marked, and + discards nothing — every mark and logged day survives, so it can + be switched back on. */} + + {ignoredCount > 0 && (