-
Notifications
You must be signed in to change notification settings - Fork 100
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
Showing
9 changed files
with
249 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { ref, computed, markRaw } from 'vue' | ||
import { defineStore } from 'pinia' | ||
import type { ComfyExtension } from '@/types/comfy' | ||
import { useKeybindingStore } from './keybindingStore' | ||
import { useCommandStore } from './commandStore' | ||
import { useSettingStore } from './settingStore' | ||
import { app } from '@/scripts/app' | ||
|
||
export const useExtensionStore = defineStore('extension', () => { | ||
// For legacy reasons, the name uniquely identifies an extension | ||
const extensionByName = ref<Record<string, ComfyExtension>>({}) | ||
const extensions = computed(() => Object.values(extensionByName.value)) | ||
// Not using computed because disable extension requires reloading of the page. | ||
// Dynamically update this list won't affect extensions that are already loaded. | ||
const disabledExtensionNames = ref<Set<string>>(new Set()) | ||
|
||
// Disabled extension names that are currently not in the extension list. | ||
// If a node pack is disabled in the backend, we shouldn't remove the configuration | ||
// of the frontend extension disable list, in case the node pack is re-enabled. | ||
const inactiveDisabledExtensionNames = computed(() => { | ||
return Array.from(disabledExtensionNames.value).filter( | ||
(name) => !(name in extensionByName.value) | ||
) | ||
}) | ||
|
||
const isExtensionEnabled = (name: string) => | ||
!disabledExtensionNames.value.has(name) | ||
const enabledExtensions = computed(() => { | ||
return extensions.value.filter((ext) => isExtensionEnabled(ext.name)) | ||
}) | ||
|
||
function registerExtension(extension: ComfyExtension) { | ||
if (!extension.name) { | ||
throw new Error("Extensions must have a 'name' property.") | ||
} | ||
|
||
if (extensionByName.value[extension.name]) { | ||
throw new Error(`Extension named '${extension.name}' already registered.`) | ||
} | ||
|
||
if (disabledExtensionNames.value.has(extension.name)) { | ||
console.log(`Extension ${extension.name} is disabled.`) | ||
} | ||
|
||
extensionByName.value[extension.name] = markRaw(extension) | ||
useKeybindingStore().loadExtensionKeybindings(extension) | ||
useCommandStore().loadExtensionCommands(extension) | ||
|
||
/* | ||
* Extensions are currently stored in both extensionStore and app.extensions. | ||
* Legacy jest tests still depend on app.extensions being populated. | ||
*/ | ||
app.extensions.push(extension) | ||
} | ||
|
||
function loadDisabledExtensionNames() { | ||
disabledExtensionNames.value = new Set( | ||
useSettingStore().get('Comfy.Extension.Disabled') | ||
) | ||
// pysssss.Locking is replaced by pin/unpin in ComfyUI core. | ||
// https://github.com/Comfy-Org/litegraph.js/pull/117 | ||
disabledExtensionNames.value.add('pysssss.Locking') | ||
} | ||
|
||
// Some core extensions are registered before the store is initialized, e.g. | ||
// colorPalette. | ||
// Register them manually here so the state of app.extensions and | ||
// extensionByName are in sync. | ||
for (const ext of app.extensions) { | ||
extensionByName.value[ext.name] = markRaw(ext) | ||
} | ||
|
||
return { | ||
extensions, | ||
enabledExtensions, | ||
inactiveDisabledExtensionNames, | ||
isExtensionEnabled, | ||
registerExtension, | ||
loadDisabledExtensionNames | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters