Account

Dashboard

{#if error}

{error}

{:else if !stats}

Loading…

{:else} {#if section === 'overview' || section === 'content' || section === 'audience'}
Window {#each [7, 30, 90] as d (d)} {/each}
{/if} {#if section === 'overview'} {#if (stats.attention ?? []).length}
{#each stats.attention as a}
{a.text}
{/each}
{:else}
✓ All clear — nothing needs attention right now.
{/if}

Pulse

{stats.visitors.today}Visitors today
{stats.visitors.d7}Visitors (7d)
{stats.visitors.d30}Visitors ({range}d)
{stats.content.served}Articles live
{stats.content.accepted_7d}Fresh (7d)
{stats.content.latest_brief_size}In today's brief
{healthy}/{sources.length}Sources healthy
{stats.accounts.total}Accounts
{:else if section === 'content'}

Corpus

{stats.content.served}Articles live
{stats.content.rejected}Filtered out
{stats.content.added_24h}Ingested (24h)
{stats.content.added_7d}Ingested (7d)
{stats.content.accepted_7d}Fresh & live (7d)
{stats.content.latest_brief_size}In today's brief

Coverage

{coverage(stats.content.with_image, stats.content.served)}%Articles with image ({stats.content.with_image}/{stats.content.served})
{coverage(stats.content.summaries, stats.content.served)}%Summaries ({stats.content.summaries}/{stats.content.served})
{coverage(stats.content.summaries_with_image, stats.content.summaries)}%Summary pages w/ image
{stats.content.brief_with_image}/{stats.content.latest_brief_size}Brief w/ image
{stats.content.recent_enrich_fail}Image misses (24h)

Most-opened articles

{#if stats.top_articles.length} {:else}

No opens yet.

{/if}

Popular groupings

{#if stats.top_groupings.length}
    {#each stats.top_groupings as g (g.tag)}
  • {g.tag.replace('-', ' ')} {g.opens}
  • {/each}
{:else}

No data yet.

{/if}

Popular topics

{#if stats.top_topics.length}
    {#each stats.top_topics as t (t.topic)}
  • {t.topic} {t.opens}
  • {/each}
{:else}

No data yet.

{/if}
{:else if section === 'sources'}

Add a source

e.key === 'Enter' && addCandidate()} />
{#if addErr}

{addErr}

{/if}

Fetches a sample (safely) and stages it as a candidate — nothing is added live until you Promote.

{#if pendingCandidates.length}

Candidate queue ({pendingCandidates.length})

{/if}

Sources

{healthy} healthy · {resting} resting · {flagged} flagged · {paused} paused · {retired} retired · {sources.length} total

{#each [['all', 'All'], ['healthy', 'Healthy'], ['resting', 'Resting'], ['flagged', 'Flagged'], ['paused', 'Paused'], ['retired', 'Retired']] as [key, label] (key)} {/each}
{#each shownSources as s (s.id)} {@const st = sstatus(s)} 0} class:flag={s.review_flag} class:paused={st !== 'active'}> {/each}
SourceServedAcceptDup Last successNext pollFailsStatusActions
{s.name}{#if s.category}{s.category}{/if} {#if s.review_flag && s.review_reason}“{s.review_reason}”{/if} {s.served} {s.acceptance_rate != null ? s.acceptance_rate + '%' : '—'} {s.duplicate_rate != null ? s.duplicate_rate + '%' : '—'} {s.last_success_at ? fwhen(s.last_success_at) : '—'} {st === 'active' ? fwhen(s.next_due_at) : '—'} {s.failures || ''} {#if st === 'retired'}retired {:else if st === 'paused'}paused {:else if rateLimited(s)}rate-limited · rests until {fwhen(s.retry_after_at)}{#if s.review_flag} · review{/if} {:else if s.failures > 0}⚠ resting{#if s.review_flag} · review{/if} {:else if s.review_flag}⚑ review {:else}✓ ok{/if} {#if !s.content_visible}· hidden{/if} {#if st === 'retired'} {:else} {/if}

“served” = accepted, non-duplicate articles live · accept/dup % is of all ingested · pausing stops polling but keeps existing articles live · times in your local zone

{:else if section === 'audience'}

Visitors

{stats.visitors.today}Today
{stats.visitors.d7}Last 7 days
{stats.visitors.d30}Last {range} days

Returning visitors ({range}d)

{stats.retention.new}New (1 day)
{stats.retention['2-3']}2–3 days
{stats.retention['4-7']}4–7 days
{stats.retention['8+']}8+ days

Accounts

{stats.accounts.total}Total
{stats.accounts.new_today}New today
{stats.accounts.new_7d}New this week
{stats.accounts.active_7d}Active this week

Reading funnel

{stats.funnel.summary_viewed}Summaries read
{stats.funnel.source_click}→ Source (from summary)
{stats.funnel.source_rate}%Summary → source rate
{stats.funnel.full_story}Straight to source

Emotional mix & friction

{stats.emotional_mix.not_today}Not today
{stats.emotional_mix.less_like_this}Less like this
{stats.emotional_mix.hide_topic}Hide topic
{stats.replace.used}Replaces
{stats.paywall.paywalled_source_open}Paywalled opens

Sharing

{#each Object.entries(stats.shares) as [kind, n]}
{n}{SHARE_LABEL[kind] ?? kind}
{/each}

Daily trend

{#if stats.daily.length} {@const dmax = Math.max(1, ...stats.daily.map((d) => Math.max(d.opens, d.visits)))}
{#each stats.daily as d (d.day)}
{/each}

visits   opens

{:else}

No data yet.

{/if}
{:else if section === 'feedback'}

Feedback {#if feedback.length}({unreadCount} unread / {feedback.length}){/if}

{#if feedback.length}
{#each fbCats as cat (cat)} {/each}
{#if shownFeedback.length} {:else}

Nothing in this filter.

{/if} {:else}

No feedback yet.

{/if} {/if} {/if}
{#if flagging} {/if}