mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-28 18:22:40 +00:00
Workspaces 3 create a workspace (#8221)
## Summary Add workspace creation and management (create, edit, delete, leave, switch workspaces). Follow-up PR will add invite and membership flow. ## Changes - Workspace CRUD dialogs - Workspace switcher popover in topbar - Workspace settings panel - Subscription panel for workspace context ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8221-Workspaces-3-create-a-workspace-2ef6d73d36508155975ffa6e315971ec) by [Unito](https://www.unito.io) --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,19 +3,21 @@ import type { Component } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { useCurrentUser } from '@/composables/auth/useCurrentUser'
|
||||
import { useFeatureFlags } from '@/composables/useFeatureFlags'
|
||||
import { useVueFeatureFlags } from '@/composables/useVueFeatureFlags'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import type { SettingTreeNode } from '@/platform/settings/settingStore'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import type { SettingParams } from '@/platform/settings/types'
|
||||
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
||||
import { isElectron } from '@/utils/envUtil'
|
||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
||||
import { buildTree } from '@/utils/treeUtil'
|
||||
import { useVueFeatureFlags } from '@/composables/useVueFeatureFlags'
|
||||
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
||||
|
||||
interface SettingPanelItem {
|
||||
node: SettingTreeNode
|
||||
component: Component
|
||||
props?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export function useSettingUI(
|
||||
@@ -27,15 +29,21 @@ export function useSettingUI(
|
||||
| 'user'
|
||||
| 'credits'
|
||||
| 'subscription'
|
||||
| 'workspace'
|
||||
) {
|
||||
const { t } = useI18n()
|
||||
const { isLoggedIn } = useCurrentUser()
|
||||
const settingStore = useSettingStore()
|
||||
const activeCategory = ref<SettingTreeNode | null>(null)
|
||||
|
||||
const { flags } = useFeatureFlags()
|
||||
const { shouldRenderVueNodes } = useVueFeatureFlags()
|
||||
const { isActiveSubscription } = useSubscription()
|
||||
|
||||
const teamWorkspacesEnabled = computed(
|
||||
() => isCloud && flags.teamWorkspacesEnabled
|
||||
)
|
||||
|
||||
const settingRoot = computed<SettingTreeNode>(() => {
|
||||
const root = buildTree(
|
||||
Object.values(settingStore.settingsById).filter(
|
||||
@@ -64,6 +72,33 @@ export function useSettingUI(
|
||||
() => settingRoot.value.children ?? []
|
||||
)
|
||||
|
||||
// Core setting categories (built-in to ComfyUI) in display order
|
||||
// 'Other' includes floating settings that don't have a specific category
|
||||
const CORE_CATEGORIES_ORDER = [
|
||||
'Comfy',
|
||||
'LiteGraph',
|
||||
'Appearance',
|
||||
'3D',
|
||||
'Mask Editor',
|
||||
'Other'
|
||||
]
|
||||
const CORE_CATEGORIES = new Set(CORE_CATEGORIES_ORDER)
|
||||
|
||||
const coreSettingCategories = computed<SettingTreeNode[]>(() => {
|
||||
const categories = settingCategories.value.filter((node) =>
|
||||
CORE_CATEGORIES.has(node.label)
|
||||
)
|
||||
return categories.sort(
|
||||
(a, b) =>
|
||||
CORE_CATEGORIES_ORDER.indexOf(a.label) -
|
||||
CORE_CATEGORIES_ORDER.indexOf(b.label)
|
||||
)
|
||||
})
|
||||
|
||||
const customNodeSettingCategories = computed<SettingTreeNode[]>(() =>
|
||||
settingCategories.value.filter((node) => !CORE_CATEGORIES.has(node.label))
|
||||
)
|
||||
|
||||
// Define panel items
|
||||
const aboutPanel: SettingPanelItem = {
|
||||
node: {
|
||||
@@ -118,6 +153,22 @@ export function useSettingUI(
|
||||
)
|
||||
}
|
||||
|
||||
// Workspace panel: only available on cloud with team workspaces enabled
|
||||
const workspacePanel: SettingPanelItem = {
|
||||
node: {
|
||||
key: 'workspace',
|
||||
label: 'Workspace',
|
||||
children: []
|
||||
},
|
||||
component: defineAsyncComponent(
|
||||
() => import('@/components/dialog/content/setting/WorkspacePanel.vue')
|
||||
)
|
||||
}
|
||||
|
||||
const shouldShowWorkspacePanel = computed(
|
||||
() => teamWorkspacesEnabled.value && isLoggedIn.value
|
||||
)
|
||||
|
||||
const keybindingPanel: SettingPanelItem = {
|
||||
node: {
|
||||
key: 'keybinding',
|
||||
@@ -156,13 +207,14 @@ export function useSettingUI(
|
||||
aboutPanel,
|
||||
creditsPanel,
|
||||
userPanel,
|
||||
...(shouldShowWorkspacePanel.value ? [workspacePanel] : []),
|
||||
keybindingPanel,
|
||||
extensionPanel,
|
||||
...(isElectron() ? [serverConfigPanel] : []),
|
||||
...(shouldShowPlanCreditsPanel.value && subscriptionPanel
|
||||
? [subscriptionPanel]
|
||||
: [])
|
||||
].filter((panel) => panel.component)
|
||||
].filter((panel) => panel !== null && panel.component)
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -186,7 +238,47 @@ export function useSettingUI(
|
||||
)
|
||||
})
|
||||
|
||||
const groupedMenuTreeNodes = computed<SettingTreeNode[]>(() => [
|
||||
// Sidebar structure when team workspaces is enabled
|
||||
const workspaceMenuTreeNodes = computed<SettingTreeNode[]>(() => [
|
||||
// Workspace settings
|
||||
translateCategory({
|
||||
key: 'workspace',
|
||||
label: 'Workspace',
|
||||
children: [
|
||||
...(shouldShowWorkspacePanel.value ? [workspacePanel.node] : []),
|
||||
...(isLoggedIn.value &&
|
||||
!(isCloud && window.__CONFIG__?.subscription_required)
|
||||
? [creditsPanel.node]
|
||||
: [])
|
||||
].map(translateCategory)
|
||||
}),
|
||||
// General settings - Profile + all core settings + special panels
|
||||
translateCategory({
|
||||
key: 'general',
|
||||
label: 'General',
|
||||
children: [
|
||||
translateCategory(userPanel.node),
|
||||
...coreSettingCategories.value.map(translateCategory),
|
||||
translateCategory(keybindingPanel.node),
|
||||
translateCategory(extensionPanel.node),
|
||||
translateCategory(aboutPanel.node),
|
||||
...(isElectron() ? [translateCategory(serverConfigPanel.node)] : [])
|
||||
]
|
||||
}),
|
||||
// Custom node settings (only shown if custom nodes have registered settings)
|
||||
...(customNodeSettingCategories.value.length > 0
|
||||
? [
|
||||
translateCategory({
|
||||
key: 'other',
|
||||
label: 'Other',
|
||||
children: customNodeSettingCategories.value.map(translateCategory)
|
||||
})
|
||||
]
|
||||
: [])
|
||||
])
|
||||
|
||||
// Sidebar structure when team workspaces is disabled (legacy)
|
||||
const legacyMenuTreeNodes = computed<SettingTreeNode[]>(() => [
|
||||
// Account settings - show different panels based on distribution and auth state
|
||||
{
|
||||
key: 'account',
|
||||
@@ -223,6 +315,12 @@ export function useSettingUI(
|
||||
}
|
||||
])
|
||||
|
||||
const groupedMenuTreeNodes = computed<SettingTreeNode[]>(() =>
|
||||
teamWorkspacesEnabled.value
|
||||
? workspaceMenuTreeNodes.value
|
||||
: legacyMenuTreeNodes.value
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
activeCategory.value = defaultCategory.value
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user