Build images / sign-extension (push) Successful in 4s
CI / lint (push) Successful in 4s
CI / extension-version (push) Successful in 5s
Build images / build-ml (push) Successful in 6s
Build images / build-agent (push) Successful in 8s
CI / frontend-build (push) Successful in 22s
CI / backend-lint-and-test (push) Successful in 35s
Build images / build-web (push) Successful in 54s
Build images / smoke-web (push) Skipped
Build images / promote (push) Skipped
CI / integration (push) Successful in 1m47s
The surface shipped at /system with no nav entry, reachable only by clicking the health dot beside the brand — a target you have to already suspect something is wrong to go looking for. Operator-flagged: it needs a path someone can walk to. Settings is where you go to ask the instance about itself, so the view becomes a tab there, beside Activity — Activity answers "what is the queue doing", System answers "is anything left to do it". - SystemView.vue moves to components/settings/SystemHealthTab.vue; the content is unchanged apart from shedding its own container and h1. - SettingsView adopts useTabQuery (the composable Browse and Subscriptions already use) so a tab can be linked TO. The health dot now points at ?tab=system, and /system redirects there so the previous build's link and any bookmark still land. - The tab drops its own 10s poll. v-window keeps a visited item mounted rather than destroyed, so that timer would have gone on firing behind Maintenance — and TopNav already polls the same store every 15s for the dot. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TTjbZZ6JirCMSaJzQV1RhA
142 lines
5.7 KiB
Vue
142 lines
5.7 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="system">System</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>
|
|
|
|
<!-- Is every part of the stack still running (milestone 365). Sits
|
|
beside Activity deliberately: Activity answers "what is the queue
|
|
doing", this answers "is anything left to do it". -->
|
|
<v-window-item value="system">
|
|
<SystemHealthTab />
|
|
</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, 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 SystemHealthTab from '../components/settings/SystemHealthTab.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 { useTabQuery } from '../composables/useTabQuery.js'
|
|
import { useMLStore } from '../stores/ml.js'
|
|
|
|
// ?tab= sync (the same composable Browse/Subscriptions use) so a tab can be
|
|
// linked TO — the health dot beside the brand points at ?tab=system, and the
|
|
// old /system path redirects there.
|
|
const VALID_TABS = ['overview', 'system', 'activity', 'cleanup', 'maintenance']
|
|
const { tab } = useTabQuery(VALID_TABS, '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>
|