diff --git a/frontend/src/components/settings/HeadsCard.vue b/frontend/src/components/settings/HeadsCard.vue
index 7117b0d..aad490a 100644
--- a/frontend/src/components/settings/HeadsCard.vue
+++ b/frontend/src/components/settings/HeadsCard.vue
@@ -159,6 +159,42 @@
+
+
+
+ mdi-image-off-outline
+ Hide presentation chrome
+
+
+
+ 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.
+ wip 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.
+
+
+
+
+
+
+
How auto-apply is landing
@@ -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) {