mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-10 18:10:08 +00:00
Add frontend extension management panel (#1141)
* Manage register of extension in pinia * Add disabled extensions setting * nit * Disable extension * Add virtual divider * Basic extension panel * Style cell * nit * Fix loading * inactive rules * nit * Calculate changes * nit * Experimental setting guard
This commit is contained in:
@@ -57,6 +57,9 @@
|
||||
<TabPanel key="keybinding" value="Keybinding">
|
||||
<KeybindingPanel />
|
||||
</TabPanel>
|
||||
<TabPanel key="extension" value="Extension">
|
||||
<ExtensionPanel />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</div>
|
||||
@@ -78,6 +81,7 @@ import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
||||
import { flattenTree } from '@/utils/treeUtil'
|
||||
import AboutPanel from './setting/AboutPanel.vue'
|
||||
import KeybindingPanel from './setting/KeybindingPanel.vue'
|
||||
import ExtensionPanel from './setting/ExtensionPanel.vue'
|
||||
|
||||
interface ISettingGroup {
|
||||
label: string
|
||||
@@ -96,11 +100,24 @@ const keybindingPanelNode: SettingTreeNode = {
|
||||
children: []
|
||||
}
|
||||
|
||||
const extensionPanelNode: SettingTreeNode = {
|
||||
key: 'extension',
|
||||
label: 'Extension',
|
||||
children: []
|
||||
}
|
||||
|
||||
const extensionPanelNodeList = computed<SettingTreeNode[]>(() => {
|
||||
const settingStore = useSettingStore()
|
||||
const showExtensionPanel = settingStore.get('Comfy.Settings.ExtensionPanel')
|
||||
return showExtensionPanel ? [extensionPanelNode] : []
|
||||
})
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
const settingRoot = computed<SettingTreeNode>(() => settingStore.settingTree)
|
||||
const categories = computed<SettingTreeNode[]>(() => [
|
||||
...(settingRoot.value.children || []),
|
||||
keybindingPanelNode,
|
||||
...extensionPanelNodeList.value,
|
||||
aboutPanelNode
|
||||
])
|
||||
const activeCategory = ref<SettingTreeNode | null>(null)
|
||||
@@ -226,4 +243,15 @@ const tabValue = computed(() =>
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Show a separator line above the Keybinding tab */
|
||||
/* This indicates the start of custom setting panels */
|
||||
.settings-sidebar :deep(.p-listbox-option[aria-label='Keybinding']) {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.settings-sidebar :deep(.p-listbox-option[aria-label='Keybinding'])::before {
|
||||
@apply content-[''] top-0 left-0 absolute w-full;
|
||||
border-top: 1px solid var(--p-divider-border-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
93
src/components/dialog/content/setting/ExtensionPanel.vue
Normal file
93
src/components/dialog/content/setting/ExtensionPanel.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div class="extension-panel">
|
||||
<DataTable :value="extensionStore.extensions" stripedRows size="small">
|
||||
<Column field="name" :header="$t('extensionName')" sortable></Column>
|
||||
<Column
|
||||
:pt="{
|
||||
bodyCell: 'flex items-center justify-end'
|
||||
}"
|
||||
>
|
||||
<template #body="slotProps">
|
||||
<ToggleSwitch
|
||||
v-model="editingEnabledExtensions[slotProps.data.name]"
|
||||
@change="updateExtensionStatus"
|
||||
/>
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
<div class="mt-4">
|
||||
<Message v-if="hasChanges" severity="info">
|
||||
<ul>
|
||||
<li v-for="ext in changedExtensions" :key="ext.name">
|
||||
<span>
|
||||
{{ extensionStore.isExtensionEnabled(ext.name) ? '[-]' : '[+]' }}
|
||||
</span>
|
||||
{{ ext.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</Message>
|
||||
<Button
|
||||
:label="$t('reloadToApplyChanges')"
|
||||
icon="pi pi-refresh"
|
||||
@click="applyChanges"
|
||||
:disabled="!hasChanges"
|
||||
text
|
||||
fluid
|
||||
severity="danger"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useExtensionStore } from '@/stores/extensionStore'
|
||||
import { useSettingStore } from '@/stores/settingStore'
|
||||
import DataTable from 'primevue/datatable'
|
||||
import Column from 'primevue/column'
|
||||
import ToggleSwitch from 'primevue/toggleswitch'
|
||||
import Button from 'primevue/button'
|
||||
import Message from 'primevue/message'
|
||||
|
||||
const extensionStore = useExtensionStore()
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
const editingEnabledExtensions = ref<Record<string, boolean>>({})
|
||||
|
||||
onMounted(() => {
|
||||
extensionStore.extensions.forEach((ext) => {
|
||||
editingEnabledExtensions.value[ext.name] =
|
||||
extensionStore.isExtensionEnabled(ext.name)
|
||||
})
|
||||
})
|
||||
|
||||
const changedExtensions = computed(() => {
|
||||
return extensionStore.extensions.filter(
|
||||
(ext) =>
|
||||
editingEnabledExtensions.value[ext.name] !==
|
||||
extensionStore.isExtensionEnabled(ext.name)
|
||||
)
|
||||
})
|
||||
|
||||
const hasChanges = computed(() => {
|
||||
return changedExtensions.value.length > 0
|
||||
})
|
||||
|
||||
const updateExtensionStatus = () => {
|
||||
const editingDisabledExtensionNames = Object.entries(
|
||||
editingEnabledExtensions.value
|
||||
)
|
||||
.filter(([_, enabled]) => !enabled)
|
||||
.map(([name]) => name)
|
||||
|
||||
settingStore.set('Comfy.Extension.Disabled', [
|
||||
...extensionStore.inactiveDisabledExtensionNames,
|
||||
...editingDisabledExtensionNames
|
||||
])
|
||||
}
|
||||
|
||||
const applyChanges = () => {
|
||||
// Refresh the page to apply changes
|
||||
window.location.reload()
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user