mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-14 01:20:03 +00:00
Add inline progress text and progress bar
This commit is contained in:
@@ -60,6 +60,7 @@ function createWrapper(pinia = createTestingPinia({ createSpy: vi.fn })) {
|
|||||||
stubs: {
|
stubs: {
|
||||||
SubgraphBreadcrumb: true,
|
SubgraphBreadcrumb: true,
|
||||||
QueueProgressOverlay: true,
|
QueueProgressOverlay: true,
|
||||||
|
QueueInlineProgressSummary: true,
|
||||||
CurrentUserButton: true,
|
CurrentUserButton: true,
|
||||||
LoginButton: true,
|
LoginButton: true,
|
||||||
ContextMenu: {
|
ContextMenu: {
|
||||||
|
|||||||
@@ -1,101 +1,121 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="!workspaceStore.focusMode"
|
v-if="!workspaceStore.focusMode"
|
||||||
class="ml-1 flex gap-x-0.5 pt-1"
|
class="ml-1 flex flex-col gap-1 pt-1"
|
||||||
@mouseenter="isTopMenuHovered = true"
|
@mouseenter="isTopMenuHovered = true"
|
||||||
@mouseleave="isTopMenuHovered = false"
|
@mouseleave="isTopMenuHovered = false"
|
||||||
>
|
>
|
||||||
<div class="min-w-0 flex-1">
|
<div class="flex gap-x-0.5">
|
||||||
<SubgraphBreadcrumb />
|
<div class="min-w-0 flex-1">
|
||||||
|
<SubgraphBreadcrumb />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mx-1 flex flex-col items-end gap-1">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<div
|
||||||
|
v-if="managerState.shouldShowManagerButtons.value"
|
||||||
|
class="pointer-events-auto flex h-12 shrink-0 items-center rounded-lg border border-interface-stroke bg-comfy-menu-bg px-2 shadow-interface"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
v-tooltip.bottom="customNodesManagerTooltipConfig"
|
||||||
|
variant="secondary"
|
||||||
|
size="icon"
|
||||||
|
:aria-label="t('menu.customNodesManager')"
|
||||||
|
class="relative"
|
||||||
|
@click="openCustomNodeManager"
|
||||||
|
>
|
||||||
|
<i class="icon-[lucide--puzzle] size-4" />
|
||||||
|
<span
|
||||||
|
v-if="shouldShowRedDot"
|
||||||
|
class="absolute top-0.5 right-1 size-2 rounded-full bg-red-500"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
ref="actionbarContainerRef"
|
||||||
|
class="actionbar-container relative pointer-events-auto flex gap-2 h-12 items-center rounded-lg border border-interface-stroke bg-comfy-menu-bg px-2 shadow-interface"
|
||||||
|
>
|
||||||
|
<ActionBarButtons />
|
||||||
|
<!-- Support for legacy topbar elements attached by custom scripts, hidden if no elements present -->
|
||||||
|
<div
|
||||||
|
ref="legacyCommandsContainerRef"
|
||||||
|
class="[&:not(:has(*>*:not(:empty)))]:hidden"
|
||||||
|
></div>
|
||||||
|
<ComfyActionbar
|
||||||
|
:top-menu-container="actionbarContainerRef"
|
||||||
|
:queue-overlay-expanded="isQueueOverlayExpanded"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
v-tooltip.bottom="queueHistoryTooltipConfig"
|
||||||
|
type="destructive"
|
||||||
|
size="md"
|
||||||
|
:aria-pressed="
|
||||||
|
isQueuePanelV2Enabled
|
||||||
|
? activeSidebarTabId === 'assets'
|
||||||
|
: isQueueProgressOverlayVisible
|
||||||
|
? isQueueOverlayExpanded
|
||||||
|
: undefined
|
||||||
|
"
|
||||||
|
class="px-3"
|
||||||
|
data-testid="queue-overlay-toggle"
|
||||||
|
@click="toggleQueueOverlay"
|
||||||
|
@contextmenu.stop.prevent="showQueueContextMenu"
|
||||||
|
>
|
||||||
|
<span class="text-sm font-normal tabular-nums">
|
||||||
|
{{ activeJobsLabel }}
|
||||||
|
</span>
|
||||||
|
<span class="sr-only">
|
||||||
|
{{
|
||||||
|
isQueuePanelV2Enabled
|
||||||
|
? t('sideToolbar.queueProgressOverlay.viewJobHistory')
|
||||||
|
: t('sideToolbar.queueProgressOverlay.expandCollapsedQueue')
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
<ContextMenu
|
||||||
|
ref="queueContextMenu"
|
||||||
|
:model="queueContextMenuItems"
|
||||||
|
/>
|
||||||
|
<CurrentUserButton
|
||||||
|
v-if="isLoggedIn && !isIntegratedTabBar"
|
||||||
|
class="shrink-0"
|
||||||
|
/>
|
||||||
|
<LoginButton v-else-if="isDesktop && !isIntegratedTabBar" />
|
||||||
|
<Button
|
||||||
|
v-if="!isRightSidePanelOpen"
|
||||||
|
v-tooltip.bottom="rightSidePanelTooltipConfig"
|
||||||
|
type="secondary"
|
||||||
|
size="icon"
|
||||||
|
:aria-label="t('rightSidePanel.togglePanel')"
|
||||||
|
@click="rightSidePanelStore.togglePanel"
|
||||||
|
>
|
||||||
|
<i class="icon-[lucide--panel-right] size-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<QueueProgressOverlay
|
||||||
|
v-if="isQueueProgressOverlayVisible"
|
||||||
|
v-model:expanded="isQueueOverlayExpanded"
|
||||||
|
:menu-hovered="isTopMenuHovered"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mx-1 flex flex-col items-end gap-1">
|
<div>
|
||||||
<div class="flex items-center gap-2">
|
<QueueInlineProgressSummary
|
||||||
<div
|
v-if="
|
||||||
v-if="managerState.shouldShowManagerButtons.value"
|
isInlineProgressVisible && isActionbarEnabled && !isActionbarFloating
|
||||||
class="pointer-events-auto flex h-12 shrink-0 items-center rounded-lg border border-interface-stroke bg-comfy-menu-bg px-2 shadow-interface"
|
"
|
||||||
>
|
class="pr-1"
|
||||||
<Button
|
:hidden="isQueueOverlayExpanded"
|
||||||
v-tooltip.bottom="customNodesManagerTooltipConfig"
|
|
||||||
variant="secondary"
|
|
||||||
size="icon"
|
|
||||||
:aria-label="t('menu.customNodesManager')"
|
|
||||||
class="relative"
|
|
||||||
@click="openCustomNodeManager"
|
|
||||||
>
|
|
||||||
<i class="icon-[lucide--puzzle] size-4" />
|
|
||||||
<span
|
|
||||||
v-if="shouldShowRedDot"
|
|
||||||
class="absolute top-0.5 right-1 size-2 rounded-full bg-red-500"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="actionbar-container pointer-events-auto flex gap-2 h-12 items-center rounded-lg border border-interface-stroke bg-comfy-menu-bg px-2 shadow-interface"
|
|
||||||
>
|
|
||||||
<ActionBarButtons />
|
|
||||||
<!-- Support for legacy topbar elements attached by custom scripts, hidden if no elements present -->
|
|
||||||
<div
|
|
||||||
ref="legacyCommandsContainerRef"
|
|
||||||
class="[&:not(:has(*>*:not(:empty)))]:hidden"
|
|
||||||
></div>
|
|
||||||
<ComfyActionbar />
|
|
||||||
<Button
|
|
||||||
v-tooltip.bottom="queueHistoryTooltipConfig"
|
|
||||||
type="destructive"
|
|
||||||
size="md"
|
|
||||||
:aria-pressed="
|
|
||||||
isQueuePanelV2Enabled
|
|
||||||
? activeSidebarTabId === 'assets'
|
|
||||||
: isQueueProgressOverlayVisible
|
|
||||||
? isQueueOverlayExpanded
|
|
||||||
: undefined
|
|
||||||
"
|
|
||||||
class="px-3"
|
|
||||||
data-testid="queue-overlay-toggle"
|
|
||||||
@click="toggleQueueOverlay"
|
|
||||||
@contextmenu.stop.prevent="showQueueContextMenu"
|
|
||||||
>
|
|
||||||
<span class="text-sm font-normal tabular-nums">
|
|
||||||
{{ activeJobsLabel }}
|
|
||||||
</span>
|
|
||||||
<span class="sr-only">
|
|
||||||
{{
|
|
||||||
isQueuePanelV2Enabled
|
|
||||||
? t('sideToolbar.queueProgressOverlay.viewJobHistory')
|
|
||||||
: t('sideToolbar.queueProgressOverlay.expandCollapsedQueue')
|
|
||||||
}}
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
<ContextMenu ref="queueContextMenu" :model="queueContextMenuItems" />
|
|
||||||
<CurrentUserButton
|
|
||||||
v-if="isLoggedIn && !isIntegratedTabBar"
|
|
||||||
class="shrink-0"
|
|
||||||
/>
|
|
||||||
<LoginButton v-else-if="isDesktop && !isIntegratedTabBar" />
|
|
||||||
<Button
|
|
||||||
v-if="!isRightSidePanelOpen"
|
|
||||||
v-tooltip.bottom="rightSidePanelTooltipConfig"
|
|
||||||
type="secondary"
|
|
||||||
size="icon"
|
|
||||||
:aria-label="t('rightSidePanel.togglePanel')"
|
|
||||||
@click="rightSidePanelStore.togglePanel"
|
|
||||||
>
|
|
||||||
<i class="icon-[lucide--panel-right] size-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<QueueProgressOverlay
|
|
||||||
v-if="isQueueProgressOverlayVisible"
|
|
||||||
v-model:expanded="isQueueOverlayExpanded"
|
|
||||||
:menu-hovered="isTopMenuHovered"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useLocalStorage } from '@vueuse/core'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import ContextMenu from 'primevue/contextmenu'
|
import ContextMenu from 'primevue/contextmenu'
|
||||||
import type { MenuItem } from 'primevue/menuitem'
|
import type { MenuItem } from 'primevue/menuitem'
|
||||||
@@ -104,6 +124,7 @@ import { useI18n } from 'vue-i18n'
|
|||||||
|
|
||||||
import ComfyActionbar from '@/components/actionbar/ComfyActionbar.vue'
|
import ComfyActionbar from '@/components/actionbar/ComfyActionbar.vue'
|
||||||
import SubgraphBreadcrumb from '@/components/breadcrumb/SubgraphBreadcrumb.vue'
|
import SubgraphBreadcrumb from '@/components/breadcrumb/SubgraphBreadcrumb.vue'
|
||||||
|
import QueueInlineProgressSummary from '@/components/queue/QueueInlineProgressSummary.vue'
|
||||||
import QueueProgressOverlay from '@/components/queue/QueueProgressOverlay.vue'
|
import QueueProgressOverlay from '@/components/queue/QueueProgressOverlay.vue'
|
||||||
import ActionBarButtons from '@/components/topbar/ActionBarButtons.vue'
|
import ActionBarButtons from '@/components/topbar/ActionBarButtons.vue'
|
||||||
import CurrentUserButton from '@/components/topbar/CurrentUserButton.vue'
|
import CurrentUserButton from '@/components/topbar/CurrentUserButton.vue'
|
||||||
@@ -147,6 +168,15 @@ const { shouldShowRedDot: showReleaseRedDot } = storeToRefs(releaseStore)
|
|||||||
const { shouldShowRedDot: shouldShowConflictRedDot } =
|
const { shouldShowRedDot: shouldShowConflictRedDot } =
|
||||||
useConflictAcknowledgment()
|
useConflictAcknowledgment()
|
||||||
const isTopMenuHovered = ref(false)
|
const isTopMenuHovered = ref(false)
|
||||||
|
const actionbarContainerRef = ref<HTMLElement>()
|
||||||
|
const isActionbarDocked = useLocalStorage('Comfy.MenuPosition.Docked', true)
|
||||||
|
const actionbarPosition = computed(() => settingStore.get('Comfy.UseNewMenu'))
|
||||||
|
const isActionbarEnabled = computed(
|
||||||
|
() => actionbarPosition.value !== 'Disabled'
|
||||||
|
)
|
||||||
|
const isActionbarFloating = computed(
|
||||||
|
() => isActionbarEnabled.value && !isActionbarDocked.value
|
||||||
|
)
|
||||||
const activeJobsLabel = computed(() => {
|
const activeJobsLabel = computed(() => {
|
||||||
const count = activeJobsCount.value
|
const count = activeJobsCount.value
|
||||||
return t(
|
return t(
|
||||||
@@ -164,6 +194,7 @@ const isQueuePanelV2Enabled = computed(() =>
|
|||||||
const isQueueProgressOverlayVisible = computed(
|
const isQueueProgressOverlayVisible = computed(
|
||||||
() => !isQueuePanelV2Enabled.value
|
() => !isQueuePanelV2Enabled.value
|
||||||
)
|
)
|
||||||
|
const isInlineProgressVisible = computed(() => isQueuePanelV2Enabled.value)
|
||||||
const queueHistoryTooltipConfig = computed(() =>
|
const queueHistoryTooltipConfig = computed(() =>
|
||||||
buildTooltipConfig(t('sideToolbar.queueProgressOverlay.viewJobHistory'))
|
buildTooltipConfig(t('sideToolbar.queueProgressOverlay.viewJobHistory'))
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
content: { class: isDocked ? 'p-0' : 'p-1' }
|
content: { class: isDocked ? 'p-0' : 'p-1' }
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div ref="panelRef" class="flex items-center select-none gap-2">
|
<div ref="panelRef" class="relative flex items-center select-none gap-2">
|
||||||
<span
|
<span
|
||||||
ref="dragHandleRef"
|
ref="dragHandleRef"
|
||||||
:class="
|
:class="
|
||||||
@@ -43,6 +43,13 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
|
<Teleport v-if="inlineProgressTarget" :to="inlineProgressTarget">
|
||||||
|
<QueueInlineProgress
|
||||||
|
:hidden="queueOverlayExpanded"
|
||||||
|
data-testid="queue-inline-progress"
|
||||||
|
/>
|
||||||
|
</Teleport>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -59,6 +66,7 @@ import Panel from 'primevue/panel'
|
|||||||
import { computed, nextTick, ref, watch } from 'vue'
|
import { computed, nextTick, ref, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
import QueueInlineProgress from '@/components/queue/QueueInlineProgress.vue'
|
||||||
import Button from '@/components/ui/button/Button.vue'
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
||||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||||
@@ -69,6 +77,11 @@ import { cn } from '@/utils/tailwindUtil'
|
|||||||
|
|
||||||
import ComfyRunButton from './ComfyRunButton'
|
import ComfyRunButton from './ComfyRunButton'
|
||||||
|
|
||||||
|
const { topMenuContainer, queueOverlayExpanded = false } = defineProps<{
|
||||||
|
topMenuContainer?: HTMLElement | null
|
||||||
|
queueOverlayExpanded?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
const settingsStore = useSettingStore()
|
const settingsStore = useSettingStore()
|
||||||
const commandStore = useCommandStore()
|
const commandStore = useCommandStore()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@@ -76,6 +89,9 @@ const { isIdle: isExecutionIdle } = storeToRefs(useExecutionStore())
|
|||||||
|
|
||||||
const position = computed(() => settingsStore.get('Comfy.UseNewMenu'))
|
const position = computed(() => settingsStore.get('Comfy.UseNewMenu'))
|
||||||
const visible = computed(() => position.value !== 'Disabled')
|
const visible = computed(() => position.value !== 'Disabled')
|
||||||
|
const isQueuePanelV2Enabled = computed(() =>
|
||||||
|
settingsStore.get('Comfy.Queue.QPOV2')
|
||||||
|
)
|
||||||
|
|
||||||
const panelRef = ref<HTMLElement | null>(null)
|
const panelRef = ref<HTMLElement | null>(null)
|
||||||
const dragHandleRef = ref<HTMLElement | null>(null)
|
const dragHandleRef = ref<HTMLElement | null>(null)
|
||||||
@@ -252,6 +268,12 @@ const onMouseLeaveDropZone = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const inlineProgressTarget = computed(() => {
|
||||||
|
if (!visible.value || !isQueuePanelV2Enabled.value) return null
|
||||||
|
if (isDocked.value) return topMenuContainer ?? null
|
||||||
|
return panelRef.value
|
||||||
|
})
|
||||||
|
|
||||||
// Handle drag state changes
|
// Handle drag state changes
|
||||||
watch(isDragging, (dragging) => {
|
watch(isDragging, (dragging) => {
|
||||||
if (dragging) {
|
if (dragging) {
|
||||||
|
|||||||
33
src/components/queue/QueueInlineProgress.vue
Normal file
33
src/components/queue/QueueInlineProgress.vue
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="shouldShow"
|
||||||
|
aria-hidden="true"
|
||||||
|
class="pointer-events-none absolute inset-0 overflow-hidden rounded-[7px]"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="pointer-events-none absolute bottom-0 left-0 h-[3px] bg-interface-panel-job-progress-primary transition-[width]"
|
||||||
|
:style="{ width: `${totalPercent}%` }"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="pointer-events-none absolute bottom-0 left-0 h-[3px] bg-interface-panel-job-progress-secondary transition-[width]"
|
||||||
|
:style="{ width: `${currentNodePercent}%` }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
import { useQueueProgress } from '@/composables/queue/useQueueProgress'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
hidden?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { totalPercent, currentNodePercent } = useQueueProgress()
|
||||||
|
|
||||||
|
const shouldShow = computed(
|
||||||
|
() =>
|
||||||
|
!props.hidden && (totalPercent.value > 0 || currentNodePercent.value > 0)
|
||||||
|
)
|
||||||
|
</script>
|
||||||
72
src/components/queue/QueueInlineProgressSummary.vue
Normal file
72
src/components/queue/QueueInlineProgressSummary.vue
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="shouldShow" class="flex justify-end">
|
||||||
|
<div
|
||||||
|
class="flex items-center gap-4 whitespace-nowrap text-[0.75rem] leading-[normal] drop-shadow-[1px_1px_8px_rgba(0,0,0,0.4)]"
|
||||||
|
role="status"
|
||||||
|
aria-live="polite"
|
||||||
|
aria-atomic="true"
|
||||||
|
>
|
||||||
|
<div class="flex items-center gap-1 text-base-foreground">
|
||||||
|
<span class="font-normal">
|
||||||
|
{{ t('sideToolbar.queueProgressOverlay.inlineTotalLabel') }}:
|
||||||
|
</span>
|
||||||
|
<span class="w-[5ch] shrink-0 text-right font-bold tabular-nums">
|
||||||
|
{{ totalPercentFormatted }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-1 text-muted-foreground">
|
||||||
|
<span
|
||||||
|
class="w-[16ch] shrink-0 truncate text-right"
|
||||||
|
:title="currentNodeName"
|
||||||
|
>
|
||||||
|
{{ currentNodeName }}:
|
||||||
|
</span>
|
||||||
|
<span class="w-[5ch] shrink-0 text-right tabular-nums">
|
||||||
|
{{ currentNodePercentFormatted }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
import { st } from '@/i18n'
|
||||||
|
import { useQueueProgress } from '@/composables/queue/useQueueProgress'
|
||||||
|
import { useExecutionStore } from '@/stores/executionStore'
|
||||||
|
import { normalizeI18nKey } from '@/utils/formatUtil'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
hidden?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
const executionStore = useExecutionStore()
|
||||||
|
const {
|
||||||
|
totalPercent,
|
||||||
|
totalPercentFormatted,
|
||||||
|
currentNodePercent,
|
||||||
|
currentNodePercentFormatted
|
||||||
|
} = useQueueProgress()
|
||||||
|
|
||||||
|
const currentNodeName = computed(() => {
|
||||||
|
const node = executionStore.executingNode
|
||||||
|
if (!node) return t('g.emDash')
|
||||||
|
const title = (node.title ?? '').toString().trim()
|
||||||
|
if (title) return title
|
||||||
|
const nodeType = (node.type ?? '').toString().trim() || t('g.untitled')
|
||||||
|
const key = `nodeDefs.${normalizeI18nKey(nodeType)}.display_name`
|
||||||
|
return st(key, nodeType)
|
||||||
|
})
|
||||||
|
|
||||||
|
const shouldShow = computed(
|
||||||
|
() =>
|
||||||
|
!props.hidden &&
|
||||||
|
(!executionStore.isIdle ||
|
||||||
|
totalPercent.value > 0 ||
|
||||||
|
currentNodePercent.value > 0)
|
||||||
|
)
|
||||||
|
</script>
|
||||||
@@ -742,6 +742,7 @@
|
|||||||
"title": "Queue Progress",
|
"title": "Queue Progress",
|
||||||
"total": "Total: {percent}",
|
"total": "Total: {percent}",
|
||||||
"colonPercent": ": {percent}",
|
"colonPercent": ": {percent}",
|
||||||
|
"inlineTotalLabel": "Total",
|
||||||
"currentNode": "Current node:",
|
"currentNode": "Current node:",
|
||||||
"viewAllJobs": "View all jobs",
|
"viewAllJobs": "View all jobs",
|
||||||
"viewList": "List view",
|
"viewList": "List view",
|
||||||
|
|||||||
Reference in New Issue
Block a user