Combine Merge and Sources admin tabs, add explanation to Poll interval
Category priority and Sources now live at the top of the combined tab, each collapsed by default behind the same CollapsibleSection pattern used for the Widgets tab. Merge strictness and the rest of the synthesis settings sit below, uncollapsed. The Poll interval panel's hint now explains that it's a fallback only — every source sets its own poll interval, so this global default has no effect in current usage.
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
// Same collapsed-by-default shell as WidgetSection, minus the enable/disable
|
||||
// checkbox — for sections that are always "on" (Category priority, Sources) and
|
||||
// just need to stay out of the way until expanded.
|
||||
let {
|
||||
title,
|
||||
defaultExpanded = false,
|
||||
children
|
||||
}: {
|
||||
title: string;
|
||||
defaultExpanded?: boolean;
|
||||
children: Snippet;
|
||||
} = $props();
|
||||
|
||||
let expanded = $state(defaultExpanded);
|
||||
</script>
|
||||
|
||||
<div class="section">
|
||||
<button class="section-head" onclick={() => (expanded = !expanded)} aria-expanded={expanded}>
|
||||
<span class="chevron" class:open={expanded}>▸</span>
|
||||
<span class="title">{title}</span>
|
||||
</button>
|
||||
{#if expanded}
|
||||
<div class="section-body">
|
||||
{@render children()}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.section {
|
||||
background: var(--surface-1);
|
||||
border-radius: 12px;
|
||||
margin-bottom: 14px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.section-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 12px 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
text-align: left;
|
||||
}
|
||||
.chevron {
|
||||
display: inline-block;
|
||||
font-size: 10px;
|
||||
color: var(--text-muted);
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
.chevron.open {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.section-body {
|
||||
padding: 14px;
|
||||
padding-top: 0;
|
||||
border-top: 0.5px solid var(--border);
|
||||
}
|
||||
.section-body > :global(*:first-child) {
|
||||
margin-top: 14px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { AdminSettings } from '$lib/adminTypes';
|
||||
import type { AdminSettings, AdminSource } from '$lib/adminTypes';
|
||||
import { updateSettings, createCategory, deleteCategory } from '$lib/adminApi';
|
||||
import SaveStatus from './SaveStatus.svelte';
|
||||
import CollapsibleSection from './CollapsibleSection.svelte';
|
||||
import SourcesTab from './SourcesTab.svelte';
|
||||
|
||||
let { settings }: { settings: AdminSettings } = $props();
|
||||
let { settings, sources }: { settings: AdminSettings; sources: AdminSource[] } = $props();
|
||||
|
||||
let local = $state({ ...settings, categoryPriority: [...settings.categoryPriority] });
|
||||
let status = $state<'idle' | 'saving' | 'saved' | 'error'>('idle');
|
||||
@@ -89,78 +91,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="panel">
|
||||
<div class="head">
|
||||
<span class="panel-title">Merge strictness</span>
|
||||
<SaveStatus {status} />
|
||||
</div>
|
||||
<p class="hint">How similar articles must be before they're combined into one story.</p>
|
||||
<div class="slider-row">
|
||||
<span class="end">Loose</span>
|
||||
<input
|
||||
type="range"
|
||||
min="1"
|
||||
max="5"
|
||||
step="1"
|
||||
bind:value={local.mergeStrictness}
|
||||
oninput={scheduleSave}
|
||||
/>
|
||||
<span class="end">Strict</span>
|
||||
<span class="value">{local.mergeStrictness}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-2">
|
||||
<div class="panel">
|
||||
<span class="panel-title">Poll interval</span>
|
||||
<p class="hint">How often each source is checked for new items.</p>
|
||||
<select bind:value={local.defaultPollIntervalMinutes} onchange={scheduleSave}>
|
||||
<option value={5}>Every 5 minutes</option>
|
||||
<option value={15}>Every 15 minutes</option>
|
||||
<option value={60}>Every hour</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<span class="panel-title">Hold before publish</span>
|
||||
<p class="hint">Wait window to gather more sources before finalizing a story.</p>
|
||||
<select bind:value={local.holdBeforePublishMinutes} onchange={scheduleSave}>
|
||||
<option value={0}>Publish immediately</option>
|
||||
<option value={30}>Wait 30 minutes</option>
|
||||
<option value={120}>Wait 2 hours</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<span class="panel-title">Follow-up articles</span>
|
||||
<p class="hint">
|
||||
Instead of editing a published article, a distinct follow-up is created once enough new
|
||||
corroborating sources arrive after enough time has passed.
|
||||
</p>
|
||||
<div class="grid-2">
|
||||
<div>
|
||||
<label class="field-label" for="followup-hours">Minimum time since last article</label>
|
||||
<select id="followup-hours" bind:value={local.followUpMinHoursSinceLast} onchange={scheduleSave}>
|
||||
<option value={1}>1 hour</option>
|
||||
<option value={6}>6 hours</option>
|
||||
<option value={12}>12 hours</option>
|
||||
<option value={24}>24 hours</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="field-label" for="followup-sources">Minimum new sources</label>
|
||||
<select id="followup-sources" bind:value={local.followUpMinNewSources} onchange={scheduleSave}>
|
||||
<option value={1}>1</option>
|
||||
<option value={2}>2</option>
|
||||
<option value={3}>3</option>
|
||||
<option value={4}>4</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<span class="panel-title">Category priority</span>
|
||||
<CollapsibleSection title="Category priority">
|
||||
<p class="hint">
|
||||
Synthesis queue processes higher-ranked categories first. Nothing is dropped — lower
|
||||
categories just wait longer when the queue is busy. This list also drives the site's nav —
|
||||
@@ -225,6 +156,84 @@
|
||||
{addingCategory ? 'Adding…' : '+ Add'}
|
||||
</button>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
|
||||
<CollapsibleSection title="Sources">
|
||||
<SourcesTab {sources} categories={local.categoryPriority} />
|
||||
</CollapsibleSection>
|
||||
|
||||
<div class="panel">
|
||||
<div class="head">
|
||||
<span class="panel-title">Merge strictness</span>
|
||||
<SaveStatus {status} />
|
||||
</div>
|
||||
<p class="hint">How similar articles must be before they're combined into one story.</p>
|
||||
<div class="slider-row">
|
||||
<span class="end">Loose</span>
|
||||
<input
|
||||
type="range"
|
||||
min="1"
|
||||
max="5"
|
||||
step="1"
|
||||
bind:value={local.mergeStrictness}
|
||||
oninput={scheduleSave}
|
||||
/>
|
||||
<span class="end">Strict</span>
|
||||
<span class="value">{local.mergeStrictness}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-2">
|
||||
<div class="panel">
|
||||
<span class="panel-title">Poll interval</span>
|
||||
<p class="hint">
|
||||
Fallback only — every source above sets its own poll interval, so this value has no
|
||||
effect unless a source somehow has none set (not currently possible through this admin
|
||||
panel or the API).
|
||||
</p>
|
||||
<select bind:value={local.defaultPollIntervalMinutes} onchange={scheduleSave}>
|
||||
<option value={5}>Every 5 minutes</option>
|
||||
<option value={15}>Every 15 minutes</option>
|
||||
<option value={60}>Every hour</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<span class="panel-title">Hold before publish</span>
|
||||
<p class="hint">Wait window to gather more sources before finalizing a story.</p>
|
||||
<select bind:value={local.holdBeforePublishMinutes} onchange={scheduleSave}>
|
||||
<option value={0}>Publish immediately</option>
|
||||
<option value={30}>Wait 30 minutes</option>
|
||||
<option value={120}>Wait 2 hours</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<span class="panel-title">Follow-up articles</span>
|
||||
<p class="hint">
|
||||
Instead of editing a published article, a distinct follow-up is created once enough new
|
||||
corroborating sources arrive after enough time has passed.
|
||||
</p>
|
||||
<div class="grid-2">
|
||||
<div>
|
||||
<label class="field-label" for="followup-hours">Minimum time since last article</label>
|
||||
<select id="followup-hours" bind:value={local.followUpMinHoursSinceLast} onchange={scheduleSave}>
|
||||
<option value={1}>1 hour</option>
|
||||
<option value={6}>6 hours</option>
|
||||
<option value={12}>12 hours</option>
|
||||
<option value={24}>24 hours</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="field-label" for="followup-sources">Minimum new sources</label>
|
||||
<select id="followup-sources" bind:value={local.followUpMinNewSources} onchange={scheduleSave}>
|
||||
<option value={1}>1</option>
|
||||
<option value={2}>2</option>
|
||||
<option value={3}>3</option>
|
||||
<option value={4}>4</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import MergeTab from '$lib/components/admin/MergeTab.svelte';
|
||||
import SourcesTab from '$lib/components/admin/SourcesTab.svelte';
|
||||
import ModelsTab from '$lib/components/admin/ModelsTab.svelte';
|
||||
import RetentionTab from '$lib/components/admin/RetentionTab.svelte';
|
||||
import EventsTab from '$lib/components/admin/EventsTab.svelte';
|
||||
@@ -12,8 +11,7 @@
|
||||
let { data }: { data: PageData } = $props();
|
||||
|
||||
const tabs = [
|
||||
{ id: 'merge', label: 'Merge' },
|
||||
{ id: 'sources', label: 'Sources' },
|
||||
{ id: 'merge', label: 'Sources & Merge' },
|
||||
{ id: 'models', label: 'Models' },
|
||||
{ id: 'retention', label: 'Retention' },
|
||||
{ id: 'events', label: 'Tracked events' },
|
||||
@@ -38,9 +36,7 @@
|
||||
|
||||
<div class="content">
|
||||
{#if active === 'merge'}
|
||||
<MergeTab settings={data.settings} />
|
||||
{:else if active === 'sources'}
|
||||
<SourcesTab sources={data.sources} categories={data.settings.categoryPriority} />
|
||||
<MergeTab settings={data.settings} sources={data.sources} />
|
||||
{:else if active === 'models'}
|
||||
<ModelsTab settings={data.settings} models={data.models} aiStatus={data.aiStatus} />
|
||||
{:else if active === 'retention'}
|
||||
|
||||
Reference in New Issue
Block a user