feat(settings): surface the near-duplicate (pHash) control + reorder import tab
The phash_threshold knob (controls whether edits/variants of an image are dropped as near-duplicates on import) was buried at the bottom of the import filters form and labelled opaquely, so it read as 'missing'. Hoist it to the TOP of the form as a 'Near-duplicate sensitivity' section: a labelled slider (Exact / Strict / Default / Loose stops, 0-16) for the gist + the precise number field, both bound to phash_threshold, with copy that says plainly to lower it if variants are being dropped. Also swap the import-tab order to filters → trigger → recent-tasks (filters on top per operator); the task list stays directly under the trigger for hit/miss feedback adjacency. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,42 @@
|
||||
<v-card>
|
||||
<v-card-title>Import filters</v-card-title>
|
||||
<v-card-text v-if="store.settings">
|
||||
<!-- Near-duplicate dedup sensitivity, hoisted to the top: it's the
|
||||
most-asked knob — too loose and edits/variants of the same image
|
||||
get dropped as duplicates on import. Slider with sane labelled
|
||||
stops for the gist + a number field for precision; both bind the
|
||||
same phash_threshold. -->
|
||||
<div class="fc-phash">
|
||||
<div class="fc-phash__title">Near-duplicate sensitivity</div>
|
||||
<div class="fc-help mb-1">
|
||||
How aggressively imports merge look-alike images (perceptual-hash
|
||||
distance). <strong>Lower it if edits/variants of the same image are
|
||||
being dropped as duplicates;</strong> raise it to collapse more
|
||||
look-alikes. Applies to new imports.
|
||||
</div>
|
||||
<v-row align="center" no-gutters>
|
||||
<v-col cols="12" sm="9">
|
||||
<v-slider
|
||||
v-model="local.phash_threshold"
|
||||
:min="0" :max="16" :step="1"
|
||||
:ticks="PHASH_TICKS" show-ticks="always" tick-size="4"
|
||||
thumb-label color="accent" hide-details
|
||||
class="fc-phash__slider"
|
||||
@end="save"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="8" sm="3" class="ps-sm-4 mt-2 mt-sm-0">
|
||||
<v-text-field
|
||||
v-model.number="local.phash_threshold"
|
||||
label="Distance" type="number" min="0"
|
||||
density="compact" hide-details @blur="save"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
|
||||
<v-divider class="my-5" />
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-text-field
|
||||
@@ -41,14 +77,6 @@
|
||||
:disabled="!local.skip_single_color" @end="save"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-text-field
|
||||
v-model.number="local.phash_threshold"
|
||||
label="Perceptual-hash threshold" type="number" min="0"
|
||||
density="compact" hide-details @blur="save"
|
||||
/>
|
||||
<div class="fc-help">Higher = looser near-duplicate matching.</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-alert v-if="store.settingsError" type="error" variant="tonal" class="mt-2" closable>
|
||||
@@ -66,6 +94,9 @@ import { reactive, watch } from 'vue'
|
||||
import { useImportStore } from '../../stores/import.js'
|
||||
|
||||
const store = useImportStore()
|
||||
// Labelled stops so the less-initiated get the gist without knowing what a
|
||||
// Hamming distance is. 0 = byte-for-byte only; 10 = the shipped default.
|
||||
const PHASH_TICKS = { 0: 'Exact', 4: 'Strict', 10: 'Default', 16: 'Loose' }
|
||||
// Downloader + schedule-defaults fields moved to
|
||||
// /subscriptions?tab=settings (operator decision 2026-05-27). This form
|
||||
// now only owns image-import filters.
|
||||
@@ -89,4 +120,11 @@ async function save() {
|
||||
color: rgb(var(--v-theme-on-surface-variant));
|
||||
margin-top: 2px;
|
||||
}
|
||||
.fc-phash__title {
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
color: rgb(var(--v-theme-on-surface));
|
||||
}
|
||||
/* Headroom so the tick labels (Exact/Strict/Default/Loose) aren't clipped. */
|
||||
.fc-phash__slider { margin-bottom: 18px; }
|
||||
</style>
|
||||
|
||||
@@ -44,14 +44,15 @@
|
||||
</v-window-item>
|
||||
|
||||
<v-window-item value="import">
|
||||
<!-- Order: trigger → recent tasks → filters. Tasks sit directly
|
||||
below the trigger so operator sees hit/miss feedback without
|
||||
scrolling past the filter card (operator-flagged 2026-05-25). -->
|
||||
<!-- Order: filters → trigger → recent tasks. Filters hoisted above the
|
||||
trigger (operator-flagged 2026-06-04); the task list stays
|
||||
directly below the trigger so hit/miss feedback is adjacent to the
|
||||
button that produced it (operator-flagged 2026-05-25). -->
|
||||
<ImportFiltersForm />
|
||||
<v-divider class="my-6" />
|
||||
<ImportTriggerPanel />
|
||||
<v-divider class="my-6" />
|
||||
<ImportTaskList />
|
||||
<v-divider class="my-6" />
|
||||
<ImportFiltersForm />
|
||||
</v-window-item>
|
||||
|
||||
<v-window-item value="cleanup">
|
||||
|
||||
Reference in New Issue
Block a user