mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-09 17:40:09 +00:00
* basic about page * Remove frontend version from setting dialog header * Style about page * basic system stats * Basic styling * Reword * Format memory amount
219 lines
5.5 KiB
Vue
219 lines
5.5 KiB
Vue
<template>
|
|
<div class="settings-container">
|
|
<div class="settings-sidebar">
|
|
<SearchBox
|
|
class="settings-search-box"
|
|
v-model:modelValue="searchQuery"
|
|
@search="handleSearch"
|
|
:placeholder="$t('searchSettings') + '...'"
|
|
/>
|
|
<Listbox
|
|
v-model="activeCategory"
|
|
:options="categories"
|
|
optionLabel="label"
|
|
scrollHeight="100%"
|
|
:disabled="inSearch"
|
|
:pt="{ root: { class: 'border-none' } }"
|
|
/>
|
|
</div>
|
|
<Divider layout="vertical" />
|
|
<div class="settings-content">
|
|
<Tabs :value="tabValue">
|
|
<TabPanels class="settings-tab-panels">
|
|
<TabPanel key="search-results" value="Search Results">
|
|
<div v-if="searchResults.length > 0">
|
|
<SettingGroup
|
|
v-for="(group, i) in searchResults"
|
|
:key="group.label"
|
|
:divider="i !== 0"
|
|
:group="group"
|
|
/>
|
|
</div>
|
|
<NoResultsPlaceholder
|
|
v-else
|
|
icon="pi pi-search"
|
|
:title="$t('noResultsFound')"
|
|
:message="$t('searchFailedMessage')"
|
|
/>
|
|
</TabPanel>
|
|
<TabPanel
|
|
v-for="category in categories"
|
|
:key="category.key"
|
|
:value="category.label"
|
|
>
|
|
<SettingGroup
|
|
v-for="(group, i) in sortedGroups(category)"
|
|
:key="group.label"
|
|
:divider="i !== 0"
|
|
:group="{
|
|
label: group.label,
|
|
settings: flattenTree<SettingParams>(group)
|
|
}"
|
|
/>
|
|
</TabPanel>
|
|
<TabPanel key="about" value="About">
|
|
<AboutPanel />
|
|
</TabPanel>
|
|
</TabPanels>
|
|
</Tabs>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed, onMounted, watch } from 'vue'
|
|
import Listbox from 'primevue/listbox'
|
|
import Tabs from 'primevue/tabs'
|
|
import TabPanels from 'primevue/tabpanels'
|
|
import TabPanel from 'primevue/tabpanel'
|
|
import Divider from 'primevue/divider'
|
|
import { SettingTreeNode, useSettingStore } from '@/stores/settingStore'
|
|
import { SettingParams } from '@/types/settingTypes'
|
|
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 || []),
|
|
aboutPanelNode
|
|
])
|
|
const activeCategory = ref<SettingTreeNode | null>(null)
|
|
const searchResults = ref<ISettingGroup[]>([])
|
|
|
|
watch(activeCategory, (newCategory, oldCategory) => {
|
|
if (newCategory === null) {
|
|
activeCategory.value = oldCategory
|
|
}
|
|
})
|
|
|
|
onMounted(() => {
|
|
activeCategory.value = categories.value[0]
|
|
})
|
|
|
|
const sortedGroups = (category: SettingTreeNode) => {
|
|
return [...(category.children || [])].sort((a, b) =>
|
|
a.label.localeCompare(b.label)
|
|
)
|
|
}
|
|
|
|
const searchQuery = ref<string>('')
|
|
const searchInProgress = ref<boolean>(false)
|
|
watch(searchQuery, () => (searchInProgress.value = true))
|
|
|
|
const handleSearch = (query: string) => {
|
|
if (!query) {
|
|
searchResults.value = []
|
|
return
|
|
}
|
|
|
|
const allSettings = flattenTree<SettingParams>(settingRoot.value)
|
|
const filteredSettings = allSettings.filter(
|
|
(setting) =>
|
|
setting.id.toLowerCase().includes(query.toLowerCase()) ||
|
|
setting.name.toLowerCase().includes(query.toLowerCase())
|
|
)
|
|
|
|
const groupedSettings: { [key: string]: SettingParams[] } = {}
|
|
filteredSettings.forEach((setting) => {
|
|
const groupLabel = setting.id.split('.')[1]
|
|
if (!groupedSettings[groupLabel]) {
|
|
groupedSettings[groupLabel] = []
|
|
}
|
|
groupedSettings[groupLabel].push(setting)
|
|
})
|
|
|
|
searchResults.value = Object.entries(groupedSettings).map(
|
|
([label, settings]) => ({
|
|
label,
|
|
settings
|
|
})
|
|
)
|
|
searchInProgress.value = false
|
|
}
|
|
|
|
const inSearch = computed(
|
|
() => searchQuery.value.length > 0 && !searchInProgress.value
|
|
)
|
|
const tabValue = computed(() =>
|
|
inSearch.value ? 'Search Results' : activeCategory.value?.label
|
|
)
|
|
</script>
|
|
|
|
<style>
|
|
.settings-tab-panels {
|
|
padding-top: 0px !important;
|
|
}
|
|
</style>
|
|
|
|
<style scoped>
|
|
.settings-container {
|
|
display: flex;
|
|
height: 70vh;
|
|
width: 60vw;
|
|
max-width: 1000px;
|
|
overflow: hidden;
|
|
/* Prevents container from scrolling */
|
|
}
|
|
|
|
.settings-sidebar {
|
|
width: 250px;
|
|
flex-shrink: 0;
|
|
/* Prevents sidebar from shrinking */
|
|
overflow-y: auto;
|
|
padding: 10px;
|
|
}
|
|
|
|
.settings-search-box {
|
|
width: 100%;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.settings-content {
|
|
flex-grow: 1;
|
|
overflow-y: auto;
|
|
/* Allows vertical scrolling */
|
|
}
|
|
|
|
/* Ensure the Listbox takes full width of the sidebar */
|
|
.settings-sidebar :deep(.p-listbox) {
|
|
width: 100%;
|
|
}
|
|
|
|
/* Optional: Style scrollbars for webkit browsers */
|
|
.settings-sidebar::-webkit-scrollbar,
|
|
.settings-content::-webkit-scrollbar {
|
|
width: 1px;
|
|
}
|
|
|
|
.settings-sidebar::-webkit-scrollbar-thumb,
|
|
.settings-content::-webkit-scrollbar-thumb {
|
|
background-color: transparent;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.settings-container {
|
|
flex-direction: column;
|
|
height: auto;
|
|
}
|
|
|
|
.settings-sidebar {
|
|
width: 100%;
|
|
}
|
|
}
|
|
</style>
|