CI / lint (push) Successful in 4s
Build images / sign-extension (push) Successful in 4s
CI / extension-version (push) Successful in 4s
Build images / build-agent (push) Successful in 4s
CI / frontend-build (push) Successful in 21s
extension / lint (push) Successful in 25s
CI / backend-lint-and-test (push) Successful in 32s
Build images / build-ml (push) Successful in 2m50s
Build images / build-web (push) Successful in 2m49s
CI / integration (push) Successful in 3m52s
A dim line at the foot of Settings: `FabledCurator 2026.08.28.1249 · dev`. This is no longer a convenience. Milestone 318 stopped publishing version image tags, so an instance's own report is the ONLY answer to "which build is this?" — there is no registry name left to check it against. Note #3127 §5 says it directly: a wrong answer here has no second source to contradict it. Three states, kept distinct because collapsing any two of them lies: not asked yet render nothing asked, no version render "unknown" asked, has a version render it A blank footer reads as "no version", which is a different claim from "I cannot say". And a failed health call deliberately does NOT mark the build loaded — a network blip says nothing about the image, and presenting it as "unknown" would look like a defective build. Carried on /api/health rather than a new route: it answers at the same cost (two module constants, no I/O) and TopNav already fetches it app-wide, so a separate endpoint would mean a second request for two strings. Both fields are OMITTED when unset rather than sent empty. Absence already means "cannot say" — an image predating the field says exactly that by not having the key — so a second spelling would make every reader special-case it. The pre-existing test asserting the body is EXACTLY {"status": "ok"} is what keeps a well-meaning `or ""` default from creeping in. FC_CHANNEL now has one definition. It was read from the environment in extension.py and would have been read again here; the new build_info module holds both, and extension.py binds it as a module-level name so existing tests monkeypatch it exactly as before. Separate from config.py on purpose: those are operator settings meant to be changed, these describe the artifact. Channel sits beside the version, never inside it (rule 149), asserted from both ends. A `-dev` suffix would read as a 0 segment to the extension's parseInt comparator and make every dev build compare equal — #2993 exactly. Not hidden, per the operator and §7: the JS bundle and asset hashes fingerprint the build anyway, and "I'm on 2026.08.28.1249" is the single most useful line in a bug report.
128 lines
4.9 KiB
Vue
128 lines
4.9 KiB
Vue
<template>
|
|
<!-- Centered, width-capped column so the tab strip + every panel sit in a
|
|
tidy centered measure rather than stretching full-width (operator
|
|
2026-06-18). v-container is margin-auto by default; the cap keeps long
|
|
maintenance/import panels readable. -->
|
|
<v-container class="pt-2 pb-6 fc-settings">
|
|
<!-- Sticky tabs: operator-flagged 2026-05-25 — long Import / Maintenance
|
|
panels pushed the tab strip out of the viewport, forcing a scroll-
|
|
to-top just to change tab. AppShell's TopNav is 64px sticky, so the
|
|
tab strip lives directly under it. The .fc-chrome-continues fade
|
|
continues the nav's gradient across the strip (operator 2026-07-13). -->
|
|
<v-tabs
|
|
v-model="tab" color="accent" class="mb-4 fc-chrome-continues"
|
|
style="position: sticky; top: var(--fc-nav-h, 64px); z-index: 4;"
|
|
>
|
|
<v-tab value="overview">Overview</v-tab>
|
|
<v-tab value="activity">Activity</v-tab>
|
|
<v-tab value="cleanup">Cleanup</v-tab>
|
|
<v-tab value="maintenance">Maintenance</v-tab>
|
|
</v-tabs>
|
|
|
|
<v-window v-model="tab">
|
|
<v-window-item value="overview">
|
|
<SystemStatsCards :stats="system.stats" />
|
|
<SystemActivitySummary
|
|
class="mt-4"
|
|
@open-activity="tab = 'activity'"
|
|
/>
|
|
<!-- Health strip (2026-07-02): the same GPU + downloads panels the
|
|
Activity tab uses — Overview answers "is everything healthy?"
|
|
across ALL systems, not just the Celery queues above. -->
|
|
<div class="fc-health-row mt-4">
|
|
<GpuActivityPanel @open-maintenance="tab = 'maintenance'" />
|
|
<DownloadsActivityPanel />
|
|
</div>
|
|
<v-alert
|
|
v-if="system.stats && (system.stats.tasks.pending + system.stats.tasks.queued) > 0"
|
|
type="info" variant="tonal" class="mt-4" closable
|
|
>
|
|
{{ system.stats.tasks.pending + system.stats.tasks.queued }} import task(s) pending.
|
|
<v-btn variant="text" size="small" @click="tab = 'activity'">View in Activity</v-btn>
|
|
</v-alert>
|
|
</v-window-item>
|
|
|
|
<v-window-item value="activity">
|
|
<SystemActivityTab @open-maintenance="tab = 'maintenance'" />
|
|
</v-window-item>
|
|
|
|
<v-window-item value="cleanup">
|
|
<CleanupView />
|
|
</v-window-item>
|
|
|
|
<v-window-item value="maintenance">
|
|
<MaintenancePanel />
|
|
</v-window-item>
|
|
</v-window>
|
|
|
|
<!-- Which build is this? With no version image tags (milestone 318) the
|
|
instance's own report is the only answer, so it is shown rather than
|
|
hidden. The instinct to treat it as information disclosure does not
|
|
survive contact: the JS bundle and asset hashes fingerprint the build
|
|
anyway, and "I'm on 2026.08.28.1249" is the single most useful line in
|
|
a bug report.
|
|
|
|
Channel sits BESIDE the version, never inside it (rule 149) — a
|
|
`-dev` suffix would read as a 0 segment to the extension's comparator
|
|
and make every dev build compare equal (#2993). -->
|
|
<div v-if="system.buildLoaded" class="text-caption text-medium-emphasis text-center mt-8">
|
|
FabledCurator {{ system.buildVersion || 'unknown' }}
|
|
<span v-if="system.buildChannel"> · {{ system.buildChannel }}</span>
|
|
</div>
|
|
</v-container>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
|
import { useSystemStore } from '../stores/system.js'
|
|
import SystemStatsCards from '../components/settings/SystemStatsCards.vue'
|
|
import SystemActivitySummary from '../components/settings/SystemActivitySummary.vue'
|
|
import SystemActivityTab from '../components/settings/SystemActivityTab.vue'
|
|
import GpuActivityPanel from '../components/settings/GpuActivityPanel.vue'
|
|
import DownloadsActivityPanel from '../components/settings/DownloadsActivityPanel.vue'
|
|
import MaintenancePanel from '../components/settings/MaintenancePanel.vue'
|
|
import CleanupView from './CleanupView.vue'
|
|
import { useMLStore } from '../stores/ml.js'
|
|
|
|
const tab = ref('overview')
|
|
const system = useSystemStore()
|
|
const mlStore = useMLStore()
|
|
|
|
let pollId = null
|
|
|
|
function startPolling() {
|
|
if (pollId) return
|
|
system.refreshStats()
|
|
pollId = setInterval(() => {
|
|
if (!document.hidden) system.refreshStats()
|
|
}, 5000)
|
|
}
|
|
|
|
function stopPolling() {
|
|
if (pollId) { clearInterval(pollId); pollId = null }
|
|
}
|
|
|
|
onMounted(startPolling)
|
|
onUnmounted(stopPolling)
|
|
|
|
watch(tab, (t) => {
|
|
if (t === 'maintenance') mlStore.loadSettings()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* Centered, capped measure for the whole Settings surface (tabs + panels). */
|
|
.fc-settings {
|
|
max-width: 1140px;
|
|
}
|
|
/* Overview health strip: the two pulse panels side by side, stacking on
|
|
narrow screens. Cancel the panels' own bottom margin — the row owns gaps. */
|
|
.fc-health-row {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
|
|
gap: 16px;
|
|
align-items: start;
|
|
}
|
|
.fc-health-row :deep(.v-card) { margin-bottom: 0 !important; }
|
|
</style>
|