From a5f0d2b201bd434191daf97a44f8adaffa7c7f30 Mon Sep 17 00:00:00 2001 From: Chenlei Hu Date: Thu, 8 Aug 2024 17:52:41 -0400 Subject: [PATCH] Categorize setting items (#338) * Basic setting panel rework * refactor * Style the setting item * Reject invalid value * nit * nit * Sort settings by label * info chip as icon * nit --- src/components/dialog/GlobalDialog.vue | 6 +- .../dialog/content/SettingDialogContent.vue | 189 +++++++++--------- .../dialog/content/setting/InputSlider.vue | 55 +++-- .../dialog/content/setting/SettingGroup.vue | 143 +++++++++++++ .../dialog/header/SettingDialogHeader.vue | 24 +++ .../sidebar/SideBarSettingsToggleIcon.vue | 4 +- src/scripts/ui/dialog.ts | 1 + src/stores/dialogStore.ts | 4 + src/stores/settingStore.ts | 13 ++ src/utils/treeUtil.ts | 49 +++++ 10 files changed, 379 insertions(+), 109 deletions(-) create mode 100644 src/components/dialog/content/setting/SettingGroup.vue create mode 100644 src/components/dialog/header/SettingDialogHeader.vue create mode 100644 src/utils/treeUtil.ts diff --git a/src/components/dialog/GlobalDialog.vue b/src/components/dialog/GlobalDialog.vue index fa97c3b89..26956036d 100644 --- a/src/components/dialog/GlobalDialog.vue +++ b/src/components/dialog/GlobalDialog.vue @@ -12,7 +12,11 @@ @unmaximize="maximized = false" > - - - - - - - -
- - {{ setting.name }} - - - - -
+
+
+ +
+ +
+ + + + + + + +
+
diff --git a/src/components/dialog/content/setting/InputSlider.vue b/src/components/dialog/content/setting/InputSlider.vue index 8c287ac6a..3bdb32354 100644 --- a/src/components/dialog/content/setting/InputSlider.vue +++ b/src/components/dialog/content/setting/InputSlider.vue @@ -5,37 +5,68 @@ @update:modelValue="updateValue" class="slider-part" :class="sliderClass" - v-bind="$attrs" + :min="min" + :max="max" + :step="step" /> - @@ -44,7 +75,6 @@ const updateValue = (newValue: string | number) => { display: flex; align-items: center; gap: 1rem; - /* Adjust this value to control space between slider and input */ } .slider-part { @@ -52,7 +82,6 @@ const updateValue = (newValue: string | number) => { } .input-part { - width: 5rem; - /* Adjust this value to control input width */ + width: 5rem !important; } diff --git a/src/components/dialog/content/setting/SettingGroup.vue b/src/components/dialog/content/setting/SettingGroup.vue new file mode 100644 index 000000000..380882a99 --- /dev/null +++ b/src/components/dialog/content/setting/SettingGroup.vue @@ -0,0 +1,143 @@ + + + + + diff --git a/src/components/dialog/header/SettingDialogHeader.vue b/src/components/dialog/header/SettingDialogHeader.vue new file mode 100644 index 000000000..aaab03934 --- /dev/null +++ b/src/components/dialog/header/SettingDialogHeader.vue @@ -0,0 +1,24 @@ + + + + + diff --git a/src/components/sidebar/SideBarSettingsToggleIcon.vue b/src/components/sidebar/SideBarSettingsToggleIcon.vue index 92e5101a7..24a36a9c4 100644 --- a/src/components/sidebar/SideBarSettingsToggleIcon.vue +++ b/src/components/sidebar/SideBarSettingsToggleIcon.vue @@ -10,12 +10,12 @@ import SideBarIcon from './SideBarIcon.vue' import { useDialogStore } from '@/stores/dialogStore' import SettingDialogContent from '@/components/dialog/content/SettingDialogContent.vue' -const frontendVersion = window['__COMFYUI_FRONTEND_VERSION__'] +import SettingDialogHeader from '@/components/dialog/header/SettingDialogHeader.vue' const dialogStore = useDialogStore() const showSetting = () => { dialogStore.showDialog({ - title: `Settings (v${frontendVersion})`, + headerComponent: SettingDialogHeader, component: SettingDialogContent }) } diff --git a/src/scripts/ui/dialog.ts b/src/scripts/ui/dialog.ts index 535736e5e..c262178cf 100644 --- a/src/scripts/ui/dialog.ts +++ b/src/scripts/ui/dialog.ts @@ -1,3 +1,4 @@ +import { useDialogStore } from '@/stores/dialogStore' import { $el } from '../ui' export class ComfyDialog< diff --git a/src/stores/dialogStore.ts b/src/stores/dialogStore.ts index 00bc9834d..67ec6dab0 100644 --- a/src/stores/dialogStore.ts +++ b/src/stores/dialogStore.ts @@ -7,6 +7,7 @@ import { type Component, markRaw } from 'vue' interface DialogState { isVisible: boolean title: string + headerComponent: Component | null component: Component | null props: Record } @@ -15,6 +16,7 @@ export const useDialogStore = defineStore('dialog', { state: (): DialogState => ({ isVisible: false, title: '', + headerComponent: null, component: null, props: {} }), @@ -22,10 +24,12 @@ export const useDialogStore = defineStore('dialog', { actions: { showDialog(options: { title?: string + headerComponent?: Component component: Component props?: Record }) { this.title = options.title + this.headerComponent = markRaw(options.headerComponent) this.component = markRaw(options.component) this.props = options.props || {} this.isVisible = true diff --git a/src/stores/settingStore.ts b/src/stores/settingStore.ts index 9a24e49f6..1061a0dda 100644 --- a/src/stores/settingStore.ts +++ b/src/stores/settingStore.ts @@ -10,7 +10,13 @@ import { app } from '@/scripts/app' import { ComfySettingsDialog } from '@/scripts/ui/settings' import { SettingParams } from '@/types/settingTypes' +import { buildTree } from '@/utils/treeUtil' import { defineStore } from 'pinia' +import type { TreeNode } from 'primevue/treenode' + +export interface SettingTreeNode extends TreeNode { + data?: SettingParams +} interface State { settingValues: Record @@ -22,6 +28,13 @@ export const useSettingStore = defineStore('setting', { settingValues: {}, settings: {} }), + getters: { + settingTree(): SettingTreeNode { + return buildTree(Object.values(this.settings), (setting: SettingParams) => + setting.id.split('.') + ) + } + }, actions: { addSettings(settings: ComfySettingsDialog) { for (const id in settings.settingsLookup) { diff --git a/src/utils/treeUtil.ts b/src/utils/treeUtil.ts new file mode 100644 index 000000000..0bf54b7d3 --- /dev/null +++ b/src/utils/treeUtil.ts @@ -0,0 +1,49 @@ +import type { TreeNode } from 'primevue/treenode' + +export function buildTree( + items: T[], + key: string | ((item: T) => string[]) +): TreeNode { + const root: TreeNode = { + key: 'root', + label: 'root', + children: [] + } + + const map: Record = { + root: root + } + + for (const item of items) { + const keys = typeof key === 'string' ? item[key] : key(item) + let parent = root + for (const k of keys) { + const id = parent.key + '/' + k + if (!map[id]) { + const node: TreeNode = { + key: id, + label: k, + leaf: false, + children: [] + } + map[id] = node + parent.children.push(node) + } + parent = map[id] + } + parent.leaf = true + parent.data = item + } + return root +} + +export function flattenTree(tree: TreeNode): T[] { + const result: T[] = [] + const stack: TreeNode[] = [tree] + while (stack.length) { + const node = stack.pop()! + if (node.leaf && node.data) result.push(node.data) + stack.push(...(node.children || [])) + } + return result +}