mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-04 23:20:07 +00:00
Add about panel in settings dialog (#799)
* basic about page * Remove frontend version from setting dialog header * Style about page * basic system stats * Basic styling * Reword * Format memory amount
This commit is contained in:
40
src/components/common/DeviceInfo.vue
Normal file
40
src/components/common/DeviceInfo.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<template v-for="col in deviceColumns" :key="col.field">
|
||||
<div class="font-medium">{{ $t(col.header) }}</div>
|
||||
<div>{{ formatValue(props.device[col.field], col.field) }}</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { DeviceStats } from '@/types/apiTypes'
|
||||
|
||||
const props = defineProps<{
|
||||
device: DeviceStats
|
||||
}>()
|
||||
|
||||
const deviceColumns = [
|
||||
{ field: 'name', header: 'Name' },
|
||||
{ field: 'type', header: 'Type' },
|
||||
{ field: 'vram_total', header: 'VRAM Total' },
|
||||
{ field: 'vram_free', header: 'VRAM Free' },
|
||||
{ field: 'torch_vram_total', header: 'Torch VRAM Total' },
|
||||
{ field: 'torch_vram_free', header: 'Torch VRAM Free' }
|
||||
]
|
||||
|
||||
const formatValue = (value: any, field: string) => {
|
||||
if (
|
||||
['vram_total', 'vram_free', 'torch_vram_total', 'torch_vram_free'].includes(
|
||||
field
|
||||
)
|
||||
) {
|
||||
const mb = Math.round(value / (1024 * 1024))
|
||||
if (mb >= 1024) {
|
||||
return `${(mb / 1024).toFixed(2)} GB`
|
||||
}
|
||||
return `${mb} MB`
|
||||
}
|
||||
return value
|
||||
}
|
||||
</script>
|
||||
55
src/components/common/SystemStatsPanel.vue
Normal file
55
src/components/common/SystemStatsPanel.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div class="system-stats">
|
||||
<div class="mb-6">
|
||||
<h2 class="text-2xl font-semibold mb-4">{{ $t('systemInfo') }}</h2>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<template v-for="col in systemColumns" :key="col.field">
|
||||
<div class="font-medium">{{ $t(col.header) }}</div>
|
||||
<div>{{ systemInfo[col.field] }}</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<div>
|
||||
<h2 class="text-2xl font-semibold mb-4">{{ $t('devices') }}</h2>
|
||||
<TabView v-if="props.stats.devices.length > 1">
|
||||
<TabPanel
|
||||
v-for="device in props.stats.devices"
|
||||
:key="device.index"
|
||||
:header="device.name"
|
||||
>
|
||||
<DeviceInfo :device="device" />
|
||||
</TabPanel>
|
||||
</TabView>
|
||||
<DeviceInfo v-else :device="props.stats.devices[0]" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import TabView from 'primevue/tabview'
|
||||
import TabPanel from 'primevue/tabpanel'
|
||||
import Divider from 'primevue/divider'
|
||||
import type { SystemStats } from '@/types/apiTypes'
|
||||
import DeviceInfo from '@/components/common/DeviceInfo.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
stats: SystemStats
|
||||
}>()
|
||||
|
||||
const systemInfo = computed(() => ({
|
||||
...props.stats.system,
|
||||
argv: props.stats.system.argv.join(' ')
|
||||
}))
|
||||
|
||||
const systemColumns = [
|
||||
{ field: 'os', header: 'OS' },
|
||||
{ field: 'python_version', header: 'Python Version' },
|
||||
{ field: 'embedded_python', header: 'Embedded Python' },
|
||||
{ field: 'pytorch_version', header: 'Pytorch Version' },
|
||||
{ field: 'argv', header: 'Arguments' }
|
||||
]
|
||||
</script>
|
||||
@@ -51,6 +51,9 @@
|
||||
}"
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel key="about" value="About">
|
||||
<AboutPanel />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</div>
|
||||
@@ -70,17 +73,25 @@ import SettingGroup from './setting/SettingGroup.vue'
|
||||
import SearchBox from '@/components/common/SearchBox.vue'
|
||||
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
||||
import { flattenTree } from '@/utils/treeUtil'
|
||||
import AboutPanel from './setting/AboutPanel.vue'
|
||||
|
||||
interface ISettingGroup {
|
||||
label: string
|
||||
settings: SettingParams[]
|
||||
}
|
||||
|
||||
const aboutPanelNode: SettingTreeNode = {
|
||||
key: 'about',
|
||||
label: 'About',
|
||||
children: []
|
||||
}
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
const settingRoot = computed<SettingTreeNode>(() => settingStore.settingTree)
|
||||
const categories = computed<SettingTreeNode[]>(
|
||||
() => settingRoot.value.children || []
|
||||
)
|
||||
const categories = computed<SettingTreeNode[]>(() => [
|
||||
...(settingRoot.value.children || []),
|
||||
aboutPanelNode
|
||||
])
|
||||
const activeCategory = ref<SettingTreeNode | null>(null)
|
||||
const searchResults = ref<ISettingGroup[]>([])
|
||||
|
||||
|
||||
68
src/components/dialog/content/setting/AboutPanel.vue
Normal file
68
src/components/dialog/content/setting/AboutPanel.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold mb-2">{{ $t('about') }}</h2>
|
||||
<div class="space-y-2">
|
||||
<a
|
||||
v-for="link in links"
|
||||
:key="link.url"
|
||||
:href="link.url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center no-underline"
|
||||
>
|
||||
<Tag class="mr-2">
|
||||
<template #icon>
|
||||
<i :class="[link.icon, 'mr-2 text-xl']"></i>
|
||||
</template>
|
||||
{{ link.label }}
|
||||
</Tag>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<SystemStatsPanel
|
||||
v-if="systemStatsStore.systemStats"
|
||||
:stats="systemStatsStore.systemStats"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSystemStatsStore } from '@/stores/systemStatsStore'
|
||||
import Tag from 'primevue/tag'
|
||||
import Divider from 'primevue/divider'
|
||||
import { computed, onMounted } from 'vue'
|
||||
import SystemStatsPanel from '@/components/common/SystemStatsPanel.vue'
|
||||
|
||||
const systemStatsStore = useSystemStatsStore()
|
||||
const frontendVersion = window['__COMFYUI_FRONTEND_VERSION__']
|
||||
const coreVersion = computed(
|
||||
() => systemStatsStore.systemStats?.system?.comfyui_version ?? ''
|
||||
)
|
||||
|
||||
const links = computed(() => [
|
||||
{
|
||||
label: `ComfyUI ${coreVersion.value}`,
|
||||
url: 'https://github.com/comfyanonymous/ComfyUI',
|
||||
icon: 'pi pi-github'
|
||||
},
|
||||
{
|
||||
label: `ComfyUI_frontend v${frontendVersion}`,
|
||||
url: 'https://github.com/Comfy-Org/ComfyUI_frontend',
|
||||
icon: 'pi pi-github'
|
||||
},
|
||||
{
|
||||
label: 'Discord',
|
||||
url: 'https://www.comfy.org/discord',
|
||||
icon: 'pi pi-discord'
|
||||
},
|
||||
{ label: 'ComfyOrg', url: 'https://www.comfy.org/', icon: 'pi pi-globe' }
|
||||
])
|
||||
|
||||
onMounted(async () => {
|
||||
if (!systemStatsStore.systemStats) {
|
||||
await systemStatsStore.fetchSystemStats()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -3,16 +3,10 @@
|
||||
<h2>
|
||||
<i class="pi pi-cog"></i>
|
||||
<span>{{ $t('settings') }}</span>
|
||||
<Tag :value="frontendVersion" severity="secondary" class="version-tag" />
|
||||
</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Tag from 'primevue/tag'
|
||||
const frontendVersion = 'v' + window['__COMFYUI_FRONTEND_VERSION__']
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pi-cog {
|
||||
font-size: 1.25rem;
|
||||
|
||||
@@ -2,6 +2,9 @@ import { createI18n } from 'vue-i18n'
|
||||
|
||||
const messages = {
|
||||
en: {
|
||||
systemInfo: 'System Info',
|
||||
devices: 'Devices',
|
||||
about: 'About',
|
||||
add: 'Add',
|
||||
confirm: 'Confirm',
|
||||
reset: 'Reset',
|
||||
|
||||
34
src/stores/systemStatsStore.ts
Normal file
34
src/stores/systemStatsStore.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { api } from '@/scripts/api'
|
||||
import type { SystemStats } from '@/types/apiTypes'
|
||||
|
||||
export const useSystemStatsStore = defineStore('systemStats', () => {
|
||||
const systemStats = ref<SystemStats | null>(null)
|
||||
const isLoading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
async function fetchSystemStats() {
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
systemStats.value = await api.getSystemStats()
|
||||
} catch (err) {
|
||||
error.value =
|
||||
err instanceof Error
|
||||
? err.message
|
||||
: 'An error occurred while fetching system stats'
|
||||
console.error('Error fetching system stats:', err)
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
systemStats,
|
||||
isLoading,
|
||||
error,
|
||||
fetchSystemStats
|
||||
}
|
||||
})
|
||||
@@ -387,6 +387,17 @@ const zPromptResponse = z.object({
|
||||
})
|
||||
.optional()
|
||||
})
|
||||
|
||||
const zDeviceStats = z.object({
|
||||
name: z.string(),
|
||||
type: z.string(),
|
||||
index: z.number(),
|
||||
vram_total: z.number(),
|
||||
vram_free: z.number(),
|
||||
torch_vram_total: z.number(),
|
||||
torch_vram_free: z.number()
|
||||
})
|
||||
|
||||
export const zSystemStats = z.object({
|
||||
system: z.object({
|
||||
os: z.string(),
|
||||
@@ -396,17 +407,7 @@ export const zSystemStats = z.object({
|
||||
pytorch_version: z.string(),
|
||||
argv: z.array(z.string())
|
||||
}),
|
||||
devices: z.array(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
type: z.string(),
|
||||
index: z.number().optional(),
|
||||
vram_total: z.number(),
|
||||
vram_free: z.number(),
|
||||
torch_vram_total: z.number(),
|
||||
torch_vram_free: z.number()
|
||||
})
|
||||
)
|
||||
devices: z.array(zDeviceStats)
|
||||
})
|
||||
const zUser = z.object({
|
||||
storage: z.enum(['server', 'browser']),
|
||||
@@ -500,6 +501,7 @@ export type EmbeddingsResponse = z.infer<typeof zEmbeddingsResponse>
|
||||
export type ExtensionsResponse = z.infer<typeof zExtensionsResponse>
|
||||
export type PromptResponse = z.infer<typeof zPromptResponse>
|
||||
export type Settings = z.infer<typeof zSettings>
|
||||
export type DeviceStats = z.infer<typeof zDeviceStats>
|
||||
export type SystemStats = z.infer<typeof zSystemStats>
|
||||
export type User = z.infer<typeof zUser>
|
||||
export type UserData = z.infer<typeof zUserData>
|
||||
|
||||
Reference in New Issue
Block a user