feat: add pipeline busy status indicator with breathing effect
- Add pipeline_busy field to health check response - Track pipeline busy state in frontend store - Add breathing animation for pipeline status button - Enhance dark mode visibility with stronger contrast
This commit is contained in:
@@ -45,6 +45,7 @@ export type LightragStatus = {
|
|||||||
core_version?: string
|
core_version?: string
|
||||||
api_version?: string
|
api_version?: string
|
||||||
auth_mode?: 'enabled' | 'disabled'
|
auth_mode?: 'enabled' | 'disabled'
|
||||||
|
pipeline_busy: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LightragDocumentsScanProgress = {
|
export type LightragDocumentsScanProgress = {
|
||||||
|
@@ -2,6 +2,7 @@ import { useState, useEffect, useCallback } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useSettingsStore } from '@/stores/settings'
|
import { useSettingsStore } from '@/stores/settings'
|
||||||
import Button from '@/components/ui/Button'
|
import Button from '@/components/ui/Button'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
@@ -45,15 +46,67 @@ const getDisplayFileName = (doc: DocStatusResponse, maxLength: number = 20): str
|
|||||||
: fileName;
|
: fileName;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const pulseStyle = `
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
background-color: rgb(255 0 0 / 0.1);
|
||||||
|
border-color: rgb(255 0 0 / 0.2);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background-color: rgb(255 0 0 / 0.2);
|
||||||
|
border-color: rgb(255 0 0 / 0.4);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-color: rgb(255 0 0 / 0.1);
|
||||||
|
border-color: rgb(255 0 0 / 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .pipeline-busy {
|
||||||
|
animation: dark-pulse 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dark-pulse {
|
||||||
|
0% {
|
||||||
|
background-color: rgb(255 0 0 / 0.2);
|
||||||
|
border-color: rgb(255 0 0 / 0.4);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background-color: rgb(255 0 0 / 0.3);
|
||||||
|
border-color: rgb(255 0 0 / 0.6);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-color: rgb(255 0 0 / 0.2);
|
||||||
|
border-color: rgb(255 0 0 / 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pipeline-busy {
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
border: 1px solid;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export default function DocumentManager() {
|
export default function DocumentManager() {
|
||||||
const [showPipelineStatus, setShowPipelineStatus] = useState(false)
|
const [showPipelineStatus, setShowPipelineStatus] = useState(false)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const health = useBackendState.use.health()
|
const health = useBackendState.use.health()
|
||||||
|
const pipelineBusy = useBackendState.use.pipelineBusy()
|
||||||
const [docs, setDocs] = useState<DocsStatusesResponse | null>(null)
|
const [docs, setDocs] = useState<DocsStatusesResponse | null>(null)
|
||||||
const currentTab = useSettingsStore.use.currentTab()
|
const currentTab = useSettingsStore.use.currentTab()
|
||||||
const showFileName = useSettingsStore.use.showFileName()
|
const showFileName = useSettingsStore.use.showFileName()
|
||||||
const setShowFileName = useSettingsStore.use.setShowFileName()
|
const setShowFileName = useSettingsStore.use.setShowFileName()
|
||||||
|
|
||||||
|
// Add pulse style to document
|
||||||
|
useEffect(() => {
|
||||||
|
const style = document.createElement('style')
|
||||||
|
style.textContent = pulseStyle
|
||||||
|
document.head.appendChild(style)
|
||||||
|
return () => {
|
||||||
|
document.head.removeChild(style)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
const fetchDocuments = useCallback(async () => {
|
const fetchDocuments = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const docs = await getDocuments()
|
const docs = await getDocuments()
|
||||||
@@ -132,6 +185,9 @@ export default function DocumentManager() {
|
|||||||
side="bottom"
|
side="bottom"
|
||||||
tooltip={t('documentPanel.documentManager.pipelineStatusTooltip')}
|
tooltip={t('documentPanel.documentManager.pipelineStatusTooltip')}
|
||||||
size="sm"
|
size="sm"
|
||||||
|
className={cn(
|
||||||
|
pipelineBusy && 'pipeline-busy'
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<ActivityIcon /> {t('documentPanel.documentManager.pipelineStatusButton')}
|
<ActivityIcon /> {t('documentPanel.documentManager.pipelineStatusButton')}
|
||||||
</Button>
|
</Button>
|
||||||
|
@@ -6,14 +6,14 @@ interface BackendState {
|
|||||||
health: boolean
|
health: boolean
|
||||||
message: string | null
|
message: string | null
|
||||||
messageTitle: string | null
|
messageTitle: string | null
|
||||||
|
|
||||||
status: LightragStatus | null
|
status: LightragStatus | null
|
||||||
|
|
||||||
lastCheckTime: number
|
lastCheckTime: number
|
||||||
|
pipelineBusy: boolean
|
||||||
|
|
||||||
check: () => Promise<boolean>
|
check: () => Promise<boolean>
|
||||||
clear: () => void
|
clear: () => void
|
||||||
setErrorMessage: (message: string, messageTitle: string) => void
|
setErrorMessage: (message: string, messageTitle: string) => void
|
||||||
|
setPipelineBusy: (busy: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AuthState {
|
interface AuthState {
|
||||||
@@ -34,6 +34,7 @@ const useBackendStateStoreBase = create<BackendState>()((set) => ({
|
|||||||
messageTitle: null,
|
messageTitle: null,
|
||||||
lastCheckTime: Date.now(),
|
lastCheckTime: Date.now(),
|
||||||
status: null,
|
status: null,
|
||||||
|
pipelineBusy: false,
|
||||||
|
|
||||||
check: async () => {
|
check: async () => {
|
||||||
const health = await checkHealth()
|
const health = await checkHealth()
|
||||||
@@ -51,7 +52,8 @@ const useBackendStateStoreBase = create<BackendState>()((set) => ({
|
|||||||
message: null,
|
message: null,
|
||||||
messageTitle: null,
|
messageTitle: null,
|
||||||
lastCheckTime: Date.now(),
|
lastCheckTime: Date.now(),
|
||||||
status: health
|
status: health,
|
||||||
|
pipelineBusy: health.pipeline_busy
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -71,6 +73,10 @@ const useBackendStateStoreBase = create<BackendState>()((set) => ({
|
|||||||
|
|
||||||
setErrorMessage: (message: string, messageTitle: string) => {
|
setErrorMessage: (message: string, messageTitle: string) => {
|
||||||
set({ health: false, message, messageTitle })
|
set({ health: false, message, messageTitle })
|
||||||
|
},
|
||||||
|
|
||||||
|
setPipelineBusy: (busy: boolean) => {
|
||||||
|
set({ pipelineBusy: busy })
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user