This commit is contained in:
2026-03-28 00:24:18 -03:00
parent 49da927da0
commit f6ef95ebea
6 changed files with 176 additions and 19 deletions

View File

@@ -0,0 +1,77 @@
import { ref, type Ref } from 'vue'
/**
* Generic registry composable — fetches typed data from a URL, caches it,
* exposes it reactively.
*
* Use for any data that is loaded once at app init and rarely changes:
* stage definitions, config schemas, available models, etc.
*
* The registry is shared across all consumers (singleton per URL).
*/
const cache = new Map<string, { data: Ref<any>; loading: Ref<boolean>; error: Ref<string | null>; promise: Promise<void> | null }>()
export function useRegistry<T>(url: string): {
data: Ref<T[]>
loading: Ref<boolean>
error: Ref<string | null>
refresh: () => Promise<void>
} {
if (!cache.has(url)) {
const data = ref<T[]>([]) as Ref<T[]>
const loading = ref(false)
const error = ref<string | null>(null)
const entry = { data, loading, error, promise: null as Promise<void> | null }
cache.set(url, entry)
async function doFetch() {
loading.value = true
error.value = null
try {
const resp = await fetch(url)
if (!resp.ok) {
error.value = `Failed to fetch registry: ${resp.status}`
return
}
data.value = await resp.json()
} catch (e) {
error.value = String(e)
} finally {
loading.value = false
}
}
entry.promise = doFetch()
}
const entry = cache.get(url)!
async function refresh() {
const data = entry.data
const loading = entry.loading
const error = entry.error
loading.value = true
error.value = null
try {
const resp = await fetch(url)
if (!resp.ok) {
error.value = `Failed to fetch registry: ${resp.status}`
return
}
data.value = await resp.json()
} catch (e) {
error.value = String(e)
} finally {
loading.value = false
}
}
return {
data: entry.data as Ref<T[]>,
loading: entry.loading,
error: entry.error,
refresh,
}
}

View File

@@ -3,6 +3,7 @@ export { DataSource, type DataSourceStatus } from './datasources/DataSource'
export { SSEDataSource } from './datasources/SSEDataSource'
export { StaticDataSource } from './datasources/StaticDataSource'
export { useDataSource } from './composables/useDataSource'
export { useRegistry } from './composables/useRegistry'
// Components
export { default as Panel } from './components/Panel.vue'

View File

@@ -20,9 +20,7 @@ const emit = defineEmits<{
'open-stage-editor': [stage: string]
}>()
const regionStageSet = computed(() => new Set(props.regionStages ?? [
'detect_edges', 'detect_objects', 'run_ocr', 'match_brands', 'escalate_vlm', 'escalate_cloud',
]))
const regionStageSet = computed(() => new Set(props.regionStages ?? []))
const statusColors: Record<string, string> = {
pending: 'var(--status-idle)',