fix(settings): maintenance tiles start collapsed; remember manual open state
CI / lint (push) Successful in 2s
CI / frontend-build (push) Successful in 17s
CI / backend-lint-and-test (push) Successful in 26s
CI / integration (push) Successful in 3m25s

GpuAgentCard was hardcoded :open=true, HeadsCard opened whenever any head
existed, TagEvalCard whenever a persisted run existed — so a fresh Settings
load greeted the operator with several tiles already expanded. All three now
force-open only while their task is actually running (the #877 resurface
behavior on the busy-driven tiles is untouched).

MaintenanceTile additionally persists MANUAL expand/collapse per tile in
localStorage, so the section reloads the way the operator left it; a forced
open while a task runs stays transient and is never saved as a preference.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
This commit is contained in:
2026-07-01 22:28:52 -04:00
parent 1b1d3732dc
commit 3d6201734c
4 changed files with 17 additions and 7 deletions
@@ -7,8 +7,10 @@
Usage: wrap a card's action body in the default slot; pass icon/title/blurb.
`destructive` tints the icon error-red for delete actions. `open` can be forced
(e.g. keep a running task's tile expanded). Keyboard accessible: the header is a
real <button> with aria-expanded + focus ring.
(e.g. keep a running task's tile expanded). Manual expand/collapse persists per
tile in localStorage, so the page reloads the way the operator left it.
Keyboard accessible: the header is a real <button> with aria-expanded + focus
ring.
-->
<template>
<v-card class="fc-tile" :class="{ 'fc-tile--open': isOpen }">
@@ -53,12 +55,21 @@ const props = defineProps({
open: { type: Boolean, default: false },
})
const local = ref(props.open)
watch(() => props.open, (v) => { local.value = v })
// Only MANUAL toggles are saved (keyed by tile title): a forced `open` while a
// task is mid-run is transient state, not a preference — persisting it would
// resurrect the "several tiles open by default" bug this replaces. When the
// force clears, the tile falls back to the operator's saved choice.
const storeKey = `fc.tile.${props.title}`
function savedOpen() {
try { return localStorage.getItem(storeKey) === '1' } catch { return false }
}
const local = ref(props.open || savedOpen())
watch(() => props.open, (v) => { local.value = v || savedOpen() })
const isOpen = computed(() => local.value)
function toggle() {
local.value = !local.value
try { localStorage.setItem(storeKey, local.value ? '1' : '0') } catch { /* non-fatal */ }
}
</script>
@@ -3,7 +3,6 @@
icon="mdi-expansion-card"
title="GPU agent (CCIP + crops)"
blurb="Connect a desktop-GPU agent to embed characters (CCIP) and crops. It pulls work over HTTP — your database and Redis stay private."
:open="true"
>
<p class="fc-muted text-body-2 mb-3">
The agent is a container you run on the machine with the GPU. It
@@ -3,7 +3,7 @@
icon="mdi-brain"
title="Concept heads (the learning suggester)"
blurb="Train the per-concept heads that turn your tags into suggestions — they replace Camie and sharpen every time you accept or reject."
:open="headCount > 0 || running"
:open="running"
>
<p class="fc-muted text-body-2 mb-3">
A <strong>head</strong> is a tiny classifier trained on the SigLIP
@@ -3,7 +3,7 @@
icon="mdi-flask-outline"
title="Tagging eval (heads vs centroid)"
blurb="Measure whether a trained head beats the old centroid on your own tags — and whether tagging more sharpens it."
:open="!!run"
:open="running"
>
<p class="fc-muted text-body-2 mb-3">
Reuses the SigLIP embeddings already stored on your images (no re-embed, no