refactor(ui): settings-card primitives + fix threshold clamp / card misgroup (#161)
Tier-3 frontend DRY for the ML settings cards, plus the F-D2 clamp bug and the F-D3 card misgrouping. New primitives (components/common + composables): - <SettingToggleRow> — the accent-icon + .fc-section-h label + right-aligned switch row (HeadsCard x3, CropProposersCard). iconColor prop absorbs the on/off dim. - <SettingNumberField> — compact numeric field that CLAMPS to [min,max] on commit. This fixes F-D2: HeadsCard/CropProposersCard previously sent Number(raw) straight to the API, so an out-of-range threshold bounced off the 400 validator (only TranslationCard clamped). density prop for the grid cards. - useSettingSave(patchFn) — the busy + patch + toast + revert-on-failure flow each card hand-rolled (HeadsCard x6 handlers, CropProposersCard, MLBackfillCard, VideoEmbeddingCard). Returns ok/false for the optimistic-switch revert. Adopted in HeadsCard, CropProposersCard, MLBackfillCard (handler only — its plain labelled switch is a different affordance), VideoEmbeddingCard. F-D3: MLThresholdSliders.vue actually rendered a "Video embedding" (frame- sampling) card but sat under "Tagging → Suggestion thresholds". Renamed it VideoEmbeddingCard.vue and moved it to the "GPU agent & embeddings" section. Left deliberately (over-DRY guard): TranslationCard uses an inline error ALERT (not a toast), already clamps its confidence with a NaN fallback, and lives on the ImportStore — a genuinely different save pattern, so forcing it onto useSettingSave would change its UX. Behaviour-preserving refactor; CI has no Vue type-check so this needs a live UI pass (toggles persist + revert on failure, thresholds clamp on blur, video card now under Embeddings). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NsmJSQxnNxGgtM5Yz4GAqi
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
<!--
|
||||
Canonical settings toggle row (DRY pass #161): an accent icon + an uppercase
|
||||
.fc-section-h label + a right-aligned switch. Hand-rolled identically in the
|
||||
ML settings cards (HeadsCard x3, CropProposersCard, MLBackfillCard).
|
||||
|
||||
Two-way binds `modelValue` (so the parent switch state stays optimistic) AND
|
||||
emits `change` with the new boolean, so the parent can persist + revert on
|
||||
failure — matching the prior `v-model` + `@update:model-value=handler` pattern.
|
||||
-->
|
||||
<template>
|
||||
<div class="d-flex align-center mb-1" style="gap: 10px;">
|
||||
<v-icon v-if="icon" size="18" :color="iconColor">{{ icon }}</v-icon>
|
||||
<span class="fc-section-h">{{ label }}</span>
|
||||
<v-switch
|
||||
:model-value="modelValue"
|
||||
:loading="loading"
|
||||
:disabled="disabled"
|
||||
hide-details density="compact" color="success" class="ml-auto"
|
||||
@update:model-value="onSwitch"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
modelValue: { type: Boolean, default: false },
|
||||
label: { type: String, default: '' },
|
||||
icon: { type: String, default: '' },
|
||||
// Icon tint. Default accent; pass null for the theme default (e.g. when a row
|
||||
// is off). null (not undefined) so the default doesn't override it.
|
||||
iconColor: { type: String, default: 'accent' },
|
||||
loading: { type: Boolean, default: false },
|
||||
disabled: { type: Boolean, default: false },
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
|
||||
function onSwitch(v) {
|
||||
const b = !!v
|
||||
emit('update:modelValue', b)
|
||||
emit('change', b)
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user