feat(ui): presentation-chrome auto-hide Settings controls (#141 step 4)
HeadsCard gains a "Hide presentation chrome" section: on/off switch + "Hide confidence" (presentation_auto_apply_threshold) + "Flag if content ≥" (presentation_conflict_threshold), wired to MLSettings via patchSettings and loaded on mount. Makes the step-4 sweep's thresholds operator-tunable (config-in-UI). wip is called out as never auto-hidden. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
This commit is contained in:
@@ -159,6 +159,42 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Presentation chrome auto-hide (#141) -->
|
||||
<div class="fc-auto mt-6">
|
||||
<div class="d-flex align-center mb-1" style="gap: 10px;">
|
||||
<v-icon size="18" color="accent">mdi-image-off-outline</v-icon>
|
||||
<span class="fc-section-h">Hide presentation chrome</span>
|
||||
<v-switch
|
||||
v-model="presentationEnabled" :loading="settingBusy" hide-details
|
||||
density="compact" color="success" class="ml-auto"
|
||||
@update:model-value="onTogglePresentation"
|
||||
/>
|
||||
</div>
|
||||
<p class="fc-muted text-body-2 mb-3">
|
||||
Auto-hide banners and editor screenshots from the gallery once a head has
|
||||
learned them (≥ {{ minPositives }} examples) and clears
|
||||
{{ Math.round((presentationThresholdInput || 0) * 100) }}% confidence.
|
||||
<code>wip</code> is never auto-hidden. If a hidden image also looks like
|
||||
real content (≥ {{ Math.round((presentationConflictInput || 0) * 100) }}%
|
||||
on a content tag), it's flagged in the Hidden view instead of buried.
|
||||
Every auto-hide is reversible.
|
||||
</p>
|
||||
<div class="d-flex mb-3" style="gap: 12px;">
|
||||
<v-text-field
|
||||
v-model.number="presentationThresholdInput" label="Hide confidence"
|
||||
type="number" min="0.5" max="0.999" step="0.01" density="compact"
|
||||
hide-details style="max-width: 200px;" :disabled="settingBusy"
|
||||
@change="onSavePresentation"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model.number="presentationConflictInput" label="Flag if content ≥"
|
||||
type="number" min="0" max="1" step="0.05" density="compact"
|
||||
hide-details style="max-width: 200px;" :disabled="settingBusy"
|
||||
@change="onSavePresentation"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Performance / tuning -->
|
||||
<div v-if="metricsConcepts.length" class="mt-5">
|
||||
<div class="fc-section-h mb-1">How auto-apply is landing</div>
|
||||
@@ -218,6 +254,11 @@ const autoStatus = ref(null)
|
||||
const metricsData = ref(null)
|
||||
let autoTimer = null
|
||||
|
||||
// --- Presentation chrome auto-hide state (#141) ---
|
||||
const presentationEnabled = ref(true)
|
||||
const presentationThresholdInput = ref(0.90)
|
||||
const presentationConflictInput = ref(0.50)
|
||||
|
||||
const autoRunning = computed(() => autoStatus.value?.running_id != null)
|
||||
const lastSweep = computed(() =>
|
||||
(autoStatus.value?.runs || []).find(r => r.status !== 'running') || null)
|
||||
@@ -248,6 +289,9 @@ onMounted(async () => {
|
||||
autoEnabled.value = !!s.head_auto_apply_enabled
|
||||
autoPrecisionInput.value = s.head_auto_apply_precision ?? 0.97
|
||||
autoMinPosInput.value = s.head_auto_apply_min_positives ?? 30
|
||||
presentationEnabled.value = s.presentation_auto_apply_enabled ?? true
|
||||
presentationThresholdInput.value = s.presentation_auto_apply_threshold ?? 0.90
|
||||
presentationConflictInput.value = s.presentation_conflict_threshold ?? 0.50
|
||||
} catch { /* non-fatal */ }
|
||||
await refresh()
|
||||
if (running.value) startPoll()
|
||||
@@ -332,6 +376,32 @@ async function onSaveSettings() {
|
||||
settingBusy.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function onTogglePresentation(val) {
|
||||
settingBusy.value = true
|
||||
try {
|
||||
await mlSettings.patchSettings({ presentation_auto_apply_enabled: !!val })
|
||||
toast({ text: val ? 'Chrome auto-hide on' : 'Chrome auto-hide off', type: 'success' })
|
||||
} catch (e) {
|
||||
presentationEnabled.value = !val // revert the switch
|
||||
toast({ text: `Could not update: ${e.message}`, type: 'error' })
|
||||
} finally {
|
||||
settingBusy.value = false
|
||||
}
|
||||
}
|
||||
async function onSavePresentation() {
|
||||
settingBusy.value = true
|
||||
try {
|
||||
await mlSettings.patchSettings({
|
||||
presentation_auto_apply_threshold: Number(presentationThresholdInput.value),
|
||||
presentation_conflict_threshold: Number(presentationConflictInput.value),
|
||||
})
|
||||
} catch (e) {
|
||||
toast({ text: `Could not save: ${e.message}`, type: 'error' })
|
||||
} finally {
|
||||
settingBusy.value = false
|
||||
}
|
||||
}
|
||||
function onPreview() { startSweep(true) }
|
||||
function onApplyNow() { startSweep(false) }
|
||||
async function startSweep(dryRun) {
|
||||
|
||||
Reference in New Issue
Block a user